diff --git a/.gitignore b/.gitignore index 7e621d26..d22760e7 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,7 @@ .rbx/ db/*.sqlite3 db/*.sqlite3-journal -log/*.log* +log/*.log tmp/ .sass-cache/ coverage/* @@ -19,12 +19,7 @@ config/gitlab.yml config/database.yml config/initializers/omniauth.rb config/unicorn.rb -config/resque.yml -config/aws.yml db/data.yml .idea .DS_Store .chef -vendor/bundle/* -rails_best_practices_output.html -doc/code/* diff --git a/.rspec b/.rspec index 5902dd3a..53607ea5 100644 --- a/.rspec +++ b/.rspec @@ -1 +1 @@ ---colour --drb +--colour diff --git a/.simplecov b/.simplecov deleted file mode 100644 index d979288d..00000000 --- a/.simplecov +++ /dev/null @@ -1,4 +0,0 @@ -# .simplecov -SimpleCov.start 'rails' do - merge_timeout 3600 -end diff --git a/.travis.yml b/.travis.yml index 609f2967..868a6c6c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,21 +1,26 @@ -language: ruby env: - - DB=mysql TRAVIS=true + - DB=postgresql + - DB=mysql before_install: - sudo apt-get install libicu-dev -y + - wget -P /tmp http://phantomjs.googlecode.com/files/phantomjs-1.7.0-linux-i686.tar.bz2 + - tar -xf /tmp/phantomjs-1.7.0-linux-i686.tar.bz2 -C /tmp/ + - sudo rm -rf /usr/local/phantomjs + - sudo mv /tmp/phantomjs-1.7.0-linux-i686 /usr/local/phantomjs - gem install charlock_holmes -v="0.6.9" branches: only: - 'master' rvm: - - 1.9.3-p392 + - 1.9.3 services: - mysql - postgresql before_script: - "cp config/database.yml.$DB config/database.yml" - "cp config/gitlab.yml.example config/gitlab.yml" - - "bundle exec rake db:setup RAILS_ENV=test" + - "bundle exec rake db:create RAILS_ENV=test" + - "bundle exec rake db:migrate RAILS_ENV=test" - "bundle exec rake db:seed_fu RAILS_ENV=test" - "sh -e /etc/init.d/xvfb start" script: "bundle exec rake travis --trace" diff --git a/CHANGELOG b/CHANGELOG index 872661d4..2eca1f14 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,111 +1,3 @@ -v 5.1.0 - - You can login with email or username now - - Corrected project transfer rollback when repository cannot be moved - - Move both repo and wiki when project transfer requrested - - Admin area: project editing was removed from admin namespace - - Access: admin user has now access to any project. - -v 5.0.0 - - Replaced gitolite with gitlab-shell - - Removed gitolite-related libraries - - State machine added - - Setup gitlab as git user - - Internal API - - Show team tab for empty projects - - Import repository feature - - Updated rails - - Use lambda for scopes - - Redesign admin area -> users - - Redesign admin area -> user - - Secure link to file attachments - - Add validations for Group and Team names - - Restyle team page for project - - Update capybara, rspec-rails, poltergeist to recent versions - - Wiki on git using Gollum - - Added Solarized Dark theme for code review - - Dont show user emails in autocomplete lists, profile pages - - Added settings tab for group, team, project - - Replace user popup with icons in header - - Handle project moving with gitlab-shell - - Added select2-rails for selectboxes with ajax data load - - Fixed search field on projects page - - Added teams to search autocomplete - - Move groups and teams on dashboard sidebar to sub-tabs - - API: improved return codes and docs. (Felix Gilcher, Sebastian Ziebell) - - Redesign wall to be more like chat - - Snippets, Wall features are disabled by default for new projects - -v 4.2.0 - - Teams - - User show page. Via /u/username - - Show help contents on pages for better navigation - - Async gitolite calls - - added satellites logs - - can_create_group, can_create_team booleans for User - - Process web hooks async - - GFM: Fix images escaped inside links - - Network graph improved - - Switchable branches for network graph - - API: Groups - - Fixed project download - -v 4.1.0 - - Optional Sign-Up - - Discussions - - Satellites outside of tmp - - Line numbers for blame - - Project public mode - - Public area with unauthorized access - - Load dashboard events with ajax - - remember dashboard filter in cookies - - replace resque with sidekiq - - fix routing issues - - cleanup rake tasks - - fix backup/restore - - scss cleanup - - show preview for note images - - improved network-graph - - get rid of app/roles/ - - added new classes Team, Repository - - Reduce amount of gitolite calls - - Ability to add user in all group projects - - remove deprecated configs - - replaced Korolev font with open font - - restyled admin/dashboard page - - restyled admin/projects page - -v 4.0.0 - - Remove project code and path from API. Use id instead - - Return valid clonable url to repo for web hook - - Fixed backup issue - - Reorganized settings - - Fixed commits compare - - Refactored scss - - Improve status checks - - Validates presence of User#name - - Fixed postgres support - - Removed sqlite support - - Modified post-receive hook - - Milestones can be closed now - - Show comment events on dashboard - - Quick add team members via group#people page - - [API] expose created date for hooks and SSH keys - - [API] list, create issue notes - - [API] list, create snippet notes - - [API] list, create wall notes - - Remove project code - use path instead - - added username field to user - - rake task to fill usernames based on emails create namespaces for users - - STI Group < Namespace - - Project has namespace_id - - Projects with namespaces also namespaced in gitolite and stored in subdir - - Moving project to group will move it under group namespace - - Ability to move project from namespaces to another - - Fixes commit patches getting escaped (see #2036) - - Support diff and patch generation for commits and merge request - - MergeReqest doesn't generate a temporary file for the patch any more - - Update the UI to allow downloading Patch or Diff - v 3.1.0 - Updated gems - Services: Gitlab CI integration @@ -142,7 +34,7 @@ v 3.0.0 - Fixed bug with gitolite keys - UI improved - Increased perfomance of application - - Show user avatar in last commit when browsing Files + - Show user avatar in last commit when browsing Files - Refactored Gitlab::Merge - Use Font Awsome for icons - Separate observing of Note and MergeRequestsa diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f322a81c..5f831446 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,53 +1,26 @@ -# Contribute to GitLab +## Contribute to GitLab -This guide details how to use pull requests and the issues to improve GitLab. +If you want to contribute to GitLab, follow this process: -## Closing policy for pull requests and issues +1. Fork the project +2. Create a feature branch +3. Code +4. Create a pull request -Pull requests and issues not in line with the guidelines listed in this document will be closed with just a link to this paragraph. GitLab is a popular open source project and the capacity to deal with issues and pull requests is limited. To get support for your problems please use other channels as detailed in [the getting help section of the readme](https://github.com/gitlabhq/gitlabhq#getting-help). Professional [support subscriptions](http://www.gitlab.com/subscription/) and [consulting services](http://www.gitlab.com/consultancy/) are available from [GitLab.com](http://www.gitlab.com/). +We will only accept pull requests if: -## Pull requests +* Your code has proper tests and all tests pass +* Your code can be merged w/o problems +* It won't break existing functionality +* It's quality code +* We like it :) -We welcome pull request with improvements to GitLab code and/or documentation. The issues we would really like a pull request for are listed with the [status 'accepting merge/pull requests' on our feedback forum](http://feedback.gitlab.com/forums/176466-general/status/796455) but other improvements are also welcome. +For examples of feedback on pull requests please look at the [closed pull requests](https://github.com/gitlabhq/gitlabhq/pulls?direction=desc&page=1&sort=created&state=closed). -### Pull request guidelines +## Installation - If you can please submit a pull request with the fix including tests. The workflow to make a pull request is as follows: +Install the Gitlab development in a virtual machine with the [Gitlab Vagrant virtual machine](https://github.com/gitlabhq/gitlab-vagrant-vm). Installing it in a virtual machine makes it much easier to set up all the dependencies for integration testing. -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 +## Running tests -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). - -## Issue tracker - -The [issue tracker](https://github.com/gitlabhq/gitlabhq/issues) is only for obvious bugs or misbehavior in the master branch of GitLab. When submitting an issue please conform to the issue submission guidelines listed below. - -Please send a pull request with a tested solution or a pull request with a failing test instead of opening an issue if you can. If you're unsure where to post, post to the [Support Forum](https://groups.google.com/forum/#!forum/gitlabhq) first. There are a lot of helpful GitLab users there who may be able to help you quickly. If your particular issue turns out to be a bug, it will find its way from there. - -### Issue tracker guidelines - -**Search** for similar entries before submitting your own, there's a good chance somebody else had the same issue or idea. Show your support with `:+1:` and/or join the discussion. - -* Summarize your issue in one sentence (what goes wrong, what did you expect to happen) -* Describe your issue in detail -* How can we reproduce the issue on the [GitLab Vagrant virtual machine](https://github.com/gitlabhq/gitlab-vagrant-vm) (start with: vagrant destroy && vagrant up && vagrant ssh) -* Add the last commit sha1 of the GitLab version you used to replicate the issue -* Add logs or screen shots when possible -* Link to the line of code that might be responsible for the problem -* Describe your setup (use relevant parts from `sudo -u gitlab -H bundle exec rake gitlab:env:info`) +For more information on running the tests please read the [development tips](https://github.com/gitlabhq/gitlabhq/blob/master/doc/development.md) diff --git a/Capfile.example b/Capfile.example deleted file mode 100644 index 8863835d..00000000 --- a/Capfile.example +++ /dev/null @@ -1,4 +0,0 @@ -load 'deploy' -load 'deploy/assets' -require 'bundler/capistrano' -load 'config/deploy' diff --git a/Gemfile b/Gemfile index 01e3b0f2..f723f587 100644 --- a/Gemfile +++ b/Gemfile @@ -1,4 +1,4 @@ -source "https://rubygems.org" +source "http://rubygems.org" def darwin_only(require_as) RUBY_PLATFORM.include?('darwin') && require_as @@ -8,58 +8,51 @@ def linux_only(require_as) RUBY_PLATFORM.include?('linux') && require_as end -gem "rails", "3.2.13" +gem "rails", "3.2.9" # Supported DBs +gem "sqlite3", group: :sqlite gem "mysql2", group: :mysql gem "pg", group: :postgres # Auth -gem "devise" -gem 'omniauth', "~> 1.1.3" +gem "devise", "~> 2.1.0" +gem 'omniauth', "~> 1.1.1" gem 'omniauth-google-oauth2' gem 'omniauth-twitter' gem 'omniauth-github' -# Extracting information from a git repository -# Since gollum requires grit we cannot use gitlab-grit gem name any more. Use grit instead -gem "grit", '~> 2.5.0', git: 'https://github.com/gitlabhq/grit.git', ref: '42297cdcee16284d2e4eff23d41377f52fc28b9d' -gem 'grit_ext', '~> 0.8.1' +# GITLAB patched libs +gem "grit", git: "https://github.com/gitlabhq/grit.git", ref: '7f35cb98ff17d534a07e3ce6ec3d580f67402837' +gem "omniauth-ldap", git: "https://github.com/gitlabhq/omniauth-ldap.git", ref: 'f038dd852d7bd473a557e385d5d7c2fd5dc1dc2e' +gem 'yaml_db', git: "https://github.com/gitlabhq/yaml_db.git", ref: '98e9a5dca43e3fedd3268c76a73af40d1bdf1dfd' +gem 'grack', git: "https://github.com/gitlabhq/grack.git", ref: 'ba46f3b0845c6a09d488ae6abdce6ede37e227e8' +gem 'grit_ext', git: "https://github.com/gitlabhq/grit_ext.git", ref: '212fd40bea61f3c6a167223768e7295dc32bbc10' -# 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" - -# Dump db to yml file. Mostly used to migrate from sqlite to mysql -gem 'gitlab_yaml_db', '1.0.0', require: "yaml_db" +# Gitolite client (for work with gitolite-admin repo) +gem "gitolite", '1.1.0' # Syntax highlighter -gem "gitlab-pygments.rb", '~> 0.3.2', require: 'pygments.rb' +gem "pygments.rb", git: "https://github.com/gitlabhq/pygments.rb.git", ref: '4db80c599067e2d5f23c5c243bf85b8ca0368ad4' # Language detection gem "github-linguist", "~> 2.3.4" , require: "linguist" # API -gem "grape", "~> 0.3.1" -gem "grape-entity", "~> 0.2.0" +gem "grape", "~> 0.2.1" # Format dates and times # based on human-friendly examples gem "stamp" -# Enumeration fields -gem 'enumerize' - # Pagination gem "kaminari", "~> 0.14.1" # HAML -gem "haml-rails" +gem "haml-rails", "~> 0.3.5" # Files attachments -gem "carrierwave" +gem "carrierwave", "~> 0.7.1" # Authorization gem "six" @@ -75,21 +68,18 @@ gem "redcarpet", "~> 2.2.2" gem "github-markup", "~> 0.7.4", require: 'github/markup' # Servers -gem "unicorn" - -# State machine -gem "state_machine" +gem "thin", '~> 1.5.0' +gem "unicorn", "~> 4.4.0" # Issue tags gem "acts-as-taggable-on", "2.3.3" # Decorators -gem "draper" +gem "draper", "~> 0.18.0" # Background jobs -gem 'slim' -gem 'sinatra', require: nil -gem 'sidekiq' +gem "resque", "~> 1.23.0" +gem 'resque_mailer' # HTTP requests gem "httparty" @@ -100,20 +90,10 @@ gem "colored" # GitLab settings gem 'settingslogic' -# Wiki -# - Use latest master to resolve Gem dependency with Pygemnts -# github-linquist needs pygments 0.4.2 but Gollum 2.4.11 -# requires pygments 0.3.2. The latest master Gollum has been updated -# to use pygments 0.4.2. Change this after next Gollum release. -gem "gollum", "~> 2.4.0", git: "https://github.com/gollum/gollum.git", ref: "5dcd3c8c8f" - # Misc gem "foreman" gem "git" -# Cache -gem "redis-rails" - group :assets do gem "sass-rails", "~> 3.2.5" gem "coffee-rails", "~> 3.2.2" @@ -121,16 +101,14 @@ group :assets do gem "therubyracer" gem 'chosen-rails', "0.9.8" - gem 'select2-rails' - gem 'jquery-atwho-rails', "0.1.7" + gem 'jquery-atwho-rails', "0.1.6" gem "jquery-rails", "2.1.3" gem "jquery-ui-rails", "2.0.2" gem "modernizr", "2.6.2" - gem "raphael-rails", git: "https://github.com/gitlabhq/raphael-rails.git" + gem "raphael-rails", "2.1.0" gem 'bootstrap-sass', "2.2.1.1" - gem "font-awesome-sass-rails", "~> 3.0.0" + gem "font-awesome-sass-rails", "~> 2.0.0" gem "gemoji", "~> 1.2.1", require: 'emoji/railtie' - gem "gon" end group :development do @@ -138,21 +116,9 @@ group :development do gem "letter_opener" gem 'quiet_assets', '~> 1.0.1' gem 'rack-mini-profiler' - # Better errors handler - gem 'better_errors' - gem 'binding_of_caller' - - gem 'rails_best_practices' - - # Docs generator - gem "sdoc" - - # thin instead webrick - gem 'thin' end group :development, :test do - gem 'coveralls', require: false gem 'rails-dev-tweaks' gem 'spinach-rails' gem "rspec-rails" @@ -173,19 +139,18 @@ group :development, :test do gem 'rb-inotify', require: linux_only('rb-inotify') # PhantomJS driver for Capybara - gem 'poltergeist', '1.1.0' - - gem 'spork', '~> 1.0rc' + gem 'poltergeist' end group :test do gem "simplecov", require: false gem "shoulda-matchers", "1.3.0" gem 'email_spec' + gem 'resque_spec' gem "webmock" gem 'test_after_commit' end group :production do - gem "gitlab_meta", '5.0' + gem "gitlab_meta", '3.1' end diff --git a/Gemfile.lock b/Gemfile.lock index 586b7bda..0e3a9810 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -6,10 +6,18 @@ GIT activerecord (>= 2.3.0) 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: 42297cdcee16284d2e4eff23d41377f52fc28b9d - ref: 42297cdcee16284d2e4eff23d41377f52fc28b9d + revision: 7f35cb98ff17d534a07e3ce6ec3d580f67402837 + ref: 7f35cb98ff17d534a07e3ce6ec3d580f67402837 specs: grit (2.5.0) diff-lcs (~> 1.1) @@ -17,95 +25,98 @@ GIT posix-spawn (~> 0.3.6) GIT - remote: https://github.com/gitlabhq/raphael-rails.git - revision: cb2c92a040b9b941a5f1aa1ea866cc26e944fe58 + remote: https://github.com/gitlabhq/grit_ext.git + revision: 212fd40bea61f3c6a167223768e7295dc32bbc10 + ref: 212fd40bea61f3c6a167223768e7295dc32bbc10 specs: - raphael-rails (2.1.0) + grit_ext (0.6.0) + charlock_holmes (~> 0.6.9) GIT - remote: https://github.com/gollum/gollum.git - revision: 5dcd3c8c8f68158e43ff79861279088ee56d0ebe - ref: 5dcd3c8c8f + remote: https://github.com/gitlabhq/omniauth-ldap.git + revision: f038dd852d7bd473a557e385d5d7c2fd5dc1dc2e + ref: f038dd852d7bd473a557e385d5d7c2fd5dc1dc2e specs: - gollum (2.4.11) - github-markdown (~> 0.5.3) - github-markup (>= 0.7.5, < 1.0.0) - grit (~> 2.5.0) - mustache (>= 0.99.4, < 1.0.0) - nokogiri (~> 1.5.6) - pygments.rb (~> 0.4.2) - sanitize (~> 2.0.3) - sinatra (~> 1.3.5) - stringex (~> 1.5.1) - useragent (~> 0.4.16) + omniauth-ldap (1.0.2) + net-ldap (~> 0.2.2) + omniauth (~> 1.0) + pyu-ruby-sasl (~> 0.0.3.1) + rubyntlm (~> 0.1.1) + +GIT + remote: https://github.com/gitlabhq/pygments.rb.git + revision: 4db80c599067e2d5f23c5c243bf85b8ca0368ad4 + ref: 4db80c599067e2d5f23c5c243bf85b8ca0368ad4 + specs: + pygments.rb (0.3.2) + posix-spawn (~> 0.3.6) + yajl-ruby (~> 1.1.0) + +GIT + remote: https://github.com/gitlabhq/yaml_db.git + revision: 98e9a5dca43e3fedd3268c76a73af40d1bdf1dfd + ref: 98e9a5dca43e3fedd3268c76a73af40d1bdf1dfd + specs: + yaml_db (0.2.2) GEM - remote: https://rubygems.org/ + remote: http://rubygems.org/ specs: - actionmailer (3.2.13) - actionpack (= 3.2.13) - mail (~> 2.5.3) - actionpack (3.2.13) - activemodel (= 3.2.13) - activesupport (= 3.2.13) + actionmailer (3.2.9) + actionpack (= 3.2.9) + mail (~> 2.4.4) + actionpack (3.2.9) + activemodel (= 3.2.9) + activesupport (= 3.2.9) builder (~> 3.0.0) erubis (~> 2.7.0) journey (~> 1.0.4) - rack (~> 1.4.5) + rack (~> 1.4.0) rack-cache (~> 1.2) rack-test (~> 0.6.1) sprockets (~> 2.2.1) - activemodel (3.2.13) - activesupport (= 3.2.13) + activemodel (3.2.9) + activesupport (= 3.2.9) builder (~> 3.0.0) - activerecord (3.2.13) - activemodel (= 3.2.13) - activesupport (= 3.2.13) + activerecord (3.2.9) + activemodel (= 3.2.9) + activesupport (= 3.2.9) arel (~> 3.0.2) tzinfo (~> 0.3.29) - activeresource (3.2.13) - activemodel (= 3.2.13) - activesupport (= 3.2.13) - activesupport (3.2.13) - i18n (= 0.6.1) + activeresource (3.2.9) + activemodel (= 3.2.9) + activesupport (= 3.2.9) + activesupport (3.2.9) + i18n (~> 0.6) multi_json (~> 1.0) acts-as-taggable-on (2.3.3) rails (~> 3.0) addressable (2.3.2) arel (3.0.2) awesome_print (1.1.0) - backports (2.6.7) + backports (2.6.5) bcrypt-ruby (3.0.1) - better_errors (0.3.2) - coderay (>= 1.0.0) - erubis (>= 2.7.0) - binding_of_caller (0.7.1) - debug_inspector (>= 0.0.1) + blankslate (3.1.2) bootstrap-sass (2.2.1.1) sass (~> 3.2) builder (3.0.4) - capybara (2.0.2) + capybara (1.1.3) mime-types (>= 1.16) nokogiri (>= 1.3.3) rack (>= 1.0.0) rack-test (>= 0.5.4) selenium-webdriver (~> 2.0) - xpath (~> 1.0.0) - carrierwave (0.8.0) + xpath (~> 0.1.4) + carrierwave (0.7.1) activemodel (>= 3.2.0) activesupport (>= 3.2.0) - celluloid (0.12.4) - facter (>= 1.6.12) - timers (>= 1.0.0) charlock_holmes (0.6.9) - childprocess (0.3.8) - ffi (~> 1.0, >= 1.0.11) + childprocess (0.3.6) + ffi (~> 1.0, >= 1.0.6) chosen-rails (0.9.8) railties (~> 3.0) thor (~> 0.14) - code_analyzer (0.3.1) - sexp_processor - coderay (1.0.9) + coderay (1.0.8) coffee-rails (3.2.2) coffee-script (>= 2.2.0) railties (~> 3.2.0) @@ -115,54 +126,41 @@ GEM coffee-script-source (1.4.0) colored (1.2) colorize (0.5.8) - connection_pool (1.0.0) - coveralls (0.6.2) - colorize - multi_json (~> 1.3) - rest-client - simplecov (>= 0.7) - thor - crack (0.3.2) + crack (0.3.1) daemons (1.1.9) database_cleaner (0.9.1) - debug_inspector (0.0.2) - descendants_tracker (0.0.1) - devise (2.2.3) + devise (2.1.2) bcrypt-ruby (~> 3.0) orm_adapter (~> 0.1) railties (~> 3.1) warden (~> 1.2.1) - diff-lcs (1.2.1) - draper (1.1.0) - actionpack (>= 3.0) - activesupport (>= 3.0) - request_store (~> 1.0.3) + diff-lcs (1.1.3) + draper (0.18.0) + actionpack (~> 3.2) + activesupport (~> 3.2) 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) execjs (1.4.0) multi_json (~> 1.0) - facter (1.6.18) factory_girl (4.1.0) activesupport (>= 3.0.0) factory_girl_rails (4.1.0) factory_girl (~> 4.1.0) railties (>= 3.0.0) - faraday (0.8.6) + faraday (0.8.4) multipart-post (~> 1.1) - faye-websocket (0.4.7) + faye-websocket (0.4.6) eventmachine (>= 0.12.0) ffaker (1.15.0) - ffi (1.4.0) - font-awesome-sass-rails (3.0.0.1) + ffi (1.1.5) + font-awesome-sass-rails (2.0.0.0) railties (>= 3.1.1) sass-rails (>= 3.1.1) - foreman (0.61.0) + foreman (0.60.2) thor (>= 0.13.6) gemoji (1.2.1) gherkin-ruby (0.2.1) @@ -172,112 +170,99 @@ GEM escape_utils (~> 0.2.3) mime-types (~> 1.19) pygments.rb (>= 0.2.13) - github-markdown (0.5.3) - github-markup (0.7.5) - gitlab-grack (1.0.0) - rack (~> 1.4.1) - 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) - omniauth (~> 1.0) - pyu-ruby-sasl (~> 0.0.3.1) - rubyntlm (~> 0.1.1) - gitlab_yaml_db (1.0.0) - gon (4.0.2) - grape (0.3.2) + github-markup (0.7.4) + gitlab_meta (3.1) + gitolite (1.1.0) + gratr19 (~> 0.4.4.1) + grit (~> 2.5.0) + hashery (~> 1.5.0) + grape (0.2.2) activesupport - builder - hashie (>= 1.2.0) + hashie (~> 1.2) multi_json (>= 1.3.2) - multi_xml (>= 0.5.2) + multi_xml rack rack-accept rack-mount virtus - grape-entity (0.2.0) - activesupport - multi_json (>= 1.3.2) - grit_ext (0.8.1) - charlock_holmes (~> 0.6.9) + gratr19 (0.4.4.1) growl (1.0.3) - guard (1.6.2) - listen (>= 0.6.0) + guard (1.5.4) + listen (>= 0.4.2) lumberjack (>= 1.0.2) pry (>= 0.9.10) - terminal-table (>= 1.4.3) thor (>= 0.14.6) - guard-rspec (2.5.1) + guard-rspec (2.1.2) guard (>= 1.1) rspec (~> 2.11) guard-spinach (0.0.2) guard (>= 1.1) spinach - haml (4.0.0) - tilt - haml-rails (0.4) + haml (3.1.7) + haml-rails (0.3.5) actionpack (>= 3.1, < 4.1) activesupport (>= 3.1, < 4.1) - haml (>= 3.1, < 4.1) + haml (~> 3.1) railties (>= 3.1, < 4.1) + hashery (1.5.0) + blankslate hashie (1.2.0) hike (1.2.1) http_parser.rb (0.5.3) - httparty (0.10.2) + httparty (0.9.0) multi_json (~> 1.0) - multi_xml (>= 0.5.2) + multi_xml httpauth (0.2.0) i18n (0.6.1) journey (1.0.4) - jquery-atwho-rails (0.1.7) + jquery-atwho-rails (0.1.6) jquery-rails (2.1.3) railties (>= 3.1.0, < 5.0) thor (~> 0.14) jquery-ui-rails (2.0.2) jquery-rails railties (>= 3.1.0) - json (1.7.7) + json (1.7.5) jwt (0.1.5) multi_json (>= 1.0) kaminari (0.14.1) actionpack (>= 3.0.0) activesupport (>= 3.0.0) - kgio (2.8.0) + kgio (2.7.4) launchy (2.1.2) addressable (~> 2.3) letter_opener (1.0.0) launchy (>= 2.0.4) - libv8 (3.11.8.17) - listen (0.7.3) - lumberjack (1.0.3) - mail (2.5.3) + libv8 (3.3.10.4) + libwebsocket (0.1.6) + websocket + listen (0.5.3) + lumberjack (1.0.2) + mail (2.4.4) i18n (>= 0.4.0) mime-types (~> 1.16) treetop (~> 1.4.8) method_source (0.8.1) - mime-types (1.21) + mime-types (1.19) modernizr (2.6.2) sprockets (~> 2.0) - multi_json (1.7.2) - multi_xml (0.5.3) + multi_json (1.3.7) + multi_xml (0.5.1) multipart-post (1.1.5) - mustache (0.99.4) mysql2 (0.3.11) net-ldap (0.2.2) - nokogiri (1.5.6) + nokogiri (1.5.5) oauth (0.4.7) - oauth2 (0.8.1) + oauth2 (0.8.0) faraday (~> 0.8) httpauth (~> 0.1) jwt (~> 0.1.4) multi_json (~> 1.0) rack (~> 1.2) - omniauth (1.1.3) + omniauth (1.1.1) hashie (~> 1.2) rack - omniauth-github (1.1.0) + omniauth-github (1.0.3) omniauth (~> 1.0) omniauth-oauth2 (~> 1.1) omniauth-google-oauth2 (0.1.13) @@ -294,23 +279,22 @@ GEM omniauth-oauth (~> 1.0) orm_adapter (0.4.0) pg (0.14.1) - poltergeist (1.1.0) - capybara (~> 2.0, >= 2.0.1) + poltergeist (1.0.2) + capybara (~> 1.1) + childprocess (~> 0.3) faye-websocket (~> 0.4, >= 0.4.4) http_parser.rb (~> 0.5.3) + multi_json (~> 1.0) polyglot (0.3.3) posix-spawn (0.3.6) - pry (0.9.12) + pry (0.9.10) coderay (~> 1.0.5) method_source (~> 0.8) - slop (~> 3.4) - pygments.rb (0.4.2) - posix-spawn (~> 0.3.6) - yajl-ruby (~> 1.1.0) + slop (~> 3.3.1) pyu-ruby-sasl (0.0.3.3) quiet_assets (1.0.1) railties (~> 3.1) - rack (1.4.5) + rack (1.4.1) rack-accept (0.4.5) rack (>= 0.4) rack-cache (1.2) @@ -319,182 +303,140 @@ GEM rack (>= 1.1.3) rack-mount (0.8.3) rack (>= 1.0.0) - rack-protection (1.4.0) + rack-protection (1.2.0) rack - rack-ssl (1.3.3) + rack-ssl (1.3.2) rack rack-test (0.6.2) rack (>= 1.0) - rails (3.2.13) - actionmailer (= 3.2.13) - actionpack (= 3.2.13) - activerecord (= 3.2.13) - activeresource (= 3.2.13) - activesupport (= 3.2.13) + rails (3.2.9) + actionmailer (= 3.2.9) + actionpack (= 3.2.9) + activerecord (= 3.2.9) + activeresource (= 3.2.9) + activesupport (= 3.2.9) bundler (~> 1.0) - railties (= 3.2.13) + railties (= 3.2.9) rails-dev-tweaks (0.6.1) actionpack (~> 3.1) railties (~> 3.1) - rails_best_practices (1.13.4) - activesupport - awesome_print - code_analyzer - colored - erubis - i18n - ruby-progressbar - railties (3.2.13) - actionpack (= 3.2.13) - activesupport (= 3.2.13) + railties (3.2.9) + actionpack (= 3.2.9) + activesupport (= 3.2.9) rack-ssl (~> 1.3.2) rake (>= 0.8.7) rdoc (~> 3.4) thor (>= 0.14.6, < 2.0) raindrops (0.10.0) - rake (10.0.4) + rake (10.0.1) + raphael-rails (2.1.0) rb-fsevent (0.9.2) rb-inotify (0.8.8) ffi (>= 0.5.0) - rdoc (3.12.2) + rdoc (3.12) json (~> 1.4) redcarpet (2.2.2) - redis (3.0.3) - redis-actionpack (3.2.3) - actionpack (~> 3.2.3) - redis-rack (~> 1.4.0) - redis-store (~> 1.1.0) - redis-activesupport (3.2.3) - activesupport (~> 3.2.3) - redis-store (~> 1.1.0) + redis (3.0.2) redis-namespace (1.2.1) redis (~> 3.0.0) - redis-rack (1.4.2) - rack (~> 1.4.1) - redis-store (~> 1.1.0) - redis-rails (3.2.3) - redis-actionpack (~> 3.2.3) - redis-activesupport (~> 3.2.3) - redis-store (~> 1.1.0) - redis-store (1.1.3) - redis (>= 2.2.0) - ref (1.0.4) - request_store (1.0.5) - rest-client (1.6.7) - mime-types (>= 1.16) - rspec (2.13.0) - rspec-core (~> 2.13.0) - rspec-expectations (~> 2.13.0) - rspec-mocks (~> 2.13.0) - rspec-core (2.13.1) - rspec-expectations (2.13.0) - diff-lcs (>= 1.1.3, < 2.0) - rspec-mocks (2.13.0) - rspec-rails (2.13.0) + resque (1.23.0) + multi_json (~> 1.0) + redis-namespace (~> 1.0) + sinatra (>= 0.9.2) + vegas (~> 0.1.2) + resque_mailer (2.1.0) + actionmailer (~> 3.0) + resque_spec (0.12.5) + resque (>= 1.19.0) + rspec (>= 2.5.0) + rspec (2.12.0) + rspec-core (~> 2.12.0) + rspec-expectations (~> 2.12.0) + rspec-mocks (~> 2.12.0) + rspec-core (2.12.0) + rspec-expectations (2.12.0) + diff-lcs (~> 1.1.3) + rspec-mocks (2.12.0) + rspec-rails (2.12.0) actionpack (>= 3.0) activesupport (>= 3.0) railties (>= 3.0) - rspec-core (~> 2.13.0) - rspec-expectations (~> 2.13.0) - rspec-mocks (~> 2.13.0) - ruby-progressbar (1.0.2) + rspec-core (~> 2.12.0) + rspec-expectations (~> 2.12.0) + rspec-mocks (~> 2.12.0) rubyntlm (0.1.1) rubyzip (0.9.9) - sanitize (2.0.3) - nokogiri (>= 1.4.4, < 1.6) - sass (3.2.7) - sass-rails (3.2.6) + sass (3.2.3) + sass-rails (3.2.5) railties (~> 3.2.0) sass (>= 3.1.10) tilt (~> 1.3) - sdoc (0.3.20) - json (>= 1.1.3) - rdoc (~> 3.10) seed-fu (2.2.0) activerecord (~> 3.1) activesupport (~> 3.1) - select2-rails (3.3.1) - sass-rails (>= 3.2) - thor (~> 0.14) - selenium-webdriver (2.30.0) + selenium-webdriver (2.26.0) childprocess (>= 0.2.5) + libwebsocket (~> 0.1.3) multi_json (~> 1.0) rubyzip - websocket (~> 1.0.4) - settingslogic (2.0.9) - sexp_processor (4.2.0) + settingslogic (2.0.8) shoulda-matchers (1.3.0) activesupport (>= 3.0.0) - sidekiq (2.8.0) - celluloid (~> 0.12.0) - connection_pool (~> 1.0) - multi_json (~> 1) - redis (~> 3) - redis-namespace simplecov (0.7.1) multi_json (~> 1.0) simplecov-html (~> 0.7.1) simplecov-html (0.7.1) - sinatra (1.3.5) - rack (~> 1.4) - rack-protection (~> 1.3) + sinatra (1.3.3) + rack (~> 1.3, >= 1.3.6) + rack-protection (~> 1.2) tilt (~> 1.3, >= 1.3.3) six (0.2.0) - slim (1.3.6) - temple (~> 0.5.5) - tilt (~> 1.3.3) - slop (3.4.4) - spinach (0.7.0) + slop (3.3.3) + spinach (0.5.2) colorize gherkin-ruby (~> 0.2.0) - spinach-rails (0.2.0) - capybara (~> 2.0.0) + spinach-rails (0.1.8) + capybara (~> 1) railties (>= 3) spinach (>= 0.4) - spork (1.0.0rc3) - sprockets (2.2.2) + sprockets (2.2.1) hike (~> 1.2) multi_json (~> 1.0) rack (~> 1.0) tilt (~> 1.1, != 1.3.0) - stamp (0.5.0) - state_machine (1.1.2) - stringex (1.5.1) - temple (0.5.5) - terminal-table (1.4.5) + sqlite3 (1.3.6) + stamp (0.3.0) test_after_commit (0.0.1) - therubyracer (0.11.4) - libv8 (~> 3.11.8.12) - ref + therubyracer (0.10.2) + libv8 (~> 3.3.10) thin (1.5.0) daemons (>= 1.0.9) eventmachine (>= 0.12.6) rack (>= 1.0.0) - thor (0.18.0) - tilt (1.3.6) - timers (1.1.0) + thor (0.16.0) + tilt (1.3.3) treetop (1.4.12) polyglot polyglot (>= 0.3.1) - tzinfo (0.3.37) + tzinfo (0.3.35) uglifier (1.3.0) execjs (>= 0.3.0) multi_json (~> 1.0, >= 1.0.2) - unicorn (4.6.2) + unicorn (4.4.0) kgio (~> 2.6) rack raindrops (~> 0.7) - useragent (0.4.16) - virtus (0.5.4) + vegas (0.1.11) + rack (>= 1.0.0) + virtus (0.5.2) backports (~> 2.6.1) - descendants_tracker (~> 0.0.1) warden (1.2.1) rack (>= 1.0) webmock (1.9.0) addressable (>= 2.2.7) crack (>= 0.1.7) - websocket (1.0.7) - xpath (1.0.0) + websocket (1.0.2) + xpath (0.1.4) nokogiri (~> 1.3) yajl-ruby (1.1.0) @@ -505,45 +447,36 @@ DEPENDENCIES acts-as-taggable-on (= 2.3.3) annotate! awesome_print - better_errors - binding_of_caller bootstrap-sass (= 2.2.1.1) capybara - carrierwave + carrierwave (~> 0.7.1) chosen-rails (= 0.9.8) coffee-rails (~> 3.2.2) colored - coveralls database_cleaner - devise - draper + devise (~> 2.1.0) + draper (~> 0.18.0) email_spec - enumerize factory_girl_rails ffaker - font-awesome-sass-rails (~> 3.0.0) + font-awesome-sass-rails (~> 2.0.0) foreman gemoji (~> 1.2.1) git github-linguist (~> 2.3.4) github-markup (~> 0.7.4) - gitlab-grack (~> 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) - gollum (~> 2.4.0)! - gon - grape (~> 0.3.1) - grape-entity (~> 0.2.0) - grit (~> 2.5.0)! - grit_ext (~> 0.8.1) + gitlab_meta (= 3.1) + gitolite (= 1.1.0) + grack! + grape (~> 0.2.1) + grit! + grit_ext! growl guard-rspec guard-spinach - haml-rails + haml-rails (~> 0.3.5) httparty - jquery-atwho-rails (= 0.1.7) + jquery-atwho-rails (= 0.1.6) jquery-rails (= 2.1.3) jquery-ui-rails (= 2.0.2) kaminari (~> 0.14.1) @@ -551,42 +484,40 @@ DEPENDENCIES letter_opener modernizr (= 2.6.2) mysql2 - omniauth (~> 1.1.3) + omniauth (~> 1.1.1) omniauth-github omniauth-google-oauth2 + omniauth-ldap! omniauth-twitter pg - poltergeist (= 1.1.0) + poltergeist pry + pygments.rb! quiet_assets (~> 1.0.1) rack-mini-profiler - rails (= 3.2.13) + rails (= 3.2.9) rails-dev-tweaks - rails_best_practices - raphael-rails! + raphael-rails (= 2.1.0) rb-fsevent rb-inotify redcarpet (~> 2.2.2) - redis-rails + resque (~> 1.23.0) + resque_mailer + resque_spec rspec-rails sass-rails (~> 3.2.5) - sdoc seed-fu - select2-rails settingslogic shoulda-matchers (= 1.3.0) - sidekiq simplecov - sinatra six - slim spinach-rails - spork (~> 1.0rc) + sqlite3 stamp - state_machine test_after_commit therubyracer - thin + thin (~> 1.5.0) uglifier (~> 1.3.0) - unicorn + unicorn (~> 4.4.0) webmock + yaml_db! diff --git a/Procfile b/Procfile index 66ca562f..09ec8cc2 100644 --- a/Procfile +++ b/Procfile @@ -1,2 +1,2 @@ -web: bundle exec unicorn_rails -p $PORT -worker: bundle exec sidekiq -q post_receive,mailer,system_hook,project_web_hook,common,default,gitlab_shell +web: bundle exec rails s -p $PORT +worker: bundle exec rake environment resque:work QUEUE=* VVERBOSE=1 diff --git a/README.md b/README.md index 3ab4c051..1816629d 100644 --- a/README.md +++ b/README.md @@ -1,161 +1,44 @@ -## GitLab: self hosted Git management software +# Welcome to GitLab [![build status](https://secure.travis-ci.org/gitlabhq/gitlabhq.png)](https://secure.travis-ci.org/gitlabhq/gitlabhq) [![build status](https://secure.travis-ci.org/gitlabhq/grit.png)](https://secure.travis-ci.org/gitlabhq/grit) [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/gitlabhq/gitlabhq) -![logo](https://raw.github.com/gitlabhq/gitlabhq/master/public/gitlab_logo.png) +GitLab is a free project and repository management application -### GitLab allows you to - * keep your code secure on your own server - * manage repositories, users and access permissions - * communicate through issues, line-comments and wiki pages - * perform code review with merge requests -### GitLab is +## Application details -* powered by Ruby on Rails -* completely free and open source (MIT license) -* used by more than 10.000 organizations to keep their code secure +* based on Ruby on Rails +* distributed under the MIT License +* works with gitolite -### Code status +## Requirements -* [![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) - -* [![Dependency Status](https://gemnasium.com/gitlabhq/gitlabhq.png)](https://gemnasium.com/gitlabhq/gitlabhq) - -* [![Coverage Status](https://coveralls.io/repos/gitlabhq/gitlabhq/badge.png?branch=master)](https://coveralls.io/r/gitlabhq/gitlabhq) - -### 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) - -* GitLab.com commercial services: [Homepage](http://www.gitlab.com/) [Subscription](http://www.gitlab.com/subscription/) [Consultancy](http://www.gitlab.com/consultancy/) [GitLab Cloud](http://www.gitlab.com/cloud/) [Blog](http://blog.gitlab.com/) - -* GitLab CI: [Readme](https://github.com/gitlabhq/gitlab-ci/blob/master/README.md) of the GitLab open-source continuous integration server - -### Requirements - -* Ubuntu/Debian** -* ruby 1.9.3 +* Ubuntu/Debian +* ruby 1.9.3+ * MySQL * git -* gitlab-shell +* gitolite * redis -** More details are in the [requirements doc](https://github.com/gitlabhq/gitlabhq/blob/master/doc/install/requirements.md) +## Install -### Installation +Checkout wiki pages for installation information, migration, etc. -#### Official production installation +## Community -Follow the installation guide for production server. +[Google Group](https://groups.google.com/group/gitlabhq) -* [Installation guide for latest stable release (5.0)](https://github.com/gitlabhq/gitlabhq/blob/5-0-stable/doc/install/installation.md) - **Recommended** +## Contacts -* [Installation guide for the current master branch (5.1)](https://github.com/gitlabhq/gitlabhq/blob/master/doc/install/installation.md) +Twitter: + * @gitlabhq + * @dzaporozhets -#### Official development installation +Email -If you want to contribute, please first read our [Contributing Guidelines](https://github.com/gitlabhq/gitlabhq/blob/master/CONTRIBUTING.md) and then we suggest you to use the Vagrant virtual machine project to get an environment working with all dependencies. + * m@gitlabhq.com -* [Vagrant virtual machine](https://github.com/gitlabhq/gitlab-vagrant-vm) +## Contribute - -#### Unsupported production installation - -* [GitLab recipes](https://github.com/gitlabhq/gitlab-recipes) for setup on different platforms - -* [Unofficial installation guides](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Unofficial-Installation-Guides) - -* [BitNami one-click installers](http://bitnami.com/stack/gitlab) - -* [TurnKey Linux virtual appliance](http://www.turnkeylinux.org/gitlab) - - -### New versions and upgrading - -Each month on the 22th a new version is released together with an upgrade guide. - -* [Upgrade guides](https://github.com/gitlabhq/gitlabhq/wiki) - -* [Changelog](https://github.com/gitlabhq/gitlabhq/blob/master/CHANGELOG) - -* [Roadmap](https://github.com/gitlabhq/gitlabhq/blob/master/ROADMAP.md) - - -### Getting started - -1. The Installation guide contains instructions to download an init script and run that on boot. With the init script you can also start GitLab - - sudo service gitlab start - - or - - sudo /etc/init.d/gitlab restart - -2. Start it with [Foreman](https://github.com/ddollar/foreman) in development mode - - bundle exec foreman start -p 3000 - - or start it manually - - bundle exec rails s - bundle exec rake sidekiq:start - -### Running the tests - -* Seed the database - - 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 - - -### GitLab interfaces - -* [GitLab API](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/README.md) - -* [Rake tasks](https://github.com/gitlabhq/gitlabhq/tree/master/doc/raketasks) - -* [Directory structure](https://github.com/gitlabhq/gitlabhq/blob/master/doc/install/structure.md) - -* [Databases](https://github.com/gitlabhq/gitlabhq/blob/master/doc/install/databases.md) - - -### Getting help - -* [Troubleshooting guide](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Trouble-Shooting-Guide) contains solutions to common problems. - -* [Support forum](https://groups.google.com/forum/#!forum/gitlabhq) is the best place to ask questions. For example you can use it if you have questions about: permission denied errors, invisible repos, can't clone/pull/push or with web hooks that don't fire. Please search for similar issues before posting your own, there's a good chance somebody else had the same issue you have now and had it resolved. There are a lot of helpful GitLab users there who may be able to help you quickly. If your particular issue turns out to be a bug, it will find its way from there to a fix. - -* [Feedback and suggestions forum](http://gitlab.uservoice.com/forums/176466-general) is the place to propose and discuss new features for GitLab. - -* [Support subscription](http://www.gitlab.com/subscription/) connect you to the knowledge of GitLab experts that will resolve your issues and answer your questions. - -* [Consultancy](http://www.gitlab.com/consultancy/) allows you hire GitLab exports for installations, upgrades and customizations. - -* [Contributing guide](https://github.com/gitlabhq/gitlabhq/blob/master/CONTRIBUTING.md) describes how to submit pull requests and issues. Pull requests and issues not in line with the guidelines in this document will be closed without comment. - - -### Getting in touch - -* [Core team](https://github.com/gitlabhq?tab=members) - -* [Contributors](https://github.com/gitlabhq/gitlabhq/graphs/contributors) - -* [Leader](https://github.com/randx) - -* [Contact page](http://gitlab.org/contact/) +[Development Tips](https://github.com/gitlabhq/gitlabhq/blob/master/doc/development.md) +Want to help - send a pull request. +We'll accept good pull requests. diff --git a/ROADMAP.md b/ROADMAP.md index 9c4bd2db..295a5244 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -1,5 +1,25 @@ ## GitLab Roadmap -### v5.1 April 22 +### Common -* Not decided yet. +* Help page for service tasks like repos import, backup etc +* Hide last push widget after following link +* Add comment events +* gitolite namespaces for projects per user/group. It will allow us same project names for different users + +### Issues + +* labels autocomplete via jquery autocomplete +* Import/Export issues +* Form: Assign to me link right to the selectbox + +### Merge Request + +* Save code fragments with MR comments + +### Services + +* Campfire integration service +* Hipchat integration service +* Travis CI integration service +* Jenkins CI integration service diff --git a/VERSION b/VERSION index 52df2a23..fd2a0186 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -5.1.0pre +3.1.0 diff --git a/app/assets/fonts/korolev-medium-compressed.otf b/app/assets/fonts/korolev-medium-compressed.otf new file mode 100644 index 00000000..e3817cec Binary files /dev/null and b/app/assets/fonts/korolev-medium-compressed.otf differ diff --git a/app/assets/images/ajax_loader_gray.gif b/app/assets/images/ajax_loader_gray.gif deleted file mode 100644 index af3f618b..00000000 Binary files a/app/assets/images/ajax_loader_gray.gif and /dev/null differ diff --git a/app/assets/images/comment_add.png b/app/assets/images/comment_add.png new file mode 100644 index 00000000..836557ac Binary files /dev/null and b/app/assets/images/comment_add.png differ diff --git a/app/assets/images/diff_file_add.png b/app/assets/images/diff_file_add.png new file mode 100644 index 00000000..0f01128d Binary files /dev/null and b/app/assets/images/diff_file_add.png differ diff --git a/app/assets/images/diff_file_delete.png b/app/assets/images/diff_file_delete.png new file mode 100644 index 00000000..b4d360f3 Binary files /dev/null and b/app/assets/images/diff_file_delete.png differ diff --git a/app/assets/images/diff_file_info.png b/app/assets/images/diff_file_info.png new file mode 100644 index 00000000..8529e823 Binary files /dev/null and b/app/assets/images/diff_file_info.png differ diff --git a/app/assets/images/diff_file_notice.png b/app/assets/images/diff_file_notice.png new file mode 100644 index 00000000..d3b292f4 Binary files /dev/null and b/app/assets/images/diff_file_notice.png differ diff --git a/app/assets/images/diff_note_add.png b/app/assets/images/diff_note_add.png deleted file mode 100644 index 8ec15b70..00000000 Binary files a/app/assets/images/diff_note_add.png and /dev/null differ diff --git a/app/assets/images/download.png b/app/assets/images/download.png new file mode 100644 index 00000000..5791e723 Binary files /dev/null and b/app/assets/images/download.png differ diff --git a/app/assets/images/event_filter_comments.png b/app/assets/images/event_filter_comments.png new file mode 100644 index 00000000..aed113fa Binary files /dev/null and b/app/assets/images/event_filter_comments.png differ diff --git a/app/assets/images/event_filter_merged.png b/app/assets/images/event_filter_merged.png new file mode 100644 index 00000000..30aea0b6 Binary files /dev/null and b/app/assets/images/event_filter_merged.png differ diff --git a/app/assets/images/event_filter_push.png b/app/assets/images/event_filter_push.png new file mode 100644 index 00000000..930faee6 Binary files /dev/null and b/app/assets/images/event_filter_push.png differ diff --git a/app/assets/images/event_filter_team.png b/app/assets/images/event_filter_team.png new file mode 100644 index 00000000..2dc66c85 Binary files /dev/null and b/app/assets/images/event_filter_team.png differ diff --git a/app/assets/images/event_mr_merged.png b/app/assets/images/event_mr_merged.png new file mode 100644 index 00000000..30aea0b6 Binary files /dev/null and b/app/assets/images/event_mr_merged.png differ diff --git a/app/assets/images/event_push.png b/app/assets/images/event_push.png new file mode 100644 index 00000000..930faee6 Binary files /dev/null and b/app/assets/images/event_push.png differ diff --git a/app/assets/images/home_icon.PNG b/app/assets/images/home_icon.PNG new file mode 100644 index 00000000..b1d60d59 Binary files /dev/null and b/app/assets/images/home_icon.PNG differ diff --git a/app/assets/images/icon-attachment.png b/app/assets/images/icon-attachment.png new file mode 100644 index 00000000..168ad8dc Binary files /dev/null and b/app/assets/images/icon-attachment.png differ diff --git a/app/assets/images/list_view_icon.jpg b/app/assets/images/list_view_icon.jpg new file mode 100644 index 00000000..45a34cb4 Binary files /dev/null and b/app/assets/images/list_view_icon.jpg differ diff --git a/app/assets/images/logo-black.png b/app/assets/images/logo-black.png deleted file mode 100644 index 6567f2e5..00000000 Binary files a/app/assets/images/logo-black.png and /dev/null differ diff --git a/app/assets/images/logo-white.png b/app/assets/images/logo-white.png deleted file mode 100644 index ecec2405..00000000 Binary files a/app/assets/images/logo-white.png and /dev/null differ diff --git a/app/assets/images/logo.png b/app/assets/images/logo.png new file mode 100644 index 00000000..2d08c9f6 Binary files /dev/null and b/app/assets/images/logo.png differ diff --git a/app/assets/images/logo_basic.png b/app/assets/images/logo_basic.png new file mode 100644 index 00000000..bc5ec128 Binary files /dev/null and b/app/assets/images/logo_basic.png differ diff --git a/app/assets/images/logo_dark.png b/app/assets/images/logo_dark.png new file mode 100644 index 00000000..4a3e3391 Binary files /dev/null and b/app/assets/images/logo_dark.png differ diff --git a/app/assets/images/logo_text.png b/app/assets/images/logo_text.png new file mode 100644 index 00000000..c7466393 Binary files /dev/null and b/app/assets/images/logo_text.png differ diff --git a/app/assets/images/logo_text_tr.png b/app/assets/images/logo_text_tr.png new file mode 100644 index 00000000..fdb32ee2 Binary files /dev/null and b/app/assets/images/logo_text_tr.png differ diff --git a/app/assets/images/logo_white.png b/app/assets/images/logo_white.png new file mode 100644 index 00000000..366e3f3f Binary files /dev/null and b/app/assets/images/logo_white.png differ diff --git a/app/assets/images/onion_skin_sprites.gif b/app/assets/images/onion_skin_sprites.gif deleted file mode 100644 index 85d20260..00000000 Binary files a/app/assets/images/onion_skin_sprites.gif and /dev/null differ diff --git a/app/assets/images/service-disabled-gitlab-ci.png b/app/assets/images/service-disabled-gitlab-ci.png new file mode 100644 index 00000000..8d1f9d0b Binary files /dev/null and b/app/assets/images/service-disabled-gitlab-ci.png differ diff --git a/app/assets/images/service-gitlab-ci.png b/app/assets/images/service-gitlab-ci.png new file mode 100644 index 00000000..bcb30a3f Binary files /dev/null and b/app/assets/images/service-gitlab-ci.png differ diff --git a/app/assets/images/solarized_dark.png b/app/assets/images/solarized_dark.png deleted file mode 100644 index 728964bc..00000000 Binary files a/app/assets/images/solarized_dark.png and /dev/null differ diff --git a/app/assets/images/swipemode_sprites.gif b/app/assets/images/swipemode_sprites.gif deleted file mode 100644 index 327b3c31..00000000 Binary files a/app/assets/images/swipemode_sprites.gif and /dev/null differ diff --git a/app/assets/images/switch_icon.png b/app/assets/images/switch_icon.png deleted file mode 100644 index 7c11f206..00000000 Binary files a/app/assets/images/switch_icon.png and /dev/null differ diff --git a/app/assets/javascripts/admin.js.coffee b/app/assets/javascripts/admin.js.coffee index 1dafdf4b..76454c29 100644 --- a/app/assets/javascripts/admin.js.coffee +++ b/app/assets/javascripts/admin.js.coffee @@ -10,8 +10,3 @@ $ -> $('.log-tabs a').click (e) -> e.preventDefault() $(this).tab('show') - - $('.log-bottom').click (e) -> - e.preventDefault() - visible_log = $(".file_content:visible") - visible_log.animate({ scrollTop: visible_log.find('ol').height() }, "fast") diff --git a/app/assets/javascripts/api.js.coffee b/app/assets/javascripts/api.js.coffee deleted file mode 100644 index ca721517..00000000 --- a/app/assets/javascripts/api.js.coffee +++ /dev/null @@ -1,53 +0,0 @@ -@Api = - users_path: "/api/:version/users.json" - user_path: "/api/:version/users/:id.json" - notes_path: "/api/:version/projects/:id/notes.json" - - # Get 20 (depends on api) recent notes - # and sort the ascending from oldest to newest - notes: (project_id, callback) -> - url = Api.buildUrl(Api.notes_path) - url = url.replace(':id', project_id) - - $.ajax( - url: url, - data: - private_token: gon.api_token - gfm: true - recent: true - dataType: "json" - ).done (notes) -> - notes.sort (a, b) -> - return a.id - b.id - callback(notes) - - user: (user_id, callback) -> - url = Api.buildUrl(Api.user_path) - url = url.replace(':id', user_id) - - $.ajax( - url: url - data: - private_token: gon.api_token - dataType: "json" - ).done (user) -> - callback(user) - - # Return users list. Filtered by query - # Only active users retrieved - users: (query, callback) -> - url = Api.buildUrl(Api.users_path) - - $.ajax( - url: url - data: - private_token: gon.api_token - search: query - per_page: 20 - active: true - dataType: "json" - ).done (users) -> - callback(users) - - buildUrl: (url) -> - return url.replace(':version', gon.api_version) diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index adb4009f..49effdf9 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -17,7 +17,6 @@ //= require bootstrap //= require modernizr //= require chosen-jquery -//= require select2 //= require raphael //= require g.raphael-min //= require g.bar-min diff --git a/app/assets/javascripts/behaviors/details_behavior.coffee b/app/assets/javascripts/behaviors/details_behavior.coffee deleted file mode 100644 index 7ad5c818..00000000 --- a/app/assets/javascripts/behaviors/details_behavior.coffee +++ /dev/null @@ -1,5 +0,0 @@ -$ -> - $("body").on "click", ".js-details-target", -> - container = $(@).closest(".js-details-container") - - container.toggleClass("open") diff --git a/app/assets/javascripts/behaviors/toggler_behavior.coffee b/app/assets/javascripts/behaviors/toggler_behavior.coffee deleted file mode 100644 index d2181e7b..00000000 --- a/app/assets/javascripts/behaviors/toggler_behavior.coffee +++ /dev/null @@ -1,13 +0,0 @@ -$ -> - $("body").on "click", ".js-toggler-target", -> - container = $(@).closest(".js-toggler-container") - - container.toggleClass("on") - - $("body").on "click", ".js-toggle-visibility-link", (e) -> - $(@).find('i'). - toggleClass('icon-chevron-down'). - toggleClass('icon-chevron-up') - container = $(".js-toggle-visibility-container") - container.toggleClass("hide") - e.preventDefault() diff --git a/app/assets/javascripts/branch-graph.js.coffee b/app/assets/javascripts/branch-graph.js.coffee deleted file mode 100644 index 2a668de2..00000000 --- a/app/assets/javascripts/branch-graph.js.coffee +++ /dev/null @@ -1,309 +0,0 @@ -class BranchGraph - constructor: (@element, @options) -> - @preparedCommits = {} - @mtime = 0 - @mspace = 0 - @parents = {} - @colors = ["#000"] - @offsetX = 120 - @offsetY = 20 - @unitTime = 30 - @unitSpace = 10 - @load() - - load: -> - $.ajax - url: @options.url - method: "get" - dataType: "json" - success: $.proxy((data) -> - $(".loading", @element).hide() - @prepareData data.days, data.commits - @buildGraph() - , this) - - prepareData: (@days, @commits) -> - @collectParents() - - for c in @commits - c.isParent = true if c.id of @parents - @preparedCommits[c.id] = c - - @collectColors() - - collectParents: -> - for c in @commits - @mtime = Math.max(@mtime, c.time) - @mspace = Math.max(@mspace, c.space) - for p in c.parents - @parents[p[0]] = true - @mspace = Math.max(@mspace, p[1]) - - collectColors: -> - k = 0 - while k < @mspace - @colors.push Raphael.getColor(.8) - # Skipping a few colors in the spectrum to get more contrast between colors - Raphael.getColor() - Raphael.getColor() - k++ - - buildGraph: -> - graphHeight = $(@element).height() - graphWidth = $(@element).width() - ch = Math.max(graphHeight, @offsetY + @unitTime * @mtime + 150) - cw = Math.max(graphWidth, @offsetX + @unitSpace * @mspace + 300) - @r = r = Raphael(@element.get(0), cw, ch) - top = r.set() - cuday = 0 - cumonth = "" - barHeight = Math.max(graphHeight, @unitTime * @days.length + 320) - - r.rect(0, 0, 26, barHeight).attr fill: "#222" - r.rect(26, 0, 20, barHeight).attr fill: "#444" - - for day, mm in @days - if cuday isnt day[0] - # Dates - r.text(36, @offsetY + @unitTime * mm, day[0]) - .attr( - font: "12px Monaco, monospace" - fill: "#DDD" - ) - cuday = day[0] - - if cumonth isnt day[1] - # Months - r.text(13, @offsetY + @unitTime * mm, day[1]) - .attr( - font: "12px Monaco, monospace" - fill: "#EEE" - ) - cumonth = day[1] - - for commit in @commits - x = @offsetX + @unitSpace * (@mspace - commit.space) - y = @offsetY + @unitTime * commit.time - - @drawDot(x, y, commit) - - @drawLines(x, y, commit) - - @appendLabel(x, y, commit.refs) if commit.refs - - @appendAnchor(top, commit, x, y) - - @markCommit(x, y, commit, graphHeight) - - top.toFront() - @bindEvents() - - bindEvents: -> - drag = {} - element = @element - dragger = (event) -> - element.scrollLeft drag.sl - (event.clientX - drag.x) - element.scrollTop drag.st - (event.clientY - drag.y) - - element.on mousedown: (event) -> - drag = - x: event.clientX - y: event.clientY - st: element.scrollTop() - sl: element.scrollLeft() - $(window).on "mousemove", dragger - - $(window).on - mouseup: -> - $(window).off "mousemove", dragger - keydown: (event) -> - # left - element.scrollLeft element.scrollLeft() - 50 if event.keyCode is 37 - # top - element.scrollTop element.scrollTop() - 50 if event.keyCode is 38 - # right - element.scrollLeft element.scrollLeft() + 50 if event.keyCode is 39 - # bottom - element.scrollTop element.scrollTop() + 50 if event.keyCode is 40 - - appendLabel: (x, y, refs) -> - r = @r - shortrefs = refs - # Truncate if longer than 15 chars - shortrefs = shortrefs.substr(0, 15) + "…" if shortrefs.length > 17 - text = r.text(x + 4, y, shortrefs).attr( - "text-anchor": "start" - font: "10px Monaco, monospace" - fill: "#FFF" - title: refs - ) - textbox = text.getBBox() - # Create rectangle based on the size of the textbox - rect = r.rect(x, y - 7, textbox.width + 5, textbox.height + 5, 4).attr( - fill: "#000" - "fill-opacity": .5 - stroke: "none" - ) - triangle = r.path(["M", x - 5, y, "L", x - 15, y - 4, "L", x - 15, y + 4, "Z"]).attr( - fill: "#000" - "fill-opacity": .5 - stroke: "none" - ) - - label = r.set(rect, text) - label.transform(["t", -rect.getBBox().width - 15, 0]) - - # Set text to front - text.toFront() - - appendAnchor: (top, commit, x, y) -> - r = @r - options = @options - anchor = r.circle(x, y, 10).attr( - fill: "#000" - opacity: 0 - cursor: "pointer" - ).click(-> - window.open options.commit_url.replace("%s", commit.id), "_blank" - ).hover(-> - @tooltip = r.commitTooltip(x + 5, y, commit) - top.push @tooltip.insertBefore(this) - , -> - @tooltip and @tooltip.remove() and delete @tooltip - ) - top.push anchor - - drawDot: (x, y, commit) -> - r = @r - r.circle(x, y, 3).attr( - fill: @colors[commit.space] - stroke: "none" - ) - r.rect(@offsetX + @unitSpace * @mspace + 10, y - 10, 20, 20).attr( - fill: "url(#{commit.author.icon})" - stroke: @colors[commit.space] - "stroke-width": 2 - ) - r.text(@offsetX + @unitSpace * @mspace + 35, y, commit.message.split("\n")[0]).attr( - "text-anchor": "start" - font: "14px Monaco, monospace" - ) - - drawLines: (x, y, commit) -> - r = @r - for parent, i in commit.parents - parentCommit = @preparedCommits[parent[0]] - parentY = @offsetY + @unitTime * parentCommit.time - parentX1 = @offsetX + @unitSpace * (@mspace - parentCommit.space) - parentX2 = @offsetX + @unitSpace * (@mspace - parent[1]) - - # Set line color - if parentCommit.space <= commit.space - color = @colors[commit.space] - - else - color = @colors[parentCommit.space] - - # Build line shape - if parent[1] is commit.space - offset = [0, 5] - arrow = "l-2,5,4,0,-2,-5,0,5" - - else if parent[1] < commit.space - offset = [3, 3] - arrow = "l5,0,-2,4,-3,-4,4,2" - - else - offset = [-3, 3] - arrow = "l-5,0,2,4,3,-4,-4,2" - - # Start point - route = ["M", x + offset[0], y + offset[1]] - - # Add arrow if not first parent - if i > 0 - route.push(arrow) - - # Circumvent if overlap - if commit.space isnt parentCommit.space or commit.space isnt parent[1] - route.push( - "L", parentX2, y + 10, - "L", parentX2, parentY - 5, - ) - - # End point - route.push("L", parentX1, parentY) - - r - .path(route) - .attr( - stroke: color - "stroke-width": 2) - - markCommit: (x, y, commit, graphHeight) -> - if commit.id is @options.commit_id - r = @r - r.path(["M", x + 5, y, "L", x + 15, y + 4, "L", x + 15, y - 4, "Z"]).attr( - fill: "#000" - "fill-opacity": .5 - stroke: "none" - ) - # Displayed in the center - @element.scrollTop(y - graphHeight / 2) - -Raphael::commitTooltip = (x, y, commit) -> - boxWidth = 300 - boxHeight = 200 - icon = @image(commit.author.icon, x, y, 20, 20) - nameText = @text(x + 25, y + 10, commit.author.name) - idText = @text(x, y + 35, commit.id) - messageText = @text(x, y + 50, commit.message) - textSet = @set(icon, nameText, idText, messageText).attr( - "text-anchor": "start" - font: "12px Monaco, monospace" - ) - nameText.attr( - font: "14px Arial" - "font-weight": "bold" - ) - - idText.attr fill: "#AAA" - @textWrap messageText, boxWidth - 50 - rect = @rect(x - 10, y - 10, boxWidth, 100, 4).attr( - fill: "#FFF" - stroke: "#000" - "stroke-linecap": "round" - "stroke-width": 2 - ) - tooltip = @set(rect, textSet) - rect.attr( - height: tooltip.getBBox().height + 10 - width: tooltip.getBBox().width + 10 - ) - - tooltip.transform ["t", 20, 20] - tooltip - -Raphael::textWrap = (t, width) -> - content = t.attr("text") - abc = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" - t.attr text: abc - letterWidth = t.getBBox().width / abc.length - t.attr text: content - words = content.split(" ") - x = 0 - s = [] - - for word in words - if x + (word.length * letterWidth) > width - s.push "\n" - x = 0 - x += word.length * letterWidth - s.push word + " " - - t.attr text: s.join("") - b = t.getBBox() - h = Math.abs(b.y2) - Math.abs(b.y) + 1 - t.attr y: b.y + h - -@BranchGraph = BranchGraph diff --git a/app/assets/javascripts/commit/file.js.coffee b/app/assets/javascripts/commit/file.js.coffee deleted file mode 100644 index a45ee58d..00000000 --- a/app/assets/javascripts/commit/file.js.coffee +++ /dev/null @@ -1,7 +0,0 @@ -class CommitFile - - constructor: (file) -> - if $('.image', file).length - new ImageFile(file) - -this.CommitFile = CommitFile \ No newline at end of file diff --git a/app/assets/javascripts/commit/image-file.js.coffee b/app/assets/javascripts/commit/image-file.js.coffee deleted file mode 100644 index f901a9e2..00000000 --- a/app/assets/javascripts/commit/image-file.js.coffee +++ /dev/null @@ -1,128 +0,0 @@ -class ImageFile - - # Width where images must fits in, for 2-up this gets divided by 2 - @availWidth = 900 - @viewModes = ['two-up', 'swipe'] - - constructor: (@file) -> - # Determine if old and new file has same dimensions, if not show 'two-up' view - this.requestImageInfo $('.two-up.view .frame.deleted img', @file), (deletedWidth, deletedHeight) => - this.requestImageInfo $('.two-up.view .frame.added img', @file), (width, height) => - if width == deletedWidth && height == deletedHeight - this.initViewModes() - else - this.initView('two-up') - - initViewModes: -> - viewMode = ImageFile.viewModes[0] - - $('.view-modes', @file).removeClass 'hide' - $('.view-modes-menu', @file).on 'click', 'li', (event) => - unless $(event.currentTarget).hasClass('active') - this.activateViewMode(event.currentTarget.className) - - this.activateViewMode(viewMode) - - activateViewMode: (viewMode) -> - $('.view-modes-menu li', @file) - .removeClass('active') - .filter(".#{viewMode}").addClass 'active' - $(".view:visible:not(.#{viewMode})", @file).fadeOut 200, => - $(".view.#{viewMode}", @file).fadeIn(200) - this.initView viewMode - - initView: (viewMode) -> - this.views[viewMode].call(this) - - prepareFrames = (view) -> - maxWidth = 0 - maxHeight = 0 - $('.frame', view).each (index, frame) => - width = $(frame).width() - height = $(frame).height() - maxWidth = if width > maxWidth then width else maxWidth - maxHeight = if height > maxHeight then height else maxHeight - .css - width: maxWidth - height: maxHeight - - [maxWidth, maxHeight] - - views: - 'two-up': -> - $('.two-up.view .wrap', @file).each (index, wrap) => - $('img', wrap).each -> - currentWidth = $(this).width() - if currentWidth > ImageFile.availWidth / 2 - $(this).width ImageFile.availWidth / 2 - - this.requestImageInfo $('img', wrap), (width, height) -> - $('.image-info .meta-width', wrap).text "#{width}px" - $('.image-info .meta-height', wrap).text "#{height}px" - $('.image-info', wrap).removeClass('hide') - - 'swipe': -> - maxWidth = 0 - maxHeight = 0 - - $('.swipe.view', @file).each (index, view) => - - [maxWidth, maxHeight] = prepareFrames(view) - - $('.swipe-frame', view).css - width: maxWidth + 16 - height: maxHeight + 28 - - $('.swipe-wrap', view).css - width: maxWidth + 1 - height: maxHeight + 2 - - $('.swipe-bar', view).css - left: 0 - .draggable - axis: 'x' - containment: 'parent' - drag: (event) -> - $('.swipe-wrap', view).width (maxWidth + 1) - $(this).position().left - stop: (event) -> - $('.swipe-wrap', view).width (maxWidth + 1) - $(this).position().left - - 'onion-skin': -> - maxWidth = 0 - maxHeight = 0 - - dragTrackWidth = $('.drag-track', @file).width() - $('.dragger', @file).width() - - $('.onion-skin.view', @file).each (index, view) => - - [maxWidth, maxHeight] = prepareFrames(view) - - $('.onion-skin-frame', view).css - width: maxWidth + 16 - height: maxHeight + 28 - - $('.swipe-wrap', view).css - width: maxWidth + 1 - height: maxHeight + 2 - - $('.dragger', view).css - left: dragTrackWidth - .draggable - axis: 'x' - containment: 'parent' - drag: (event) -> - $('.frame.added', view).css('opacity', $(this).position().left / dragTrackWidth) - stop: (event) -> - $('.frame.added', view).css('opacity', $(this).position().left / dragTrackWidth) - - - - requestImageInfo: (img, callback) -> - domImg = img.get(0) - if domImg.complete - callback.call(this, domImg.naturalWidth, domImg.naturalHeight) - else - img.on 'load', => - callback.call(this, domImg.naturalWidth, domImg.naturalHeight) - -this.ImageFile = ImageFile \ No newline at end of file diff --git a/app/assets/javascripts/commits.js b/app/assets/javascripts/commits.js new file mode 100644 index 00000000..b31fe485 --- /dev/null +++ b/app/assets/javascripts/commits.js @@ -0,0 +1,59 @@ +var CommitsList = { + ref:null, + limit:0, + offset:0, + disable:false, + + init: + function(ref, limit) { + $(".day-commits-table li.commit").live('click', function(e){ + if(e.target.nodeName != "A") { + location.href = $(this).attr("url"); + e.stopPropagation(); + return false; + } + }); + + this.ref=ref; + this.limit=limit; + this.offset=limit; + this.initLoadMore(); + $('.loading').show(); + }, + + getOld: + function() { + $('.loading').show(); + $.ajax({ + type: "GET", + url: location.href, + data: "limit=" + this.limit + "&offset=" + this.offset + "&ref=" + this.ref, + complete: function(){ $('.loading').hide()}, + dataType: "script"}); + }, + + append: + function(count, html) { + $("#commits_list").append(html); + if(count > 0) { + this.offset += count; + } else { + this.disable = true; + } + }, + + initLoadMore: + function() { + $(document).endlessScroll({ + bottomPixels: 400, + fireDelay: 1000, + fireOnce:true, + ceaseFire: function() { + return CommitsList.disable; + }, + callback: function(i) { + CommitsList.getOld(); + } + }); + } +} diff --git a/app/assets/javascripts/commits.js.coffee b/app/assets/javascripts/commits.js.coffee deleted file mode 100644 index 47d6fcf8..00000000 --- a/app/assets/javascripts/commits.js.coffee +++ /dev/null @@ -1,54 +0,0 @@ -class CommitsList - @data = - ref: null - limit: 0 - offset: 0 - @disable = false - - @showProgress: -> - $('.loading').show() - - @hideProgress: -> - $('.loading').hide() - - @init: (ref, limit) -> - $(".day-commits-table li.commit").live 'click', (event) -> - if event.target.nodeName != "A" - location.href = $(this).attr("url") - e.stopPropagation() - return false - - @data.ref = ref - @data.limit = limit - @data.offset = limit - - this.initLoadMore() - this.showProgress(); - - @getOld: -> - this.showProgress() - $.ajax - type: "GET" - url: location.href - data: @data - complete: this.hideProgress - dataType: "script" - - @append: (count, html) -> - $("#commits-list").append(html) - if count > 0 - @data.offset += count - else - @disable = true - - @initLoadMore: -> - $(document).endlessScroll - bottomPixels: 400 - fireDelay: 1000 - fireOnce: true - ceaseFire: => - @disable - callback: => - this.getOld() - -this.CommitsList = CommitsList \ No newline at end of file diff --git a/app/assets/javascripts/dashboard.js.coffee b/app/assets/javascripts/dashboard.js.coffee deleted file mode 100644 index 4189c90b..00000000 --- a/app/assets/javascripts/dashboard.js.coffee +++ /dev/null @@ -1,39 +0,0 @@ -window.dashboardPage = -> - Pager.init 20, true - initSidebarTab() - $(".event_filter_link").bind "click", (event) -> - event.preventDefault() - toggleFilter $(this) - reloadActivities() - -reloadActivities = -> - $(".content_list").html '' - Pager.init 20, true - -toggleFilter = (sender) -> - sender.parent().toggleClass "inactive" - event_filters = $.cookie("event_filter") - filter = sender.attr("id").split("_")[0] - if event_filters - event_filters = event_filters.split(",") - else - event_filters = new Array() - - index = event_filters.indexOf(filter) - if index is -1 - event_filters.push filter - else - event_filters.splice index, 1 - - $.cookie "event_filter", event_filters.join(",") - -initSidebarTab = -> - key = "dashboard_sidebar_filter" - - # store selection in cookie - $('.dash-sidebar-tabs a').on 'click', (e) -> - $.cookie(key, $(e.target).attr('id')) - - # show tab from cookie - sidebar_filter = $.cookie(key) - $("#" + sidebar_filter).tab('show') if sidebar_filter diff --git a/app/assets/javascripts/extensions/array.js b/app/assets/javascripts/extensions/array.js deleted file mode 100644 index 7fccc9c9..00000000 --- a/app/assets/javascripts/extensions/array.js +++ /dev/null @@ -1,7 +0,0 @@ -Array.prototype.first = function() { - return this[0]; -} - -Array.prototype.last = function() { - return this[this.length-1]; -} \ No newline at end of file diff --git a/app/assets/javascripts/extensions/jquery.js.coffee b/app/assets/javascripts/extensions/jquery.js.coffee deleted file mode 100644 index 8a997fe3..00000000 --- a/app/assets/javascripts/extensions/jquery.js.coffee +++ /dev/null @@ -1,9 +0,0 @@ -$.fn.showAndHide = -> - $(@).show(). - delay(3000). - fadeOut() - -$.fn.enableButton = -> - $(@).removeAttr('disabled'). - removeClass('disabled') - diff --git a/app/assets/javascripts/gfm_auto_complete.js.coffee b/app/assets/javascripts/gfm_auto_complete.js.coffee index 1cc9d34d..ffc4c409 100644 --- a/app/assets/javascripts/gfm_auto_complete.js.coffee +++ b/app/assets/javascripts/gfm_auto_complete.js.coffee @@ -1,38 +1,52 @@ # Creates the variables for setting up GFM auto-completion window.GitLab ?= {} -GitLab.GfmAutoComplete = +GitLab.GfmAutoComplete ?= {} + +# Emoji +data = [] +template = "
  • ${name} ${name}
  • " +GitLab.GfmAutoComplete.Emoji = {data, template} + +# Team Members +data = [] +url = ''; +params = {private_token: '', page: 1} +GitLab.GfmAutoComplete.Members = {data, url, params} + +# Add GFM auto-completion to all input fields, that accept GFM input. +GitLab.GfmAutoComplete.setup = -> + input = $('.js-gfm-input') + # Emoji - Emoji: - data: [] - template: '
  • ${name} ${name}
  • ' + input.atWho ':', + data: GitLab.GfmAutoComplete.Emoji.data, + tpl: GitLab.GfmAutoComplete.Emoji.template # Team Members - Members: - data: [] - url: '' - params: - private_token: '' - template: '
  • ${username} ${name}
  • ' + input.atWho '@', (query, callback) -> + (getMoreMembers = -> + $.getJSON(GitLab.GfmAutoComplete.Members.url, GitLab.GfmAutoComplete.Members.params) + .success (members) -> + # pick the data we need + newMembersData = $.map(members, (m) -> m.name ) - # Add GFM auto-completion to all input fields, that accept GFM input. - setup: -> - input = $('.js-gfm-input') + # add the new page of data to the rest + $.merge(GitLab.GfmAutoComplete.Members.data, newMembersData) - # Emoji - input.atWho ':', - data: @Emoji.data - tpl: @Emoji.template + # show the pop-up with a copy of the current data + callback(GitLab.GfmAutoComplete.Members.data[..]) - # Team Members - input.atWho '@', - tpl: @Members.template - callback: (query, callback) => - request_params = $.extend({}, @Members.params, query: query) - $.getJSON(@Members.url, request_params).done (members) => - new_members_data = $.map(members, (m) -> - username: m.username, - name: m.name - ) - callback(new_members_data) + # are we past the last page? + if newMembersData.length is 0 + # set static data and stop callbacks + input.atWho '@', + data: GitLab.GfmAutoComplete.Members.data + callback: null + else + # get next page + getMoreMembers() + # so the next request gets the next page + GitLab.GfmAutoComplete.Members.params.page += 1 + ).call() diff --git a/app/assets/javascripts/issues.js b/app/assets/javascripts/issues.js index 9ba1a3f1..e2fe1075 100644 --- a/app/assets/javascripts/issues.js +++ b/app/assets/javascripts/issues.js @@ -1,3 +1,43 @@ +function switchToNewIssue(){ + $(".issues_content").hide("fade", { direction: "left" }, 150, function(){ + $('select#issue_assignee_id').chosen(); + $('select#issue_milestone_id').chosen(); + $("#new_issue_dialog").show("fade", { direction: "right" }, 150); + $('.top-tabs .add_new').hide(); + disableButtonIfEmptyField("#issue_title", ".save-btn"); + GitLab.GfmAutoComplete.setup(); + }); +} + +function switchToEditIssue(){ + $(".issues_content").hide("fade", { direction: "left" }, 150, function(){ + $('select#issue_assignee_id').chosen(); + $('select#issue_milestone_id').chosen(); + $("#edit_issue_dialog").show("fade", { direction: "right" }, 150); + $('.add_new').hide(); + disableButtonIfEmptyField("#issue_title", ".save-btn"); + GitLab.GfmAutoComplete.setup(); + }); +} + +function switchFromNewIssue(){ + backToIssues(); +} + +function switchFromEditIssue(){ + backToIssues(); +} + +function backToIssues(){ + $("#edit_issue_dialog, #new_issue_dialog").hide("fade", { direction: "right" }, 150, function(){ + $(".issues_content").show("fade", { direction: "left" }, 150, function() { + $("#edit_issue_dialog").html(""); + $("#new_issue_dialog").html(""); + $('.add_new').show(); + }); + }); +} + function initIssuesSearch() { var href = $('#issue_search_form').attr('action'); var last_terms = ''; @@ -11,7 +51,7 @@ function initIssuesSearch() { last_terms = terms; if (terms.length >= 2 || terms.length == 0) { - $.get(href, { 'status': status, 'terms': terms, 'milestone_id': milestone_id }, function(response) { + $.get(href, { 'f': status, 'terms': terms, 'milestone_id': milestone_id }, function(response) { $('#issues-table').html(response); }); } @@ -36,15 +76,23 @@ function issuesPage(){ $(this).closest("form").submit(); }); - $('body').on('ajax:success', '.close_issue, .reopen_issue', function(){ + $("#new_issue_link").click(function(){ + updateNewIssueURL(); + }); + + $('body').on('ajax:success', '.close_issue, .reopen_issue, #new_issue', function(){ var t = $(this), totalIssues, - reopen = t.hasClass('reopen_issue'); - $('.issue_counter').each(function(){ + reopen = t.hasClass('reopen_issue'), + newIssue = false; + if( this.id == 'new_issue' ){ + newIssue = true; + } + $('.issue_counter, #new_issue').each(function(){ var issue = $(this); totalIssues = parseInt( $(this).html(), 10 ); - if( reopen && issue.closest('.main_menu').length ){ + if( newIssue || ( reopen && issue.closest('.main_menu').length ) ){ $(this).html( totalIssues+1 ); }else { $(this).html( totalIssues-1 ); @@ -78,3 +126,20 @@ function issuesCheckChanged() { $('.issues_filters').show(); } } + +function updateNewIssueURL(){ + var new_issue_link = $("#new_issue_link"); + var milestone_id = $("#milestone_id").val(); + var assignee_id = $("#assignee_id").val(); + var new_href = ""; + if(milestone_id){ + new_href = "issue[milestone_id]=" + milestone_id + "&"; + } + if(assignee_id){ + new_href = new_href + "issue[assignee_id]=" + assignee_id; + } + if(new_href.length){ + new_href = new_issue_link.attr("href") + "?" + new_href; + new_issue_link.attr("href", new_href); + } +}; diff --git a/app/assets/javascripts/lib/jquery.timeago.js b/app/assets/javascripts/lib/jquery.timeago.js deleted file mode 100644 index cc17aa7d..00000000 --- a/app/assets/javascripts/lib/jquery.timeago.js +++ /dev/null @@ -1,181 +0,0 @@ -/** - * Timeago is a jQuery plugin that makes it easy to support automatically - * updating fuzzy timestamps (e.g. "4 minutes ago" or "about 1 day ago"). - * - * @name timeago - * @version 1.1.0 - * @requires jQuery v1.2.3+ - * @author Ryan McGeary - * @license MIT License - http://www.opensource.org/licenses/mit-license.php - * - * For usage and examples, visit: - * http://timeago.yarp.com/ - * - * Copyright (c) 2008-2013, Ryan McGeary (ryan -[at]- mcgeary [*dot*] org) - */ - -(function (factory) { - if (typeof define === 'function' && define.amd) { - // AMD. Register as an anonymous module. - define(['jquery'], factory); - } else { - // Browser globals - factory(jQuery); - } -}(function ($) { - $.timeago = function(timestamp) { - if (timestamp instanceof Date) { - return inWords(timestamp); - } else if (typeof timestamp === "string") { - return inWords($.timeago.parse(timestamp)); - } else if (typeof timestamp === "number") { - return inWords(new Date(timestamp)); - } else { - return inWords($.timeago.datetime(timestamp)); - } - }; - var $t = $.timeago; - - $.extend($.timeago, { - settings: { - refreshMillis: 60000, - allowFuture: false, - strings: { - prefixAgo: null, - prefixFromNow: null, - suffixAgo: "ago", - suffixFromNow: "from now", - seconds: "less than a minute", - minute: "about a minute", - minutes: "%d minutes", - hour: "about an hour", - hours: "about %d hours", - day: "a day", - days: "%d days", - month: "about a month", - months: "%d months", - year: "about a year", - years: "%d years", - wordSeparator: " ", - numbers: [] - } - }, - inWords: function(distanceMillis) { - var $l = this.settings.strings; - var prefix = $l.prefixAgo; - var suffix = $l.suffixAgo; - if (this.settings.allowFuture) { - if (distanceMillis < 0) { - prefix = $l.prefixFromNow; - suffix = $l.suffixFromNow; - } - } - - var seconds = Math.abs(distanceMillis) / 1000; - var minutes = seconds / 60; - var hours = minutes / 60; - var days = hours / 24; - var years = days / 365; - - function substitute(stringOrFunction, number) { - var string = $.isFunction(stringOrFunction) ? stringOrFunction(number, distanceMillis) : stringOrFunction; - var value = ($l.numbers && $l.numbers[number]) || number; - return string.replace(/%d/i, value); - } - - var words = seconds < 45 && substitute($l.seconds, Math.round(seconds)) || - seconds < 90 && substitute($l.minute, 1) || - minutes < 45 && substitute($l.minutes, Math.round(minutes)) || - minutes < 90 && substitute($l.hour, 1) || - hours < 24 && substitute($l.hours, Math.round(hours)) || - hours < 42 && substitute($l.day, 1) || - days < 30 && substitute($l.days, Math.round(days)) || - days < 45 && substitute($l.month, 1) || - days < 365 && substitute($l.months, Math.round(days / 30)) || - years < 1.5 && substitute($l.year, 1) || - substitute($l.years, Math.round(years)); - - var separator = $l.wordSeparator || ""; - if ($l.wordSeparator === undefined) { separator = " "; } - return $.trim([prefix, words, suffix].join(separator)); - }, - parse: function(iso8601) { - var s = $.trim(iso8601); - s = s.replace(/\.\d+/,""); // remove milliseconds - s = s.replace(/-/,"/").replace(/-/,"/"); - s = s.replace(/T/," ").replace(/Z/," UTC"); - s = s.replace(/([\+\-]\d\d)\:?(\d\d)/," $1$2"); // -04:00 -> -0400 - return new Date(s); - }, - datetime: function(elem) { - var iso8601 = $t.isTime(elem) ? $(elem).attr("datetime") : $(elem).attr("title"); - return $t.parse(iso8601); - }, - isTime: function(elem) { - // jQuery's `is()` doesn't play well with HTML5 in IE - return $(elem).get(0).tagName.toLowerCase() === "time"; // $(elem).is("time"); - } - }); - - // functions that can be called via $(el).timeago('action') - // init is default when no action is given - // functions are called with context of a single element - var functions = { - init: function(){ - var refresh_el = $.proxy(refresh, this); - refresh_el(); - var $s = $t.settings; - if ($s.refreshMillis > 0) { - setInterval(refresh_el, $s.refreshMillis); - } - }, - update: function(time){ - $(this).data('timeago', { datetime: $t.parse(time) }); - refresh.apply(this); - } - }; - - $.fn.timeago = function(action, options) { - var fn = action ? functions[action] : functions.init; - if(!fn){ - throw new Error("Unknown function name '"+ action +"' for timeago"); - } - // each over objects here and call the requested function - this.each(function(){ - fn.call(this, options); - }); - return this; - }; - - function refresh() { - var data = prepareData(this); - if (!isNaN(data.datetime)) { - $(this).text(inWords(data.datetime)); - } - return this; - } - - function prepareData(element) { - element = $(element); - if (!element.data("timeago")) { - element.data("timeago", { datetime: $t.datetime(element) }); - var text = $.trim(element.text()); - if (text.length > 0 && !($t.isTime(element) && element.attr("title"))) { - element.attr("title", text); - } - } - return element.data("timeago"); - } - - function inWords(date) { - return $t.inWords(distance(date)); - } - - function distance(date) { - return (new Date().getTime() - date.getTime()); - } - - // fix for IE6 suckage - document.createElement("abbr"); - document.createElement("time"); -})); diff --git a/app/assets/javascripts/lib/md5.js b/app/assets/javascripts/lib/md5.js deleted file mode 100644 index b63716ea..00000000 --- a/app/assets/javascripts/lib/md5.js +++ /dev/null @@ -1,211 +0,0 @@ -function md5 (str) { - // http://kevin.vanzonneveld.net - // + original by: Webtoolkit.info (http://www.webtoolkit.info/) - // + namespaced by: Michael White (http://getsprink.com) - // + tweaked by: Jack - // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) - // + input by: Brett Zamir (http://brett-zamir.me) - // + bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) - // - depends on: utf8_encode - // * example 1: md5('Kevin van Zonneveld'); - // * returns 1: '6e658d4bfcb59cc13f96c14450ac40b9' - var xl; - - var rotateLeft = function (lValue, iShiftBits) { - return (lValue << iShiftBits) | (lValue >>> (32 - iShiftBits)); - }; - - var addUnsigned = function (lX, lY) { - var lX4, lY4, lX8, lY8, lResult; - lX8 = (lX & 0x80000000); - lY8 = (lY & 0x80000000); - lX4 = (lX & 0x40000000); - lY4 = (lY & 0x40000000); - lResult = (lX & 0x3FFFFFFF) + (lY & 0x3FFFFFFF); - if (lX4 & lY4) { - return (lResult ^ 0x80000000 ^ lX8 ^ lY8); - } - if (lX4 | lY4) { - if (lResult & 0x40000000) { - return (lResult ^ 0xC0000000 ^ lX8 ^ lY8); - } else { - return (lResult ^ 0x40000000 ^ lX8 ^ lY8); - } - } else { - return (lResult ^ lX8 ^ lY8); - } - }; - - var _F = function (x, y, z) { - return (x & y) | ((~x) & z); - }; - var _G = function (x, y, z) { - return (x & z) | (y & (~z)); - }; - var _H = function (x, y, z) { - return (x ^ y ^ z); - }; - var _I = function (x, y, z) { - return (y ^ (x | (~z))); - }; - - var _FF = function (a, b, c, d, x, s, ac) { - a = addUnsigned(a, addUnsigned(addUnsigned(_F(b, c, d), x), ac)); - return addUnsigned(rotateLeft(a, s), b); - }; - - var _GG = function (a, b, c, d, x, s, ac) { - a = addUnsigned(a, addUnsigned(addUnsigned(_G(b, c, d), x), ac)); - return addUnsigned(rotateLeft(a, s), b); - }; - - var _HH = function (a, b, c, d, x, s, ac) { - a = addUnsigned(a, addUnsigned(addUnsigned(_H(b, c, d), x), ac)); - return addUnsigned(rotateLeft(a, s), b); - }; - - var _II = function (a, b, c, d, x, s, ac) { - a = addUnsigned(a, addUnsigned(addUnsigned(_I(b, c, d), x), ac)); - return addUnsigned(rotateLeft(a, s), b); - }; - - var convertToWordArray = function (str) { - var lWordCount; - var lMessageLength = str.length; - var lNumberOfWords_temp1 = lMessageLength + 8; - var lNumberOfWords_temp2 = (lNumberOfWords_temp1 - (lNumberOfWords_temp1 % 64)) / 64; - var lNumberOfWords = (lNumberOfWords_temp2 + 1) * 16; - var lWordArray = new Array(lNumberOfWords - 1); - var lBytePosition = 0; - var lByteCount = 0; - while (lByteCount < lMessageLength) { - lWordCount = (lByteCount - (lByteCount % 4)) / 4; - lBytePosition = (lByteCount % 4) * 8; - lWordArray[lWordCount] = (lWordArray[lWordCount] | (str.charCodeAt(lByteCount) << lBytePosition)); - lByteCount++; - } - lWordCount = (lByteCount - (lByteCount % 4)) / 4; - lBytePosition = (lByteCount % 4) * 8; - lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80 << lBytePosition); - lWordArray[lNumberOfWords - 2] = lMessageLength << 3; - lWordArray[lNumberOfWords - 1] = lMessageLength >>> 29; - return lWordArray; - }; - - var wordToHex = function (lValue) { - var wordToHexValue = "", - wordToHexValue_temp = "", - lByte, lCount; - for (lCount = 0; lCount <= 3; lCount++) { - lByte = (lValue >>> (lCount * 8)) & 255; - wordToHexValue_temp = "0" + lByte.toString(16); - wordToHexValue = wordToHexValue + wordToHexValue_temp.substr(wordToHexValue_temp.length - 2, 2); - } - return wordToHexValue; - }; - - var x = [], - k, AA, BB, CC, DD, a, b, c, d, S11 = 7, - S12 = 12, - S13 = 17, - S14 = 22, - S21 = 5, - S22 = 9, - S23 = 14, - S24 = 20, - S31 = 4, - S32 = 11, - S33 = 16, - S34 = 23, - S41 = 6, - S42 = 10, - S43 = 15, - S44 = 21; - - str = this.utf8_encode(str); - x = convertToWordArray(str); - a = 0x67452301; - b = 0xEFCDAB89; - c = 0x98BADCFE; - d = 0x10325476; - - xl = x.length; - for (k = 0; k < xl; k += 16) { - AA = a; - BB = b; - CC = c; - DD = d; - a = _FF(a, b, c, d, x[k + 0], S11, 0xD76AA478); - d = _FF(d, a, b, c, x[k + 1], S12, 0xE8C7B756); - c = _FF(c, d, a, b, x[k + 2], S13, 0x242070DB); - b = _FF(b, c, d, a, x[k + 3], S14, 0xC1BDCEEE); - a = _FF(a, b, c, d, x[k + 4], S11, 0xF57C0FAF); - d = _FF(d, a, b, c, x[k + 5], S12, 0x4787C62A); - c = _FF(c, d, a, b, x[k + 6], S13, 0xA8304613); - b = _FF(b, c, d, a, x[k + 7], S14, 0xFD469501); - a = _FF(a, b, c, d, x[k + 8], S11, 0x698098D8); - d = _FF(d, a, b, c, x[k + 9], S12, 0x8B44F7AF); - c = _FF(c, d, a, b, x[k + 10], S13, 0xFFFF5BB1); - b = _FF(b, c, d, a, x[k + 11], S14, 0x895CD7BE); - a = _FF(a, b, c, d, x[k + 12], S11, 0x6B901122); - d = _FF(d, a, b, c, x[k + 13], S12, 0xFD987193); - c = _FF(c, d, a, b, x[k + 14], S13, 0xA679438E); - b = _FF(b, c, d, a, x[k + 15], S14, 0x49B40821); - a = _GG(a, b, c, d, x[k + 1], S21, 0xF61E2562); - d = _GG(d, a, b, c, x[k + 6], S22, 0xC040B340); - c = _GG(c, d, a, b, x[k + 11], S23, 0x265E5A51); - b = _GG(b, c, d, a, x[k + 0], S24, 0xE9B6C7AA); - a = _GG(a, b, c, d, x[k + 5], S21, 0xD62F105D); - d = _GG(d, a, b, c, x[k + 10], S22, 0x2441453); - c = _GG(c, d, a, b, x[k + 15], S23, 0xD8A1E681); - b = _GG(b, c, d, a, x[k + 4], S24, 0xE7D3FBC8); - a = _GG(a, b, c, d, x[k + 9], S21, 0x21E1CDE6); - d = _GG(d, a, b, c, x[k + 14], S22, 0xC33707D6); - c = _GG(c, d, a, b, x[k + 3], S23, 0xF4D50D87); - b = _GG(b, c, d, a, x[k + 8], S24, 0x455A14ED); - a = _GG(a, b, c, d, x[k + 13], S21, 0xA9E3E905); - d = _GG(d, a, b, c, x[k + 2], S22, 0xFCEFA3F8); - c = _GG(c, d, a, b, x[k + 7], S23, 0x676F02D9); - b = _GG(b, c, d, a, x[k + 12], S24, 0x8D2A4C8A); - a = _HH(a, b, c, d, x[k + 5], S31, 0xFFFA3942); - d = _HH(d, a, b, c, x[k + 8], S32, 0x8771F681); - c = _HH(c, d, a, b, x[k + 11], S33, 0x6D9D6122); - b = _HH(b, c, d, a, x[k + 14], S34, 0xFDE5380C); - a = _HH(a, b, c, d, x[k + 1], S31, 0xA4BEEA44); - d = _HH(d, a, b, c, x[k + 4], S32, 0x4BDECFA9); - c = _HH(c, d, a, b, x[k + 7], S33, 0xF6BB4B60); - b = _HH(b, c, d, a, x[k + 10], S34, 0xBEBFBC70); - a = _HH(a, b, c, d, x[k + 13], S31, 0x289B7EC6); - d = _HH(d, a, b, c, x[k + 0], S32, 0xEAA127FA); - c = _HH(c, d, a, b, x[k + 3], S33, 0xD4EF3085); - b = _HH(b, c, d, a, x[k + 6], S34, 0x4881D05); - a = _HH(a, b, c, d, x[k + 9], S31, 0xD9D4D039); - d = _HH(d, a, b, c, x[k + 12], S32, 0xE6DB99E5); - c = _HH(c, d, a, b, x[k + 15], S33, 0x1FA27CF8); - b = _HH(b, c, d, a, x[k + 2], S34, 0xC4AC5665); - a = _II(a, b, c, d, x[k + 0], S41, 0xF4292244); - d = _II(d, a, b, c, x[k + 7], S42, 0x432AFF97); - c = _II(c, d, a, b, x[k + 14], S43, 0xAB9423A7); - b = _II(b, c, d, a, x[k + 5], S44, 0xFC93A039); - a = _II(a, b, c, d, x[k + 12], S41, 0x655B59C3); - d = _II(d, a, b, c, x[k + 3], S42, 0x8F0CCC92); - c = _II(c, d, a, b, x[k + 10], S43, 0xFFEFF47D); - b = _II(b, c, d, a, x[k + 1], S44, 0x85845DD1); - a = _II(a, b, c, d, x[k + 8], S41, 0x6FA87E4F); - d = _II(d, a, b, c, x[k + 15], S42, 0xFE2CE6E0); - c = _II(c, d, a, b, x[k + 6], S43, 0xA3014314); - b = _II(b, c, d, a, x[k + 13], S44, 0x4E0811A1); - a = _II(a, b, c, d, x[k + 4], S41, 0xF7537E82); - d = _II(d, a, b, c, x[k + 11], S42, 0xBD3AF235); - c = _II(c, d, a, b, x[k + 2], S43, 0x2AD7D2BB); - b = _II(b, c, d, a, x[k + 9], S44, 0xEB86D391); - a = addUnsigned(a, AA); - b = addUnsigned(b, BB); - c = addUnsigned(c, CC); - d = addUnsigned(d, DD); - } - - var temp = wordToHex(a) + wordToHex(b) + wordToHex(c) + wordToHex(d); - - return temp.toLowerCase(); -} diff --git a/app/assets/javascripts/lib/utf8_encode.js b/app/assets/javascripts/lib/utf8_encode.js deleted file mode 100644 index 39ffe44d..00000000 --- a/app/assets/javascripts/lib/utf8_encode.js +++ /dev/null @@ -1,70 +0,0 @@ -function utf8_encode (argString) { - // http://kevin.vanzonneveld.net - // + original by: Webtoolkit.info (http://www.webtoolkit.info/) - // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) - // + improved by: sowberry - // + tweaked by: Jack - // + bugfixed by: Onno Marsman - // + improved by: Yves Sucaet - // + bugfixed by: Onno Marsman - // + bugfixed by: Ulrich - // + bugfixed by: Rafal Kukawski - // + improved by: kirilloid - // + bugfixed by: kirilloid - // * example 1: utf8_encode('Kevin van Zonneveld'); - // * returns 1: 'Kevin van Zonneveld' - - if (argString === null || typeof argString === "undefined") { - return ""; - } - - var string = (argString + ''); // .replace(/\r\n/g, "\n").replace(/\r/g, "\n"); - var utftext = '', - start, end, stringl = 0; - - start = end = 0; - stringl = string.length; - for (var n = 0; n < stringl; n++) { - var c1 = string.charCodeAt(n); - var enc = null; - - if (c1 < 128) { - end++; - } else if (c1 > 127 && c1 < 2048) { - enc = String.fromCharCode( - (c1 >> 6) | 192, - ( c1 & 63) | 128 - ); - } else if (c1 & 0xF800 != 0xD800) { - enc = String.fromCharCode( - (c1 >> 12) | 224, - ((c1 >> 6) & 63) | 128, - ( c1 & 63) | 128 - ); - } else { // surrogate pairs - if (c1 & 0xFC00 != 0xD800) { throw new RangeError("Unmatched trail surrogate at " + n); } - var c2 = string.charCodeAt(++n); - if (c2 & 0xFC00 != 0xDC00) { throw new RangeError("Unmatched lead surrogate at " + (n-1)); } - c1 = ((c1 & 0x3FF) << 10) + (c2 & 0x3FF) + 0x10000; - enc = String.fromCharCode( - (c1 >> 18) | 240, - ((c1 >> 12) & 63) | 128, - ((c1 >> 6) & 63) | 128, - ( c1 & 63) | 128 - ); - } - if (enc !== null) { - if (end > start) { - utftext += string.slice(start, end); - } - utftext += enc; - start = end = n + 1; - } - } - - if (end > start) { - utftext += string.slice(start, stringl); - } - - return utftext; -} diff --git a/app/assets/javascripts/main.js.coffee b/app/assets/javascripts/main.js.coffee index 39ec86e6..3f4b0f61 100644 --- a/app/assets/javascripts/main.js.coffee +++ b/app/assets/javascripts/main.js.coffee @@ -7,20 +7,6 @@ window.slugify = (text) -> window.ajaxGet = (url) -> $.ajax({type: "GET", url: url, dataType: "script"}) -window.showAndHide = (selector) -> - -window.errorMessage = (message) -> - ehtml = $("

    ") - ehtml.addClass("error_message") - ehtml.html(message) - ehtml - -window.split = (val) -> - return val.split( /,\s*/ ) - -window.extractLast = (term) -> - return split( term ).pop() - # Disable button if text field is empty window.disableButtonIfEmptyField = (field_selector, button_selector) -> field = $(field_selector) @@ -34,22 +20,10 @@ window.disableButtonIfEmptyField = (field_selector, button_selector) -> else closest_submit.enable() -window.sanitize = (str) -> - return str.replace(/<(?:.|\n)*?>/gm, '') - -window.linkify = (str) -> - exp = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig - return str.replace(exp,"$1") - - $ -> # Click a .one_click_select field, select the contents $(".one_click_select").on 'click', -> $(@).select() - # Click a .appear-link, appear-data fadeout - $(".appear-link").on 'click', -> - $('.appear-data').fadeIn() - # Initialize chosen selects $('select.chosen').chosen() @@ -59,17 +33,6 @@ $ -> # Bottom tooltip $('.has_bottom_tooltip').tooltip(placement: 'bottom') - # Form submitter - $('.trigger-submit').on 'change', -> - $(@).parents('form').submit() - - $("abbr.timeago").timeago() - - # Flash - 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) -> diff --git a/app/assets/javascripts/merge_requests.js b/app/assets/javascripts/merge_requests.js new file mode 100644 index 00000000..cc6b0771 --- /dev/null +++ b/app/assets/javascripts/merge_requests.js @@ -0,0 +1,129 @@ +var MergeRequest = { + diffs_loaded: false, + commits_loaded: false, + opts: false, + + init: + function(opts) { + var self = this; + self.opts = opts; + + self.initTabs(); + self.initMergeWidget(); + + $(".mr_show_all_commits").bind("click", function() { + self.showAllCommits(); + }); + + $(".line_note_link, .line_note_reply_link").live("click", function(e) { + var form = $(".per_line_form"); + $(this).parent().parent().after(form); + form.find("#note_line_code").val($(this).attr("line_code")); + form.show(); + return false; + }); + }, + + initMergeWidget: + function() { + var self = this; + self.showState(self.opts.current_state); + + if($(".automerge_widget").length && self.opts.check_enable){ + $.get(self.opts.url_to_automerge_check, function(data){ + self.showState(data.state); + }, "json"); + } + }, + + initTabs: + function() { + $(".mr_nav_tabs a").live("click", function() { + $(".mr_nav_tabs a").parent().removeClass("active"); + $(this).parent().addClass("active"); + }); + + var current_tab; + if(this.opts.action == "diffs") { + current_tab = $(".mr_nav_tabs .merge-diffs-tab"); + } else { + current_tab = $(".mr_nav_tabs .merge-notes-tab"); + } + current_tab.parent().addClass("active"); + + this.initNotesTab(); + this.initDiffTab(); + }, + + initNotesTab: + function() { + $(".mr_nav_tabs a.merge-notes-tab").live("click", function(e) { + $(".merge-request-diffs").hide(); + $(".merge_request_notes").show(); + var mr_path = $(".merge-notes-tab").attr("data-url"); + history.pushState({ path: mr_path }, '', mr_path); + e.preventDefault(); + }); + }, + + initDiffTab: + function() { + $(".mr_nav_tabs a.merge-diffs-tab").live("click", function(e) { + if(!MergeRequest.diffs_loaded) { + MergeRequest.loadDiff(); + } + $(".merge_request_notes").hide(); + $(".merge-request-diffs").show(); + var mr_diff_path = $(".merge-diffs-tab").attr("data-url"); + history.pushState({ path: mr_diff_path }, '', mr_diff_path); + e.preventDefault(); + }); + + }, + + showState: + function(state){ + $(".automerge_widget").hide(); + $(".automerge_widget." + state).show(); + }, + + + loadDiff: + function() { + $(".dashboard-loader").show(); + $.ajax({ + type: "GET", + url: $(".merge-diffs-tab").attr("data-url"), + beforeSend: function(){ $('.status').addClass("loading")}, + complete: function(){ + MergeRequest.diffs_loaded = true; + $(".merge_request_notes").hide(); + $('.status').removeClass("loading"); + }, + dataType: "script"}); + }, + + showAllCommits: + function() { + $(".first_mr_commits").remove(); + $(".all_mr_commits").removeClass("hide"); + }, + + already_cannot_be_merged: + function(){ + $(".automerge_widget").hide(); + $(".merge_in_progress").hide(); + $(".automerge_widget.already_cannot_be_merged").show(); + } +}; + +/* + * Filter merge requests + */ +function merge_requestsPage() { + $("#assignee_id").chosen(); + $("#milestone_id").chosen(); + $("#milestone_id, #assignee_id").on("change", function(){ + $(this).closest("form").submit(); + }); +} diff --git a/app/assets/javascripts/merge_requests.js.coffee b/app/assets/javascripts/merge_requests.js.coffee deleted file mode 100644 index 890ca400..00000000 --- a/app/assets/javascripts/merge_requests.js.coffee +++ /dev/null @@ -1,101 +0,0 @@ -# -# * Filter merge requests -# -@merge_requestsPage = -> - $('#assignee_id').chosen() - $('#milestone_id').chosen() - $('#milestone_id, #assignee_id').on 'change', -> - $(this).closest('form').submit() - -class MergeRequest - - constructor: (@opts) -> - this.$el = $('.merge-request') - @diffs_loaded = false - @commits_loaded = false - - this.activateTab(@opts.action) - - this.bindEvents() - - this.initMergeWidget() - this.$('.show-all-commits').on 'click', => - this.showAllCommits() - - # Local jQuery finder - $: (selector) -> - this.$el.find(selector) - - initMergeWidget: -> - this.showState( @opts.current_status ) - - if this.$('.automerge_widget').length and @opts.check_enable - $.get @opts.url_to_automerge_check, (data) => - this.showState( data.merge_status ) - , 'json' - - if @opts.ci_enable - $.get @opts.url_to_ci_check, (data) => - this.showCiState data.status - , 'json' - - bindEvents: -> - this.$('.nav-tabs').on 'click', 'a', (event) => - a = $(event.currentTarget) - - href = a.attr('href') - History.replaceState {path: href}, document.title, href - - event.preventDefault() - - this.$('.nav-tabs').on 'click', 'li', (event) => - this.activateTab($(event.currentTarget).data('action')) - - this.$('.accept_merge_request').on 'click', -> - $('.automerge_widget.can_be_merged').hide() - $('.merge-in-progress').show() - - activateTab: (action) -> - this.$('.nav-tabs li').removeClass 'active' - this.$('.tab-content').hide() - switch action - when 'diffs' - this.$('.nav-tabs .diffs-tab').addClass 'active' - this.loadDiff() unless @diffs_loaded - this.$('.diffs').show() - else - this.$('.nav-tabs .notes-tab').addClass 'active' - this.$('.notes').show() - - showState: (state) -> - $('.automerge_widget').hide() - $('.automerge_widget.' + state).show() - - showCiState: (state) -> - $('.ci_widget').hide() - $('.ci_widget.ci-' + state).show() - - loadDiff: (event) -> - $('.dashboard-loader').show() - $.ajax - type: 'GET' - url: this.$('.nav-tabs .diffs-tab a').attr('href') - beforeSend: => - this.$('.status').addClass 'loading' - - complete: => - @diffs_loaded = true - this.$('.status').removeClass 'loading' - - dataType: 'script' - - showAllCommits: -> - this.$('.first-commits').remove() - this.$('.all-commits').removeClass 'hide' - - alreadyOrCannotBeMerged: -> - this.$('.automerge_widget').hide() - this.$('.merge-in-progress').hide() - this.$('.automerge_widget.already_cannot_be_merged').show() - -this.MergeRequest = MergeRequest diff --git a/app/assets/javascripts/milestones.js.coffee b/app/assets/javascripts/milestones.js.coffee index 99a52bf4..e40a69eb 100644 --- a/app/assets/javascripts/milestones.js.coffee +++ b/app/assets/javascripts/milestones.js.coffee @@ -1,14 +1,14 @@ $ -> - $('.milestone-issue-filter li[data-closed]').addClass('hide') + $('.milestone-issue-filter tr[data-closed]').addClass('hide') $('.milestone-issue-filter ul.nav li a').click -> $('.milestone-issue-filter li').toggleClass('active') - $('.milestone-issue-filter li[data-closed]').toggleClass('hide') + $('.milestone-issue-filter tr[data-closed]').toggleClass('hide') false - $('.milestone-merge-requests-filter li[data-closed]').addClass('hide') + $('.milestone-merge-requests-filter tr[data-closed]').addClass('hide') $('.milestone-merge-requests-filter ul.nav li a').click -> $('.milestone-merge-requests-filter li').toggleClass('active') - $('.milestone-merge-requests-filter li[data-closed]').toggleClass('hide') + $('.milestone-merge-requests-filter tr[data-closed]').toggleClass('hide') false diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js index f5005ec2..b6f65b7a 100644 --- a/app/assets/javascripts/notes.js +++ b/app/assets/javascripts/notes.js @@ -4,304 +4,77 @@ var NoteList = { target_params: null, target_id: 0, target_type: null, + top_id: 0, + bottom_id: 0, + loading_more_disabled: false, + reversed: false, - init: function(tid, tt, path) { - NoteList.notes_path = path + ".js"; - NoteList.target_id = tid; - NoteList.target_type = tt; - NoteList.target_params = "target_type=" + NoteList.target_type + "&target_id=" + NoteList.target_id; + init: + function(tid, tt, path) { + this.notes_path = path + ".js"; + this.target_id = tid; + this.target_type = tt; + this.reversed = $("#notes-list").is(".reversed"); + this.target_params = "target_type=" + this.target_type + "&target_id=" + this.target_id; - NoteList.setupMainTargetNoteForm(); + // get initial set of notes + this.getContent(); - // get initial set of notes - NoteList.getContent(); - - // add a new diff note - $(document).on("click", - ".js-add-diff-note-button", - NoteList.addDiffNote); - - // reply to diff/discussion notes - $(document).on("click", - ".js-discussion-reply-button", - NoteList.replyToDiscussionNote); - - // setup note preview - $(document).on("click", - ".js-note-preview-button", - NoteList.previewNote); - - // update the file name when an attachment is selected - $(document).on("change", - ".js-note-attachment-input", - NoteList.updateFormAttachment); - - // hide diff note form - $(document).on("click", - ".js-close-discussion-note-form", - NoteList.removeDiscussionNoteForm); - - // remove a note (in general) - $(document).on("click", - ".js-note-delete", - NoteList.removeNote); - - // reset main target form after submit - $(document).on("ajax:complete", - ".js-main-target-form", - NoteList.resetMainTargetForm); - - - $(document).on("click", - ".js-choose-note-attachment-button", - NoteList.chooseNoteAttachment); - - $(document).on("click", - ".js-show-outdated-discussion", - function(e) { $(this).next('.outdated-discussion').show(); e.preventDefault() }); - }, - - - /** - * When clicking on buttons - */ - - /** - * Called when clicking on the "add a comment" button on the side of a diff line. - * - * Inserts a temporary row for the form below the line. - * Sets up the form and shows it. - */ - addDiffNote: function(e) { - e.preventDefault(); - - // find the form - var form = $(".js-new-note-form"); - var row = $(this).closest("tr"); - var nextRow = row.next(); - - // does it already have notes? - if (nextRow.is(".notes_holder")) { - $.proxy(NoteList.replyToDiscussionNote, - nextRow.find(".js-discussion-reply-button") - ).call(); - } else { - // add a notes row and insert the form - row.after(''); - form.clone().appendTo(row.next().find(".notes_content")); - - // show the form - NoteList.setupDiscussionNoteForm($(this), row.next().find("form")); - } - }, - - /** - * Called when clicking the "Choose File" button. - * - * Opesn the file selection dialog. - */ - chooseNoteAttachment: function() { - var form = $(this).closest("form"); - - form.find(".js-note-attachment-input").click(); - }, - - /** - * Shows the note preview. - * - * Lets the server render GFM into Html and displays it. - * - * Note: uses the Toggler behavior to toggle preview/edit views/buttons - */ - previewNote: function(e) { - e.preventDefault(); - - var form = $(this).closest("form"); - var preview = form.find('.js-note-preview'); - var noteText = form.find('.js-note-text').val(); - - if(noteText.trim().length === 0) { - preview.text('Nothing to preview.'); - } else { - preview.text('Loading...'); - $.post($(this).data('url'), {note: noteText}) - .success(function(previewData) { - preview.html(previewData); + $("#notes-list, #new-notes-list").on("ajax:success", ".delete-note", function() { + $(this).closest('li').fadeOut(function() { + $(this).remove(); + NoteList.updateVotes(); }); - } - }, + }); - /** - * Called in response to "cancel" on a diff note form. - * - * Shows the reply button again. - * Removes the form and if necessary it's temporary row. - */ - removeDiscussionNoteForm: function() { - var form = $(this).closest("form"); - var row = form.closest("tr"); + $(".note-form-holder").on("ajax:before", function(){ + $(".submit_note").disable(); + }) - // show the reply button (will only work for replys) - form.prev(".js-discussion-reply-button").show(); + $(".note-form-holder").on("ajax:complete", function(){ + $(".submit_note").enable(); + $('#preview-note').hide(); + $('#note_note').show(); + }) - if (row.is(".js-temp-notes-holder")) { - // remove temporary row for diff lines - row.remove(); - } else { - // only remove the form - form.remove(); - } - }, + disableButtonIfEmptyField(".note-text", ".submit_note"); - /** - * Called in response to deleting a note of any kind. - * - * Removes the actual note from view. - * Removes the whole discussion if the last note is being removed. - */ - removeNote: function() { - var note = $(this).closest(".note"); - var notes = note.closest(".notes"); + $("#note_attachment").change(function(e){ + var val = $('.input-file').val(); + var filename = val.replace(/^.*[\\\/]/, ''); + $(".file_name").text(filename); + }); - // check if this is the last note for this line - if (notes.find(".note").length === 1) { - // for discussions - notes.closest(".discussion").remove(); - - // for diff lines - notes.closest("tr").remove(); - } - - note.remove(); - NoteList.updateVotes(); - }, - - /** - * Called when clicking on the "reply" button for a diff line. - * - * Shows the note form below the notes. - */ - replyToDiscussionNote: function() { - // find the form - var form = $(".js-new-note-form"); - - // hide reply button - $(this).hide(); - // insert the form after the button - form.clone().insertAfter($(this)); - - // show the form - NoteList.setupDiscussionNoteForm($(this), $(this).next("form")); - }, - - - /** - * Helper for inserting and setting up note forms. - */ - - - /** - * Called in response to creating a note failing validation. - * - * Adds the rendered errors to the respective form. - * If "discussionId" is null or undefined, the main target form is assumed. - */ - errorsOnForm: function(errorsHtml, discussionId) { - // find the form - if (discussionId) { - var form = $("form[rel='"+discussionId+"']"); - } else { - var form = $(".js-main-target-form"); - } - - form.find(".js-errors").remove(); - form.prepend(errorsHtml); - - form.find(".js-note-text").focus(); - }, - - - /** - * Shows the diff/discussion form and does some setup on it. - * - * Sets some hidden fields in the form. - * - * Note: dataHolder must have the "discussionId", "lineCode", "noteableType" - * and "noteableId" data attributes set. - */ - setupDiscussionNoteForm: function(dataHolder, form) { - // setup note target - form.attr("rel", dataHolder.data("discussionId")); - form.find("#note_commit_id").val(dataHolder.data("commitId")); - form.find("#note_line_code").val(dataHolder.data("lineCode")); - form.find("#note_noteable_type").val(dataHolder.data("noteableType")); - form.find("#note_noteable_id").val(dataHolder.data("noteableId")); - - NoteList.setupNoteForm(form); - - form.find(".js-note-text").focus(); - }, - - /** - * Shows the main form and does some setup on it. - * - * Sets some hidden fields in the form. - */ - setupMainTargetNoteForm: function() { - // find the form - var form = $(".js-new-note-form"); - // insert the form after the button - form.clone().replaceAll($(".js-main-target-form")); - - form = form.prev("form"); - - // show the form - NoteList.setupNoteForm(form); - - // fix classes - form.removeClass("js-new-note-form"); - form.addClass("js-main-target-form"); - - // remove unnecessary fields and buttons - form.find("#note_line_code").remove(); - form.find(".js-close-discussion-note-form").remove(); - }, - - /** - * General note form setup. - * - * * deactivates the submit button when text is empty - * * hides the preview button when text is empty - * * setup GFM auto complete - * * show the form - */ - setupNoteForm: function(form) { - disableButtonIfEmptyField(form.find(".js-note-text"), form.find(".js-comment-button")); - - form.removeClass("js-new-note-form"); - - // setup preview buttons - form.find(".js-note-edit-button, .js-note-preview-button") - .tooltip({ placement: 'left' }); - - previewButton = form.find(".js-note-preview-button"); - form.find(".js-note-text").on("input", function() { - if ($(this).val().trim() !== "") { - previewButton.removeClass("turn-off").addClass("turn-on"); - } else { - previewButton.removeClass("turn-on").addClass("turn-off"); + if(this.reversed) { + var textarea = $(".note-text"); + $('.note_advanced_opts').hide(); + textarea.css("height", "40px"); + textarea.on("focus", function(){ + $(this).css("height", "80px"); + $('.note_advanced_opts').show(); + }); } - }); - // remove notify commit author checkbox for non-commit notes - if (form.find("#note_noteable_type").val() !== "Commit") { - form.find(".js-notify-commit-author").remove(); - } + // Setup note preview + $(document).on('click', '#preview-link', function(e) { + $('#preview-note').text('Loading...'); - GitLab.GfmAutoComplete.setup(); + $(this).text($(this).text() === "Edit" ? "Preview" : "Edit"); - form.show(); - }, + var note_text = $('#note_note').val(); + + if(note_text.trim().length === 0) { + $('#preview-note').text('Nothing to preview.'); + } else { + $.post($(this).attr('href'), {note: note_text}).success(function(data) { + $('#preview-note').html(data); + }); + } + + $('#preview-note, #note_note').toggle(); + e.preventDefault(); + }); + }, /** @@ -311,93 +84,162 @@ var NoteList = { /** - * Gets an initial set of notes. + * Gets an inital set of notes. */ - getContent: function() { - $.ajax({ - url: NoteList.notes_path, - data: NoteList.target_params, - complete: function(){ $('.js-notes-busy').removeClass("loading")}, - beforeSend: function() { $('.js-notes-busy').addClass("loading") }, - dataType: "script" - }); - }, + getContent: + function() { + $.ajax({ + type: "GET", + url: this.notes_path, + data: this.target_params, + complete: function(){ $('.notes-status').removeClass("loading")}, + beforeSend: function() { $('.notes-status').addClass("loading") }, + dataType: "script"}); + }, /** * Called in response to getContent(). * Replaces the content of #notes-list with the given html. */ - setContent: function(newNoteIds, html) { - $("#notes-list").html(html); - }, + setContent: + function(first_id, last_id, html) { + this.top_id = first_id; + this.bottom_id = last_id; + $("#notes-list").html(html); + + // init infinite scrolling + this.initLoadMore(); + + // init getting new notes + if (this.reversed) { + this.initRefreshNew(); + } + }, /** - * Adds a single common note to #notes-list. - */ - appendNewNote: function(id, html) { - $("#notes-list").append(html); - NoteList.updateVotes(); - }, - - /** - * Adds a single discussion note to #notes-list. + * Handle loading more notes when scrolling to the bottom of the page. + * The id of the last note in the list is in this.bottom_id. * - * Also removes the corresponding form. + * Set up refreshing only new notes after all notes have been loaded. */ - appendNewDiscussionNote: function(discussionId, diffRowHtml, noteHtml) { - var form = $("form[rel='"+discussionId+"']"); - var row = form.closest("tr"); - // is this the first note of discussion? - if (row.is(".js-temp-notes-holder")) { - // insert the note and the reply button after the temp row - row.after(diffRowHtml); - // remove the note (will be added again below) - row.next().find(".note").remove(); - } - - // append new note to all matching discussions - $(".notes[rel='"+discussionId+"']").append(noteHtml); - - // cleanup after successfully creating a diff/discussion note - $.proxy(NoteList.removeDiscussionNoteForm, form).call(); - }, /** - * Called in response the main target form has been successfully submitted. - * - * Removes any errors. - * Resets text and preview. - * Resets buttons. + * Initializes loading more notes when scrolling to the bottom of the page. */ - resetMainTargetForm: function(){ - var form = $(this); - - // remove validation errors - form.find(".js-errors").remove(); - - // reset text and preview - var previewContainer = form.find(".js-toggler-container.note_text_and_preview"); - if (previewContainer.is(".on")) { - previewContainer.removeClass("on"); - } - form.find(".js-note-text").val("").trigger("input"); - }, + initLoadMore: + function() { + $(document).endlessScroll({ + bottomPixels: 400, + fireDelay: 1000, + fireOnce:true, + ceaseFire: function() { + return NoteList.loading_more_disabled; + }, + callback: function(i) { + NoteList.getMore(); + } + }); + }, /** - * Called after an attachment file has been selected. - * - * Updates the file name for the selected attachment. + * Gets an additional set of notes. */ - updateFormAttachment: function() { - var form = $(this).closest("form"); + getMore: + function() { + // only load more notes if there are no "new" notes + $('.loading').show(); + $.ajax({ + type: "GET", + url: this.notes_path, + data: this.target_params + "&loading_more=1&" + (this.reversed ? "before_id" : "after_id") + "=" + this.bottom_id, + complete: function(){ $('.notes-status').removeClass("loading")}, + beforeSend: function() { $('.notes-status').addClass("loading") }, + dataType: "script"}); + }, - // get only the basename - var filename = $(this).val().replace(/^.*[\\\/]/, ''); + /** + * Called in response to getMore(). + * Append notes to #notes-list. + */ + appendMoreNotes: + function(id, html) { + if(id != this.bottom_id) { + this.bottom_id = id; + $("#notes-list").append(html); + } + }, - form.find(".js-attachment-filename").text(filename); - }, + /** + * Called in response to getMore(). + * Disables loading more notes when scrolling to the bottom of the page. + * Initalizes refreshing new notes. + */ + finishedLoadingMore: + function() { + this.loading_more_disabled = true; + + // from now on only get new notes + if (!this.reversed) { + this.initRefreshNew(); + } + // make sure we are up to date + this.updateVotes(); + }, + + + /** + * Handle refreshing and adding of new notes. + * + * New notes are all notes that are created after the site has been loaded. + * The "old" notes are in #notes-list the "new" ones will be in #new-notes-list. + * The id of the last "old" note is in this.bottom_id. + */ + + + /** + * Initializes getting new notes every n seconds. + */ + initRefreshNew: + function() { + setInterval("NoteList.getNew()", 10000); + }, + + /** + * Gets the new set of notes. + */ + getNew: + function() { + $.ajax({ + type: "GET", + url: this.notes_path, + data: this.target_params + "&loading_new=1&after_id=" + (this.reversed ? this.top_id : this.bottom_id), + dataType: "script"}); + }, + + /** + * Called in response to getNew(). + * Replaces the content of #new-notes-list with the given html. + */ + replaceNewNotes: + function(html) { + $("#new-notes-list").html(html); + this.updateVotes(); + }, + + /** + * Adds a single note to #new-notes-list. + */ + appendNewNote: + function(id, html) { + if (this.reversed) { + $("#new-notes-list").prepend(html); + } else { + $("#new-notes-list").append(html); + } + this.updateVotes(); + }, /** * Recalculates the votes and updates them (if they are displayed at all). @@ -407,24 +249,67 @@ var NoteList = { * Might produce inaccurate results when not all notes have been loaded and a * recalculation is triggered (e.g. when deleting a note). */ - updateVotes: function() { - var votes = $("#votes .votes"); - var notes = $("#notes-list .note .vote"); + updateVotes: + function() { + var votes = $("#votes .votes"); + var notes = $("#notes-list, #new-notes-list").find(".note .vote"); - // only update if there is a vote display - if (votes.size()) { - var upvotes = notes.filter(".upvote").size(); - var downvotes = notes.filter(".downvote").size(); - var votesCount = upvotes + downvotes; - var upvotesPercent = votesCount ? (100.0 / votesCount * upvotes) : 0; - var downvotesPercent = votesCount ? (100.0 - upvotesPercent) : 0; + // only update if there is a vote display + if (votes.size()) { + var upvotes = notes.filter(".upvote").size(); + var downvotes = notes.filter(".downvote").size(); + var votesCount = upvotes + downvotes; + var upvotesPercent = votesCount ? (100.0 / votesCount * upvotes) : 0; + var downvotesPercent = votesCount ? (100.0 - upvotesPercent) : 0; - // change vote bar lengths - votes.find(".bar-success").css("width", upvotesPercent+"%"); - votes.find(".bar-danger").css("width", downvotesPercent+"%"); - // replace vote numbers - votes.find(".upvotes").text(votes.find(".upvotes").text().replace(/\d+/, upvotes)); - votes.find(".downvotes").text(votes.find(".downvotes").text().replace(/\d+/, downvotes)); + // change vote bar lengths + votes.find(".bar-success").css("width", upvotesPercent+"%"); + votes.find(".bar-danger").css("width", downvotesPercent+"%"); + // replace vote numbers + votes.find(".upvotes").text(votes.find(".upvotes").text().replace(/\d+/, upvotes)); + votes.find(".downvotes").text(votes.find(".downvotes").text().replace(/\d+/, downvotes)); + } } - } }; + +var PerLineNotes = { + init: + function() { + /** + * Called when clicking on the "add note" or "reply" button for a diff line. + * + * Shows the note form below the line. + * Sets some hidden fields in the form. + */ + $(".diff_file_content").on("click", ".line_note_link, .line_note_reply_link", function(e) { + var form = $(".per_line_form"); + $(this).closest("tr").after(form); + form.find("#note_line_code").val($(this).data("lineCode")); + form.show(); + e.preventDefault(); + }); + + disableButtonIfEmptyField(".line-note-text", ".submit_inline_note"); + + /** + * Called in response to successfully deleting a note on a diff line. + * + * Removes the actual note from view. + * Removes the reply button if the last note for that line has been removed. + */ + $(".diff_file_content").on("ajax:success", ".delete-note", function() { + var trNote = $(this).closest("tr"); + trNote.fadeOut(function() { + $(this).remove(); + }); + + // check if this is the last note for this line + // elements must really be removed for this to work reliably + var trLine = trNote.prev(); + var trRpl = trNote.next(); + if (trLine.is(".line_holder") && trRpl.is(".reply")) { + trRpl.fadeOut(function() { $(this).remove(); }); + } + }); + } +} diff --git a/app/assets/javascripts/pager.js b/app/assets/javascripts/pager.js new file mode 100644 index 00000000..769e8a62 --- /dev/null +++ b/app/assets/javascripts/pager.js @@ -0,0 +1,49 @@ +var Pager = { + limit:0, + offset:0, + disable:false, + + init: + function(limit) { + this.limit=limit; + this.offset=limit; + this.initLoadMore(); + }, + + getOld: + function() { + $('.loading').show(); + $.ajax({ + type: "GET", + url: location.href, + data: "limit=" + this.limit + "&offset=" + this.offset, + complete: function(){ $('.loading').hide()}, + dataType: "script"}); + }, + + append: + function(count, html) { + $(".content_list").append(html); + if(count > 0) { + this.offset += count; + } else { + this.disable = true; + } + }, + + initLoadMore: + function() { + $(document).endlessScroll({ + bottomPixels: 400, + fireDelay: 1000, + fireOnce:true, + ceaseFire: function() { + return Pager.disable; + }, + callback: function(i) { + $('.loading').show(); + Pager.getOld(); + } + }); + } +} diff --git a/app/assets/javascripts/pager.js.coffee b/app/assets/javascripts/pager.js.coffee deleted file mode 100644 index 5f606acd..00000000 --- a/app/assets/javascripts/pager.js.coffee +++ /dev/null @@ -1,42 +0,0 @@ -@Pager = - limit: 0 - offset: 0 - disable: false - init: (limit, preload) -> - @limit = limit - if preload - @offset = 0 - @getOld() - else - @offset = limit - @initLoadMore() - - getOld: -> - $(".loading").show() - $.ajax - type: "GET" - url: location.href - data: "limit=" + @limit + "&offset=" + @offset - complete: -> - $(".loading").hide() - - dataType: "script" - - append: (count, html) -> - $(".content_list").append html - if count > 0 - @offset += count - else - @disable = true - - initLoadMore: -> - $(document).endlessScroll - bottomPixels: 400 - fireDelay: 1000 - fireOnce: true - ceaseFire: -> - Pager.disable - - callback: (i) -> - $(".loading").show() - Pager.getOld() diff --git a/app/assets/javascripts/profile.js.coffee b/app/assets/javascripts/profile.js.coffee index 213133bc..e536afad 100644 --- a/app/assets/javascripts/profile.js.coffee +++ b/app/assets/javascripts/profile.js.coffee @@ -8,15 +8,3 @@ $ -> # Go up the hierarchy and show the corresponding submission feedback element $(@).closest('fieldset').find('.update-feedback').show('highlight', {color: '#DFF0D8'}, 500) - - $('.update-username form').on 'ajax:before', -> - $('.loading-gif').show() - $(this).find('.update-success').hide() - $(this).find('.update-failed').hide() - - $('.update-username form').on 'ajax:complete', -> - $(this).find('.btn-save').enableButton() - $(this).find('.loading-gif').hide() - - $('.update-notifications').on 'ajax:complete', -> - $(this).find('.btn-save').enableButton() diff --git a/app/assets/javascripts/projects.js.coffee b/app/assets/javascripts/projects.js.coffee index 24106c61..3059723d 100644 --- a/app/assets/javascripts/projects.js.coffee +++ b/app/assets/javascripts/projects.js.coffee @@ -1,4 +1,8 @@ window.Projects = -> + $('#project_name').on 'change', -> + slug = slugify $(@).val() + $('#project_code, #project_path').val slug + $('.new_project, .edit_project').on 'ajax:before', -> $('.project_new_holder, .project_edit_holder').hide() $('.save-project-loader').show() @@ -19,17 +23,9 @@ $ -> $('.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') - +class @GraphNav + @init: -> + $('.graph svg').css 'position', 'relative' + $('body').bind 'keyup', (e) -> + $('.graph svg').animate(left: '+=400') if e.keyCode is 37 # left + $('.graph svg').animate(left: '-=400') if e.keyCode is 39 # right diff --git a/app/assets/javascripts/tree.js.coffee b/app/assets/javascripts/tree.js.coffee index 10d0df70..3f8ed6c2 100644 --- a/app/assets/javascripts/tree.js.coffee +++ b/app/assets/javascripts/tree.js.coffee @@ -6,12 +6,17 @@ $ -> $('span.log_loading:first').removeClass('hide') $('#tree-slider .tree-item-file-name a, .breadcrumb li > a').live "click", -> - $("#tree-content-holder").hide("slide", { direction: "left" }, 400) + $("#tree-content-holder").hide("slide", { direction: "left" }, 150) # Make the entire tree-item row clickable, but not if clicking another link (like a commit message) $("#tree-slider .tree-item").live 'click', (e) -> $('.tree-item-file-name a', this).trigger('click') if (e.target.nodeName != "A") - + + # Show/Hide the loading spinner + $('#tree-slider .tree-item-file-name a, .breadcrumb a, .project-refs-form').live + "ajax:beforeSend": -> $('.tree_progress').addClass("loading") + "ajax:complete": -> $('.tree_progress').removeClass("loading") + # Maintain forward/back history while browsing the file tree ((window) -> History = window.History @@ -23,17 +28,12 @@ $ -> return false $('#tree-slider .tree-item-file-name a, .breadcrumb li > a').live 'click', (e) -> - History.pushState(null, null, decodeURIComponent($(@).attr('href'))) + History.pushState(null, null, $(@).attr('href')) return false History.Adapter.bind window, 'statechange', -> state = History.getState() - $.ajax({ - url: state.url, - dataType: 'script', - beforeSend: -> $('.tree_progress').addClass("loading"), - complete: -> $('.tree_progress').removeClass("loading") - }) + window.ajaxGet(state.url) )(window) # See if there are lines selected diff --git a/app/assets/javascripts/users_select.js.coffee b/app/assets/javascripts/users_select.js.coffee deleted file mode 100644 index f9e523ea..00000000 --- a/app/assets/javascripts/users_select.js.coffee +++ /dev/null @@ -1,36 +0,0 @@ -$ -> - userFormatResult = (user) -> - avatar = gon.gravatar_url - avatar = avatar.replace('%{hash}', md5(user.email)) - avatar = avatar.replace('%{size}', '24') - - markup = "

    " - markup += "
    " - markup += "
    " + user.name + "
    " - markup += "
    " + user.username + "
    " - markup += "
    " - markup - - userFormatSelection = (user) -> - user.name - - $('.ajax-users-select').select2 - placeholder: "Search for a user" - multiple: $('.ajax-users-select').hasClass('multiselect') - minimumInputLength: 0 - query: (query) -> - Api.users query.term, (users) -> - data = { results: users } - query.callback(data) - - initSelection: (element, callback) -> - id = $(element).val() - if id isnt "" - Api.user(id, callback) - - - formatResult: userFormatResult - formatSelection: userFormatSelection - dropdownCssClass: "ajax-users-dropdown" - escapeMarkup: (m) -> # we do not want to escape markup since we are displaying html in results - m diff --git a/app/assets/javascripts/wall.js.coffee b/app/assets/javascripts/wall.js.coffee deleted file mode 100644 index e2fca3dd..00000000 --- a/app/assets/javascripts/wall.js.coffee +++ /dev/null @@ -1,83 +0,0 @@ -@Wall = - note_ids: [] - project_id: null - - init: (project_id) -> - Wall.project_id = project_id - Wall.getContent() - Wall.initRefresh() - Wall.initForm() - - # - # Gets an initial set of notes. - # - getContent: -> - Api.notes Wall.project_id, (notes) -> - $.each notes, (i, note) -> - # render note if it not present in loaded list - # or skip if rendered - if $.inArray(note.id, Wall.note_ids) == -1 - Wall.note_ids.push(note.id) - Wall.renderNote(note) - Wall.scrollDown() - $("abbr.timeago").timeago() - - initRefresh: -> - setInterval("Wall.refresh()", 10000) - - refresh: -> - Wall.getContent() - - scrollDown: -> - notes = $('ul.notes') - $('body, html').scrollTop(notes.height()) - - initForm: -> - form = $('.wall-note-form') - form.find("#target_type").val('wall') - - form.on 'ajax:success', -> - Wall.refresh() - form.find(".js-note-text").val("").trigger("input") - - form.on 'ajax:complete', -> - form.find(".js-comment-button").removeAttr('disabled') - form.find(".js-comment-button").removeClass('disabled') - - form.on "click", ".js-choose-note-attachment-button", -> - form.find(".js-note-attachment-input").click() - - form.on "change", ".js-note-attachment-input", -> - filename = $(this).val().replace(/^.*[\\\/]/, '') - form.find(".js-attachment-filename").text(filename) - - form.find('.note_text').keydown (e) -> - if e.ctrlKey && e.keyCode == 13 - form.find('.js-comment-button').submit() - - form.show() - - renderNote: (note) -> - template = Wall.noteTemplate() - template = template.replace('{{author_name}}', note.author.name) - template = template.replace('{{created_at}}', note.created_at) - template = template.replace('{{text}}', linkify(sanitize(note.body))) - - if note.attachment - file = '' + note.attachment + '' - else - file = '' - template = template.replace('{{file}}', file) - - - $('ul.notes').append(template) - - noteTemplate: -> - return '
  • - {{author_name}} - - {{text}} - {{file}} - - {{created_at}} -
  • ' diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css new file mode 100644 index 00000000..424ba71f --- /dev/null +++ b/app/assets/stylesheets/application.css @@ -0,0 +1,11 @@ +/* + * This is a manifest file that'll automatically include all the stylesheets available in this directory + * and any sub-directories. You're free to add application-wide styles to this file and they'll appear at + * the top of the compiled file, but it's generally better to create a new file per style scope. + *= require jquery.ui.all + *= require jquery.ui.aristo + *= require jquery.atwho + *= require chosen + *= require_self + *= require main +*/ diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss deleted file mode 100644 index fd15d5c6..00000000 --- a/app/assets/stylesheets/application.scss +++ /dev/null @@ -1,57 +0,0 @@ -/* - * This is a manifest file that'll automatically include all the stylesheets available in this directory - * and any sub-directories. You're free to add application-wide styles to this file and they'll appear at - * the top of the compiled file, but it's generally better to create a new file per style scope. - *= require jquery.ui.gitlab - *= require jquery.atwho - *= require chosen - *= require select2 - *= require_self -*/ - -/** - * GitLab bootstrap: - */ -@import "gitlab_bootstrap.scss"; - -@import "common.scss"; -@import "selects.scss"; - -@import "sections/header.scss"; -@import "sections/nav.scss"; -@import "sections/commits.scss"; -@import "sections/issues.scss"; -@import "sections/projects.scss"; -@import "sections/snippets.scss"; -@import "sections/votes.scss"; -@import "sections/merge_requests.scss"; -@import "sections/graph.scss"; -@import "sections/events.scss"; -@import "sections/themes.scss"; -@import "sections/tree.scss"; -@import "sections/notes.scss"; -@import "sections/profile.scss"; -@import "sections/login.scss"; -@import "sections/editor.scss"; -@import "sections/admin.scss"; -@import "sections/wiki.scss"; -@import "sections/wall.scss"; - -@import "highlight/white.scss"; -@import "highlight/dark.scss"; -@import "highlight/solarized_dark.scss"; - -/** - * UI themes: - */ -@import "themes/ui_basic.scss"; -@import "themes/ui_mars.scss"; -@import "themes/ui_modern.scss"; -@import "themes/ui_gray.scss"; -@import "themes/ui_color.scss"; - -/** - * Styles for JS behaviors. - */ -@import "behaviors.scss"; - diff --git a/app/assets/stylesheets/behaviors.scss b/app/assets/stylesheets/behaviors.scss deleted file mode 100644 index 3fdc2048..00000000 --- a/app/assets/stylesheets/behaviors.scss +++ /dev/null @@ -1,14 +0,0 @@ -// Details -//-------- -.js-details-container .content { display: none; } -.js-details-container .content.hide { display: block; } -.js-details-container.open .content { display: block; } -.js-details-container.open .content.hide { display: none; } - - -// Toggler -//-------- -.js-toggler-container .turn-on { display: inherit; } -.js-toggler-container .turn-off { display: none; } -.js-toggler-container.on .turn-on { display: none; } -.js-toggler-container.on .turn-off { display: inherit; } diff --git a/app/assets/stylesheets/common.scss b/app/assets/stylesheets/common.scss index 4e7aa968..e45cb876 100644 --- a/app/assets/stylesheets/common.scss +++ b/app/assets/stylesheets/common.scss @@ -1,49 +1,53 @@ -html { - overflow-y: scroll; -} - /** LAYOUT **/ body { - margin-bottom: 20px; + margin-bottom:20px; } .container { - padding-top: 0; - z-index: 5; + padding-top:0; + z-index:5; } .container .content { - margin: 0 0; + margin:0 0; } +.container .sidebar { + width: 200px; + height:100%; + min-height:450px; + float:right; +} + + .visible_link, .author_link { color: $link_color; } -.help li { color:$style_color; } +.help li { color:#111 } .back_link { - text-decoration: underline; - font-size: 14px; - font-weight: bold; - padding: 10px 0; - padding-bottom: 0; + text-decoration:underline; + font-size:14px; + font-weight:bold; + padding:10px 0; + padding-bottom:0; } .info_link { - margin-right: 5px; - float: left; + margin-right:5px; + float:left; img { - width: 20px; + width:20px; } } .download_repo_link { background: url("images.png") no-repeat 0 -48px; - padding-left: 20px; + padding-left:20px; } table a code { @@ -57,27 +61,32 @@ table a code { } .loading { - margin: 20px auto; + margin:20px auto; background: url(ajax_loader.gif) no-repeat center center; - width: 40px; - height: 40px; - &.loading-gray { - background: url(ajax_loader_gray.gif) no-repeat center center; - } + width:40px; + height:40px; } /** FLASH message **/ -.flash-container { - display: none; - .alert { - cursor: pointer; - margin: 0; - text-align: center; - border-radius: 0; +#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; - span { - font-size: 14px; - } + h4 { + color:#666; + font-size:18px; + line-height:38px; + padding-top:5px; + margin:2px; + font-weight:normal; } } @@ -85,36 +94,71 @@ table a code { margin-right:50px } -span.update-author { - display: block; - color: #999; - font-weight: normal; - font-style: italic; - strong { - font-weight: bold; - font-style: normal; - } +.handle:hover { + cursor:move; } +span.update-author { + display:block; +} +span.update-author { + color:#999; + font-weight:normal; + font-style:italic; +} +span.update-author strong { + font-weight:bold; + font-style: normal; +} + +/** UPDATE ITEM **/ +span.update-author { + display:block; +} +/** END UPDATE ITEM **/ .dashboard-loader { - float: left; - margin: 10px; - display: none; + float:left; + margin:10px; + display:none; } .user-mention { - color: #2FA0BB; - font-weight: bold; + color:#2FA0BB; + font-weight:bold; } .neib { - margin-right: 10px; + margin-right:10px; } .label { - padding: 0px 4px; - font-size: 10px; - font-style: normal; - background-color: $link_color; + background-color: #474D57; + + &.label-tag { + background: none; + border: none; + padding:4px 6px; + color:#444; + text-shadow:0 0 1px #fff; + + &.grouped { + float: left; + margin-right: 6px; + padding: 6px; + } + } + &.label-issue { + background-color: #eee; + border: 1px solid #ccc; + padding:4px 6px; + color:#444; + text-shadow:0 0 1px #fff; + + &.grouped { + float: left; + margin-right: 6px; + padding: 6px; + } + } &.label-success { background-color: #8D8; @@ -157,40 +201,55 @@ form { .field_with_errors { - display: inline; + display:inline; } ul.breadcrumb { - background: white; - border: none; + background:white; + border:none; li { display: inline; text-shadow: 0 1px 0 white } a { - color: #474D57; - font-weight: bold; - font-size: 14px; + color:#474D57; + font-weight:bold; + font-size:14px; + } + + .arrow { + background: url("images.png") no-repeat -85px -77px; + width: 19px; + height: 16px; + float: left; + position: relative; + left: -10px; + padding:0; + margin:0; } } input[type=text] { &.large_text { - padding: 6px; - font-size: 16px; + padding:6px; + font-size:16px; } } +input.git_clone_url { + width:325px; +} + .merge-request-form-holder { select { - width: 300px; + width:300px; } } /** Issues **/ #issue_assignee_id { - width: 300px; + width:300px; } #new_issue_dialog textarea{ @@ -198,11 +257,26 @@ input[type=text] { } .project_list_url { - width: 250px; + width:250px; background:#fff !important; } +/** bordered list **/ +ul.bordered-list { + margin:5px 0px; + padding:0px; + li { + padding: 5px 0; + border-bottom: 1px solid #EEE; + overflow: hidden; + display: block; + margin:0px; + } +} + +ul.bordered-list li:last-child { border:none } + .line_holder { &:hover { td { @@ -213,10 +287,10 @@ input[type=text] { li.commit { .avatar { - width: 24px; + width:24px; top:-5px; - margin-right: 10px; - margin-left: 10px; + margin-right:10px; + margin-left:10px; } code { @@ -236,10 +310,102 @@ p.time { .styled_image { - border: 2px solid #ddd; + border:2px solid #ddd; } +.ico { + background: url("images.png") no-repeat -85px -77px; + width: 19px; + height: 16px; + float: left; + position: relative; + margin-right: 10px; + top: 8px; + + &.project { + background-position: -37px -77px; + } + + &.activities { + background-position:-162px -22px; + } + &.projects { + background-position:-209px -21px; + } +} + +.leftbar { + h5, .title { + padding:5px 10px; + } + + h4 { + font-size:14px; + padding:2px 10px; + color:#666; + border-bottom:1px solid #f1f1f1; + } + a:last-child h4 { border:none; } + + a:hover { + h4 { + color:#111; + background:$hover; + border-color:#CCC; + .ico.project { + background-position:-209px -21px; + } + } + } + .bottom { + padding:10px; + } +} + +.votes { + font-size: 13px; + line-height: 15px; + .progress { + height: 4px; + margin: 0; + .bar { + float: left; + height: 100%; + } + .bar-success { + background-color: #468847; + @include bg-gradient(#62C462, #51A351); + } + .bar-danger { + background-color: #B94A48; + @include bg-gradient(#EE5F5B, #BD362F); + } + } + .upvotes { + display: inline-block; + color: #468847; + } + .downvotes { + display: inline-block; + color: #B94A48; + } +} +.votes-block { + margin: 14px 6px 6px 0; + .downvotes { + float: right; + } +} +.votes-inline { + display: inline-block; + margin: 0 8px; + .progress { + display: inline-block; + padding: 0 0 2px; + width: 45px; + } +} /* Fix for readme code (stopped it from being yellow) */ .readme { @@ -252,57 +418,63 @@ p.time { } } -.search-holder { - label, input { - height: 30px; - padding: 0; - font-size: 14px; - } - label { - line-height: 30px; - color: #666; - } -} .highlight_word { - border-bottom: 2px solid #F90; + background:#EEDC94; } .status_info { - font-size: 14px; - padding: 5px 15px; - line-height: 26px; - text-align: center; - float: right; - position: relative; - top: -5px; - @include border-radius(4px); + font-size:14px; + padding:5px 15px; + line-height:24px; + width:60px; + text-align:center; + float:left; + margin-right:20px; + &.success { + background: #5BB75B; + color: white; + text-shadow: 0 1px #111; + border-color: #9A9; + } &.error { background: #DA4E49; - color: #FFF; + border-color: #BD362F; + color: white; + text-shadow: 0 1px #111; } } .arrow{ background: #E3E5EA; padding: 5px; - margin-top: 5px; - @include border-radius(5px); + margin-top:5px; + border-radius: 5px; text-shadow: none; color: #999; line-height: 16px; - font-weight: bold; + font-weight:bold; } .thin_area{ height: 150px; } +.gitlab_pagination { + span a { color:$link_color; } + .prev, .next, .current, .page a { + padding:10px; + } + .current { + border-bottom:2px solid $style_color; + } +} + // Fixes alignment on notes. .new_note { label { - text-align: left; + text-align:left; } } @@ -313,12 +485,16 @@ li.note { li { border-bottom:none !important; } + .file { + padding-left:20px; + background:url("icon-attachment.png") no-repeat left center; + } } } .markdown { img { - max-width: 100%; + max-width:100%; } } @@ -328,19 +504,19 @@ li.note { .team_member_show { td:first-child { - color: #aaa; + color:#aaa; } } .remember_me { - text-align: left; + text-align:left; input { - margin: 0; + margin:0; } span { - padding-left: 5px; + padding-left:5px; } } @@ -354,11 +530,10 @@ li.note { .data { a { h1 { - line-height: 48px; - font-size: 48px; - padding: 20px; - text-align: center; - font-weight: normal; + line-height:48px; + font-size:48px; + padding:20px; + text-align:center; } } } @@ -366,12 +541,12 @@ li.note { .rss-icon { img { - width: 24px; - vertical-align: top; + width:24px; + vertical-align:top; } strong { - line-height: 24px; + line-height:24px; } } @@ -379,43 +554,43 @@ li.note { /* CHZN reset few styles */ .chzn-container-single .chzn-single { - background: #FFF; + background:#FFF; border: 1px solid #bbb; - box-shadow: none; + box-shadow:none; } .chzn-container-active .chzn-single { - background: #fff; + background:#fff; } .supp_diff_link, -.show-all-commits { - cursor: pointer; +.mr_show_all_commits { + cursor:pointer; } .merge_request, .issue { &.today{ background: #EFE; - border-color: #CEC; + border-color:#CEC; } &.closed { background: #F5f5f5; - border-color: #E5E5E5; + border-color:#E5E5E5; } &.merged { background: #F5f5f5; - border-color: #E5E5E5; + border-color:#E5E5E5; } } .git_error_tips { @extend .span6; - text-align: left; - margin-top: 40px; + text-align:left; + margin-top:40px; pre { - background: white; - border: none; + background:white; + border:none; font-size: 12px; } } @@ -427,35 +602,18 @@ li.note { margin-bottom: 10px; background: #FEE; padding-left: 20px; - - &.centered { - text-align: center; - } -} - -.warning_message { - border-left: 4px solid #ed9; - color: #b90; - padding: 10px; - margin-bottom: 10px; - background: #ffffe6; - padding-left: 20px; - - &.centered { - text-align: center; - } } .oauth_select_holder { - padding: 20px; + padding:20px; img { - padding: 5px; - margin-right: 10px; + padding:5px; + margin-right:10px; } .active { img { - border: 1px solid #ccc; - background: $hover; + border:1px solid #ccc; + background:$hover; @include border-radius(5px); } } @@ -469,89 +627,29 @@ li.note { .gitlab-promo { a { - color: #aaa; + color:#aaa; margin-right: 30px; } } pre { &.clean { - background: none; - border: none; - margin: 0; - padding: 0; + background:none; + border:none; + margin:0; + padding:0; } } -.milestone { - &.milestone-closed { - background: #eee; - } - .progress { - margin-bottom: 0; - margin-top: 4px; - } +.milestone .progress { + margin-bottom: 0; + margin-top:4px; } .float-link { - float: left; - margin-right: 15px; + float:left; + margin-right:15px; .s16 { - margin-right: 5px; + margin-right:5px; } } - -.dashboard-search-filter { - padding:5px; - - .search-text-input { - float:left; - @extend .span2; - } - .btn { - margin-left: 5px; - float:left; - } -} - -h1.http_status_code { - font-size: 56px; - line-height: 100px; - font-weight: normal; - color: #456; -} - -.control-group { - .controls { - span { - &.descr { - position: relative; - top: 2px; - left: 5px; - color: #666; - } - } - } -} - -img.emoji { - height: 20px; - vertical-align: middle; - width: 20px; -} - -.appear-data { - display: none; -} - -.label-branch { - @include border-radius(4px); - padding: 2px 4px; - border: none; - font-size: 14px; - background: #474D57; - color: #fff; - font-family: $monospace_font; - text-shadow: 0 1px 1px #111; - font-weight: normal; -} diff --git a/app/assets/stylesheets/gitlab_bootstrap.scss b/app/assets/stylesheets/gitlab_bootstrap.scss deleted file mode 100644 index 2ad1bf94..00000000 --- a/app/assets/stylesheets/gitlab_bootstrap.scss +++ /dev/null @@ -1,28 +0,0 @@ -/** Override bootstrap variables **/ -$baseFontSize: 13px !default; -$baseLineHeight: 18px !default; - -// BOOTSTRAP -@import "bootstrap"; -@import "bootstrap/responsive-utilities"; -@import "bootstrap/responsive-1200px-min"; - -@import "font-awesome"; - -/** - * GitLab bootstrap. - * Overrides some styles of twitter bootstrap. - * Also give some common classes for GitLab app - */ -@import "gitlab_bootstrap/variables.scss"; -@import "gitlab_bootstrap/fonts.scss"; -@import "gitlab_bootstrap/mixins.scss"; -@import "gitlab_bootstrap/avatar.scss"; -@import "gitlab_bootstrap/nav.scss"; -@import "gitlab_bootstrap/common.scss"; -@import "gitlab_bootstrap/typography.scss"; -@import "gitlab_bootstrap/buttons.scss"; -@import "gitlab_bootstrap/blocks.scss"; -@import "gitlab_bootstrap/files.scss"; -@import "gitlab_bootstrap/tables.scss"; -@import "gitlab_bootstrap/lists.scss"; diff --git a/app/assets/stylesheets/gitlab_bootstrap/avatar.scss b/app/assets/stylesheets/gitlab_bootstrap/avatar.scss deleted file mode 100644 index de1fb155..00000000 --- a/app/assets/stylesheets/gitlab_bootstrap/avatar.scss +++ /dev/null @@ -1,8 +0,0 @@ -/** AVATARS **/ -img.avatar { float: left; margin-right: 12px; width: 40px; border: 1px solid #ddd; padding: 1px; } -img.avatar.s16 { width: 16px; height: 16px; margin-right: 6px; } -img.avatar.s24 { width: 24px; height: 24px; margin-right: 8px; } -img.avatar.s32 { width: 32px; height: 32px; margin-right: 10px; } -img.avatar.s90 { width: 90px; height: 90px; margin-right: 15px; } -img.lil_av { padding-left: 4px; padding-right: 3px; } -img.small { width: 80px; } diff --git a/app/assets/stylesheets/gitlab_bootstrap/blocks.scss b/app/assets/stylesheets/gitlab_bootstrap/blocks.scss index 842e7a08..ae66bd20 100644 --- a/app/assets/stylesheets/gitlab_bootstrap/blocks.scss +++ b/app/assets/stylesheets/gitlab_bootstrap/blocks.scss @@ -1,33 +1,23 @@ /** * =================================== - * Contain UI block elements: - * .ui-box - for any block & widgets + * Contain 3 main UI block elements: + * .main_box - for show pages + * .ui-box - for simple block & widgets * =================================== */ /** - * UI Block + * UI box element + * contains top, middle, bottom blocks * */ -.ui-box { - background: #F9F9F9; - margin-bottom: 25px; - border: 1px solid #CCC; - @include solid-shade; +.main_box { + @extend .borders; + @extend .prepend-top-20; + @extend .append-bottom-20; + border-width:1px; + @include solid_shade; - &.ui-box-show { - margin:20px 0; - background: #FFF; - } - - &.ui-box-danger { - .title { - @include linear-gradient(#F26E5E, #bd362f); - color: #fff; - text-shadow: 0 1px 1px #900; - font-weight: bold; - } - } img { max-width: 100%; } @@ -37,95 +27,88 @@ } } - .ui-box-head, - .ui-box-body, - .ui-box-bottom { - padding: 15px; - word-wrap: break-word; + .top_box_content, + .middle_box_content, + .bottom_box_content { + padding:15px; - .clearfix { - margin: 0; + pre { + background: none !important; + margin:0; + border:none; + padding:0; } } - .ui-box-head { - .box-title { - color: $style_color; - font-size: 18px; - font-weight: normal; - line-height: 28px; - } - h3 { - margin: 0; - } + .middle_box_content { + border-radius:0; + border:none; + font-size:12px; + background-color:#f5f5f5; + border:none; + border-top:1px solid #eee; } - .ui-box-body { - border: none; - font-size: 12px; - background-color: #f5f5f5; - border: none; - border-top: 1px solid #eee; + .bottom_box_content { + border-top:1px solid #eee; } +} - .ui-box-bottom { - border-top: 1px solid #eee; - } +/** + * Big UI Block for show page content + * + */ +.ui-box { + background:#F9F9F9; + margin-bottom: 25px; + @include round-borders-all(4px); + border-color: #CCC; + @include solid_shade; &.white { - background: #fff; + background:#fff; } ul { - margin: 0; + margin:0; } - .title { + h5, .title { + padding: 0 10px; + @include round-borders-top(4px); @include bg-gray-gradient; - border-bottom: 1px solid #CCC; - color: #456; - font-size: 16px; - text-shadow: 0 1px 1px #fff; - padding: 0px 10px; - line-height: 36px; - font-size: 14px; - font-weight: normal; + border-bottom: 1px solid #bbb; - > a { - text-shadow: 0 1px 1px #fff; + &.small { + line-height: 28px; + font-size: 14px; + line-height:28px; + text-shadow: 0 1px 1px white; } form { - margin-bottom: 0; - margin-top: 0; - } - - .btn { - position: relative; - top: -2px; + padding:9px 0; + margin:0px; } .nav-pills { - > li { - > a { - padding: 13px; - margin: 0; - font-size: 13px; - } - &.active { - > a { - background: #D5D5D5; - color: $style_color; - @include border-radius(0); - border-radius: 0; - border-left: 1px solid #CCC; - border-right: 1px solid #CCC; - } + li { + padding:3px 0; + &.active a { background-color:$style_color; } + a { + border-radius:7px; } } } } + .bottom { + @include bg-gray-gradient; + @include round-borders-bottom(4px); + border-bottom:none; + border-top: 1px solid #bbb; + } + &.padded { h5, .title { margin: -20px; @@ -133,41 +116,38 @@ padding: 5px 20px; } .middle_title { - background: #f5f5f5; + background:#f5f5f5; margin:20px -20px; padding: 0 20px; - border-top: 1px solid #eee; - border-bottom: 1px solid #eee; - font-size: 14px; - color: #777; + border-top:1px solid #eee; + border-bottom:1px solid #eee; + font-size:14px; + color:#777; } } - .row_title { - font-weight: bold; - color: #444; + font-weight:bold; + color:#444; &:hover { - color: #444; - text-decoration: underline; + color:#444; + text-decoration:underline; } } - .form-holder { - padding-top: 20px; - form { - margin-bottom: 0; - legend { - text-indent: 10px; - } - .form-actions { - margin-bottom: 0; - } + li, .wll { + padding:10px; + &:first-child { + @include round-borders-top(4px); + border-top:none; + } + + &:last-child { + @include round-borders-bottom(4px); + border:none; } } -} -.tab-pane { - .ui-box { - margin: 3px 3px 25px 3px; + .ui-box-body { + padding:10px; } } diff --git a/app/assets/stylesheets/gitlab_bootstrap/buttons.scss b/app/assets/stylesheets/gitlab_bootstrap/buttons.scss index 03497e32..f9249e87 100644 --- a/app/assets/stylesheets/gitlab_bootstrap/buttons.scss +++ b/app/assets/stylesheets/gitlab_bootstrap/buttons.scss @@ -1,45 +1,42 @@ .btn { - @include linear-gradient(#f1f1f1, #e1e1e1); - text-shadow: 0 1px 1px #FFF; - border-color: #BBB; - + @include bg-gradient(#f7f7f7, #d5d5d5); + border-color:#aaa; &:hover { - background: #f1f1f1; - @include linear-gradient(#fAfAfA, #f1f1f1); - border-color: #AAA; - color: #333; + @include bg-gray-gradient; + border-color:#bbb; + color:#333; } - &.btn-primary { - background: #2a79A3; - @include linear-gradient(#47A7b7, #2585b5); + &.primary { + background:#2a79A3; + @include bg-gradient(#47A7b7, #2585b5); border-color: #2A79A3; - color: #fff; + color:#fff; text-shadow: 0 1px 1px #268; &:hover { - background: $primary_color; - color: #fff; + background:$blue_link; + color:#fff; } &.disabled { - color: #fff; - background: #29B; + color:#fff; + background:#29B; } } &.btn-info { - background: #5aB9C3; - border-color: $primary_color; - color: #fff; + background:#5aB9C3; + border-color: $blue_link; + color:#fff; text-shadow: 0 1px 1px #268; &:hover { - background: $primary_color; - color: #fff; + background:$blue_link; + color:#fff; } &.disabled { - color: #fff; - background: #29B; + color:#fff; + background:#29B; } } @@ -52,39 +49,37 @@ } &.disabled { - color: #fff; - background: #2b2; + color:#fff; + background:#2b2; } } - &.btn-create { + &.save-btn { @extend .wide; - @extend .success; + @extend .primary; } - &.btn-save { - @extend .wide; - @extend .btn-primary; + &.cancel-btn { + float:right; } - &.btn-close, - &.btn-remove { + &.wide { + padding-left:30px; + padding-right:30px; + } + + &.danger { @extend .btn-danger; border-color: #BD362F; &:hover { - color: #fff; + color:#fff; background: #EE4E49; } } - &.btn-cancel { - float: right; - } - - &.wide { - padding-left: 20px; - padding-right: 20px; + &.danger { + @extend .btn-danger; } &.small { @@ -92,24 +87,24 @@ } &.active { - border-color: #aaa; - background-color: #ccc; + border-color:#aaa; + background-color:#ccc; } - &.btn-tiny { - font-size: 11px; - padding: 2px 6px; + &.very_small { + font-size:11px; + padding:2px 6px; line-height: 16px; - margin: 2px; + margin:2px; } &.grouped { - margin-right: 7px; - float: left; + margin-right:7px; + float:left; } &.padded { - margin-right: 3px; - padding: 4px 10px 4px; + margin-right:3px; + padding:4px 10px 4px; } } diff --git a/app/assets/stylesheets/gitlab_bootstrap/common.scss b/app/assets/stylesheets/gitlab_bootstrap/common.scss index 6bdd1652..c1b28801 100644 --- a/app/assets/stylesheets/gitlab_bootstrap/common.scss +++ b/app/assets/stylesheets/gitlab_bootstrap/common.scss @@ -2,7 +2,6 @@ .cgray { color:gray } .cred { color:#D12F19 } .cgreen { color:#4a2 } -.cblue { color:#29A } .cblack { color:#111 } .cdark { color:#444 } .cwhite { color:#fff!important } @@ -10,6 +9,12 @@ /** COMMON CLASSES **/ .left { float:left } +.right { float:right!important } +.width-50p { width:50% } +.width-49p { width:49% } +.width-30p { width:30% } +.width-65p { width:65% } +.width-100p { width:100% } .append-bottom-10 { margin-bottom:10px } .append-bottom-20 { margin-bottom:20px } .prepend-top-10 { margin-top:10px } @@ -17,63 +22,72 @@ .padded { padding:20px } .ipadded { padding:20px!important } .lborder { border-left:1px solid #eee } +.no-padding { padding:0 !important; } +.underlined { border-bottom: 1px solid #CCC; } +.no-borders { border:none; } +.vlink { color: $link_color !important; } .underlined_link { text-decoration: underline; } +.borders { border: 1px solid #ccc; @include shade; } .hint { font-style: italic; color: #999; } .light { color: #888 } -.tiny { font-weight: normal } -.vtop { vertical-align: top; } +/** PILLS & TABS**/ +.nav-pills a:hover { background-color:#888; } +.nav-pills .active a { background-color: $style_color; } +.nav-tabs > li > a, .nav-pills > li > a { color:$style_color; } +.nav.nav-tabs { + li { + > a { + padding:8px 20px; + margin-right: 7px; + line-height: 19px; + border-color: #EEE; + color:#888; + border-bottom: 1px solid #ddd; + .badge { + background-color: #eee; + color:#888; + text-shadow:0 1px 1px #fff; + } + i[class^="icon-"] { + line-height:14px; + } + } + &.active { + > a { + border-color: #CCC; + border-bottom: 1px solid #fff; + color:#333; + } + } + } +} /** ALERT MESSAGES **/ -.alert.alert-disabled { - background: #EEE; - color: #777; - border-color: #DDD; -} +.alert-message { @extend .alert; } +.alert-messag.success { @extend .alert-success; } +.alert-message.error { @extend .alert-error; } -.well { padding: 15px; } +/** AVATARS **/ +img.avatar { float:left; margin-right:12px; width:40px; border:1px solid #ddd; padding:1px; } +img.avatar.s16 { width:16px; height:16px; margin-right:6px; } +img.avatar.s24 { width:24px; height:24px; margin-right:8px; } +img.avatar.s32 { width:32px; height:32px; margin-right:10px; } +img.lil_av { padding-left: 4px; padding-right:3px; } +img.small { width: 80px; } /** HELPERS **/ -.nothing_here_message { - text-align: center; - padding: 20px; - color: #666; - font-weight: normal; - font-size: 16px; - line-height: 36px; -} - -p.slead { color: #456; font-size: 16px; margin-bottom: 12px; font-weight: 200; line-height: 24px; } +.nothing_here_message { text-align:center; padding:20px; color:#777; } +p.slead { color:#456; font-size:16px; margin-bottom: 12px; font-weight: 200; line-height: 24px; } /** FORMS **/ input[type='search'].search-text-input { background-image: url("icon-search.png"); background-repeat: no-repeat; background-position: 10px; - padding-left: 25px; + padding-left:25px; @include border-radius(4px); - border: 1px solid #ccc; -} - -input[type='text'].danger { - background: #F2DEDE!important; - border-color: #D66; - text-shadow: 0 1px 1px #fff + border:1px solid #ccc; } fieldset legend { font-size: 17px; } - -/** PAGINATION **/ -.gitlab_pagination { - span a { color: $link_color; } - .prev, .next, .current, .page a { - padding: 10px; - } - .current { - border-bottom: 2px solid $style_color; - } -} - -.tab-content { - overflow: visible; -} diff --git a/app/assets/stylesheets/gitlab_bootstrap/files.scss b/app/assets/stylesheets/gitlab_bootstrap/files.scss index d0bf3bdd..4887d1c9 100644 --- a/app/assets/stylesheets/gitlab_bootstrap/files.scss +++ b/app/assets/stylesheets/gitlab_bootstrap/files.scss @@ -3,9 +3,9 @@ * */ .file_holder { - border: 1px solid #BBB; - margin-bottom: 1em; - @include solid-shade; + border:1px solid #BBB; + margin-bottom:1em; + @include solid_shade; .file_title { border-bottom: 1px solid #bbb; @@ -16,39 +16,48 @@ text-align: left; color: #666; padding: 9px 10px; - height: 18px; + height:18px; .options { - float: right; + float:right; margin-top: -5px; } .file_name { - color: $style_color; - font-size: 14px; + color:$style_color; + font-size:14px; text-shadow: 0 1px 1px #fff; small { - color: #999; - font-size: 13px; + color:#999; + font-size:13px; } } } .file_content { - background: #fff; + background:#fff; font-size: 11px; - &.image_file { - background: #eee; - text-align: center; - img { - padding: 100px; - max-width: 300px; + &.wiki { + font-size: 13px; + code { + padding:0 4px; + } + padding:20px; + h1, h2 { + line-height: 46px; + } + h3, h4 { + line-height: 40px; } } - &.wiki { - padding: 20px; - font-size: 13px; + &.image_file { + background:#eee; + text-align:center; + img { + padding:100px; + max-width:300px; + } } &.blob_file { @@ -60,60 +69,60 @@ */ &.blame { table { - border: none; - box-shadow: none; - margin: 0; + border:none; + box-shadow:none; + margin:0; } tr { border-bottom: 1px solid #eee; } td { &:first-child { - border-left: none; + border-left:none; } &:last-child { - border-right: none; + border-right:none; } - background: #fff; - padding: 5px; + background:#fff; + padding:5px; } .author, .blame_commit { - background: #f5f5f5; - vertical-align: top; + background:#f5f5f5; + vertical-align:top; } .lines { pre { - padding: 0; - margin: 0; - background: none; - border: none; + padding:0; + margin:0; + background:none; + border:none; } } } &.logs { - background: #eee; + background:#eee; max-height: 700px; overflow-y: auto; ol { - margin-left: 40px; + margin-left:40px; padding: 10px 0; border-left: 1px solid #CCC; - margin-bottom: 0; + margin-bottom:0; background: white; li { - color: #888; + color:#888; p { - margin: 0; - color: #333; - line-height: 24px; + margin:0; + color:#333; + line-height:24px; padding-left: 10px; } &:hover { - background: $hover; + background:$hover; } } } @@ -133,9 +142,9 @@ table-layout: fixed; pre { + background: none; border: none; - border-radius: 0; - font-family: $monospace_font; + font-family: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono','lucida console',monospace; font-size: 12px !important; line-height: 16px !important; margin: 0; @@ -162,7 +171,6 @@ color: #666; padding: 10px 6px 10px 0; text-align: right; - background: #EEE; a { color: #666; diff --git a/app/assets/stylesheets/gitlab_bootstrap/fonts.scss b/app/assets/stylesheets/gitlab_bootstrap/fonts.scss deleted file mode 100644 index 8cc99864..00000000 --- a/app/assets/stylesheets/gitlab_bootstrap/fonts.scss +++ /dev/null @@ -1,2 +0,0 @@ -/** Typo **/ -$monospace_font: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono', 'lucida console', monospace; diff --git a/app/assets/stylesheets/gitlab_bootstrap/lists.scss b/app/assets/stylesheets/gitlab_bootstrap/lists.scss index 0f893a55..4fe45ecc 100644 --- a/app/assets/stylesheets/gitlab_bootstrap/lists.scss +++ b/app/assets/stylesheets/gitlab_bootstrap/lists.scss @@ -1,73 +1,41 @@ -/** - * Well styled list - * - */ -.well-list { - margin: 0; - list-style: none; - li { +/** LISTS **/ + +ul { + /** + * List li block element #1 + * + */ + .wll { background-color: #FFF; - padding: 10px; + padding: 10px 5px; min-height: 20px; border-bottom: 1px solid #eee; border-bottom: 1px solid rgba(0, 0, 0, 0.05); - &.disabled { - color: #888; - } - - &.smoke { background-color: #f5f5f5; } - + &.smoke { background-color:#f5f5f5; } &:hover { - background: $hover; - border-bottom: 1px solid #ADF; + background:$hover; + border-bottom:1px solid #ADF; } - - &:last-child { - border-bottom: none; - - &.bottom { - background: #f5f5f5; - } - } - + &:last-child { border:none } .author { color: #999; } p { padding-top: 1px; - margin: 0; - color: #222; + margin:0; + color:#222; img { - position: relative; - top: 3px; + position:relative; + top:3px; } } - - .well-title { - font-size: 14px; - line-height: 18px; - } } } ol, ul { &.styled { li { - padding: 2px; + padding:2px; } } } - -/** light list with border-bottom between li **/ -ul.bordered-list { - margin: 5px 0px; - padding: 0px; - li { - padding: 5px 0; - border-bottom: 1px solid #EEE; - overflow: hidden; - display: block; - margin: 0px; - &:last-child { border:none } - } -} diff --git a/app/assets/stylesheets/gitlab_bootstrap/mixins.scss b/app/assets/stylesheets/gitlab_bootstrap/mixins.scss deleted file mode 100644 index 2f83ca0c..00000000 --- a/app/assets/stylesheets/gitlab_bootstrap/mixins.scss +++ /dev/null @@ -1,88 +0,0 @@ -/** - * Generic mixins - */ - @mixin box-shadow($shadow) { - -webkit-box-shadow: $shadow; - -moz-box-shadow: $shadow; - -ms-box-shadow: $shadow; - -o-box-shadow: $shadow; - box-shadow: $shadow; -} - -@mixin border-radius($radius) { - -webkit-border-radius: $radius; - -moz-border-radius: $radius; - -ms-border-radius: $radius; - -o-border-radius: $radius; - border-radius: $radius; -} - -@mixin linear-gradient($from, $to) { - background-image: -webkit-gradient(linear, 0 0, 0 100%, from($from), to($to)); - background-image: -webkit-linear-gradient($from, $to); - background-image: -moz-linear-gradient($from, $to); - background-image: -o-linear-gradient($from, $to); -} - -@mixin transition($transition) { - -webkit-transition: $transition; - -moz-transition: $transition; - -ms-transition: $transition; - -o-transition: $transition; - transition: $transition; -} - -/** - * Prefilled mixins - * Mixins with fixed values - */ -@mixin bg-light-gray-gradient { - background: #f1f1f1; - background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #f5f5f5), to(#e1e1e1)); - background-image: -webkit-linear-gradient(#f5f5f5 6.6%, #e1e1e1); - background-image: -moz-linear-gradient(#f5f5f5 6.6%, #e1e1e1); - background-image: -o-linear-gradient(#f5f5f5 6.6%, #e1e1e1); -} - -@mixin bg-gray-gradient { - background: #eee; - background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf)); - background-image: -webkit-linear-gradient(#eee 6.6%, #dfdfdf); - background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf); - background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf); -} - -@mixin bg-dark-gray-gradient { - background: #eee; - background-image: -webkit-linear-gradient(#e9e9e9, #d7d7d7); - background-image: -moz-linear-gradient(#e9e9e9, #d7d7d7); - background-image: -o-linear-gradient(#e9e9e9, #d7d7d7); -} - -@mixin shade { - @include box-shadow(0 0 3px #ddd); -} - -@mixin solid-shade { - @include box-shadow(0 0 0 3px #f1f1f1); -} - -@mixin header-font { - color: $style_color; - text-shadow: 0 1px 1px #FFF; - font-size: 18px; - line-height: 40px; - 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;} -} diff --git a/app/assets/stylesheets/gitlab_bootstrap/nav.scss b/app/assets/stylesheets/gitlab_bootstrap/nav.scss deleted file mode 100644 index 2eaef61c..00000000 --- a/app/assets/stylesheets/gitlab_bootstrap/nav.scss +++ /dev/null @@ -1,65 +0,0 @@ -/** - * nav-pills - * - */ -.nav-pills { - .active a { - background: $primary_color; - } - - > li > a { - @include border-radius(0); - } - &.nav-stacked { - > li > a { - border-left: 4px solid #EEE; - padding: 12px; - } - > .active > a { - border-color: #29B; - border-radius: 0; - background: #F1F1F1; - color: $style_color; - font-weight: bold; - } - } -} - -.nav-pills > .active > a > i[class^="icon-"] { background: inherit; } - - - -/** - * nav-tabs - * - */ -.nav-tabs > li > a, .nav-pills > li > a { color: $style_color; } -.nav.nav-tabs { - li { - > a { - padding: 8px 20px; - margin-right: 7px; - line-height: 20px; - border-color: #EEE; - color: #888; - border-bottom: 1px solid #ddd; - .badge { - background-color: #eee; - color: #888; - text-shadow: 0 1px 1px #fff; - } - i[class^="icon-"] { - line-height: 14px; - } - } - &.active { - > a { - border-color: #CCC; - border-bottom: 1px solid #fff; - color: #333; - } - } - } - - &.nav-small-tabs > li > a { padding: 6px 9px; } -} diff --git a/app/assets/stylesheets/gitlab_bootstrap/tables.scss b/app/assets/stylesheets/gitlab_bootstrap/tables.scss index f46ae91b..549cdfee 100644 --- a/app/assets/stylesheets/gitlab_bootstrap/tables.scss +++ b/app/assets/stylesheets/gitlab_bootstrap/tables.scss @@ -1,19 +1,13 @@ table { @extend .table; @extend .table-striped; - @include solid-shade; - border: 1px solid #bbb; - width: 100%; + @include solid_shade; + border:1px solid #bbb; + width:100%; &.low { td { - line-height: 18px; - } - } - - &.headless { - tr:first-child td{ - border-top: 1px solid #CCC; + line-height:18px; } } @@ -31,14 +25,14 @@ table { } th, td { - padding: 10px; + padding: 8px; line-height: 18px; text-align: left; } td { - border-color: #f1f1f1; - line-height: 28px; + border-color:#f1f1f1; + line-height:28px; .s16 { margin-top: 5px; @@ -46,11 +40,11 @@ table { } &:first-child { - border-left: 1px solid #bbb; + border-left:1px solid #bbb; } &:last-child { - border-right: 1px solid #bbb; + border-right:1px solid #bbb; } } @@ -59,10 +53,10 @@ table { } &.lite { - border: none; - box-shadow: none; + border:none; + box-shadow:none; tr, td { - border: none; + border:none; background:none !important; } } diff --git a/app/assets/stylesheets/gitlab_bootstrap/typography.scss b/app/assets/stylesheets/gitlab_bootstrap/typography.scss index 1f0c4802..fe3bd68b 100644 --- a/app/assets/stylesheets/gitlab_bootstrap/typography.scss +++ b/app/assets/stylesheets/gitlab_bootstrap/typography.scss @@ -5,11 +5,11 @@ h1, h2, h3, h4, h5, h6 { margin: 0; } h3, h4, h5, h6 { line-height: 36px; } -h5 { font-size: 14px; } +h5 { font-size:14px; } h3.page_title { - color: #456; - font-size: 20px; + color:#456; + font-size:20px; font-weight: normal; line-height: 28px; } @@ -21,11 +21,11 @@ h6 { /** CODE **/ pre { - font-family: $monospace_font; + font-family:'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono','lucida console',monospace; &.dark { background: #333; - color: #f5f5f5; + color:#f5f5f5; } } @@ -37,8 +37,8 @@ a { outline: none; color: $link_color; &:hover { - text-decoration: none; - color: $primary_color; + text-decoration:none; + color: $blue_link; } &.btn { @@ -53,49 +53,27 @@ a { } &.lined { - text-decoration: underline; - &:hover { text-decoration: underline; } + text-decoration:underline; + &:hover { text-decoration:underline; } } &.gray { - color: gray; + color:gray; } &.supp_diff_link { - text-align: center; - padding: 20px 0; - background: #f1f1f1; - width: 100%; - float: left; + text-align:center; + padding:20px 0; + background:#f1f1f1; + width:100%; + float:left; } &.neib { - margin-right: 15px; + margin-right:15px; } } a:focus { outline: none; } - -.monospace { - font-family: $monospace_font; -} - -/** - * Wiki typography - * - */ -.wiki { - @include md-typography; - - font-size: 13px; - line-height: 20px; - - .white .highlight pre { background: #f5f5f5; } - ul { margin: 0 0 9px 25px !important; } -} - -.md { - @include md-typography; -} diff --git a/app/assets/stylesheets/gitlab_bootstrap/variables.scss b/app/assets/stylesheets/gitlab_bootstrap/variables.scss deleted file mode 100644 index aeabe7ad..00000000 --- a/app/assets/stylesheets/gitlab_bootstrap/variables.scss +++ /dev/null @@ -1,13 +0,0 @@ -/** - * General Colors - */ -$primary_color: #2FA0BB; -$link_color: #3A89A3; -$style_color: #474D57; -$hover: #D9EDF7; - -/** - * Commit Diff Colors - */ -$added: #63c363; -$deleted: #f77; diff --git a/app/assets/stylesheets/highlight/dark.scss b/app/assets/stylesheets/highlight/dark.scss index 4196ea7a..0996cc77 100644 --- a/app/assets/stylesheets/highlight/dark.scss +++ b/app/assets/stylesheets/highlight/dark.scss @@ -1,8 +1,6 @@ -.black .highlight { - pre { - background-color: #333; - color: #eee; - } +.black .lines .highlight { + background: #333; + pre { color: #eee; } .hll { display: block; background-color: darken($hover, 65%) } .c { color: #888888; font-style: italic } /* Comment */ @@ -23,43 +21,43 @@ .gs { font-weight: bold } /* Generic.Strong */ .gu { color: #606060 } /* Generic.Subheading */ .gt { color: #aa0000 } /* Generic.Traceback */ - .kc{font-weight: bold;} /* Keyword.Constant */ - .kd{font-weight: bold;} /* Keyword.Declaration */ - .kn{font-weight: bold;} /* Keyword.Namespace */ - .kp{font-weight: bold;} /* Keyword.Pseudo */ - .kr{font-weight: bold;} /* Keyword.Reserved */ - .kt{color: #458;font-weight: bold;} /* Keyword.Type */ + .kc{font-weight:bold;} /* Keyword.Constant */ + .kd{font-weight:bold;} /* Keyword.Declaration */ + .kn{font-weight:bold;} /* Keyword.Namespace */ + .kp{font-weight:bold;} /* Keyword.Pseudo */ + .kr{font-weight:bold;} /* Keyword.Reserved */ + .kt{color:#458;font-weight:bold;} /* Keyword.Type */ .m { color: #0000DD; font-weight: bold } /* Literal.Number */ .p { color: #eee; } .s { color: #0AD; background-color: transparent } /* Literal.String */ - .na{color: #008080;} /* Name.Attribute */ - .nb{color: #0086B3;} /* Name.Builtin */ - .nc{color: #ccc;font-weight: bold;} /* Name.Class */ - .no{color: turquoise;} /* Name.Constant */ - .ni{color: #800080;} - .ne{color: #900;font-weight: bold;} /* Name.Exception */ - .nf{color: #ccc;font-weight: bold;} /* Name.Function */ - .nn{color: #79C3E0;font-weight: bold;} /* Name.Namespace */ - .nt{color: #fc5;} /* Name.Tag */ - .nv{color: #FA4;} /* Name.Variable */ + .na{color:#008080;} /* Name.Attribute */ + .nb{color:#0086B3;} /* Name.Builtin */ + .nc{color:#ccc;font-weight:bold;} /* Name.Class */ + .no{color:turquoise;} /* Name.Constant */ + .ni{color:#800080;} + .ne{color:#900;font-weight:bold;} /* Name.Exception */ + .nf{color:#ccc;font-weight:bold;} /* Name.Function */ + .nn{color:#79C3E0;font-weight:bold;} /* Name.Namespace */ + .nt{color:#fc5;} /* Name.Tag */ + .nv{color:#FA4;} /* Name.Variable */ .py { color: #336699; font-weight: bold } /* Name.Property */ .ow { color: #008800 } /* Operator.Word */ .w { color: #bbbbbb } /* Text.Whitespace */ .mf { color: #7AC; font-weight: bold } /* Literal.Number.Float */ .mh { color: #7AC; font-weight: bold } /* Literal.Number.Hex */ - .mi {color: #099;} /* Literal.Number.Integer */ + .mi {color:#099;} /* Literal.Number.Integer */ .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .sb { color: #dd2200; background-color: transparent; } /* Literal.String.Backtick */ - .sc{color: #d14;} /* Literal.String.Char */ + .sc{color:#d14;} /* Literal.String.Char */ .sd { color: #dd2200; background-color: transparent; } /* Literal.String.Doc */ - .s2{color: orange;} /* Literal.String.Double */ - .se{color: orange;} /* Literal.String.Escape */ - .sh{color: orange;} /* Literal.String.Heredoc */ - .si{color: orange;} /* Literal.String.Interpol */ - .sx{color: orange;} /* Literal.String.Other */ - .sr{color: orange;} /* Literal.String.Regex */ - .s1{color: orange;} /* Literal.String.Single */ - .ss{color: orange;} /* Literal.String.Symbol */ + .s2{color:orange;} /* Literal.String.Double */ + .se{color:orange;} /* Literal.String.Escape */ + .sh{color:orange;} /* Literal.String.Heredoc */ + .si{color:orange;} /* Literal.String.Interpol */ + .sx{color:orange;} /* Literal.String.Other */ + .sr{color:orange;} /* Literal.String.Regex */ + .s1{color:orange;} /* Literal.String.Single */ + .ss{color:orange;} /* Literal.String.Symbol */ .bp { color: #D58 } /* Name.Builtin.Pseudo */ .vc { color: #336699 } /* Name.Variable.Class */ .vg { color: #dd7700 } /* Name.Variable.Global */ diff --git a/app/assets/stylesheets/highlight/solarized_dark.scss b/app/assets/stylesheets/highlight/solarized_dark.scss deleted file mode 100644 index 3f215b4e..00000000 --- a/app/assets/stylesheets/highlight/solarized_dark.scss +++ /dev/null @@ -1,77 +0,0 @@ -.solarized-dark .highlight { - pre { - background-color: #002B36; - color: #eee; - } - - .hll { background-color: #073642 } - .c { color: #586E75 } /* Comment */ - .err { color: #93A1A1 } /* Error */ - .g { color: #93A1A1 } /* Generic */ - .k { color: #859900 } /* Keyword */ - .l { color: #93A1A1 } /* Literal */ - .n { color: #93A1A1 } /* Name */ - .o { color: #859900 } /* Operator */ - .x { color: #CB4B16 } /* Other */ - .p { color: #93A1A1 } /* Punctuation */ - .cm { color: #586E75 } /* Comment.Multiline */ - .cp { color: #859900 } /* Comment.Preproc */ - .c1 { color: #586E75 } /* Comment.Single */ - .cs { color: #859900 } /* Comment.Special */ - .gd { color: #2AA198 } /* Generic.Deleted */ - .ge { color: #93A1A1; font-style: italic } /* Generic.Emph */ - .gr { color: #DC322F } /* Generic.Error */ - .gh { color: #CB4B16 } /* Generic.Heading */ - .gi { color: #859900 } /* Generic.Inserted */ - .go { color: #93A1A1 } /* Generic.Output */ - .gp { color: #93A1A1 } /* Generic.Prompt */ - .gs { color: #93A1A1; font-weight: bold } /* Generic.Strong */ - .gu { color: #CB4B16 } /* Generic.Subheading */ - .gt { color: #93A1A1 } /* Generic.Traceback */ - .kc { color: #CB4B16 } /* Keyword.Constant */ - .kd { color: #268BD2 } /* Keyword.Declaration */ - .kn { color: #859900 } /* Keyword.Namespace */ - .kp { color: #859900 } /* Keyword.Pseudo */ - .kr { color: #268BD2 } /* Keyword.Reserved */ - .kt { color: #DC322F } /* Keyword.Type */ - .ld { color: #93A1A1 } /* Literal.Date */ - .m { color: #2AA198 } /* Literal.Number */ - .s { color: #2AA198 } /* Literal.String */ - .na { color: #93A1A1 } /* Name.Attribute */ - .nb { color: #B58900 } /* Name.Builtin */ - .nc { color: #268BD2 } /* Name.Class */ - .no { color: #CB4B16 } /* Name.Constant */ - .nd { color: #268BD2 } /* Name.Decorator */ - .ni { color: #CB4B16 } /* Name.Entity */ - .ne { color: #CB4B16 } /* Name.Exception */ - .nf { color: #268BD2 } /* Name.Function */ - .nl { color: #93A1A1 } /* Name.Label */ - .nn { color: #93A1A1 } /* Name.Namespace */ - .nx { color: #93A1A1 } /* Name.Other */ - .py { color: #93A1A1 } /* Name.Property */ - .nt { color: #268BD2 } /* Name.Tag */ - .nv { color: #268BD2 } /* Name.Variable */ - .ow { color: #859900 } /* Operator.Word */ - .w { color: #93A1A1 } /* Text.Whitespace */ - .mf { color: #2AA198 } /* Literal.Number.Float */ - .mh { color: #2AA198 } /* Literal.Number.Hex */ - .mi { color: #2AA198 } /* Literal.Number.Integer */ - .mo { color: #2AA198 } /* Literal.Number.Oct */ - .sb { color: #586E75 } /* Literal.String.Backtick */ - .sc { color: #2AA198 } /* Literal.String.Char */ - .sd { color: #93A1A1 } /* Literal.String.Doc */ - .s2 { color: #2AA198 } /* Literal.String.Double */ - .se { color: #CB4B16 } /* Literal.String.Escape */ - .sh { color: #93A1A1 } /* Literal.String.Heredoc */ - .si { color: #2AA198 } /* Literal.String.Interpol */ - .sx { color: #2AA198 } /* Literal.String.Other */ - .sr { color: #DC322F } /* Literal.String.Regex */ - .s1 { color: #2AA198 } /* Literal.String.Single */ - .ss { color: #2AA198 } /* Literal.String.Symbol */ - .bp { color: #268BD2 } /* Name.Builtin.Pseudo */ - .vc { color: #268BD2 } /* Name.Variable.Class */ - .vg { color: #268BD2 } /* Name.Variable.Global */ - .vi { color: #268BD2 } /* Name.Variable.Instance */ - .il { color: #2AA198 } /* Literal.Number.Integer.Long */ -} - diff --git a/app/assets/stylesheets/highlight/white.scss b/app/assets/stylesheets/highlight/white.scss index f200e1d7..d6792b37 100644 --- a/app/assets/stylesheets/highlight/white.scss +++ b/app/assets/stylesheets/highlight/white.scss @@ -1,8 +1,6 @@ -.white .highlight { - pre { - background-color: #fff; - color: #333; - } +.white .lines .highlight { + background: white; + pre { color: #333; } .hll { display: block; background-color: $hover } .c { color: #888888; font-style: italic } /* Comment */ @@ -22,42 +20,42 @@ .gs { font-weight: bold } /* Generic.Strong */ .gu { color: #606060 } /* Generic.Subheading */ .gt { color: #aa0000 } /* Generic.Traceback */ - .kc{font-weight: bold;} /* Keyword.Constant */ - .kd{font-weight: bold;} /* Keyword.Declaration */ - .kn{font-weight: bold;} /* Keyword.Namespace */ - .kp{font-weight: bold;} /* Keyword.Pseudo */ - .kr{font-weight: bold;} /* Keyword.Reserved */ - .kt{color: #458;font-weight: bold;} /* Keyword.Type */ + .kc{font-weight:bold;} /* Keyword.Constant */ + .kd{font-weight:bold;} /* Keyword.Declaration */ + .kn{font-weight:bold;} /* Keyword.Namespace */ + .kp{font-weight:bold;} /* Keyword.Pseudo */ + .kr{font-weight:bold;} /* Keyword.Reserved */ + .kt{color:#458;font-weight:bold;} /* Keyword.Type */ .m { color: #0000DD; font-weight: bold } /* Literal.Number */ .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */ - .na{color: #008080;} /* Name.Attribute */ - .nb{color: #0086B3;} /* Name.Builtin */ - .nc{color: #458;font-weight: bold;} /* Name.Class */ - .no{color: #008080;} /* Name.Constant */ - .ni{color: #800080;} - .ne{color: #900;font-weight: bold;} /* Name.Exception */ - .nf{color: #900;font-weight: bold;} /* Name.Function */ - .nn{color: #005;font-weight: bold;} /* Name.Namespace */ - .nt{color: #000080;} /* Name.Tag */ - .nv{color: #008080;} /* Name.Variable */ + .na{color:#008080;} /* Name.Attribute */ + .nb{color:#0086B3;} /* Name.Builtin */ + .nc{color:#458;font-weight:bold;} /* Name.Class */ + .no{color:#008080;} /* Name.Constant */ + .ni{color:#800080;} + .ne{color:#900;font-weight:bold;} /* Name.Exception */ + .nf{color:#900;font-weight:bold;} /* Name.Function */ + .nn{color:#005;font-weight:bold;} /* Name.Namespace */ + .nt{color:#000080;} /* Name.Tag */ + .nv{color:#008080;} /* Name.Variable */ .py { color: #336699; font-weight: bold } /* Name.Property */ .ow { color: #008800 } /* Operator.Word */ .w { color: #bbbbbb } /* Text.Whitespace */ .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ - .mi {color: #099;} /* Literal.Number.Integer */ + .mi {color:#099;} /* Literal.Number.Integer */ .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ - .sc{color: #d14;} /* Literal.String.Char */ + .sc{color:#d14;} /* Literal.String.Char */ .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ - .s2{color: #d14;} /* Literal.String.Double */ - .se{color: #d14;} /* Literal.String.Escape */ - .sh{color: #d14;} /* Literal.String.Heredoc */ - .si{color: #d14;} /* Literal.String.Interpol */ - .sx{color: #d14;} /* Literal.String.Other */ - .sr{color: #d14;} /* Literal.String.Regex */ - .s1{color: #d14;} /* Literal.String.Single */ - .ss{color: #d14;} /* Literal.String.Symbol */ + .s2{color:#d14;} /* Literal.String.Double */ + .se{color:#d14;} /* Literal.String.Escape */ + .sh{color:#d14;} /* Literal.String.Heredoc */ + .si{color:#d14;} /* Literal.String.Interpol */ + .sx{color:#d14;} /* Literal.String.Other */ + .sr{color:#d14;} /* Literal.String.Regex */ + .s1{color:#d14;} /* Literal.String.Single */ + .ss{color:#d14;} /* Literal.String.Symbol */ .bp { color: #003388 } /* Name.Builtin.Pseudo */ .vc { color: #336699 } /* Name.Variable.Class */ .vg { color: #dd7700 } /* Name.Variable.Global */ @@ -65,5 +63,7 @@ } .shadow { - @include box-shadow(0 5px 15px #000); + -webkit-box-shadow:0 5px 15px #000; + -moz-box-shadow:0 5px 15px #000; + box-shadow:0 5px 15px #000; } diff --git a/app/assets/stylesheets/jquery.ui.gitlab.css b/app/assets/stylesheets/jquery.ui.gitlab.css deleted file mode 100644 index 5c51600b..00000000 --- a/app/assets/stylesheets/jquery.ui.gitlab.css +++ /dev/null @@ -1,257 +0,0 @@ -/* Interaction Cues -----------------------------------*/ -.ui-state-disabled { cursor: default !important; } - - -/* Icons -----------------------------------*/ - -/* states and images */ -.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; } - - -/* Misc visuals -----------------------------------*/ - -/* Overlays */ -.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } - - -/* - * jQuery UI CSS Framework 1.8.7 - * - * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Theming/API - * - * To view and modify this theme, visit http://jqueryui.com/themeroller/?ctl=themeroller - */ - - -/* Component containers -----------------------------------*/ -.ui-widget { font-family: Arial,sans-serif; font-size: 1.1em; } -.ui-widget .ui-widget { font-size: 1em; } -.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Arial,sans-serif; font-size: 1em; } -.ui-widget-content { border: 1px solid #CCC; background: #ffffff; color: #4F4F4F; } -.ui-widget-content a { color: #4F4F4F; } -.ui-widget-header { border: 1px solid #B6B6B6; color: #4F4F4F; font-weight: bold; } -.ui-widget-header { - background: #ededed url(bg_fallback.png) 0 0 repeat-x; /* Old browsers */ - background: -moz-linear-gradient(top, #ededed 0%, #c4c4c4 100%); /* FF3.6+ */ - background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#ededed), color-stop(100%,#c4c4c4)); /* Chrome,Safari4+ */ - background: -webkit-linear-gradient(top, #ededed 0%,#c4c4c4 100%); /* Chrome10+,Safari5.1+ */ - background: -o-linear-gradient(top, #ededed 0%,#c4c4c4 100%); /* Opera11.10+ */ - background: -ms-linear-gradient(top, #ededed 0%,#c4c4c4 100%); /* IE10+ */ - background: linear-gradient(top, #ededed 0%,#c4c4c4 100%); /* W3C */ -} -.ui-widget-header a { color: #4F4F4F; } - -/* Interaction states -----------------------------------*/ -.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #B6B6B6; font-weight: normal; color: #4F4F4F; } -.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { - background: #ededed url(bg_fallback.png) 0 0 repeat-x; /* Old browsers */ - background: -moz-linear-gradient(top, #ededed 0%, #c4c4c4 100%); /* FF3.6+ */ - background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#ededed), color-stop(100%,#c4c4c4)); /* Chrome,Safari4+ */ - background: -webkit-linear-gradient(top, #ededed 0%,#c4c4c4 100%); /* Chrome10+,Safari5.1+ */ - background: -o-linear-gradient(top, #ededed 0%,#c4c4c4 100%); /* Opera11.10+ */ - background: -ms-linear-gradient(top, #ededed 0%,#c4c4c4 100%); /* IE10+ */ - background: linear-gradient(top, #ededed 0%,#c4c4c4 100%); /* W3C */ - -webkit-box-shadow: 0 1px 0 rgba(255,255,255,0.6) inset; - -moz-box-shadow: 0 1px 0 rgba(255,255,255,0.6) inset; - box-shadow: 0 1px 0 rgba(255,255,255,0.6) inset; -} -.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #4F4F4F; text-decoration: none; } -.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #9D9D9D; font-weight: normal; color: #313131; } -.ui-state-hover a, .ui-state-hover a:hover { color: #313131; text-decoration: none; } -.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { - outline: none; - color: #1c4257; border: 1px solid #7096ab; - background: #ededed url(bg_fallback.png) 0 -50px repeat-x; /* Old browsers */ - background: -moz-linear-gradient(top, #b9e0f5 0%, #92bdd6 100%); /* FF3.6+ */ - background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#b9e0f5), color-stop(100%,#92bdd6)); /* Chrome,Safari4+ */ - background: -webkit-linear-gradient(top, #b9e0f5 0%,#92bdd6 100%); /* Chrome10+,Safari5.1+ */ - background: -o-linear-gradient(top, #b9e0f5 0%,#92bdd6 100%); /* Opera11.10+ */ - background: -ms-linear-gradient(top, #b9e0f5 0%,#92bdd6 100%); /* IE10+ */ - background: linear-gradient(top, #b9e0f5 0%,#92bdd6 100%); /* W3C */ - -webkit-box-shadow: none; - -moz-box-shadow: none; - box-shadow: none; -} -.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #313131; text-decoration: none; } -.ui-widget :active { outline: none; } - -/* Interaction Cues -----------------------------------*/ -.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight { border: 1px solid #d2dbf4; background: #f4f8fd; color: #0d2054; -moz-border-radius: 0 !important; -webkit-border-radius: 0 !important; border-radius: 0 !important; } -.ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #363636; } -.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error { border: 1px solid #e2d0d0; background: #fcf0f0; color: #280b0b; -moz-border-radius: 0 !important; -webkit-border-radius: 0 !important; border-radius: 0 !important; } -.ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #cd0a0a; } -.ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #cd0a0a; } -.ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; } -.ui-priority-secondary, .ui-widget-content .ui-priority-secondary, .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; } -.ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; } - -/* Icons -----------------------------------*/ - -/* states and images */ -.ui-icon { width: 16px; height: 16px; background-image: url(ui-icons_222222_256x240.png); } -.ui-widget-content .ui-icon {background-image: url(ui-icons_222222_256x240.png); } -.ui-widget-header .ui-icon {background-image: url(ui-icons_222222_256x240.png); } -.ui-state-default .ui-icon { background-image: url(ui-icons_454545_256x240.png); } -.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(ui-icons_454545_256x240.png); } -.ui-state-active .ui-icon {background-image: url(ui-icons_454545_256x240.png); } -.ui-state-highlight .ui-icon {background-image: url(ui-icons_454545_256x240.png); } -.ui-state-error .ui-icon, .ui-state-error-text .ui-icon { background: url(icon_sprite.png) -16px 0 no-repeat !important; } -.ui-state-highlight .ui-icon, .ui-state-error .ui-icon { margin-top: -1px; } - - -/* Misc visuals -----------------------------------*/ - -/* Overlays */ -.ui-widget-overlay { background: #262b33; opacity: .70;filter:Alpha(Opacity=70); } -.ui-widget-shadow { margin: -8px 0 0 -8px; padding: 8px; background: #000000; opacity: .30;filter:Alpha(Opacity=30); -moz-border-radius: 8px; -webkit-border-radius: 8px; border-radius: 8px; } -/* - * jQuery UI Selectable 1.8.7 - * - * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Selectable#theming - */ -.ui-selectable-helper { position: absolute; z-index: 100; border:1px dotted black; } -/* - * jQuery UI Autocomplete 1.8.7 - * - * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Autocomplete#theming - */ -.ui-autocomplete { - position: absolute; cursor: default; z-index: 3; - -moz-border-radius: 0; - -webkit-border-radius: 0; - border-radius: 0; - -moz-box-shadow: 0 1px 5px rgba(0,0,0,0.3); - -webkit-box-shadow: 0 1px 5px rgba(0,0,0,0.3); - box-shadow: 0 1px 5px rgba(0,0,0,0.3); -} - -/* workarounds */ -* html .ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */ - -/* - * jQuery UI Menu 1.8.7 - * - * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Menu#theming - */ -.ui-menu { - list-style:none; - padding: 1px; - margin: 0; - display:block; - float: left; -} -.ui-menu .ui-menu { - margin-top: -3px; -} -.ui-menu .ui-menu-item { - margin:0; - padding: 0; - zoom: 1; - float: left; - clear: left; - width: 100%; -} -.ui-menu .ui-menu-item a { - text-decoration:none; - display:block; - padding:.2em .4em; - line-height:1.5; - zoom:1; - color: #666; - font-size: 13px; -} -.ui-menu .ui-menu-item a.ui-state-hover, -.ui-menu .ui-menu-item a.ui-state-active { - font-weight: normal; - margin: -1px; - background: #D9EDF7; - color: #3A89A3; - text-shadow: 0px 1px 1px #fff; - border: none; - border: 1px solid #ADE; - cursor: pointer; - font-weight: bold; -} - -/* - * jQuery UI Datepicker 1.8.7 - * - * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Datepicker#theming - */ -.ui-datepicker { - width: 17em; - padding: 0; - display: none; - border-color: #DDDDDD; - border: none; - box-shadow: none; -} -.ui-datepicker .ui-datepicker-header { - position:relative; - padding:.35em 0; - border: none; - border-bottom: 1px solid #B6B6B6; - -moz-border-radius: 0; - -webkit-border-radius: 0; - border-radius: 0; - margin-bottom: 10px; - border: 1px solid #bbb; - -webkit-box-shadow: 0 0 0 3px #F1F1F1; - -moz-box-shadow: 0 0 0 3px #f1f1f1; - -ms-box-shadow: 0 0 0 3px #f1f1f1; - -o-box-shadow: 0 0 0 3px #f1f1f1; - box-shadow: 0 0 0 3px #F1F1F1; -} - -.ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 6px; width: 1.8em; height: 1.8em; } -.ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { border: 1px none; } -.ui-datepicker .ui-datepicker-prev { left:2px; } -.ui-datepicker .ui-datepicker-next { right:2px; } -.ui-datepicker .ui-datepicker-prev span { background-position: 0px -32px !important; } -.ui-datepicker .ui-datepicker-next span { background-position: -16px -32px !important; } -.ui-datepicker .ui-datepicker-prev-hover span { background-position: 0px -48px !important; } -.ui-datepicker .ui-datepicker-next-hover span { background-position: -16px -48px !important; } -.ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px; background: url(icon_sprite.png) no-repeat; } -.ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; font-size: 12px; text-shadow: 0 1px 0 rgba(255,255,255,0.6); } -.ui-datepicker .ui-datepicker-title select { font-size:1em; margin:1px 0; } -.ui-datepicker select.ui-datepicker-month-year {width: 100%;} -.ui-datepicker select.ui-datepicker-month, -.ui-datepicker select.ui-datepicker-year { width: 49%;} -.ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; } -.ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0; } -.ui-datepicker td { border: 0; padding: 1px; line-height: 24px; background-color: #FFF!important; } -.ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; } -.ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; } -.ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; } -.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; } -.ui-datepicker table .ui-state-highlight { border-color: #ADE; } -.ui-datepicker-calendar .ui-state-default { background: transparent; border-color: #FFF; } -.ui-datepicker-calendar .ui-state-active { background: #D9EDF7; border-color: #ADE; color: #3A89A3; font-weight: bold; text-shadow: 0 1px 1px #fff; } diff --git a/app/assets/stylesheets/main.scss b/app/assets/stylesheets/main.scss new file mode 100644 index 00000000..7ae32b3a --- /dev/null +++ b/app/assets/stylesheets/main.scss @@ -0,0 +1,204 @@ +/** Override bootstrap variables **/ +$baseFontSize: 13px !default; +$baseLineHeight: 18px !default; + +@import "bootstrap"; +@import "bootstrap-responsive"; +@import 'font-awesome'; + +/** GitLab colors **/ +$link_color: #3A89A3; +$blue_link: #2FA0BB; +$style_color: #474D57; +$hover: #D9EDF7; +$hover_border: #ADF; + +/** GitLab Fonts **/ +@font-face { font-family: Korolev; src: font-url('korolev-medium-compressed.otf'); } +$monospace: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono', 'lucida console', monospace; + +/** MIXINS **/ +@mixin shade { + -moz-box-shadow: 0 0 3px #ddd; + -webkit-box-shadow: 0 0 3px #ddd; + box-shadow: 0 0 3px #ddd; +} + +@mixin solid_shade { + -moz-box-shadow: 0 0 0 3px #f1f1f1; + -webkit-box-shadow: 0 0 0 3px #f1f1f1; + box-shadow: 0 0 0 3px #f1f1f1; +} + +@mixin border-radius($radius) { + -moz-border-radius: $radius; + -webkit-border-radius: $radius; + border-radius: $radius; +} + +@mixin round-borders-bottom($radius) { + border-top: 1px solid #eaeaea; + -moz-border-radius-bottomright: $radius; + -moz-border-radius-bottomleft: $radius; + border-bottom-right-radius: $radius; + border-bottom-left-radius: $radius; + -webkit-border-bottom-left-radius: $radius; + -webkit-border-bottom-right-radius: $radius; +} + +@mixin round-borders-top($radius) { + border-top: 1px solid #eaeaea; + -moz-border-radius-topright: $radius; + -moz-border-radius-topleft: $radius; + border-top-right-radius: $radius; + border-top-left-radius: $radius; + -webkit-border-top-left-radius: $radius; + -webkit-border-top-right-radius: $radius; +} + +@mixin round-borders-all($radius) { + border: 1px solid #eaeaea; + -moz-border-radius: $radius; + -webkit-border-radius: $radius; + border-radius: $radius; +} + +@mixin bg-gradient($from, $to) { + background-image: -webkit-gradient(linear, 0 0, 0 100%, from($from), to($to)); + background-image: -webkit-linear-gradient($from, $to); + background-image: -moz-linear-gradient($from, $to); + background-image: -o-linear-gradient($from, $to); +} + +@mixin bg-light-gray-gradient { + background:#f1f1f1; + background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #f5f5f5), to(#e1e1e1)); + background-image: -webkit-linear-gradient(#f5f5f5 6.6%, #e1e1e1); + background-image: -moz-linear-gradient(#f5f5f5 6.6%, #e1e1e1); + background-image: -o-linear-gradient(#f5f5f5 6.6%, #e1e1e1); +} + +@mixin bg-gray-gradient { + background:#eee; + background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf)); + background-image: -webkit-linear-gradient(#eee 6.6%, #dfdfdf); + background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf); + background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf); +} + +@mixin bg-dark-gray-gradient { + background:#eee; + background-image: -webkit-linear-gradient(#e9e9e9, #d7d7d7); + background-image: -moz-linear-gradient(#e9e9e9, #d7d7d7); + background-image: -o-linear-gradient(#e9e9e9, #d7d7d7); +} + +/** + * Header of application. + * Contain application logo, search panel, profile icon + */ +@import "sections/header.scss"; + +/** + * Navigation menu of application. + * Panel with links to pages depends on project, profile or admin area + */ +@import "sections/nav.scss"; + +/** + * This file represent some UI that can be changed + * during web app restyle or theme select. + * + * Next items should be placed there + * - link, button colors + * - header restyles + * - main menu restyles + * + */ +@import "themes/ui_basic.scss"; + +/** + * UI themes: + */ +@import "themes/ui_mars.scss"; +@import "themes/ui_modern.scss"; +@import "themes/ui_gray.scss"; +@import "themes/ui_color.scss"; + +/** + * GitLab bootstrap. + * Overrides some styles of twitter bootstrap. + * Also give some common classes for GitLab app + */ +@import "gitlab_bootstrap/common.scss"; +@import "gitlab_bootstrap/typography.scss"; +@import "gitlab_bootstrap/buttons.scss"; +@import "gitlab_bootstrap/blocks.scss"; +@import "gitlab_bootstrap/files.scss"; +@import "gitlab_bootstrap/tables.scss"; +@import "gitlab_bootstrap/lists.scss"; + + +/** + * Most of application styles placed here. + * This file represent common UI that should not be changed between themes + * or project restyling like form width or user avatar class or commit title + * + * TODO: clean it + */ +@import "common.scss"; + +/** + * Styles related to specific part of app + */ +@import "sections/commits.scss"; +@import "sections/issues.scss"; +@import "sections/projects.scss"; +@import "sections/merge_requests.scss"; +@import "sections/graph.scss"; +@import "sections/events.scss"; +@import "sections/themes.scss"; + +/** + * This scss file redefine chozen selectbox styles for + * project Branch/Tag select element + */ +@import "ref_select.scss"; + +/** + * Code (files list) styles. Browsing project files there + */ +@import "sections/tree.scss"; + +/** + * This file represent notes(comments) styles + */ +@import "sections/notes.scss"; + +/** + * This file represent profile styles + */ +@import "sections/profile.scss"; + +/** + * Devise styles + */ +@import "sections/login.scss"; + +/** + * CODE HIGHTLIGHT BASE + * + */ +@import "highlight/white.scss"; + +/** + * CODE HIGHTLIGHT DARK schema + * + */ +@import "highlight/dark.scss"; + +/** + * File Editor styles + * + */ +@import "sections/editor.scss"; diff --git a/app/assets/stylesheets/selects.scss b/app/assets/stylesheets/ref_select.scss similarity index 53% rename from app/assets/stylesheets/selects.scss rename to app/assets/stylesheets/ref_select.scss index 07f7db75..377d0086 100644 --- a/app/assets/stylesheets/selects.scss +++ b/app/assets/stylesheets/ref_select.scss @@ -1,21 +1,6 @@ -.ajax-users-select { - width: 400px; -} - -.user-result { - .user-image { - float: left; - } - .user-name { - } - .user-username { - color: #999; - } -} - /** Branch/tag selector **/ .project-refs-form { - margin: 0; + margin:0; span { background:none !important; position:static !important; @@ -24,7 +9,7 @@ } } .project-refs-select { - width: 120px; + width:120px; } .project-refs-form .chzn-container { @@ -36,10 +21,10 @@ .chzn-drop { min-width: 400px; .chzn-results { - max-height: 300px; + max-height:300px; } .chzn-search input { - min-width: 365px; + min-width:365px; } } } @@ -48,19 +33,21 @@ .chzn-container { .chzn-search { input:focus { - @include box-shadow(none); + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; } } .chzn-drop { - margin: 7px 0; + margin:7px 0; min-width: 200px; border: 1px solid #bbb; - @include border-radius(0); + border-radius:0; .chzn-results { margin-top: 5px; - max-height: 300px; + max-height:300px; .group-result { color: $style_color; @@ -68,7 +55,7 @@ padding: 8px; } .active-result { - @include border-radius(0); + border-radius: 0; &.highlighted { background: $hover; @@ -84,7 +71,7 @@ .chzn-search { @include bg-gray-gradient; input { - min-width: 165px; + min-width:165px; border-color: #CCC; } } @@ -94,8 +81,8 @@ @include bg-light-gray-gradient; div { - background: transparent; - border-left: none; + background:transparent; + border-left:none; } span { @@ -103,26 +90,3 @@ } } } - -/** Select2 styling **/ -.select2-container .select2-choice { - background: #f1f1f1; - background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, whitesmoke), to(#e1e1e1)); - background-image: -webkit-linear-gradient(whitesmoke 6.6%, #e1e1e1); - background-image: -moz-linear-gradient(whitesmoke 6.6%, #e1e1e1); - background-image: -o-linear-gradient(whitesmoke 6.6%, #e1e1e1); -} - -.select2-container .select2-choice div { - border: none; - background: none; -} - -.select2-drop { - padding-top: 8px; -} - -.select2-no-results, .select2-searching { - padding: 7px; - color: #666; -} diff --git a/app/assets/stylesheets/sections/admin.scss b/app/assets/stylesheets/sections/admin.scss deleted file mode 100644 index 18b102d7..00000000 --- a/app/assets/stylesheets/sections/admin.scss +++ /dev/null @@ -1,5 +0,0 @@ -.admin-filter form { - label { width: 110px; } - .controls { margin-left: 130px; } - .form-actions { padding-left: 130px; background: #fff } -} diff --git a/app/assets/stylesheets/sections/commits.scss b/app/assets/stylesheets/sections/commits.scss index 1e564188..9cce5bd0 100644 --- a/app/assets/stylesheets/sections/commits.scss +++ b/app/assets/stylesheets/sections/commits.scss @@ -1,369 +1,271 @@ -/** - * Commit file - */ -.commit-committer-link, -.commit-author-link { - font-size: 13px; - color: #555; - &:hover { - color: #999; +.commit-box { + @extend .main_box; + + .commit-head { + @extend .top_box_content; + + .commit-title { + line-height: 26px; + margin:0; + } + + .commit-description { + font-size: 14px; + border: none; + background-color: white; + padding-top:10px; + } + + .browse-button { + @extend .btn; + @extend .btn-small; + float: right; + } + } + + .commit-info { + @extend .middle_box_content; + @extend .clearfix; + + .sha-block { + text-align:right; + &:first-child { + padding-bottom:6px; + } + + a { + border-bottom: 1px solid #aaa; + margin-left: 9px; + } + } + + &.merge-commit .sha-block { + clear: right; + } + + .committer { + padding-left: 32px; + } + + .author a, + .committer a { + font-size:14px; + line-height:22px; + text-shadow:0 1px 1px #fff; + color:#777; + &:hover { + color: #999; + } + } + + .avatar { + margin-right: 10px; + } } } -.file { - border: 1px solid #CCC; - margin-bottom: 1em; +/** + * + * COMMIT SHOw + * + */ +.diff_file { + border:1px solid #CCC; + margin-bottom:1em; - .header { + .diff_file_header { @extend .clearfix; padding: 5px 5px 5px 10px; color: #555; - border-bottom: 1px solid #CCC; + border-bottom:1px solid #CCC; background: #eee; - // TODO Replace with linear-gradient mixin background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf)); background-image: -webkit-linear-gradient(#eee 6.6%, #dfdfdf); background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf); background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf); - a{ - color: $style_color; - } - > span { - font-family: $monospace_font; - font-size: 14px; + font-family: $monospace; + font-size:14px; line-height: 30px; } - a.view-file{ + a.view-commit{ font-weight: bold; } .commit-short-id{ - font-family: $monospace_font; + font-family: $monospace; font-size: smaller; } .file-mode{ - font-family: $monospace_font; + font-family: $monospace; } } - .content { - overflow: auto; - overflow-y: hidden; - background: #FFF; - color: #333; + .diff_file_content { + overflow:auto; + overflow-y:hidden; + background:#fff; + color:#333; font-size: 12px; + font-family: $monospace; .old{ span.idiff{ - background-color: #FAA; + background-color:#FAA; } } .new{ span.idiff{ - background-color: #AFA; + background-color:#AFA; } } table { - font-family: $monospace_font; - border: none; - margin: 0px; - padding: 0px; td { - line-height: 18px; - font-size: 12px; - } - } - .old_line, .new_line { - margin: 0px; - padding: 0px; - border: none; - background: #EEE; - color: #666; - padding: 0px 5px; - border-right: 1px solid #ccc; - text-align: right; - min-width: 35px; - max-width: 35px; - width: 35px; - @include user-select(none); - a { - float: left; - width: 35px; - font-weight: normal; - color: #666; - &:hover { - text-decoration: underline; - } - } - } - .line_content { - display: block; - white-space: pre; - height: 18px; - margin: 0px; - padding: 0px; - border: none; - &.new { - background: #CFD; - } - &.old { - background: #FDD; - } - &.matched { - color: #ccc; - background: #fafafa; + line-height:18px; } } } - .image { - background: #ddd; - text-align: center; - padding: 30px; - .wrap{ + .diff_file_content_image { + background:#eee; + text-align:center; + .image { display: inline-block; - } + margin:50px; + max-width:400px; - .frame { - display: inline-block; - background-color: #fff; - line-height: 0; img{ - border: 1px solid #FFF; background: url('trans_bg.gif'); } - &.deleted { - border: 1px solid $deleted; + + &.diff_removed { + img{ + border: 1px solid #C00; + } } - &.added { - border: 1px solid $added; + &.diff_added { + img{ + border: 1px solid #0C0; + } + } + + .image-info{ + margin: 5px 0 0 0; } } - .image-info{ - font-size: 12px; - margin: 5px 0 0 0; - color: grey; + + &.img_compared { + .image { + max-width:300px; + } } - - .view.swipe{ - position: relative; - - .swipe-frame{ - display: block; - margin: auto; - position: relative; - } - .swipe-wrap{ - overflow: hidden; - border-left: 1px solid #999; - position: absolute; - display: block; - top: 13px; - right: 7px; - } - .frame{ - top: 0; - right: 0; - position: absolute; - &.deleted{ - margin: 0; - display: block; - top: 13px; - right: 7px; - } - } - .swipe-bar{ - display: block; - height: 100%; - width: 15px; - z-index: 100; - position: absolute; - cursor: pointer; - &:hover{ - .top-handle{ - background-position: -15px 3px; - } - .bottom-handle{ - background-position: -15px -11px; - } - }; - .top-handle{ - display: block; - height: 14px; - width: 15px; - position: absolute; - top: 0px; - background: url('swipemode_sprites.gif') 0 3px no-repeat; - } - .bottom-handle{ - display: block; - height: 14px; - width: 15px; - position: absolute; - bottom: 0px; - background: url('swipemode_sprites.gif') 0 -11px no-repeat; - } - } - } //.view.swipe - .view.onion-skin{ - .onion-skin-frame{ - display: block; - margin: auto; - position: relative; - } - .frame.added, .frame.deleted { - position: absolute; - display: block; - top: 0px; - left: 0px; - } - .controls{ - display: block; - height: 14px; - width: 300px; - z-index: 100; - position: absolute; - bottom: 0px; - left: 50%; - margin-left: -150px; - - .drag-track{ - display: block; - position: absolute; - left: 12px; - height: 10px; - width: 276px; - background: url('onion_skin_sprites.gif') -4px -20px repeat-x; - } - - .dragger { - display: block; - position: absolute; - left: 0px; - top: 0px; - height: 14px; - width: 14px; - background: url('onion_skin_sprites.gif') 0px -34px repeat-x; - cursor: pointer; - } - - .transparent { - display: block; - position: absolute; - top: 2px; - right: 0px; - height: 10px; - width: 10px; - background: url('onion_skin_sprites.gif') -2px 0px no-repeat; - } - - .opaque { - display: block; - position: absolute; - top: 2px; - left: 0px; - height: 10px; - width: 10px; - background: url('onion_skin_sprites.gif') -2px -10px no-repeat; - } - } - } //.view.onion-skin } - .view-modes{ +} - padding: 10px; - text-align: center; - - background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf)); - background-image: -webkit-linear-gradient(#eee 6.6%, #dfdfdf); - background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf); - background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf); - - ul, li{ - list-style: none; - margin: 0; - padding: 0; - display: inline-block; +.diff_file_content{ + table { + border:none; + margin:0px; + padding:0px; + tr { + td { + font-size:12px; + } } - - li{ - color: grey; - border-left: 1px solid #c1c1c1; - padding: 0 12px 0 16px; - cursor: pointer; - &:first-child{ - border-left: none; - } - &:hover{ - text-decoration: underline; - } - &.active{ - &:hover{ - text-decoration: none; - } - cursor: default; - color: #333; - } - &.disabled{ - display: none; + } + .old_line, .new_line { + margin:0px; + padding:0px; + border:none; + background:#EEE; + color:#666; + padding: 0px 5px; + border-right: 1px solid #ccc; + text-align:right; + min-width:35px; + max-width:35px; + width:35px; + moz-user-select: none; + -khtml-user-select: none; + user-select: none; + a { + float:left; + width:35px; + font-weight:normal; + color:#666; + &:hover { + text-decoration:underline; } } } + .line_content { + white-space:pre; + height:14px; + margin:0px; + padding:0px; + border:none; + &.new { + background: #CFD; + } + &.old { + background: #FDD; + } + &.matched { + color:#ccc; + background:#fafafa; + } + } } /** COMMIT BLOCK **/ -.commit-title{ - display: block; -} -.commit-title{ - margin-bottom: 10px; -} -.commit-author, .commit-committer{ - display: block; - color: #999; - font-weight: normal; - font-style: italic; -} -.commit-author strong, .commit-committer strong{ - font-weight: bold; - font-style: normal; -} +.commit-title{display: block;} +.commit-title{margin-bottom: 10px} +.commit-author, .commit-committer{display: block;color: #999; font-weight: normal; font-style: italic;} +.commit-author strong, .commit-committer strong{font-weight: bold; font-style: normal;} -/** - * COMMIT ROW - */ +/** COMMIT ROW **/ .commit { + @extend .wll; + .browse_code_link_holder { - float: right; + @extend .span2; + float:right; } .committed_ago { - float: right; + float:right; @extend .cgray; } .notes_count { - float: right; + float:right; margin: -6px 8px 6px; } code { - background: #FCEEC1; - color: $style_color; + background:#FCEEC1; + color:$style_color; } .commit_short_id { - float: left; + float:left; @extend .lined; - min-width: 65px; - font-family: $monospace_font; + min-width:65px; + font-family: $monospace; + } + + .commit-author-name { + color: #777; + &:hover { + color: #999; + } } } +.diff_file_header a, .file-stats a { color: $style_color; } @@ -392,30 +294,11 @@ } .label_commit { - @include border-radius(4px); - padding: 2px 4px; - font-size: 13px; + @include round-borders-all(4px); + padding:2px 4px; + border:none; + font-size:13px; background: #474D57; - color: #fff; - font-family: $monospace_font; -} - - -.commits-compare-switch{ - background: url("switch_icon.png") no-repeat center center; - width: 16px; - height: 18px; - text-indent: -9999px; - float: left; - margin-right: 9px; - border: 1px solid #DDD; - @include border-radius(4px); - padding: 4px; - background-color: #EEE; -} - -.commit-description { - background: none; - border: none; - margin: 0; + color:#fff; + font-family: $monospace; } diff --git a/app/assets/stylesheets/sections/editor.scss b/app/assets/stylesheets/sections/editor.scss index a71e5438..711dc0d8 100644 --- a/app/assets/stylesheets/sections/editor.scss +++ b/app/assets/stylesheets/sections/editor.scss @@ -1,7 +1,7 @@ .file-editor { #editor{ border: none; - @include border-radius(0); + border-radius: 0; height: 500px; margin: 0; padding: 0; diff --git a/app/assets/stylesheets/sections/events.scss b/app/assets/stylesheets/sections/events.scss index 94e1d0b6..369ebc81 100644 --- a/app/assets/stylesheets/sections/events.scss +++ b/app/assets/stylesheets/sections/events.scss @@ -4,25 +4,25 @@ */ .event_label { &.pushed { - padding: 0 2px; + padding:0 2px; } &.opened { - padding: 0 2px; + padding:0 2px; } &.closed { - padding: 0 2px; + padding:0 2px; } &.merged { - padding: 0 2px; + padding:0 2px; } &.left, &.joined { - padding: 0 2px; - float: none; + padding:0 2px; + float:none; } } @@ -31,71 +31,49 @@ * */ .event-item { - border-bottom: 1px solid #eee; + min-height:40px; + border-bottom:1px solid #eee; .event-title { - color: #333; + color:#333; font-weight: bold; .author_name { - color: #333; + color:#333; } } .event-body { - .commit p { - color: #555; + p { + color:#555; padding-top: 5px; } .event-info { - color: #666; - } - .event-note { - color: #555; - margin-top: 5px; - margin-left: 40px; - - .note-file-attach { - .note-image-attach { - margin-top: 4px; - margin-left: 0px; - max-width: 200px; - } - } - } - .event-note-icon { - color: #777; - float: left; - font-size: 16px; - line-height: 16px; - margin-right: 5px; + color:#666; } } .avatar { - position: relative; - top: -3px; + width:32px; } .event_icon { - position: relative; float: right; border: 1px solid #EEE; padding: 5px; @include border-radius(5px); background: #F9F9F9; - margin-left: 10px; - top: -6px; img { - width: 20px; + width:20px; } } ul { - margin-left: 50px; - margin-bottom: 5px; + margin-left:50px; + margin-bottom:5px; .avatar { - width: 18px; - margin-top: 3px; + width:18px; + margin-top:3px; } } - padding: 16px 5px; + padding: 15px 5px; &:last-child { border:none } + .wll:hover { background:none } .event_commits { margin-top: 5px; @@ -103,9 +81,9 @@ li { &.commit { background: transparent; - padding: 3px; - border: none; - font-size: 12px; + padding:3px; + border:none; + font-size:12px; } &.commits-stat { display: block; @@ -120,21 +98,21 @@ * */ .event_lp { - color: #777; - padding: 10px; - min-height: 22px; + color:#777; + padding:10px; + min-height:22px; border-left: 5px solid #5AB9C3; - margin-bottom: 20px; - background: #f9f9f9; + margin-bottom:20px; + background:#f9f9f9; .avatar { - width: 24px; + width:24px; } .btn-new-mr { @extend .btn-info; @extend .small; - @extend .pull-right; + @extend .right; margin: -3px; } } @@ -146,25 +124,21 @@ .event_filter { position: absolute; width: 40px; - margin-left: -55px; + margin-left: -50px; .filter_icon { - a { - text-align:center; - border-left: 3px solid #29B; - background: #f9f9f9; - margin-bottom: 10px; - float: left; - padding: 9px 7px; - font-size: 18px; - width: 26px; + float: left; + border-left: 3px solid #4bc; + padding: 7px; + background: #f9f9f9; + margin-bottom: 10px; + img { + width:20px; } &.inactive { - a { - color: #DDD; - border-left: 3px solid #EEE; - } + border-left: 3px solid #EEE; + opacity: 0.5; } } } diff --git a/app/assets/stylesheets/sections/graph.scss b/app/assets/stylesheets/sections/graph.scss index 7da00719..2aa4463e 100644 --- a/app/assets/stylesheets/sections/graph.scss +++ b/app/assets/stylesheets/sections/graph.scss @@ -1,10 +1,10 @@ .graph_holder { border: 1px solid #aaa; - padding: 1px; + padding:1px; h4 { - padding: 0 10px; + padding:0 10px; border-bottom: 1px solid #bbb; @include bg-gray-gradient; } @@ -12,7 +12,7 @@ .graph { background: #f1f1f1; cursor: move; - height: 500px; + height: 70%; overflow: hidden; } } diff --git a/app/assets/stylesheets/sections/header.scss b/app/assets/stylesheets/sections/header.scss index 14e4cef0..0db40ec9 100644 --- a/app/assets/stylesheets/sections/header.scss +++ b/app/assets/stylesheets/sections/header.scss @@ -5,16 +5,15 @@ header { &.navbar-gitlab { .navbar-inner { - height: 40px; - padding: 3px; + height:45px; + padding: 5px; background: #F1F1F1; - filter: none; .nav > li > a { color: $style_color; text-shadow: 0 1px 0 #fff; - font-size: 16px; - padding: 10px; + font-size: 18px; + padding: 11px; } /** NAV block with links and profile **/ @@ -25,7 +24,8 @@ header { } } - z-index: 10; + z-index:10; + /*height:60px;*/ /** * @@ -33,30 +33,27 @@ header { * */ .app_logo { - float: left; - margin-right: 9px; - position: relative; - top: -5px; - padding-top: 5px; - + width:170px; + float:left; a { - float: left; + float:left; padding: 0px; - margin: 0 6px; h1 { - background: url('logo-black.png') no-repeat center 1px; - background-size: 38px; - float: left; - height: 40px; - width: 40px; - @include header-font; - text-indent: -9999px; + width:90px; + background: url('logo_dark.png') no-repeat 0px 2px; + float:left; + margin-left:2px; + font-size:30px; + line-height:48px; + font-weight:normal; + color:$style_color; + text-shadow: 0 1px 1px #FFF; + padding-left:45px; + height:40px; + font-family: 'Korolev', sans-serif; } } - &:hover { - background-color: #EEE; - } } /** @@ -65,20 +62,16 @@ header { * */ .project_name { - position: relative; - float: left; - margin: 0; - margin-left: 10px; - @include header-font; - } - - .profile-pic { - position: relative; - top: -4px; - img { - width: 26px; - @include border-radius(4px); - } + position:relative; + float:left; + margin:0; + margin-right:30px; + font-size:30px; + line-height:48px; + font-weight:normal; + color:$style_color; + text-shadow: 0 1px 1px #FFF; + font-family: 'Korolev', sans-serif; } /** @@ -87,26 +80,139 @@ header { * */ .search { - margin-right: 10px; - margin-left: 10px; + margin-right: 45px; + margin-left:10px; + margin-top: 2px; .search-input { @extend .span2; background-image: url("icon-search.png"); background-repeat: no-repeat; background-position: 10px; - padding-left: 25px; + padding-left:25px; font-size: 13px; @include border-radius(3px); - border: 1px solid #c6c6c6; - box-shadow: none; - @include transition(all 0.15s ease-in 0s); + border:1px solid #c6c6c6; + box-shadow:none; &:focus { @extend .span3; } } } + /** + * + * Account box + * + */ + .account-box { + position: absolute; + right: 0; + top: 6px; + z-index: 10000; + width: 128px; + font-size: 11px; + float: right; + display: block; + cursor: pointer; + img { + @include border-radius(3px); + right: 5px; + position: absolute; + width: 28px; + height: 28px; + display: block; + top:1px; + &:after { + content: " "; + display: block; + position: absolute; + top: 0; + right: 0; + left: 0; + bottom: 0; + float: right; + border-radius: 5px; + border: 1px solid rgba(255, 255, 255, 0.1); + border-bottom: 0; + background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(rgba(255, 255, 255, 0.15)), to(rgba(0, 0, 0, 0.25))), -webkit-gradient(linear, left top, right bottom, color-stop(0, rgba(255, 255, 255, 0)), color-stop(0.5, rgba(255, 255, 255, 0.1)), color-stop(0.501, rgba(255, 255, 255, 0)), color-stop(1, rgba(255, 255, 255, 0))); + background: -moz-linear-gradient(top, rgba(255, 255, 255, 0.15), rgba(0, 0, 0, 0.25)), -moz-linear-gradient(left top, rgba(255, 255, 255, 0), rgba(255, 255, 255, 0.1) 50%, rgba(255, 255, 255, 0) 50%, rgba(255, 255, 255, 0)); + background: linear-gradient(top, rgba(255, 255, 255, 0.15), rgba(0, 0, 0, 0.25)), linear-gradient(left top, rgba(255, 255, 255, 0), rgba(255, 255, 255, 0.1) 50%, rgba(255, 255, 255, 0) 50%, rgba(255, 255, 255, 0)); + -webkit-background-origin: border-box; + -moz-background-origin: border; + background-origin: border-box; } } } + + .account-box { + &.hover { + height: 138px; } + &:hover > .account-links { + display: block; } } + + .account-links { + border-radius: 5px; + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); + position: relative; + &:before { + content: "."; + width: 0; + height: 0; + position: absolute; + border: 5px solid transparent; + border-color: rgba(255, 255, 255, 0); + border-bottom-color: #555; + text-indent: -9999px; + top: -10px; + line-height: 0; + right: 10px; + z-index: 10; } + background: #555; + display: none; + z-index: 100000; + @include border-radius(4px); + width: 100px; + position: absolute; + right: 5px; + top: 38px; + margin-top: 0; + float: right; + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); + a { + color: #fff; + padding: 7px 10px; + display: block; + text-shadow: none; + border-bottom: 1px solid #666; + font-size: 12px; + &:hover { + color:#fff; + background: #333; + } + } + } + + .account-box.hover .arrow-up { + top: 41px; + right: 6px; + position: absolute; } + + .account-links a { + &:first-child { + -webkit-border-top-left-radius: 5px; + -webkit-border-top-right-radius: 5px; + -moz-border-radius-topleft: 5px; + -moz-border-radius-topright: 5px; + border-top-left-radius: 5px; + border-top-right-radius: 5px; } + &:last-child { + -webkit-border-bottom-right-radius: 5px; + -webkit-border-bottom-left-radius: 5px; + -moz-border-radius-bottomright: 5px; + -moz-border-radius-bottomleft: 5px; + border-bottom-right-radius: 5px; + border-bottom-left-radius: 5px; + border-bottom: 0; } } + + /* * Dark header @@ -129,7 +235,6 @@ header { .search-input { background-color: #D2D5DA; background-color: rgba(255, 255, 255, 0.5); - border: 1px solid #AAA; &:focus { background-color: white; @@ -142,38 +247,16 @@ header { .app_logo { a { h1 { - background: url('logo-white.png') no-repeat center 1px; - background-size: 38px; - color: #fff; + background: url('logo_white.png') no-repeat 0px 2px; + color:#fff; text-shadow: 0 1px 1px #111; } } } .project_name { - a { - color: #FFF; - } - color: #fff; + color:#fff; text-shadow: 0 1px 1px #111; } } - - .app_logo { - .separator { - margin-left: 0; - margin-right: 0; - } - } - - .separator { - float: left; - height: 46px; - width: 1px; - background: white; - border-left: 1px solid #DDD; - margin-top: -3px; - margin-left: 10px; - margin-right: 10px; - } } diff --git a/app/assets/stylesheets/sections/issues.scss b/app/assets/stylesheets/sections/issues.scss index 351f2404..93622d61 100644 --- a/app/assets/stylesheets/sections/issues.scss +++ b/app/assets/stylesheets/sections/issues.scss @@ -1,30 +1,58 @@ +.issue_form_box { + @extend .main_box; + .issue_title { + @extend .top_box_content; + .clearfix { + margin-bottom:0px; + input { + @extend .span8; + } + } + } + .issue_middle_block { + @extend .middle_box_content; + height:30px; + .issue_assignee { + @extend .span6; + float:left; + } + .issue_milestone { + @extend .span4; + float:left; + } + } + .issue_description { + @extend .bottom_box_content; + } +} + .issues_table { .issue { - padding: 10px; + padding:7px 10px; .issue_check { - float: left; + float:left; padding: 8px 0; padding-right: 8px; min-width: 15px; } p { - padding-top: 0; - padding-bottom: 2px; + padding-top:0; + padding-bottom:2px; } img.avatar { - width: 32px; - margin-top: 1px; + width:32px; + margin-top:1px; } } } input.check_all_issues { - float: left; + float:left; padding: 0; - margin: 0; + margin:0; margin-right: 10px; position: relative; top: 8px; @@ -54,38 +82,65 @@ input.check_all_issues { } } -@media (min-width: 800px) { .issues_filters select { width: 160px; } } -@media (min-width: 1200px) { .issues_filters select { width: 220px; } } +@media (min-width: 800px) { .issues_filters select { width:160px; } } +@media (min-width: 1000px) { .issues_filters select { width:200px; } } +@media (min-width: 1200px) { .issues_filters select { width:220px; } } -@media (min-width: 800px) { .issues_bulk_update select { width: 120px; } } -@media (min-width: 1200px) { .issues_bulk_update select { width: 160px; } } #issues-table-holder { .issues_filters { + form { + padding:0; + margin:0; + margin-top:7px + } } .issues_bulk_update { margin: 0; form { + padding:0; + margin:0; + margin-top:7px + } + .update_selected_issues { + position:relative; + top:-2px; + margin-left:4px; float:left; } - .update_selected_issues { - position: relative; - top:5px; - margin-left: 4px; - float: left; - } - .update_issues_text { - padding: 3px; - line-height: 28px; - float: left; - color: #479; + padding:3px; + line-height: 18px; + float:left; } } } #update_status { - width: 100px; + width:100px; +} + + +/** + * Milestones list + * + */ +.milestone { + @extend .wll; +} + +/** + * Fix milestone calendar + */ + +.ui-datepicker { + border:none; + box-shadow:none; + .ui-datepicker-header { + @include solid_shade; + margin-bottom:10px; + border:1px solid #bbb; + } } diff --git a/app/assets/stylesheets/sections/login.scss b/app/assets/stylesheets/sections/login.scss index e3fe0b43..5b8763cf 100644 --- a/app/assets/stylesheets/sections/login.scss +++ b/app/assets/stylesheets/sections/login.scss @@ -1,13 +1,13 @@ /* Login Page */ -body.login-page{ - background: #EEE; - .container .content { padding-top: 5%; } +body.login-page{ + padding-top: 10%; + background:#f1f1f1; } .login-box{ width: 304px; position: relative; - @include border-radius(5px); + border-radius: 5px; margin: auto; padding: 20px; background: white; @@ -18,22 +18,27 @@ body.login-page{ display: block; } -.login-box input.text{background-color: #f1f1f1; font-size: 16px; @include border-radius(0); padding: 14px 10px; width: 280px} +.login-box input.text{background-color: #f1f1f1; font-size: 16px; border-radius: 0; padding: 14px 10px; width: 280px} .login-box input.text.top{ - @include border-radius(5px 5px 0 0); - margin-bottom: 0px; + -webkit-border-top-left-radius: 5px; + -webkit-border-top-right-radius: 5px; + -moz-border-radius-topleft: 5px; + -moz-border-radius-topright: 5px; + border-top-left-radius: 5px; + border-top-right-radius: 5px; + margin-bottom:0px; } .login-box input.text.bottom{ - @include border-radius(0 0 5px 5px); + -webkit-border-bottom-right-radius: 5px; + -webkit-border-bottom-left-radius: 5px; + -moz-border-radius-bottomright: 5px; + -moz-border-radius-bottomleft: 5px; + border-bottom-right-radius: 5px; + border-bottom-left-radius: 5px; border-top: 0; margin-bottom: 20px; } -.login-box input.text.middle{ - border-top: 0; - margin-bottom:0px; -} - .login-box a.forgot{float: right; padding-top: 6px} diff --git a/app/assets/stylesheets/sections/merge_requests.scss b/app/assets/stylesheets/sections/merge_requests.scss index 4cca0083..9087e7c2 100644 --- a/app/assets/stylesheets/sections/merge_requests.scss +++ b/app/assets/stylesheets/sections/merge_requests.scss @@ -1,3 +1,17 @@ +/** + * MR form + * + */ + +.mr_branch_box { + @extend .ui-box; + margin-bottom:20px; + + .body { + background:#f1f1f1; + } + +} /** * MR -> show: Automerge widget @@ -9,31 +23,31 @@ } form { - margin-bottom: 0; + margin-bottom:0; .clearfix { - margin-bottom: 0; + margin-bottom:0; } } .accept_group { - float: left; + float:left; border: 1px solid #ADA; padding: 2px; @include border-radius(5px); + border-radius: 5px; background: #CEB; .accept_merge_request { - font-size: 13px; - float: left; + font-size:13px; + float:left; } .remove_branch_holder { - margin-left: 20px; - margin-right: 10px; - float: left; + margin-left:20px; + margin-right:10px; + float:left; } label { - color: #444; - text-align: left + color:#444; } } @@ -43,18 +57,18 @@ } } -.merge-request .nav-tabs{ +.mr_nav_tabs { li { a { - font-weight: bold; - padding: 8px 20px; - text-align: center; + font-weight:bold; + padding:8px 20px; + text-align:center; } } } li.merge_request { - padding: 10px; + padding:7px 10px; img.avatar { width: 32px; margin-top: 1px; @@ -65,31 +79,41 @@ li.merge_request { } } -.merge-in-progress { +.merge_in_progress { @extend .padded; @extend .append-bottom-10; } +.label_branch { + @include round-borders-all(4px); + padding:2px 4px; + border:none; + font-size:14px; + background: #474D57; + color:#fff; + font-family: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono','lucida console',monospace; +} + .mr_source_commit, .mr_target_commit { .commit { - margin: 0; - padding: 0; + margin:0; + padding:0; padding: 5px; margin-bottom: 5px; .avatar { position:relative } .row_title { - color: #444; + color:#444; } .commit-author-name, .dash, .committed_ago, .browse_code_link_holder { - display: none; + display:none; } - list-style: none; + list-style:none; &:hover { - background: none; + background:none; } } } @@ -97,3 +121,25 @@ li.merge_request { .mr_direction_tip { margin-top:40px } + +.merge_requests_form_box { + @extend .main_box; + .merge_requests_middle_box { + @extend .middle_box_content; + height:30px; + .merge_requests_assignee { + @extend .span6; + float:left; + } + .merge_requests_milestone { + @extend .span4; + float:left; + } + } +} + +.status-badge { + height: 32px; + width: 100%; + @include border-radius(5px); +} diff --git a/app/assets/stylesheets/sections/nav.scss b/app/assets/stylesheets/sections/nav.scss index 514b0b7c..57072169 100644 --- a/app/assets/stylesheets/sections/nav.scss +++ b/app/assets/stylesheets/sections/nav.scss @@ -1,64 +1,94 @@ -.main-nav { - margin: 30px 0; - margin-top: 10px; - border-bottom: 1px solid #E1E1E1; - - ul { - margin: auto; - height: 39px; +/* + * Main Menu of Application + * + */ +ul.main_menu { + border-radius: 4px; + margin: auto; + margin:30px 0; + border:1px solid #BBB; + height:37px; + @include bg-gray-gradient; + position:relative; + overflow:hidden; + @include shade; + .count { position: relative; - top: 3px; - overflow: hidden; - .count { - position: relative; - top: -1px; - display: inline-block; - height: 15px; - margin: 0 0 0 5px; - padding: 0 8px 1px 8px; - height: auto; - font-size: 0.82em; - line-height: 14px; - text-align: center; - color: #777; - } - .label { - background: $hover; - text-shadow: none; - color: $style_color; - } - li { - list-style-type: none; - margin: 0; - display: table-cell; - width: 1%; - &.active { - border-bottom: 3px solid #777; - a { - color: $style_color; - font-weight: bolder; - } - } + top: -1px; + display: inline-block; + height: 15px; + margin: 0 0 0 5px; + padding: 0 8px 1px 8px; + height: auto; + font-size: 0.82em; + line-height: 14px; + text-align: center; + color: #777; + background: #f2f2f2; + border-top: 1px solid #CCC; + border-radius: 8px; + -moz-border-radius: 8px; + } + .label { + background:$hover; + text-shadow:none; + color:$style_color; + } + li { + list-style-type: none; + margin: 0; + display: table-cell; + width: 1%; + border-right: 1px solid #DDD; + border-left: 1px solid #EEE; + border-bottom:2px solid #CFCFCF; - &.home { - a { - i { - font-size: 20px; - position: relative; - top: 4px; - } - } + &:first-child{ + -webkit-border-top-left-radius: 4px; + -webkit-border-bottom-left-radius: 4px; + -moz-border-radius-topleft: 4px; + -moz-border-radius-bottomleft: 4px; + border-top-left-radius: 4px; + border-bottom-left-radius: 4px; + border-left: 0; + } + + &.active { + background-color:#D5D5D5; + border-right: 1px solid #BBB; + border-left: 1px solid #BBB; + border-radius: 0 0 1px 1px; + &:first-child{ + border-bottom:none; + border-left:none; } } - a { - display: block; - text-align: center; - font-weight: normal; - height: 36px; - line-height: 34px; - color: #777; - text-shadow: 0 1px 1px white; - padding: 0 10px; + + &.home { + a { + background: url(home_icon.PNG) no-repeat center center; + text-indent:-9999px; + min-width:20px; + img { + position:relative; + top:4px; + } + } } } + a { + display: block; + text-align: center; + font-weight:bold; + height:35px; + line-height:36px; + color: $style_color; + text-shadow:0 1px 1px white; + padding:0 10px; + } } +/* + * End of Main Menu + * + */ + diff --git a/app/assets/stylesheets/sections/notes.scss b/app/assets/stylesheets/sections/notes.scss index a8628fc5..d24d070d 100644 --- a/app/assets/stylesheets/sections/notes.scss +++ b/app/assets/stylesheets/sections/notes.scss @@ -1,301 +1,232 @@ /** * Notes + * */ -ul.notes { - display: block; - list-style: none; - margin: 0px; - padding: 0px; +#notes-list, +#new-notes-list { + display:block; + list-style:none; + margin:0px; + padding:0px; +} - .discussion-header, - .note-header { - @extend .cgray; - padding-top: 5px; - padding-bottom: 15px; - - .avatar { - float: left; - margin-right: 10px; - } - - .discussion-last-update, - .note-last-update { - font-style: italic; - } - .author { - color: $style_color; - font-weight: bold; - &:hover { - color: $primary_color; - } - } - } - - .discussion { - padding: 8px 0; - overflow: hidden; - display: block; - position:relative; - - .discussion-body { - margin-left: 50px; - - .file, - .discussion-hidden, - .notes { - @extend .borders; - background-color: #F9F9F9; - } - .file .notes { - /* reset */ - background: inherit; - border: none; - @include box-shadow(none); - - } - .discussion-hidden .note { - @extend .cgray; - padding: 8px; - text-align: center; - } - .notes .note { - border-color: #ddd; - padding: 8px; - } - .reply-btn { - margin-top: 8px; - } - } - } - - .note { - padding: 8px 0; - overflow: hidden; - display: block; - position:relative; - p { color: $style_color; } - - .avatar { - margin-top: 3px; - } - .attachment { - font-size: 14px; - margin-top: -20px; - } - .note-body { - @include md-typography; - margin-left: 45px; - } - .note-header { - padding-bottom: 5px; - } - } - - // paint top or bottom borders depending on notes direction - &:not(.reversed) .note, - &:not(.reversed) .discussion { - border-bottom: 1px solid #eee; - } - &.reversed .note, - &.reversed .discussion { - border-top: 1px solid #eee; +.issue_notes, +.wiki_notes { + .note_content { + float:left; + width:400px; } } -.file .notes_holder { - font-family: $sansFontFamily; - font-size: 13px; - line-height: 18px; +/* Note textare */ +#note_note { + height:80px; + width:99%; + font-size:14px; +} - td { - border: 1px solid #ddd; - border-left: none; - - &.notes_line { - text-align: center; - padding: 10px 0; - } - &.notes_content { - background-color: $white; - border-width: 1px 0; - padding-top: 0; - } - } - - .reply-btn { - margin-top: 8px; +#new_note { + .attach_holder { + display:none; } } -/** - * Actions for Discussions/Notes - */ +.preview_note { + margin: 2px; + border: 1px solid #ddd; + padding: 10px; + min-height: 60px; + background:#f5f5f5; +} -.discussion, .note { - &.note:hover { - .note-actions { display: block; } - } - .discussion-header:hover { - .discussion-actions { display: block; } + padding: 8px 0; + overflow: hidden; + display: block; + position:relative; + img {float: left; margin-right: 10px;} + img.emoji {float:none;margin:0;} + .note-author cite{font-style: italic;} + p { color:$style_color; } + .note-author { color: $style_color;} + + .note-title { margin-left:45px; padding-top: 5px;} + .avatar { + margin-top:3px; } - .discussion-actions, - .note-actions { - display: none; - float: right; + .delete-note { + display:none; + position:absolute; + right:0; + top:0; + } - [class^="icon-"], - [class*="icon-"] { - font-size: 16px; - line-height: 16px; - vertical-align: middle; - } - - a { - @extend .cgray; - - &:hover { - color: $primary_color; - &.danger { @extend .cred; } - } - } + &:hover { + .delete-note { display:block; } } } -.file .note .note-actions { - right: 0; - top: 0; +#notes-list:not(.reversed) .note, +#new-notes-list:not(.reversed) .note { + border-bottom: 1px solid #eee; +} +#notes-list.reversed .note, +#new-notes-list.reversed .note { + border-top: 1px solid #eee; } - - -/** - * Line note button on the side of diffs - */ - -.file tr.line_holder { - .add-diff-note { - background: url("diff_note_add.png") no-repeat left 0; - height: 22px; - margin-left: -65px; - position: absolute; - width: 22px; - z-index: 10; - - // "hide" it by default - opacity: 0.0; - filter: alpha(opacity=0); - - &:hover { - opacity: 1.0; - filter: alpha(opacity=100); - } - } - - // "show" the icon also if we just hover somewhere over the line - &:hover > td { - background: $hover !important; - - .add-diff-note { - opacity: 1.0; - filter: alpha(opacity=100); - } - } +/* mark vote notes */ +.voting_notes .note { + padding: 8px 0; } -/** - * Note Form - */ - -.comment-btn { - @extend .btn-create; -} -.reply-btn { - @extend .btn-primary; -} -.file .content tr.line_holder:hover > td { background: $hover !important; } -.file .content tr.line_holder:hover > td .line_note_link { - opacity: 1.0; - filter: alpha(opacity=100); -} -.file, -.discussion { - .new_note { - margin: 8px 5px 8px 0; - } -} -.new_note { - display: none; - - .buttons { - float: left; - margin-top: 8px; - } - .clearfix { - margin-bottom: 0; - } - .note_text_and_preview { - // makes the "absolute" position for links relative to this - position: relative; - - // preview/edit buttons - > a { - font-size: 24px; - padding: 4px; - position: absolute; - right: 10px; - } - .note_preview { - background: #f5f5f5; - border: 1px solid #ddd; - @include border-radius(4px); - min-height: 80px; - padding: 4px 6px; - } - .note_text { - border: 1px solid #DDD; - box-shadow: none; - font-size: 14px; - height: 80px; - width: 98.6%; - } - } -} - -/* loading indicator */ -.notes-busy { +.notes-status { margin: 18px; } -.note-image-attach { - @extend .span4; - @extend .thumbnail; - margin-left: 45px; + +p.notify_controls input{ + margin: 5px; } - -.common-note-form { - margin: 0; - height: 140px; - background: #F9F9F9; - padding: 3px; - padding-bottom: 25px; - border: 1px solid #DDD; +p.notify_controls span{ + font-weight: 700; } +tr.line_notes_row { + border-bottom:1px solid #DDD; + border-left: 7px solid #2A79A3; -.note-form-actions { - background: #F9F9F9; - height: 45px; - padding: 0 5px; - - .note-form-option { - margin-top: 10px; - margin-left: 30px; - @extend .pull-left; + &.reply { + background:#eee; + border-left: 7px solid #2A79A3; + border-top:1px solid #ddd; + td { + padding:7px 10px; + } + a.line_note_reply_link { + @include round-borders-all(4px); + padding: 3px 10px; + margin-left:5px; + color: white; + background: #2A79A3; + border-color: #2A79A3; + } } - - .js-notify-commit-author { - float: left; + ul { + margin:0; + li { + padding:0; + border:none; + } } } + +.line_notes_row, .per_line_form { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; } + +.per_line_form { + background:#f5f5f5; + border-top:1px solid #eee; + form { margin: 0; } + td { + border-bottom:1px solid #ddd; + } + .note_actions { + margin:0; + padding-top: 10px; + + .buttons { + float:left; + width:300px; + } + .options { + .labels { + float:left; + padding-left:10px; + label { + padding: 6px 0; + margin: 0; + width:120px; + } + } + } + } +} + +td .line_note_link { + position:absolute; + margin-left:-70px; + margin-top:-10px; + z-index:10; + background: url("comment_add.png") no-repeat left 0; + width:32px; + height:32px; + + opacity: 0.0; + filter: alpha(opacity=0); + + &:hover { + opacity: 1.0; + filter: alpha(opacity=100); + } +} + +.diff_file_content tr.line_holder:hover > td { background: $hover !important; } +.diff_file_content tr.line_holder:hover > td .line_note_link { + opacity: 1.0; + filter: alpha(opacity=100); +} + +.new_note { + .input-file { + font: 500px monospace; + opacity:0; + filter: alpha(opacity=0); + position: absolute; + z-index: 1; + top:0; + right:0; + padding:0; + margin: 0; + } + + .note_advanced_opts { + h6 { + line-height: 32px; + padding-right: 15px; + } + } + + .attachments { + position:relative; + width: 350px; + height: 50px; + overflow:hidden; + margin:0 0 5px !important; + + .input_file { + .file_upload { + position: absolute; + right:14px; + top:7px; + } + + .file_name { + line-height:30px; + width:240px; + height:28px; + overflow:hidden; + } + .input-file { + width: 260px; + height: 41px; + float: right; + } + } + } +} + +.note-text { + border: 1px solid #aaa; + box-shadow:none; +} diff --git a/app/assets/stylesheets/sections/profile.scss b/app/assets/stylesheets/sections/profile.scss index 607daf7a..e945ca00 100644 --- a/app/assets/stylesheets/sections/profile.scss +++ b/app/assets/stylesheets/sections/profile.scss @@ -1,21 +1,21 @@ .profile_history { .event_feed { - min-height: 20px; + min-height:20px; .avatar { - width: 20px; + width:20px; } } } .profile_avatar_holder { - float: left; - width: 60px; - height: 60px; - margin-right: 20px; + float:left; + width:60px; + height:60px; + margin-right:20px; img { - width: 60px; - height: 60px; - background: #fff; + width:60px; + height:60px; + background:#fff; padding: 1px; border: 1px solid #ddd; } diff --git a/app/assets/stylesheets/sections/projects.scss b/app/assets/stylesheets/sections/projects.scss index b6b1423e..c9d386ab 100644 --- a/app/assets/stylesheets/sections/projects.scss +++ b/app/assets/stylesheets/sections/projects.scss @@ -4,30 +4,50 @@ } .side { - @extend .pull-right; + @extend .span4; + @extend .right; - .ui-box { - margin: 3px; - > .title { - padding: 2px 15px; + .groups_box, + .projects_box { + h5 { + color:$style_color; + font-size:16px; + text-shadow: 0 1px 1px #fff; + padding: 2px 10px; + line-height:32px; + font-size:14px; } - .nav-projects-tabs li { padding: 0; } - .well-list { - li { padding: 15px; } - .arrow { - float: right; - padding: 10px; - margin: 0; - } - .last_activity { - padding-top: 5px; - display: block; - span, strong { - font-size: 12px; - color: #666; + ul { + li { + padding:0; + a { + display:block; + .group_name { + font-size:14px; + line-height:18px; + } + .project_name { + color:#4fa2bd; + font-size:14px; + line-height:18px; + } + .arrow { + float:right; + padding:10px; + margin:0; + } + .last_activity { + padding-top:5px; + display:block; + span, strong { + font-size:12px; + color:#666; + } + } } } } + @extend .leftbar; @extend .ui-box; } } @@ -38,24 +58,21 @@ .project_name_holder { input, label { - font-size: 16px; - line-height: 20px; - padding: 8px; + font-size:16px; + line-height:20px; + padding:8px; + } + label { + color:#888; } .btn { - padding: 6px 10px; - margin-left: 10px; - margin-bottom: 8px; + padding:6px 10px; + margin-left:10px; + margin-bottom:8px; } } .adv_settings { - h6 { margin-left: 40px; } - } - - fieldset.features { - .control-label { - font-weight: bold; - } + h6 { margin-left:40px; } } } @@ -64,73 +81,32 @@ @include bg-gray-gradient; padding: 4px 7px; border: 1px solid #CCC; - margin-bottom: 20px; + margin-bottom:20px; } .project_clone_holder { input[type="text"], .btn { - font-size: 12px; + font-size:12px; line-height: 18px; margin: 0; padding: 3px 10px; } input[type="text"] { - @extend .monospace; border: 1px solid #BBB; box-shadow: none; margin-left: -1px; - background: #FFF; } } .save-project-loader { img { - margin-top: 50px; - margin-bottom: 50px; + margin-top:50px; + margin-bottom:50px; } h3 { @extend .page_title; } } - -ul.nav.nav-projects-tabs { - @extend .nav-tabs; - - padding-left: 8px; - - li { - a { - padding: 6px 25px; - margin-top: 2px; - border-color: #DDD; - background-color: #EEE; - text-shadow: 0 1px 1px white; - color: #555; - } - &.active { - a { - font-weight: bold; - } - } - } -} - -.team_member_row form { - margin: 0px; -} - -.public-projects { - li { - margin-top: 8px; - margin-bottom: 5px; - border-bottom: 1px solid #eee; - - .description { - margin-left: 22px; - color: #aaa; - } - } -} diff --git a/app/assets/stylesheets/sections/snippets.scss b/app/assets/stylesheets/sections/snippets.scss deleted file mode 100644 index 3944814f..00000000 --- a/app/assets/stylesheets/sections/snippets.scss +++ /dev/null @@ -1,9 +0,0 @@ -.snippet.file_holder { - .file_title { - .snippet-file-name { - position: relative; - top: -4px; - left: -4px; - } - } -} diff --git a/app/assets/stylesheets/sections/themes.scss b/app/assets/stylesheets/sections/themes.scss index 4e5eaf57..2d121519 100644 --- a/app/assets/stylesheets/sections/themes.scss +++ b/app/assets/stylesheets/sections/themes.scss @@ -6,17 +6,17 @@ } .themes_opts { - padding-left: 20px; + padding-left:20px; label { - width: 175px; - margin-right: 40px; + width:175px; + margin-right:40px; .prev { @extend .thumbnail; - height: 30px; - width: 175px; - margin-bottom: 10px; + height:30px; + width:175px; + margin-bottom:10px; &.classic { background: #31363e; @@ -42,17 +42,17 @@ } .code_highlight_opts { - padding-left: 20px; + padding-left:20px; label { - width: 220px; - margin-right: 40px; + width:220px; + margin-right:40px; .prev { @extend .thumbnail; - height: 151px; - width: 220px; - margin-bottom: 10px; + height:151px; + width:220px; + margin-bottom:10px; } } } diff --git a/app/assets/stylesheets/sections/tree.scss b/app/assets/stylesheets/sections/tree.scss index def440c7..f6bdb0f3 100644 --- a/app/assets/stylesheets/sections/tree.scss +++ b/app/assets/stylesheets/sections/tree.scss @@ -1,14 +1,14 @@ .tree-holder { .tree-content-holder { - float: left; - width: 100%; + float:left; + width:100%; } .tree_progress { - display: none; - margin: 20px; + display:none; + margin:20px; &.loading { - display: block; + display:block; } } @@ -18,20 +18,20 @@ &:hover { td { background: $hover; - border-top: 1px solid #ADF; - border-bottom: 1px solid #ADF; + border-top:1px solid #ADF; + border-bottom:1px solid #ADF; } - cursor: pointer; + cursor:pointer; } } } .tree-item { .tree-item-file-name { - vertical-align: middle; + vertical-align:middle; a { &:hover { - color: $primary_color; + color:$blue_link; } } @@ -48,8 +48,8 @@ padding: 2px 10px; } td { - line-height: 20px; - background: #fafafa; + line-height:20px; + background:#fafafa; } } @@ -80,29 +80,13 @@ margin: 0; padding: 0; } - td.blame-commit { - background: #f9f9f9; - min-width: 350px; - } - td.blame-numbers { - pre { - color: #AAA; - white-space: pre; - } - background: #f1f1f1; - border-left: 1px solid #DDD; - } } } .tree-btn-group { .btn { margin-right:-3px; - padding: 2px 10px; + padding:2px 10px; } } -.tree-ref-holder { - float: left; - margin-top: 5px; -} diff --git a/app/assets/stylesheets/sections/votes.scss b/app/assets/stylesheets/sections/votes.scss deleted file mode 100644 index 4686f542..00000000 --- a/app/assets/stylesheets/sections/votes.scss +++ /dev/null @@ -1,43 +0,0 @@ -.votes { - font-size: 13px; - line-height: 15px; - .progress { - height: 4px; - margin: 0; - .bar { - float: left; - height: 100%; - } - .bar-success { - @include linear-gradient(#62C462, #51A351); - background-color: #468847; - } - .bar-danger { - @include linear-gradient(#EE5F5B, #BD362F); - background-color: #B94A48; - } - } - .upvotes { - display: inline-block; - color: #468847; - } - .downvotes { - display: inline-block; - color: #B94A48; - } -} -.votes-block { - margin: 14px 6px 6px 0; - .downvotes { - float: right; - } -} -.votes-inline { - display: inline-block; - margin: 0 8px; - .progress { - display: inline-block; - padding: 0 0 2px; - width: 45px; - } -} diff --git a/app/assets/stylesheets/sections/wall.scss b/app/assets/stylesheets/sections/wall.scss deleted file mode 100644 index d6ac08fc..00000000 --- a/app/assets/stylesheets/sections/wall.scss +++ /dev/null @@ -1,55 +0,0 @@ -.wall-page { - .wall-note-form { - @extend .span12; - - margin: 0; - height: 140px; - background: #F9F9F9; - position: fixed; - bottom: 0px; - padding: 3px; - padding-bottom: 25px; - border: 1px solid #DDD; - } - - .notes { - margin-bottom: 160px; - background: #FFE; - border: 1px solid #EED; - - > li { - @extend .clearfix; - border-bottom: 1px solid #EED; - padding: 10px; - } - - .wall-author { - color: #666; - float: left; - font-size: 12px; - width: 120px; - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; - } - - .wall-text { - border-left: 1px solid #CCC; - margin-left: 10px; - padding-left: 10px; - float: left; - width: 75%; - } - - .wall-file { - margin-left: 8px; - background: #EEE; - } - - abbr { - float: right; - color: #AAA; - border: none; - } - } -} diff --git a/app/assets/stylesheets/sections/wiki.scss b/app/assets/stylesheets/sections/wiki.scss deleted file mode 100644 index 175911d7..00000000 --- a/app/assets/stylesheets/sections/wiki.scss +++ /dev/null @@ -1,6 +0,0 @@ -h3.page_title .edit-wiki-header { - width: 780px; - margin-left: auto; - margin-right: auto; - padding-right: 7px; -} diff --git a/app/assets/stylesheets/themes/ui_basic.scss b/app/assets/stylesheets/themes/ui_basic.scss index 4e34e8b1..1f3d3d3d 100644 --- a/app/assets/stylesheets/themes/ui_basic.scss +++ b/app/assets/stylesheets/themes/ui_basic.scss @@ -4,8 +4,33 @@ * */ .ui_basic { + /* + * Common styles + * + */ + a { + color: $link_color; + &:hover { + text-decoration:none; + color: $blue_link; + } + } + + .app_logo { + .separator { + margin-left: 0; + margin-right: 0; + } + } + .separator { + float: left; + height: 60px; + width: 1px; background: white; border-left: 1px solid #DDD; + margin-top: -10px; + margin-left: 10px; + margin-right: 10px; } } diff --git a/app/assets/stylesheets/themes/ui_color.scss b/app/assets/stylesheets/themes/ui_color.scss index d7a554ff..8c60fabb 100644 --- a/app/assets/stylesheets/themes/ui_color.scss +++ b/app/assets/stylesheets/themes/ui_color.scss @@ -17,15 +17,6 @@ &.navbar-gitlab { .navbar-inner { background: #657; - .app_logo { - &:hover { - background-color: #6A5A7A; - } - } - .separator { - background: #546; - border-left: 1px solid #706080; - } } } } diff --git a/app/assets/stylesheets/themes/ui_gray.scss b/app/assets/stylesheets/themes/ui_gray.scss index f0547c72..e80137a6 100644 --- a/app/assets/stylesheets/themes/ui_gray.scss +++ b/app/assets/stylesheets/themes/ui_gray.scss @@ -17,15 +17,6 @@ &.navbar-gitlab { .navbar-inner { background: #708090; - .app_logo { - &:hover { - background-color: #6A7A8A; - } - } - .separator { - background: #607080; - border-left: 1px solid #8090A0; - } } } } diff --git a/app/assets/stylesheets/themes/ui_mars.scss b/app/assets/stylesheets/themes/ui_mars.scss index a2b8c21e..a9d21241 100644 --- a/app/assets/stylesheets/themes/ui_mars.scss +++ b/app/assets/stylesheets/themes/ui_mars.scss @@ -8,27 +8,61 @@ * */ .ui_mars { + /* * Application Header * */ header { - @extend .header-dark; + &.navbar-gitlab { .navbar-inner { - background: #474D57; - border-bottom: 1px solid #373D47; - .app_logo { - &:hover { - background-color: #373D47; - } + background: #474D57 url('bg-header.png') repeat-x bottom; + border-bottom: 1px solid #444; + + .nav > li > a { + color: #eee; + text-shadow: 0 1px 0 #444; } } } - .separator { - background: #31363E; - border-left: 1px solid #666; + .search { + float: right; + margin-right: 45px; + .search-input { + border: 1px solid rgba(0, 0, 0, 0.7); + background-color: #D2D5DA; + background-color: rgba(255, 255, 255, 0.5); + + &:focus { + background-color: white; + } + } + } + .search-input::-webkit-input-placeholder { + color: #666; + } + .app_logo { + a { + h1 { + background: url('logo_white.png') no-repeat 0px 2px; + color:#eee; + text-shadow: 0 1px 1px #111; + } + } + .separator { + display:none; + } + + } + .project_name { + color:#eee; + text-shadow: 0 1px 1px #111; } } + /* + * End of Application Header + * + */ } diff --git a/app/assets/stylesheets/themes/ui_modern.scss b/app/assets/stylesheets/themes/ui_modern.scss index a5bf414c..32b5ad7d 100644 --- a/app/assets/stylesheets/themes/ui_modern.scss +++ b/app/assets/stylesheets/themes/ui_modern.scss @@ -17,15 +17,6 @@ &.navbar-gitlab { .navbar-inner { background: #567; - .app_logo { - &:hover { - background-color: #516171; - } - } - .separator { - background: #456; - border-left: 1px solid #678; - } } } } diff --git a/app/contexts/commit_load_context.rb b/app/contexts/commit_load_context.rb index 2cf5420d..e43e5a07 100644 --- a/app/contexts/commit_load_context.rb +++ b/app/contexts/commit_load_context.rb @@ -9,15 +9,16 @@ class CommitLoadContext < BaseContext status: :ok } - commit = project.repository.commit(params[:id]) + commit = project.commit(params[:id]) if commit - line_notes = project.notes.for_commit_id(commit.id).inline + commit = CommitDecorator.decorate(commit) + line_notes = project.commit_line_notes(commit) result[:commit] = commit result[:note] = project.build_commit_note(commit) result[:line_notes] = line_notes - result[:notes_count] = project.notes.for_commit_id(commit.id).count + result[:notes_count] = line_notes.count + project.commit_notes(commit).count begin result[:suppress_diff] = true if commit.diffs.size > Commit::DIFF_SAFE_SIZE && !params[:force_show_diff] diff --git a/app/contexts/filter_context.rb b/app/contexts/filter_context.rb deleted file mode 100644 index 401d19b3..00000000 --- a/app/contexts/filter_context.rb +++ /dev/null @@ -1,31 +0,0 @@ -class FilterContext - attr_accessor :items, :params - - def initialize(items, params) - @items = items - @params = params - end - - def execute - apply_filter(items) - end - - def apply_filter items - if params[:project_id] - items = items.where(project_id: params[:project_id]) - end - - if params[:search].present? - items = items.search(params[:search]) - end - - case params[:status] - when 'closed' - items.closed - when 'all' - items - else - items.opened - end - end -end diff --git a/app/contexts/issues_list_context.rb b/app/contexts/issues_list_context.rb index 0765b30c..9bbdfe1d 100644 --- a/app/contexts/issues_list_context.rb +++ b/app/contexts/issues_list_context.rb @@ -4,16 +4,15 @@ class IssuesListContext < BaseContext attr_accessor :issues def execute - @issues = case params[:status] + @issues = case params[:f] when issues_filter[:all] then @project.issues when issues_filter[:closed] then @project.issues.closed - when issues_filter[:to_me] then @project.issues.assigned(current_user) - when issues_filter[:by_me] then @project.issues.authored(current_user) + when issues_filter[:to_me] then @project.issues.opened.assigned(current_user) else @project.issues.opened end @issues = @issues.tagged_with(params[:label_name]) if params[:label_name].present? - @issues = @issues.includes(:author, :project) + @issues = @issues.includes(:author, :project).order("updated_at") # Filter by specific assignee_id (or lack thereof)? if params[:assignee_id].present? diff --git a/app/contexts/merge_requests_load_context.rb b/app/contexts/merge_requests_load_context.rb index fde04085..4ec66cd9 100644 --- a/app/contexts/merge_requests_load_context.rb +++ b/app/contexts/merge_requests_load_context.rb @@ -14,7 +14,7 @@ class MergeRequestsLoadContext < BaseContext end merge_requests = merge_requests.page(params[:page]).per(20) - merge_requests = merge_requests.includes(:author, :project).order("created_at desc") + merge_requests = merge_requests.includes(:author, :project).order("closed, created_at desc") # Filter by specific assignee_id (or lack thereof)? if params[:assignee_id].present? diff --git a/app/contexts/notes/create_context.rb b/app/contexts/notes/create_context.rb index 36ea76ff..d93adb83 100644 --- a/app/contexts/notes/create_context.rb +++ b/app/contexts/notes/create_context.rb @@ -3,6 +3,8 @@ module Notes def execute note = project.notes.new(params[:note]) note.author = current_user + note.notify = true if params[:notify] == '1' + note.notify_author = true if params[:notify_author] == '1' note.save note end diff --git a/app/contexts/notes/load_context.rb b/app/contexts/notes/load_context.rb index 234e9ac3..f3949149 100644 --- a/app/contexts/notes/load_context.rb +++ b/app/contexts/notes/load_context.rb @@ -3,17 +3,32 @@ module Notes def execute target_type = params[:target_type] target_id = params[:target_id] + after_id = params[:after_id] + before_id = params[:before_id] @notes = case target_type when "commit" - project.notes.for_commit_id(target_id).not_inline.fresh + project.commit_notes(project.commit(target_id)).fresh.limit(20) when "issue" - project.issues.find(target_id).notes.inc_author.fresh + project.issues.find(target_id).notes.inc_author.fresh.limit(20) when "merge_request" - project.merge_requests.find(target_id).mr_and_commit_notes.inc_author.fresh + project.merge_requests.find(target_id).mr_and_commit_notes.inc_author.fresh.limit(20) when "snippet" project.snippets.find(target_id).notes.fresh + when "wall" + # this is the only case, where the order is DESC + project.common_notes.order("created_at DESC, id DESC").limit(50) + when "wiki" + project.wiki_notes.limit(20) + end + + @notes = if after_id + @notes.where("id > ?", after_id) + elsif before_id + @notes.where("id < ?", before_id) + else + @notes end end end diff --git a/app/contexts/projects/create_context.rb b/app/contexts/projects/create_context.rb deleted file mode 100644 index 56c4e1c5..00000000 --- a/app/contexts/projects/create_context.rb +++ /dev/null @@ -1,78 +0,0 @@ -module Projects - class CreateContext < BaseContext - def initialize(user, params) - @current_user, @params = user, params.dup - end - - def execute - # get namespace id - namespace_id = params.delete(:namespace_id) - - @project = Project.new(params) - - # Parametrize path for project - # - # Ex. - # 'GitLab HQ'.parameterize => "gitlab-hq" - # - @project.path = @project.name.dup.parameterize - - - if namespace_id - # Find matching namespace and check if it allowed - # for current user if namespace_id passed. - if allowed_namespace?(current_user, namespace_id) - @project.namespace_id = namespace_id unless namespace_id == Namespace.global_id - else - deny_namespace - return @project - end - else - # Set current user namespace if namespace_id is nil - @project.namespace_id = current_user.namespace_id - end - - # Disable less important features by default - @project.wall_enabled = false - @project.snippets_enabled = false - - @project.creator = current_user - - # Import project from cloneable resource - if @project.valid? && @project.import_url.present? - shell = Gitlab::Shell.new - if shell.import_repository(@project.path_with_namespace, @project.import_url) - # We should create satellite for imported repo - @project.satellite.create unless @project.satellite.exists? - true - else - @project.errors.add(:import_url, 'cannot clone repo') - end - end - - if @project.save - @project.users_projects.create(project_access: UsersProject::MASTER, user: current_user) - end - - @project - rescue => ex - @project.errors.add(:base, "Can't save project. Please try again later") - @project - end - - protected - - def deny_namespace - @project.errors.add(:namespace, "is not valid") - end - - def allowed_namespace?(user, namespace_id) - if namespace_id == Namespace.global_id - return user.admin - else - namespace = Namespace.find_by_id(namespace_id) - current_user.can?(:manage_namespace, namespace) - end - end - end -end diff --git a/app/contexts/projects/transfer_context.rb b/app/contexts/projects/transfer_context.rb deleted file mode 100644 index aed396a5..00000000 --- a/app/contexts/projects/transfer_context.rb +++ /dev/null @@ -1,27 +0,0 @@ -module Projects - class TransferContext < BaseContext - def execute(role = :default) - namespace_id = params[:project].delete(:namespace_id) - allowed_transfer = can?(current_user, :change_namespace, project) || role == :admin - - if allowed_transfer && namespace_id.present? - if namespace_id == Namespace.global_id - if project.namespace.present? - # Transfer to global namespace from anyone - project.transfer(nil) - end - elsif namespace_id.to_i != project.namespace_id - # Transfer to someone namespace - namespace = Namespace.find(namespace_id) - project.transfer(namespace) - end - end - - rescue ProjectTransferService::TransferError => ex - project.reload - project.errors.add(:namespace_id, ex.message) - false - end - end -end - diff --git a/app/contexts/projects/update_context.rb b/app/contexts/projects/update_context.rb deleted file mode 100644 index 40385fa6..00000000 --- a/app/contexts/projects/update_context.rb +++ /dev/null @@ -1,9 +0,0 @@ -module Projects - class UpdateContext < BaseContext - def execute(role = :default) - params[:project].delete(:namespace_id) - params[:project].delete(:public) unless can?(current_user, :change_public_mode, project) - project.update_attributes(params[:project], as: role) - end - end -end diff --git a/app/contexts/test_hook_context.rb b/app/contexts/test_hook_context.rb index 63eda6c7..cba5d1f8 100644 --- a/app/contexts/test_hook_context.rb +++ b/app/contexts/test_hook_context.rb @@ -1,7 +1,8 @@ class TestHookContext < BaseContext def execute hook = project.hooks.find(params[:id]) - data = GitPushService.new.sample_data(project, current_user) + commits = project.commits(project.default_branch, nil, 3) + data = project.post_receive_data(commits.last.id, commits.first.id, "refs/heads/#{project.default_branch}", current_user) hook.execute(data) end end diff --git a/app/controllers/admin/dashboard_controller.rb b/app/controllers/admin/dashboard_controller.rb index 3c27b861..827dd0cf 100644 --- a/app/controllers/admin/dashboard_controller.rb +++ b/app/controllers/admin/dashboard_controller.rb @@ -1,8 +1,12 @@ -class Admin::DashboardController < Admin::ApplicationController +class Admin::DashboardController < AdminController def index @projects = Project.order("created_at DESC").limit(10) @users = User.order("created_at DESC").limit(10) + @resque_accessible = true + @workers = Resque.workers + @pending_jobs = Resque.size(:post_receive) + rescue Redis::InheritedError @resque_accessible = false end diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb index f552fb59..0bba0199 100644 --- a/app/controllers/admin/groups_controller.rb +++ b/app/controllers/admin/groups_controller.rb @@ -1,8 +1,8 @@ -class Admin::GroupsController < Admin::ApplicationController - before_filter :group, only: [:edit, :show, :update, :destroy, :project_update, :project_teams_update] +class Admin::GroupsController < AdminController + before_filter :group, only: [:edit, :show, :update, :destroy, :project_update] def index - @groups = Group.order('name ASC') + @groups = Group.scoped @groups = @groups.search(params[:name]) if params[:name].present? @groups = @groups.page(params[:page]).per(20) end @@ -11,9 +11,6 @@ class Admin::GroupsController < Admin::ApplicationController @projects = Project.scoped @projects = @projects.not_in_group(@group) if @group.projects.present? @projects = @projects.all - @projects.reject!(&:empty_repo?) - - @users = User.active end def new @@ -25,7 +22,6 @@ class Admin::GroupsController < Admin::ApplicationController def create @group = Group.new(params[:group]) - @group.path = @group.name.dup.parameterize if @group.name @group.owner = current_user if @group.save @@ -52,29 +48,20 @@ class Admin::GroupsController < Admin::ApplicationController def project_update project_ids = params[:project_ids] - - Project.where(id: project_ids).each do |project| - project.transfer(@group) - end + Project.where(id: project_ids).update_all(group_id: @group.id) redirect_to :back, notice: 'Group was successfully updated.' end def remove_project @project = Project.find(params[:project_id]) - @project.transfer(nil) + @project.group_id = nil + @project.save redirect_to :back, notice: 'Group was successfully updated.' end - def project_teams_update - @group.add_users_to_project_teams(params[:user_ids], params[:project_access]) - redirect_to [:admin, @group], notice: 'Users was successfully added.' - end - def destroy - @group.truncate_teams - @group.destroy redirect_to admin_groups_path, notice: 'Group was successfully deleted.' @@ -83,6 +70,6 @@ class Admin::GroupsController < Admin::ApplicationController private def group - @group = Group.find_by_path(params[:id]) + @group = Group.find_by_code(params[:id]) end end diff --git a/app/controllers/admin/hooks_controller.rb b/app/controllers/admin/hooks_controller.rb index c5bf76f8..91a1d633 100644 --- a/app/controllers/admin/hooks_controller.rb +++ b/app/controllers/admin/hooks_controller.rb @@ -1,4 +1,4 @@ -class Admin::HooksController < Admin::ApplicationController +class Admin::HooksController < AdminController def index @hooks = SystemHook.all @hook = SystemHook.new diff --git a/app/controllers/admin/logs_controller.rb b/app/controllers/admin/logs_controller.rb index b999018d..28c321a9 100644 --- a/app/controllers/admin/logs_controller.rb +++ b/app/controllers/admin/logs_controller.rb @@ -1,2 +1,2 @@ -class Admin::LogsController < Admin::ApplicationController +class Admin::LogsController < AdminController end diff --git a/app/controllers/admin/projects/application_controller.rb b/app/controllers/admin/projects/application_controller.rb deleted file mode 100644 index b3f1539f..00000000 --- a/app/controllers/admin/projects/application_controller.rb +++ /dev/null @@ -1,11 +0,0 @@ -# Provides a base class for Admin controllers to subclass -# -# Automatically sets the layout and ensures an administrator is logged in -class Admin::Projects::ApplicationController < Admin::ApplicationController - - protected - - def project - @project ||= Project.find_with_namespace(params[:project_id]) - end -end diff --git a/app/controllers/admin/projects/members_controller.rb b/app/controllers/admin/projects/members_controller.rb deleted file mode 100644 index d9c0d572..00000000 --- a/app/controllers/admin/projects/members_controller.rb +++ /dev/null @@ -1,32 +0,0 @@ -class Admin::Projects::MembersController < Admin::Projects::ApplicationController - def edit - @member = team_member - @project = project - @team_member_relation = team_member_relation - end - - def update - if team_member_relation.update_attributes(params[:team_member]) - redirect_to [:admin, project], notice: 'Project Access was successfully updated.' - else - render action: "edit" - end - end - - def destroy - team_member_relation.destroy - - redirect_to :back - end - - private - - def team_member - @member ||= project.users.find_by_username(params[:id]) - end - - def team_member_relation - team_member.users_projects.find_by_project_id(project) - end - -end diff --git a/app/controllers/admin/projects_controller.rb b/app/controllers/admin/projects_controller.rb index bbb80cbb..d27b657d 100644 --- a/app/controllers/admin/projects_controller.rb +++ b/app/controllers/admin/projects_controller.rb @@ -1,30 +1,65 @@ -class Admin::ProjectsController < Admin::ApplicationController - before_filter :project, only: [:edit, :show, :update, :destroy, :team_update] +class Admin::ProjectsController < AdminController + before_filter :admin_project, only: [:edit, :show, :update, :destroy, :team_update] def index - @projects = Project.scoped - @projects = @projects.where(namespace_id: params[:namespace_id]) if params[:namespace_id].present? - @projects = @projects.where(public: true) if params[:public_only].present? - @projects = @projects.with_push if params[:with_push].present? - @projects = @projects.abandoned if params[:abandoned].present? - @projects = @projects.where(namespace_id: nil) if params[:namespace_id] == Namespace.global_id - @projects = @projects.search(params[:name]) if params[:name].present? - @projects = @projects.includes(:namespace).order("namespaces.path, projects.name ASC").page(params[:page]).per(20) + @admin_projects = Project.scoped + @admin_projects = @admin_projects.search(params[:name]) if params[:name].present? + @admin_projects = @admin_projects.order("name ASC").page(params[:page]).per(20) end def show - @repository = @project.repository - @users = User.active - @users = @users.not_in_project(@project) if @project.users.present? + @users = User.scoped + @users = @users.not_in_project(@admin_project) if @admin_project.users.present? @users = @users.all end - protected + def new + @admin_project = Project.new + end - def project - id = params[:project_id] || params[:id] + def edit + end - @project = Project.find_with_namespace(id) - @project || render_404 + def team_update + @admin_project.add_users_ids_to_team(params[:user_ids], params[:project_access]) + + redirect_to [:admin, @admin_project], notice: 'Project was successfully updated.' + end + + def create + @admin_project = Project.new(params[:project]) + @admin_project.owner = current_user + + if @admin_project.save + redirect_to [:admin, @admin_project], notice: 'Project was successfully created.' + else + render action: "new" + end + end + + def update + owner_id = params[:project].delete(:owner_id) + + if owner_id + @admin_project.owner = User.find(owner_id) + end + + if @admin_project.update_attributes(params[:project]) + redirect_to [:admin, @admin_project], notice: 'Project was successfully updated.' + else + render action: "edit" + end + end + + def destroy + @admin_project.destroy + + redirect_to admin_projects_url, notice: 'Project was successfully deleted.' + end + + private + + def admin_project + @admin_project = Project.find_by_code(params[:id]) end end diff --git a/app/controllers/admin/resque_controller.rb b/app/controllers/admin/resque_controller.rb index 7d489ab4..9d8e7e30 100644 --- a/app/controllers/admin/resque_controller.rb +++ b/app/controllers/admin/resque_controller.rb @@ -1,4 +1,4 @@ -class Admin::ResqueController < Admin::ApplicationController +class Admin::ResqueController < AdminController def show end end diff --git a/app/controllers/admin/team_members_controller.rb b/app/controllers/admin/team_members_controller.rb new file mode 100644 index 00000000..07320805 --- /dev/null +++ b/app/controllers/admin/team_members_controller.rb @@ -0,0 +1,22 @@ +class Admin::TeamMembersController < AdminController + def edit + @admin_team_member = UsersProject.find(params[:id]) + end + + def update + @admin_team_member = UsersProject.find(params[:id]) + + if @admin_team_member.update_attributes(params[:team_member]) + redirect_to [:admin, @admin_team_member.project], notice: 'Project Access was successfully updated.' + else + render action: "edit" + end + end + + def destroy + @admin_team_member = UsersProject.find(params[:id]) + @admin_team_member.destroy + + redirect_to :back + end +end diff --git a/app/controllers/admin/teams/application_controller.rb b/app/controllers/admin/teams/application_controller.rb deleted file mode 100644 index 87108214..00000000 --- a/app/controllers/admin/teams/application_controller.rb +++ /dev/null @@ -1,11 +0,0 @@ -# Provides a base class for Admin controllers to subclass -# -# Automatically sets the layout and ensures an administrator is logged in -class Admin::Teams::ApplicationController < Admin::ApplicationController - - private - - def user_team - @team = UserTeam.find_by_path(params[:team_id]) - end -end diff --git a/app/controllers/admin/teams/members_controller.rb b/app/controllers/admin/teams/members_controller.rb deleted file mode 100644 index e6469874..00000000 --- a/app/controllers/admin/teams/members_controller.rb +++ /dev/null @@ -1,41 +0,0 @@ -class Admin::Teams::MembersController < Admin::Teams::ApplicationController - def new - @users = User.potential_team_members(user_team) - @users = UserDecorator.decorate_collection @users - end - - def create - unless params[:user_ids].blank? - user_ids = params[:user_ids] - access = params[:default_project_access] - is_admin = params[:group_admin] - user_team.add_members(user_ids, access, is_admin) - end - - redirect_to admin_team_path(user_team), notice: 'Members was successfully added into Team of users.' - end - - def edit - team_member - end - - def update - options = {default_projects_access: params[:default_project_access], group_admin: params[:group_admin]} - if user_team.update_membership(team_member, options) - redirect_to admin_team_path(user_team), notice: "Membership for #{team_member.name} was successfully updated in Team of users." - else - render :edit - end - end - - def destroy - user_team.remove_member(team_member) - redirect_to admin_team_path(user_team), notice: "Member #{team_member.name} was successfully removed from Team of users." - end - - protected - - def team_member - @member ||= user_team.members.find_by_username(params[:id]) - end -end diff --git a/app/controllers/admin/teams/projects_controller.rb b/app/controllers/admin/teams/projects_controller.rb deleted file mode 100644 index 8584a188..00000000 --- a/app/controllers/admin/teams/projects_controller.rb +++ /dev/null @@ -1,41 +0,0 @@ -class Admin::Teams::ProjectsController < Admin::Teams::ApplicationController - def new - @projects = Project.scoped - @projects = @projects.without_team(user_team) if user_team.projects.any? - #@projects.reject!(&:empty_repo?) - end - - def create - unless params[:project_ids].blank? - project_ids = params[:project_ids] - access = params[:greatest_project_access] - user_team.assign_to_projects(project_ids, access) - end - - redirect_to admin_team_path(user_team), notice: 'Team of users was successfully assgned to projects.' - end - - def edit - team_project - end - - def update - if user_team.update_project_access(team_project, params[:greatest_project_access]) - redirect_to admin_team_path(user_team), notice: 'Access was successfully updated.' - else - render :edit - end - end - - def destroy - user_team.resign_from_project(team_project) - redirect_to admin_team_path(user_team), notice: 'Team of users was successfully reassigned from project.' - end - - protected - - def team_project - @project ||= user_team.projects.find_with_namespace(params[:id]) - end - -end diff --git a/app/controllers/admin/teams_controller.rb b/app/controllers/admin/teams_controller.rb deleted file mode 100644 index 786957cb..00000000 --- a/app/controllers/admin/teams_controller.rb +++ /dev/null @@ -1,59 +0,0 @@ -class Admin::TeamsController < Admin::ApplicationController - def index - @teams = UserTeam.order('name ASC') - @teams = @teams.search(params[:name]) if params[:name].present? - @teams = @teams.page(params[:page]).per(20) - end - - def show - user_team - end - - def new - @team = UserTeam.new - end - - def edit - user_team - end - - def create - @team = UserTeam.new(params[:user_team]) - @team.path = @team.name.dup.parameterize if @team.name - @team.owner = current_user - - if @team.save - redirect_to admin_team_path(@team), notice: 'Team of users was successfully created.' - else - render action: "new" - end - end - - def update - user_team_params = params[:user_team].dup - owner_id = user_team_params.delete(:owner_id) - - if owner_id - user_team.owner = User.find(owner_id) - end - - if user_team.update_attributes(user_team_params) - redirect_to admin_team_path(user_team), notice: 'Team of users was successfully updated.' - else - render action: "edit" - end - end - - def destroy - user_team.destroy - - redirect_to admin_teams_path, notice: 'Team of users was successfully deleted.' - end - - protected - - def user_team - @team ||= UserTeam.find_by_path(params[:id]) - end - -end diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index 20cb13e7..744b1912 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -1,43 +1,46 @@ -class Admin::UsersController < Admin::ApplicationController - before_filter :admin_user, only: [:show, :edit, :update, :destroy] - +class Admin::UsersController < AdminController def index @admin_users = User.scoped @admin_users = @admin_users.filter(params[:filter]) @admin_users = @admin_users.search(params[:name]) if params[:name].present? - @admin_users = @admin_users.alphabetically.page(params[:page]) + @admin_users = @admin_users.order("updated_at DESC").page(params[:page]) end def show - # Projects user can be added to - @not_in_projects = Project.scoped - @not_in_projects = @not_in_projects.without_user(admin_user) if admin_user.authorized_projects.present? + @admin_user = User.find(params[:id]) - # Projects he already own or joined - @projects = admin_user.authorized_projects.where('projects.id in (?)', admin_user.authorized_projects.map(&:id)) + @projects = if @admin_user.projects.empty? + Project + else + Project.without_user(@admin_user) + end.all end def team_update - UsersProject.add_users_into_projects( + @admin_user = User.find(params[:id]) + + UsersProject.user_bulk_import( + @admin_user, params[:project_ids], - [admin_user.id], params[:project_access] ) - redirect_to [:admin, admin_user], notice: 'Teams were successfully updated.' + redirect_to [:admin, @admin_user], notice: 'Teams were successfully updated.' end def new - @admin_user = User.new({ projects_limit: Gitlab.config.gitlab.default_projects_limit }, as: :admin) + @admin_user = User.new({ projects_limit: Gitlab.config.default_projects_limit }, as: :admin) end def edit - admin_user + @admin_user = User.find(params[:id]) end def block - if admin_user.block + @admin_user = User.find(params[:id]) + + if @admin_user.block redirect_to :back, alert: "Successfully blocked" else redirect_to :back, alert: "Error occured. User was not blocked" @@ -45,7 +48,9 @@ class Admin::UsersController < Admin::ApplicationController end def unblock - if admin_user.activate + @admin_user = User.find(params[:id]) + + if @admin_user.update_attribute(:blocked, false) redirect_to :back, alert: "Successfully unblocked" else redirect_to :back, alert: "Error occured. User was not unblocked" @@ -77,36 +82,30 @@ class Admin::UsersController < Admin::ApplicationController params[:user].delete(:password_confirmation) end - admin_user.admin = (admin && admin.to_i > 0) + @admin_user = User.find(params[:id]) + @admin_user.admin = (admin && admin.to_i > 0) respond_to do |format| - if admin_user.update_attributes(params[:user], as: :admin) - format.html { redirect_to [:admin, admin_user], notice: 'User was successfully updated.' } + if @admin_user.update_attributes(params[:user], as: :admin) + format.html { redirect_to [:admin, @admin_user], notice: 'User was successfully updated.' } format.json { head :ok } else - # restore username to keep form action url. - admin_user.username = params[:id] format.html { render action: "edit" } - format.json { render json: admin_user.errors, status: :unprocessable_entity } + format.json { render json: @admin_user.errors, status: :unprocessable_entity } end end end def destroy - if admin_user.personal_projects.count > 0 + @admin_user = User.find(params[:id]) + if @admin_user.my_own_projects.count > 0 redirect_to admin_users_path, alert: "User is a project owner and can't be removed." and return end - admin_user.destroy + @admin_user.destroy respond_to do |format| - format.html { redirect_to admin_users_path } + format.html { redirect_to admin_users_url } format.json { head :ok } end end - - protected - - def admin_user - @admin_user ||= User.find_by_username!(params[:id]) - end end diff --git a/app/controllers/admin/application_controller.rb b/app/controllers/admin_controller.rb similarity index 82% rename from app/controllers/admin/application_controller.rb rename to app/controllers/admin_controller.rb index 6a8f20f6..bce9f692 100644 --- a/app/controllers/admin/application_controller.rb +++ b/app/controllers/admin_controller.rb @@ -1,7 +1,7 @@ # Provides a base class for Admin controllers to subclass # # Automatically sets the layout and ensures an administrator is logged in -class Admin::ApplicationController < ApplicationController +class AdminController < ApplicationController layout 'admin' before_filter :authenticate_admin! diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 32b12466..ef6fc81a 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -2,15 +2,17 @@ class ApplicationController < ActionController::Base before_filter :authenticate_user! before_filter :reject_blocked! before_filter :set_current_user_for_observers - before_filter :add_abilities before_filter :dev_tools if Rails.env == 'development' - before_filter :default_headers - before_filter :add_gon_variables protect_from_forgery helper_method :abilities, :can? + rescue_from Gitlab::Gitolite::AccessDenied do |exception| + log_exception(exception) + render "errors/gitolite", layout: "errors", status: 500 + end + rescue_from Encoding::CompatibilityError do |exception| log_exception(exception) render "errors/encoding", layout: "errors", status: 500 @@ -30,17 +32,17 @@ 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." + flash[:alert] = "Your account was blocked" redirect_to new_user_session_path end 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." + flash[:alert] = "Your account was blocked" new_user_session_path else super @@ -61,25 +63,11 @@ class ApplicationController < ActionController::Base end def project - id = params[:project_id] || params[:id] - - @project = Project.find_with_namespace(id) - - if @project and can?(current_user, :read_project, @project) - @project - else - @project = nil - render_404 - end + @project ||= current_user.projects.find_by_code(params[:project_id] || params[:id]) + @project || render_404 end - def repository - @repository ||= project.repository - rescue Grit::NoSuchPathError - nil - end - - def add_abilities + def add_project_abilities abilities << Ability end @@ -91,18 +79,6 @@ class ApplicationController < ActionController::Base return access_denied! unless can?(current_user, :download_code, project) end - def authorize_create_team! - return access_denied! unless can?(current_user, :create_team, nil) - end - - def authorize_manage_user_team! - return access_denied! unless user_team.present? && can?(current_user, :manage_user_team, user_team) - end - - def authorize_admin_user_team! - return access_denied! unless user_team.present? && can?(current_user, :admin_user_team, user_team) - end - def access_denied! render "errors/access_denied", layout: "errors", status: 404 end @@ -127,10 +103,6 @@ class ApplicationController < ActionController::Base render file: Rails.root.join("public", "404"), layout: false, status: "404" end - def render_403 - render file: Rails.root.join("public", "403"), layout: false, status: "403" - end - def require_non_empty_project redirect_to @project if @project.empty_repo? end @@ -144,16 +116,4 @@ class ApplicationController < ActionController::Base def dev_tools Rack::MiniProfiler.authorize_request end - - def default_headers - 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 - gon.api_version = Gitlab::API.version - gon.api_token = current_user.private_token if current_user - gon.gravatar_url = request.ssl? ? Gitlab.config.gravatar.ssl_url : Gitlab.config.gravatar.plain_url - end end diff --git a/app/controllers/blame_controller.rb b/app/controllers/blame_controller.rb index 310b567c..37d7245c 100644 --- a/app/controllers/blame_controller.rb +++ b/app/controllers/blame_controller.rb @@ -7,7 +7,10 @@ class BlameController < ProjectResourceController before_filter :authorize_code_access! before_filter :require_non_empty_project + before_filter :assign_ref_vars + def show - @blame = Gitlab::Git::Blame.new(project.repository, @commit.id, @path) + @repo = @project.repo + @blame = Grit::Blob.blame(@repo, @commit.id, @path) end end diff --git a/app/controllers/blob_controller.rb b/app/controllers/blob_controller.rb index 530b72fe..d4a45d95 100644 --- a/app/controllers/blob_controller.rb +++ b/app/controllers/blob_controller.rb @@ -7,6 +7,8 @@ class BlobController < ProjectResourceController before_filter :authorize_code_access! before_filter :require_non_empty_project + before_filter :assign_ref_vars + def show if @tree.is_blob? send_data( diff --git a/app/controllers/commit_controller.rb b/app/controllers/commit_controller.rb index 13294108..97998352 100644 --- a/app/controllers/commit_controller.rb +++ b/app/controllers/commit_controller.rb @@ -13,17 +13,11 @@ class CommitController < ProjectResourceController @commit = result[:commit] git_not_found! unless @commit - @suppress_diff = result[:suppress_diff] - - @note = result[:note] - @line_notes = result[:line_notes] - @notes_count = result[:notes_count] - @target_type = :commit - @target_id = @commit.id - - @comments_allowed = @reply_allowed = true - @comments_target = { noteable_type: 'Commit', - commit_id: @commit.id } + @suppress_diff = result[:suppress_diff] + @note = result[:note] + @line_notes = result[:line_notes] + @notes_count = result[:notes_count] + @comments_allowed = true respond_to do |format| format.html do @@ -32,8 +26,7 @@ class CommitController < ProjectResourceController end end - format.diff { render text: @commit.to_diff } - format.patch { render text: @commit.to_patch } + format.patch end end end diff --git a/app/controllers/commits_controller.rb b/app/controllers/commits_controller.rb index cde1f459..3b8ebdb5 100644 --- a/app/controllers/commits_controller.rb +++ b/app/controllers/commits_controller.rb @@ -9,10 +9,11 @@ class CommitsController < ProjectResourceController before_filter :require_non_empty_project def show - @repo = @project.repository + @repo = @project.repo @limit, @offset = (params[:limit] || 40), (params[:offset] || 0) - @commits = @repo.commits(@ref, @path, @limit, @offset) + @commits = @project.commits(@ref, @path, @limit, @offset) + @commits = CommitDecorator.decorate(@commits) respond_to do |format| format.html # index.html.erb diff --git a/app/controllers/compare_controller.rb b/app/controllers/compare_controller.rb index 750e9c23..ae20f9c0 100644 --- a/app/controllers/compare_controller.rb +++ b/app/controllers/compare_controller.rb @@ -8,13 +8,15 @@ class CompareController < ProjectResourceController end def show - compare = Gitlab::Git::Compare.new(project.repository, params[:from], params[:to]) + result = Commit.compare(project, params[:from], params[:to]) - @commits = compare.commits - @commit = compare.commit - @diffs = compare.diffs - @refs_are_same = compare.same + @commits = result[:commits] + @commit = result[:commit] + @diffs = result[:diffs] + @refs_are_same = result[:same] @line_notes = [] + + @commits = CommitDecorator.decorate(@commits) end def create diff --git a/app/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb index 91a67985..8d9329f2 100644 --- a/app/controllers/dashboard_controller.rb +++ b/app/controllers/dashboard_controller.rb @@ -1,17 +1,14 @@ class DashboardController < ApplicationController respond_to :html - before_filter :load_projects - before_filter :event_filter, only: :show + before_filter :event_filter, only: :index - def show - @groups = current_user.authorized_groups.sort_by(&:human_name) - @has_authorized_projects = @projects.count > 0 - @teams = current_user.authorized_teams - @projects_count = @projects.count - @projects = @projects.limit(20) + def index + @groups = Group.where(id: current_user.projects.pluck(:group_id)) + @projects = current_user.projects_sorted_by_activity + @projects = @projects.page(params[:page]).per(30) - @events = Event.in_projects(current_user.authorized_projects.pluck(:id)) + @events = Event.in_projects(current_user.project_ids) @events = @event_filter.apply_filter(@events) @events = @events.limit(20).offset(params[:offset] || 0) @@ -24,32 +21,17 @@ class DashboardController < ApplicationController end end - def projects - @projects = case params[:scope] - when 'personal' then - @projects.personal(current_user) - when 'joined' then - @projects.joined(current_user) - else - @projects - end - - @projects = @projects.search(params[:search]) if params[:search].present? - @projects = @projects.page(params[:page]).per(30) - end - # Get authored or assigned open merge requests def merge_requests - @merge_requests = current_user.cared_merge_requests - @merge_requests = FilterContext.new(@merge_requests, params).execute - @merge_requests = @merge_requests.recent.page(params[:page]).per(20) + @projects = current_user.projects.all + @merge_requests = current_user.cared_merge_requests.recent.page(params[:page]).per(20) end # Get only assigned issues def issues - @issues = current_user.assigned_issues - @issues = FilterContext.new(@issues, params).execute - @issues = @issues.recent.page(params[:page]).per(20) + @projects = current_user.projects.all + @user = current_user + @issues = current_user.assigned_issues.opened.recent.page(params[:page]).per(20) @issues = @issues.includes(:author, :project) respond_to do |format| @@ -58,14 +40,7 @@ class DashboardController < ApplicationController end end - protected - - def load_projects - @projects = current_user.authorized_projects.sorted_by_activity - end - def event_filter - filters = cookies['event_filter'].split(',') if cookies['event_filter'] - @event_filter ||= EventFilter.new(filters) + @event_filter ||= EventFilter.new(params[:event_filter]) end end diff --git a/app/controllers/edit_tree_controller.rb b/app/controllers/edit_tree_controller.rb deleted file mode 100644 index aae983aa..00000000 --- a/app/controllers/edit_tree_controller.rb +++ /dev/null @@ -1,47 +0,0 @@ -# Controller for edit a repository's file -class EditTreeController < ProjectResourceController - include ExtractsPath - - # Authorize - before_filter :authorize_read_project! - before_filter :authorize_code_access! - before_filter :require_non_empty_project - - before_filter :edit_requirements, only: [:edit, :update] - - def show - @last_commit = @project.repository.last_commit_for(@ref, @path).sha - end - - def update - edit_file_action = Gitlab::Satellite::EditFileAction.new(current_user, @project, @ref, @path) - updated_successfully = edit_file_action.commit!( - params[:content], - params[:commit_message], - params[:last_commit] - ) - - if updated_successfully - redirect_to project_tree_path(@project, @id), notice: "Your changes have been successfully commited" - else - flash[:notice] = "Your changes could not be commited, because the file has been changed" - render :edit - end - end - - private - - def edit_requirements - unless @tree.is_blob? && @tree.text? - redirect_to project_tree_path(@project, @id), notice: "You can only edit text files" - end - - allowed = if project.protected_branch? @ref - can?(current_user, :push_code_to_protected_branches, project) - else - can?(current_user, :push_code, project) - end - - return access_denied! unless allowed - end -end diff --git a/app/controllers/errors_controller.rb b/app/controllers/errors_controller.rb index a0c8a000..e998d723 100644 --- a/app/controllers/errors_controller.rb +++ b/app/controllers/errors_controller.rb @@ -1,2 +1,5 @@ class ErrorsController < ApplicationController + def githost + render "errors/gitolite" + end end diff --git a/app/controllers/files_controller.rb b/app/controllers/files_controller.rb deleted file mode 100644 index 3cd2e773..00000000 --- a/app/controllers/files_controller.rb +++ /dev/null @@ -1,13 +0,0 @@ -class FilesController < ApplicationController - def download - note = Note.find(params[:id]) - - if can?(current_user, :read_project, note.project) - uploader = note.attachment - send_file uploader.file.path, disposition: 'attachment' - else - not_found! - end - end -end - diff --git a/app/controllers/graph_controller.rb b/app/controllers/graph_controller.rb deleted file mode 100644 index b4bf9565..00000000 --- a/app/controllers/graph_controller.rb +++ /dev/null @@ -1,29 +0,0 @@ -class GraphController < ProjectResourceController - include ExtractsPath - include ApplicationHelper - - # Authorize - before_filter :authorize_read_project! - before_filter :authorize_code_access! - before_filter :require_non_empty_project - - def show - if params.has_key?(:q) - if params[:q].blank? - redirect_to project_graph_path(@project, params[:id]) - return - end - - @q = params[:q] - @commit = @project.repository.commit(@q) || @commit - end - - respond_to do |format| - format.html - - format.json do - @graph = Network::Graph.new(project, @ref, @commit) - end - end - end -end diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index bdf3567f..63f70cd0 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -1,32 +1,9 @@ class GroupsController < ApplicationController respond_to :html - layout 'group', except: [:new, :create] + layout 'group' - before_filter :group, except: [:new, :create] - - # Authorize - before_filter :authorize_read_group!, except: [:new, :create] - before_filter :authorize_admin_group!, only: [:edit, :update, :destroy] - before_filter :authorize_create_group!, only: [:new, :create] - - # Load group projects - before_filter :projects, except: [:new, :create] - - def new - @group = Group.new - end - - def create - @group = Group.new(params[:group]) - @group.path = @group.name.dup.parameterize if @group.name - @group.owner = current_user - - if @group.save - redirect_to @group, notice: 'Group was successfully created.' - else - render action: "new" - end - end + before_filter :group + before_filter :projects def show @events = Event.in_projects(project_ids).limit(20).offset(params[:offset] || 0) @@ -41,16 +18,15 @@ class GroupsController < ApplicationController # Get authored or assigned open merge requests def merge_requests - @merge_requests = current_user.cared_merge_requests.of_group(@group) - @merge_requests = FilterContext.new(@merge_requests, params).execute - @merge_requests = @merge_requests.recent.page(params[:page]).per(20) + @merge_requests = current_user.cared_merge_requests + @merge_requests = @merge_requests.of_group(@group).recent.page(params[:page]).per(20) end # Get only assigned issues def issues - @issues = current_user.assigned_issues.of_group(@group) - @issues = FilterContext.new(@issues, params).execute - @issues = @issues.recent.page(params[:page]).per(20) + @user = current_user + @issues = current_user.assigned_issues.opened + @issues = @issues.of_group(@group).recent.page(params[:page]).per(20) @issues = @issues.includes(:author, :project) respond_to do |format| @@ -59,79 +35,29 @@ class GroupsController < ApplicationController end end + def search + result = SearchContext.new(project_ids, params).execute + + @projects = result[:projects] + @merge_requests = result[:merge_requests] + @issues = result[:issues] + end + def people - @project = group.projects.find(params[:project_id]) if params[:project_id] - @users = @project ? @project.users : group.users - @users.sort_by!(&:name) - - if @project - @team_member = @project.users_projects.new - else - @team_member = UsersProject.new - end - end - - def team_members - @group.add_users_to_project_teams(params[:user_ids], params[:project_access]) - redirect_to people_group_path(@group), notice: 'Users was successfully added.' - end - - def edit - end - - def update - group_params = params[:group].dup - owner_id =group_params.delete(:owner_id) - - if owner_id - @group.owner = User.find(owner_id) - @group.save - end - - if @group.update_attributes(group_params) - redirect_to @group, notice: 'Group was successfully updated.' - else - render action: "edit" - end - end - - def destroy - @group.truncate_teams - @group.destroy - - redirect_to root_path, notice: 'Group was removed.' + @users = group.users.all end protected def group - @group ||= Group.find_by_path(params[:id]) + @group ||= Group.find_by_code(params[:id]) end def projects - @projects ||= current_user.authorized_projects.where(namespace_id: group.id).sorted_by_activity + @projects ||= current_user.projects_sorted_by_activity.where(group_id: @group.id) end def project_ids projects.map(&:id) end - - # Dont allow unauthorized access to group - def authorize_read_group! - unless projects.present? or can?(current_user, :manage_group, @group) - return render_404 - end - end - - def authorize_create_group! - unless can?(current_user, :create_group, nil) - return render_404 - end - end - - def authorize_admin_group! - unless can?(current_user, :manage_group, group) - return render_404 - end - end end diff --git a/app/controllers/issues_controller.rb b/app/controllers/issues_controller.rb index 9917d198..0f28fc3a 100644 --- a/app/controllers/issues_controller.rb +++ b/app/controllers/issues_controller.rb @@ -1,6 +1,6 @@ class IssuesController < ProjectResourceController before_filter :module_enabled - before_filter :issue, only: [:edit, :update, :show] + before_filter :issue, only: [:edit, :update, :destroy, :show] # Allow read any issue before_filter :authorize_read_issue! @@ -11,6 +11,9 @@ class IssuesController < ProjectResourceController # Allow modify issue before_filter :authorize_modify_issue!, only: [:edit, :update] + # Allow destroy issue + before_filter :authorize_admin_issue!, only: [:destroy] + respond_to :js, :html def index @@ -35,8 +38,6 @@ class IssuesController < ProjectResourceController def show @note = @project.notes.new(noteable: @issue) - @target_type = :issue - @target_id = @issue.id respond_to do |format| format.html @@ -76,6 +77,15 @@ class IssuesController < ProjectResourceController end end + def destroy + @issue.destroy + + respond_to do |format| + format.html { redirect_to project_issues_path } + format.js { render nothing: true } + end + end + def sort return render_404 unless can?(current_user, :admin_issue, @project) diff --git a/app/controllers/merge_requests_controller.rb b/app/controllers/merge_requests_controller.rb index 1950ebb2..8e180c9b 100644 --- a/app/controllers/merge_requests_controller.rb +++ b/app/controllers/merge_requests_controller.rb @@ -1,9 +1,7 @@ -require 'gitlab/satellite/satellite' - class MergeRequestsController < ProjectResourceController before_filter :module_enabled - before_filter :merge_request, only: [:edit, :update, :show, :commits, :diffs, :automerge, :automerge_check, :ci_status] - before_filter :validates_merge_request, only: [:show, :diffs] + before_filter :merge_request, only: [:edit, :update, :destroy, :show, :commits, :diffs, :automerge, :automerge_check, :raw] + before_filter :validates_merge_request, only: [:show, :diffs, :raw] before_filter :define_show_vars, only: [:show, :diffs] # Allow read any merge_request @@ -15,30 +13,30 @@ class MergeRequestsController < ProjectResourceController # Allow modify merge_request before_filter :authorize_modify_merge_request!, only: [:close, :edit, :update, :sort] + # Allow destroy merge_request + before_filter :authorize_admin_merge_request!, only: [:destroy] + + def index @merge_requests = MergeRequestsLoadContext.new(project, current_user, params).execute end def show - @target_type = :merge_request - @target_id = @merge_request.id - respond_to do |format| format.html format.js - - format.diff { render text: @merge_request.to_diff } - format.patch { render text: @merge_request.to_patch } end end + def raw + send_file @merge_request.to_raw + end + def diffs @diffs = @merge_request.diffs @commit = @merge_request.last_commit - @comments_allowed = @reply_allowed = true - @comments_target = { noteable_type: 'MergeRequest', - noteable_id: @merge_request.id } + @comments_allowed = true @line_notes = @merge_request.notes.where("line_code is not null") end @@ -75,15 +73,12 @@ class MergeRequestsController < ProjectResourceController if @merge_request.unchecked? @merge_request.check_if_can_be_merged end - render json: {merge_status: @merge_request.merge_status_name} - rescue Gitlab::SatelliteNotExistError - render json: {merge_status: :no_satellite} + render json: {state: @merge_request.human_state} end def automerge - return access_denied! unless allowed_to_merge? - - if @merge_request.opened? && @merge_request.can_be_merged? + return access_denied! unless can?(current_user, :accept_mr, @project) + if @merge_request.open? && @merge_request.can_be_merged? @merge_request.should_remove_source_branch = params[:should_remove_source_branch] @merge_request.automerge!(current_user) @status = true @@ -92,19 +87,22 @@ class MergeRequestsController < ProjectResourceController end end + def destroy + @merge_request.destroy + + respond_to do |format| + format.html { redirect_to project_merge_requests_url(@project) } + end + end + def branch_from - @commit = @repository.commit(params[:ref]) + @commit = project.commit(params[:ref]) + @commit = CommitDecorator.decorate(@commit) end def branch_to - @commit = @repository.commit(params[:ref]) - end - - def ci_status - status = project.gitlab_ci_service.commit_status(merge_request.last_commit.sha) - response = { status: status } - - render json: response + @commit = project.commit(params[:ref]) + @commit = CommitDecorator.decorate(@commit) end protected @@ -126,12 +124,12 @@ class MergeRequestsController < ProjectResourceController end def validates_merge_request - # Show git not found page if target branch doesn't exist - return invalid_mr unless @project.repository.branch_names.include?(@merge_request.target_branch) + # Show git not found page if target branch doesnt exist + return git_not_found! unless @project.repo.heads.map(&:name).include?(@merge_request.target_branch) - # Show git not found page if source branch doesn't exist + # Show git not found page if source branch doesnt exist # and there is no saved commits between source & target branch - return invalid_mr if !@project.repository.branch_names.include?(@merge_request.source_branch) && @merge_request.commits.blank? + return git_not_found! if !@project.repo.heads.map(&:name).include?(@merge_request.source_branch) && @merge_request.commits.blank? end def define_show_vars @@ -141,23 +139,6 @@ class MergeRequestsController < ProjectResourceController # Get commits from repository # or from cache if already merged @commits = @merge_request.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 - - def invalid_mr - # Render special view for MR with removed source or target branch - render 'invalid' + @commits = CommitDecorator.decorate(@commits) end end diff --git a/app/controllers/milestones_controller.rb b/app/controllers/milestones_controller.rb index cdac28c1..fadfee2d 100644 --- a/app/controllers/milestones_controller.rb +++ b/app/controllers/milestones_controller.rb @@ -12,12 +12,11 @@ class MilestonesController < ProjectResourceController def index @milestones = case params[:f] - when 'all'; @project.milestones.order("state, due_date DESC") - when 'closed'; @project.milestones.closed.order("due_date DESC") - else @project.milestones.active.order("due_date ASC") + when 'all'; @project.milestones + else @project.milestones.active end - @milestones = @milestones.includes(:project) + @milestones = @milestones.includes(:project).order("due_date") @milestones = @milestones.page(params[:page]).per(20) end @@ -32,7 +31,7 @@ class MilestonesController < ProjectResourceController def show @issues = @milestone.issues - @users = UserDecorator.decorate_collection(@milestone.participants) + @users = UserDecorator.decorate(@milestone.participants) @merge_requests = @milestone.merge_requests respond_to do |format| @@ -43,7 +42,6 @@ class MilestonesController < ProjectResourceController def create @milestone = @project.milestones.new(params[:milestone]) - @milestone.author_id_of_changes = current_user.id if @milestone.save redirect_to project_milestone_path(@project, @milestone) @@ -53,7 +51,7 @@ class MilestonesController < ProjectResourceController end def update - @milestone.update_attributes(params[:milestone].merge(author_id_of_changes: current_user.id)) + @milestone.update_attributes(params[:milestone]) respond_to do |format| format.js diff --git a/app/controllers/notes_controller.rb b/app/controllers/notes_controller.rb index 000c7bbb..d794f368 100644 --- a/app/controllers/notes_controller.rb +++ b/app/controllers/notes_controller.rb @@ -6,12 +6,10 @@ class NotesController < ProjectResourceController respond_to :js def index - @notes = Notes::LoadContext.new(project, current_user, params).execute - @target_type = params[:target_type].camelize - @target_id = params[:target_id] - + notes if params[:target_type] == "merge_request" - @discussions = discussions_from_notes + @mixed_targets = true + @main_target_type = params[:target_type].camelize end respond_with(@notes) @@ -19,8 +17,6 @@ class NotesController < ProjectResourceController def create @note = Notes::CreateContext.new(project, current_user, params).execute - @target_type = params[:target_type].camelize - @target_id = params[:target_id] respond_to do |format| format.html {redirect_to :back} @@ -44,34 +40,7 @@ class NotesController < ProjectResourceController protected - def discussion_notes_for(note) - @notes.select do |other_note| - note.discussion_id == other_note.discussion_id - end - end - - def discussions_from_notes - discussion_ids = [] - discussions = [] - - @notes.each do |note| - next if discussion_ids.include?(note.discussion_id) - - # don't group notes for the main target - if note_for_main_target?(note) - discussions << [note] - else - discussions << discussion_notes_for(note) - discussion_ids << note.discussion_id - end - end - - discussions - end - - # Helps to distinguish e.g. commit notes in mr notes list - def note_for_main_target?(note) - note.for_wall? || - (@target_type.camelize == note.noteable_type && !note.for_diff_line?) + def notes + @notes = Notes::LoadContext.new(project, current_user, params).execute end end diff --git a/app/controllers/notifications_controller.rb b/app/controllers/notifications_controller.rb deleted file mode 100644 index e44e0aa8..00000000 --- a/app/controllers/notifications_controller.rb +++ /dev/null @@ -1,13 +0,0 @@ -class NotificationsController < ApplicationController - layout 'profile' - - def show - @notification = current_user.notification - @projects = current_user.authorized_projects - end - - def update - current_user.notification_level = params[:notification_level] - @saved = current_user.save - end -end diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb index c4ebf0e4..2fb783b2 100644 --- a/app/controllers/omniauth_callbacks_controller.rb +++ b/app/controllers/omniauth_callbacks_controller.rb @@ -1,5 +1,5 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController - Gitlab.config.omniauth.providers.each do |provider| + Gitlab.config.omniauth_providers.each do |provider| define_method provider['name'] do handle_omniauth end diff --git a/app/controllers/profile_controller.rb b/app/controllers/profile_controller.rb new file mode 100644 index 00000000..5f8b11fd --- /dev/null +++ b/app/controllers/profile_controller.rb @@ -0,0 +1,47 @@ +class ProfileController < ApplicationController + before_filter :user + + def show + end + + def design + end + + def update + @user.update_attributes(params[:user]) + + respond_to do |format| + format.html { redirect_to :back } + format.js + end + end + + def token + end + + def password_update + params[:user].reject!{ |k, v| k != "password" && k != "password_confirmation"} + + if @user.update_attributes(params[:user]) + flash[:notice] = "Password was successfully updated. Please login with it" + redirect_to new_user_session_path + else + render 'account' + end + end + + def reset_private_token + current_user.reset_authentication_token! + redirect_to profile_account_path + end + + def history + @events = current_user.recent_events.page(params[:page]).per(20) + end + + private + + def user + @user = current_user + end +end diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb deleted file mode 100644 index 6fa114a4..00000000 --- a/app/controllers/profiles_controller.rb +++ /dev/null @@ -1,83 +0,0 @@ -class ProfilesController < ApplicationController - include ActionView::Helpers::SanitizeHelper - - before_filter :user - layout 'profile' - - def show - end - - def design - end - - def account - end - - def update - if @user.update_attributes(user_attributes) - flash[:notice] = "Profile was successfully updated" - else - flash[:alert] = "Failed to update profile" - end - - respond_to do |format| - format.html { redirect_to :back } - format.js - end - end - - def token - end - - def update_password - params[:user].reject!{ |k, v| k != "password" && k != "password_confirmation"} - - if @user.update_attributes(params[:user]) - flash[:notice] = "Password was successfully updated. Please login with it" - redirect_to new_user_session_path - else - render 'account' - end - end - - def reset_private_token - if current_user.reset_authentication_token! - flash[:notice] = "Token was successfully updated" - end - - redirect_to account_profile_path - end - - def history - @events = current_user.recent_events.page(params[:page]).per(20) - end - - def update_username - if @user.can_change_username? - @user.update_attributes(username: params[:user][:username]) - end - - respond_to do |format| - format.js - end - end - - private - - def user - @user = current_user - end - - def user_attributes - user_attributes = params[:user] - - # Sanitize user input because we dont have strict - # validation for this fields - %w(name skype linkedin twitter bio).each do |attr| - value = user_attributes[attr] - user_attributes[attr] = sanitize(value) if value.present? - end - - user_attributes - end -end diff --git a/app/controllers/project_resource_controller.rb b/app/controllers/project_resource_controller.rb index ea78b3ff..d297bba6 100644 --- a/app/controllers/project_resource_controller.rb +++ b/app/controllers/project_resource_controller.rb @@ -1,4 +1,5 @@ class ProjectResourceController < ApplicationController before_filter :project - before_filter :repository + # Authorize + before_filter :add_project_abilities end diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb deleted file mode 100644 index 7e4776d2..00000000 --- a/app/controllers/projects/application_controller.rb +++ /dev/null @@ -1,11 +0,0 @@ -class Projects::ApplicationController < ApplicationController - - before_filter :authorize_admin_team_member! - - protected - - def user_team - @team ||= UserTeam.find_by_path(params[:id]) - end - -end diff --git a/app/controllers/projects/teams_controller.rb b/app/controllers/projects/teams_controller.rb deleted file mode 100644 index 17e73673..00000000 --- a/app/controllers/projects/teams_controller.rb +++ /dev/null @@ -1,27 +0,0 @@ -class Projects::TeamsController < Projects::ApplicationController - - def available - @teams = current_user.is_admin? ? UserTeam.scoped : current_user.user_teams - @teams = @teams.without_project(project) - unless @teams.any? - redirect_to project_team_index_path(project), notice: "No available teams for assigment." - end - end - - def assign - unless params[:team_id].blank? - team = UserTeam.find(params[:team_id]) - access = params[:greatest_project_access] - team.assign_to_project(project, access) - end - redirect_to project_team_index_path(project) - end - - def resign - team = project.user_teams.find_by_path(params[:id]) - team.resign_from_project(project) - - redirect_to project_team_index_path(project) - end - -end diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 8e55aa01..72080070 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -1,10 +1,11 @@ +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] # Authorize before_filter :authorize_read_project!, except: [:index, :new, :create] - before_filter :authorize_admin_project!, only: [:edit, :update, :destroy, :transfer] + before_filter :authorize_admin_project!, only: [:edit, :update, :destroy] before_filter :require_non_empty_project, only: [:blob, :tree, :graph] layout 'application', only: [:new, :create] @@ -17,7 +18,7 @@ class ProjectsController < ProjectResourceController end def create - @project = ::Projects::CreateContext.new(current_user, params[:project]).execute + @project = Project.create_by_user(params[:project], current_user) respond_to do |format| flash[:notice] = 'Project was successfully created.' if @project.saved? @@ -33,11 +34,8 @@ class ProjectsController < ProjectResourceController end def update - status = ::Projects::UpdateContext.new(project, current_user, params).execute - respond_to do |format| - if status - flash[:notice] = 'Project was successfully updated.' + if project.update_attributes(params[:project]) format.html { redirect_to edit_project_path(project), notice: 'Project was successfully updated.' } format.js else @@ -47,32 +45,52 @@ class ProjectsController < ProjectResourceController end end - def transfer - ::Projects::TransferContext.new(project, current_user, params).execute - end - def show limit = (params[:limit] || 20).to_i @events = @project.events.recent.limit(limit).offset(params[:offset] || 0) respond_to do |format| format.html do - if @project.empty_repo? - render "projects/empty" - else - @last_push = current_user.recent_push(@project.id) - render :show - end + unless @project.empty_repo? + @last_push = current_user.recent_push(@project.id) + render :show + else + render "projects/empty" + end end format.js end end - def destroy - return access_denied! unless can?(current_user, :remove_project, project) + def files + @notes = @project.notes.where("attachment != 'NULL'").order("created_at DESC").limit(100) + end - project.team.truncate + # + # Wall + # + + def wall + return render_404 unless @project.wall_enabled + @note = Note.new + + respond_to do |format| + format.html + end + end + + def graph + graph = Gitlab::Graph::JsonBuilder.new(project) + + @days_json, @commits_json = graph.days_json, graph.commits_json + end + + def destroy + # Disable the UsersProject update_repository call, otherwise it will be + # called once for every person removed from the project + UsersProject.skip_callback(:destroy, :after, :update_repository) project.destroy + UsersProject.set_callback(:destroy, :after, :update_repository) respond_to do |format| format.html { redirect_to root_path } diff --git a/app/controllers/public/projects_controller.rb b/app/controllers/public/projects_controller.rb deleted file mode 100644 index b929b23e..00000000 --- a/app/controllers/public/projects_controller.rb +++ /dev/null @@ -1,12 +0,0 @@ -class Public::ProjectsController < ApplicationController - skip_before_filter :authenticate_user!, - :reject_blocked, :set_current_user_for_observers, - :add_abilities - - layout 'public' - - def index - @projects = Project.public_only - @projects = @projects.includes(:namespace).order("namespaces.path, projects.name ASC").page(params[:page]).per(20) - end -end diff --git a/app/controllers/refs_controller.rb b/app/controllers/refs_controller.rb index eb8d1e19..b48d5ec7 100644 --- a/app/controllers/refs_controller.rb +++ b/app/controllers/refs_controller.rb @@ -12,9 +12,7 @@ class RefsController < ProjectResourceController respond_to do |format| format.html do new_path = if params[:destination] == "tree" - project_tree_path(@project, (@ref + "/" + params[:path])) - elsif params[:destination] == "graph" - project_graph_path(@project, @ref) + project_tree_path(@project, @ref) else project_commits_path(@project, @ref) end @@ -33,7 +31,8 @@ class RefsController < ProjectResourceController contents = @tree.contents @logs = contents.map do |content| file = params[:path] ? File.join(params[:path], content.name) : content.name - last_commit = @repo.commits(@commit.id, file, 1).last + last_commit = @project.commits(@commit.id, file, 1).last + last_commit = CommitDecorator.decorate(last_commit) { file_name: content.name, commit: last_commit @@ -46,9 +45,11 @@ class RefsController < ProjectResourceController def define_tree_vars params[:path] = nil if params[:path].blank? - @repo = project.repository - @commit = @repo.commit(@ref) - @tree = Tree.new(@commit.tree, @ref, params[:path]) + @repo = project.repo + @commit = project.commit(@ref) + @commit = CommitDecorator.decorate(@commit) + @tree = Tree.new(@commit.tree, project, @ref, params[:path]) + @tree = TreeDecorator.new(@tree) @hex_path = Digest::SHA1.hexdigest(params[:path] || "") if params[:path] diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb deleted file mode 100644 index 507a5c20..00000000 --- a/app/controllers/registrations_controller.rb +++ /dev/null @@ -1,28 +0,0 @@ -class RegistrationsController < Devise::RegistrationsController - before_filter :signup_enabled? - - def destroy - if current_user.owned_projects.count > 0 - redirect_to account_profile_path, alert: "Remove projects and groups before removing account." and return - end - current_user.destroy - - respond_to do |format| - format.html { redirect_to new_user_session_path, notice: "Account successfully removed." } - end - end - - protected - - def build_resource(hash=nil) - super - self.resource.projects_limit = Gitlab.config.gitlab.default_projects_limit - self.resource - end - - private - - def signup_enabled? - redirect_to new_user_session_path unless Gitlab.config.gitlab.signup_enabled - end -end diff --git a/app/controllers/repositories_controller.rb b/app/controllers/repositories_controller.rb index 229cb369..7678fbff 100644 --- a/app/controllers/repositories_controller.rb +++ b/app/controllers/repositories_controller.rb @@ -5,19 +5,19 @@ class RepositoriesController < ProjectResourceController before_filter :require_non_empty_project def show - @activities = @repository.commits_with_refs(20) + @activities = @project.commits_with_refs(20) end def branches - @branches = @repository.branches + @branches = @project.branches end def tags - @tags = @repository.tags + @tags = @project.tags end def stats - @stats = Gitlab::GitStats.new(@repository.raw, @repository.root_ref) + @stats = Gitlab::GitStats.new(@project.repo, @project.root_ref) @graph = @stats.graph end @@ -27,7 +27,7 @@ class RepositoriesController < ProjectResourceController end - file_path = @repository.archive_repo(params[:ref]) + file_path = @project.archive_repo(params[:ref]) if file_path # Send file to user diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb index bbd67df6..4f45f9dd 100644 --- a/app/controllers/search_controller.rb +++ b/app/controllers/search_controller.rb @@ -1,18 +1,6 @@ class SearchController < ApplicationController def show - project_id = params[:project_id] - group_id = params[:group_id] - - project_ids = current_user.authorized_projects.map(&:id) - - if group_id.present? - group_project_ids = Group.find(group_id).projects.map(&:id) - project_ids.select! { |id| group_project_ids.include?(id)} - elsif project_id.present? - project_ids.select! { |id| id == project_id.to_i} - end - - result = SearchContext.new(project_ids, params).execute + result = SearchContext.new(current_user.project_ids, params).execute @projects = result[:projects] @merge_requests = result[:merge_requests] diff --git a/app/controllers/services_controller.rb b/app/controllers/services_controller.rb index 25a06501..50f7e97a 100644 --- a/app/controllers/services_controller.rb +++ b/app/controllers/services_controller.rb @@ -26,7 +26,8 @@ class ServicesController < ProjectResourceController end def test - data = GitPushService.new.sample_data(project, current_user) + commits = project.commits(project.default_branch, nil, 3) + data = project.post_receive_data(commits.last.id, commits.first.id, "refs/heads/#{project.default_branch}", current_user) @service = project.gitlab_ci_service @service.execute(data) diff --git a/app/controllers/snippets_controller.rb b/app/controllers/snippets_controller.rb index a2e22a67..7324a459 100644 --- a/app/controllers/snippets_controller.rb +++ b/app/controllers/snippets_controller.rb @@ -1,5 +1,4 @@ class SnippetsController < ProjectResourceController - before_filter :module_enabled before_filter :snippet, only: [:show, :edit, :destroy, :update, :raw] # Allow read any snippet @@ -17,7 +16,7 @@ class SnippetsController < ProjectResourceController respond_to :html def index - @snippets = @project.snippets.fresh.non_expired + @snippets = @project.snippets end def new @@ -51,8 +50,6 @@ class SnippetsController < ProjectResourceController def show @note = @project.notes.new(noteable: @snippet) - @target_type = :snippet - @target_id = @snippet.id end def destroy @@ -63,7 +60,7 @@ class SnippetsController < ProjectResourceController redirect_to project_snippets_path(@project) end - def raw + def raw send_data( @snippet.content, type: "text/plain", @@ -85,8 +82,4 @@ class SnippetsController < ProjectResourceController def authorize_admin_snippet! return render_404 unless can?(current_user, :admin_snippet, @snippet) end - - def module_enabled - return render_404 unless @project.snippets_enabled - end end diff --git a/app/controllers/team_members_controller.rb b/app/controllers/team_members_controller.rb index ba55648a..37ed74b2 100644 --- a/app/controllers/team_members_controller.rb +++ b/app/controllers/team_members_controller.rb @@ -4,42 +4,39 @@ class TeamMembersController < ProjectResourceController before_filter :authorize_admin_project!, except: [:index, :show] def index - @team = @project.users_projects.scoped - @team = @team.send(params[:type]) if %w(masters developers reporters guests).include?(params[:type]) - @team = @team.sort_by(&:project_access).reverse.group_by(&:project_access) + end - @assigned_teams = @project.user_team_project_relationships + def show + @team_member = project.users_projects.find(params[:id]) + @events = @team_member.user.recent_events.where(:project_id => @project.id).limit(7) end def new - @user_project_relation = project.users_projects.new + @team_member = project.users_projects.new end def create - users = User.where(id: params[:user_ids].split(',')) + @project.add_users_ids_to_team( + params[:user_ids], + params[:project_access] + ) - @project.team << [users, params[:project_access]] - - if params[:redirect_to] - redirect_to params[:redirect_to] - else - redirect_to project_team_index_path(@project) - end + redirect_to project_team_index_path(@project) end def update - @user_project_relation = project.users_projects.find_by_user_id(member) - @user_project_relation.update_attributes(params[:team_member]) + @team_member = project.users_projects.find(params[:id]) + @team_member.update_attributes(params[:team_member]) - unless @user_project_relation.valid? + unless @team_member.valid? flash[:alert] = "User should have at least one role" end redirect_to project_team_index_path(@project) end def destroy - @user_project_relation = project.users_projects.find_by_user_id(member) - @user_project_relation.destroy + @team_member = project.users_projects.find(params[:id]) + @team_member.destroy respond_to do |format| format.html { redirect_to project_team_index_path(@project) } @@ -49,15 +46,9 @@ class TeamMembersController < ProjectResourceController def apply_import giver = Project.find(params[:source_project_id]) - status = @project.team.import(giver) + status = UsersProject.import_team(giver, project) notice = status ? "Succesfully imported" : "Import failed" redirect_to project_team_members_path(project), notice: notice end - - protected - - def member - @member ||= User.find_by_username(params[:id]) - end end diff --git a/app/controllers/teams/application_controller.rb b/app/controllers/teams/application_controller.rb deleted file mode 100644 index fc232026..00000000 --- a/app/controllers/teams/application_controller.rb +++ /dev/null @@ -1,13 +0,0 @@ -class Teams::ApplicationController < ApplicationController - - layout 'user_team' - - before_filter :authorize_manage_user_team! - - protected - - def user_team - @team ||= UserTeam.find_by_path(params[:team_id]) - end - -end diff --git a/app/controllers/teams/members_controller.rb b/app/controllers/teams/members_controller.rb deleted file mode 100644 index f87d422f..00000000 --- a/app/controllers/teams/members_controller.rb +++ /dev/null @@ -1,54 +0,0 @@ -class Teams::MembersController < Teams::ApplicationController - - skip_before_filter :authorize_manage_user_team!, only: [:index] - - def index - @members = user_team.members - end - - def new - @users = User.potential_team_members(user_team) - @users = UserDecorator.decorate_collection @users - end - - def create - unless params[:user_ids].blank? - user_ids = params[:user_ids].split(',') - access = params[:default_project_access] - is_admin = params[:group_admin] - user_team.add_members(user_ids, access, is_admin) - end - - redirect_to team_members_path(user_team), notice: 'Members was successfully added into Team of users.' - end - - def edit - team_member - end - - def update - member_params = params[:team_member] - - options = { - default_projects_access: member_params[:permission], - group_admin: member_params[:group_admin] - } - - if user_team.update_membership(team_member, options) - redirect_to team_members_path(user_team), notice: "Membership for #{team_member.name} was successfully updated in Team of users." - else - render :edit - end - end - - def destroy - user_team.remove_member(team_member) - redirect_to team_path(user_team), notice: "Member #{team_member.name} was successfully removed from Team of users." - end - - protected - - def team_member - @member ||= user_team.members.find_by_username(params[:id]) - end -end diff --git a/app/controllers/teams/projects_controller.rb b/app/controllers/teams/projects_controller.rb deleted file mode 100644 index 81ffa238..00000000 --- a/app/controllers/teams/projects_controller.rb +++ /dev/null @@ -1,40 +0,0 @@ -class Teams::ProjectsController < Teams::ApplicationController - def create - redirect_to :back if params[:project_ids].blank? - - project_ids = params[:project_ids] - access = params[:greatest_project_access] - - # Reject non-allowed projects - allowed_project_ids = current_user.owned_projects.map(&:id) - project_ids.select! { |id| allowed_project_ids.include?(id.to_i) } - - # Assign projects to team - user_team.assign_to_projects(project_ids, access) - - redirect_to edit_team_path(user_team), notice: 'Team of users was successfully assigned to projects.' - end - - def edit - team_project - end - - def update - if user_team.update_project_access(team_project, params[:greatest_project_access]) - redirect_to edit_team_path(user_team), notice: 'Access was successfully updated.' - else - render :edit - end - end - - def destroy - user_team.resign_from_project(team_project) - redirect_to team_projects_path(user_team), notice: 'Team of users was successfully reassigned from project.' - end - - private - - def team_project - @project ||= user_team.projects.find_with_namespace(params[:id]) - end -end diff --git a/app/controllers/teams_controller.rb b/app/controllers/teams_controller.rb deleted file mode 100644 index 26c1d84f..00000000 --- a/app/controllers/teams_controller.rb +++ /dev/null @@ -1,79 +0,0 @@ -class TeamsController < ApplicationController - # Authorize - before_filter :authorize_create_team!, only: [:new, :create] - before_filter :authorize_manage_user_team!, only: [:edit, :update] - before_filter :authorize_admin_user_team!, only: [:destroy] - - before_filter :user_team, except: [:new, :create] - - layout 'user_team', except: [:new, :create] - - def show - projects - @events = Event.in_projects(user_team.project_ids).limit(20).offset(params[:offset] || 0) - end - - def edit - projects - @avaliable_projects = current_user.admin? ? Project.without_team(user_team) : current_user.owned_projects.without_team(user_team) - end - - def update - if user_team.update_attributes(params[:user_team]) - redirect_to team_path(user_team) - else - render action: :edit - end - end - - def destroy - user_team.destroy - redirect_to dashboard_path - end - - def new - @team = UserTeam.new - end - - def create - @team = UserTeam.new(params[:user_team]) - @team.owner = current_user unless params[:owner] - @team.path = @team.name.dup.parameterize if @team.name - - if @team.save - # Add current user as Master to the team - @team.add_members([current_user.id], UsersProject::MASTER, true) - - redirect_to team_path(@team) - else - render action: :new - end - end - - # Get authored or assigned open merge requests - def merge_requests - projects - @merge_requests = MergeRequest.of_user_team(user_team) - @merge_requests = FilterContext.new(@merge_requests, params).execute - @merge_requests = @merge_requests.recent.page(params[:page]).per(20) - end - - # Get only assigned issues - def issues - projects - @issues = Issue.of_user_team(user_team) - @issues = FilterContext.new(@issues, params).execute - @issues = @issues.recent.page(params[:page]).per(20) - @issues = @issues.includes(:author, :project) - end - - protected - - def projects - @projects ||= user_team.projects.sorted_by_activity - end - - def user_team - @team ||= current_user.authorized_teams.find_by_path(params[:id]) - end -end diff --git a/app/controllers/tree_controller.rb b/app/controllers/tree_controller.rb index a03ea3ff..725f48fa 100644 --- a/app/controllers/tree_controller.rb +++ b/app/controllers/tree_controller.rb @@ -7,6 +7,9 @@ class TreeController < ProjectResourceController before_filter :authorize_code_access! before_filter :require_non_empty_project + before_filter :assign_ref_vars + before_filter :edit_requirements, only: [:edit, :update] + def show @hex_path = Digest::SHA1.hexdigest(@path) @logs_path = logs_file_project_ref_path(@project, @ref, @path) @@ -17,4 +20,40 @@ class TreeController < ProjectResourceController format.js { no_cache_headers } end end + + def edit + @last_commit = @project.last_commit_for(@ref, @path).sha + end + + def update + edit_file_action = Gitlab::Satellite::EditFileAction.new(current_user, @project, @ref, @path) + updated_successfully = edit_file_action.commit!( + params[:content], + params[:commit_message], + params[:last_commit] + ) + + if updated_successfully + redirect_to project_tree_path(@project, @id), notice: "Your changes have been successfully commited" + else + flash[:notice] = "Your changes could not be commited, because the file has been changed" + render :edit + end + end + + private + + def edit_requirements + unless @tree.is_blob? && @tree.text? + redirect_to project_tree_path(@project, @id), notice: "You can only edit text files" + end + + allowed = if project.protected_branch? @ref + can?(current_user, :push_code_to_protected_branches, project) + else + can?(current_user, :push_code, project) + end + + return access_denied! unless allowed + end end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb deleted file mode 100644 index e027057f..00000000 --- a/app/controllers/users_controller.rb +++ /dev/null @@ -1,7 +0,0 @@ -class UsersController < ApplicationController - def show - @user = User.find_by_username!(params[:username]) - @projects = @user.authorized_projects.where('projects.id in (?)', current_user.authorized_projects.map(&:id)) - @events = @user.recent_events.where(project_id: @projects.map(&:id)).limit(20) - end -end diff --git a/app/controllers/walls_controller.rb b/app/controllers/walls_controller.rb deleted file mode 100644 index 5993a5e2..00000000 --- a/app/controllers/walls_controller.rb +++ /dev/null @@ -1,20 +0,0 @@ -class WallsController < ProjectResourceController - before_filter :module_enabled - - respond_to :js, :html - - def show - @note = @project.notes.new - - respond_to do |format| - format.html - end - end - - protected - - def module_enabled - return render_404 unless @project.wall_enabled - end -end - diff --git a/app/controllers/wikis_controller.rb b/app/controllers/wikis_controller.rb index 940b1e97..a93afe11 100644 --- a/app/controllers/wikis_controller.rb +++ b/app/controllers/wikis_controller.rb @@ -2,94 +2,59 @@ class WikisController < ProjectResourceController before_filter :authorize_read_wiki! before_filter :authorize_write_wiki!, only: [:edit, :create, :history] before_filter :authorize_admin_wiki!, only: :destroy - before_filter :load_gollum_wiki - + def pages - @wiki_pages = @gollum_wiki.pages + @wikis = @project.wikis.group(:slug).order("created_at") end def show - @wiki = @gollum_wiki.find_page(params[:id], params[:version_id]) + if params[:old_page_id] + @wiki = @project.wikis.find(params[:old_page_id]) + else + @wiki = @project.wikis.where(slug: params[:id]).order("created_at").last + end + + @note = @project.notes.new(noteable: @wiki) if @wiki render 'show' else - return render('empty') unless can?(current_user, :write_wiki, @project) - @wiki = WikiPage.new(@gollum_wiki) - @wiki.title = params[:id] - - render 'edit' + if can?(current_user, :write_wiki, @project) + @wiki = @project.wikis.new(slug: params[:id]) + render 'edit' + else + render 'empty' + end end end def edit - @wiki = @gollum_wiki.find_page(params[:id]) - end - - def update - @wiki = @gollum_wiki.find_page(params[:id]) - - return render('empty') unless can?(current_user, :write_wiki, @project) - - if @wiki.update(content, format, message) - redirect_to [@project, @wiki], notice: 'Wiki was successfully updated.' - else - render 'edit' - end + @wiki = @project.wikis.where(slug: params[:id]).order("created_at").last + @wiki = Wiki.regenerate_from @wiki end def create - @wiki = WikiPage.new(@gollum_wiki) + @wiki = @project.wikis.new(params[:wiki]) + @wiki.user = current_user - if @wiki.create(wiki_params) - redirect_to project_wiki_path(@project, @wiki), notice: 'Wiki was successfully updated.' - else - render action: "edit" + respond_to do |format| + if @wiki.save + format.html { redirect_to [@project, @wiki], notice: 'Wiki was successfully updated.' } + else + format.html { render action: "edit" } + end end end def history - unless @wiki = @gollum_wiki.find_page(params[:id]) - redirect_to project_wiki_path(@project, :home), notice: "Page not found" + @wikis = @project.wikis.where(slug: params[:id]).order("created_at") + end + + def destroy + @wikis = @project.wikis.where(slug: params[:id]).delete_all + + respond_to do |format| + format.html { redirect_to project_wiki_path(@project, :index), notice: "Page was successfully deleted" } end end - - def destroy - @wiki = @gollum_wiki.find_page(params[:id]) - @wiki.delete if @wiki - redirect_to project_wiki_path(@project, :home), notice: "Page was successfully deleted" - end - - def git_access - end - - private - - def load_gollum_wiki - @gollum_wiki = GollumWiki.new(@project, current_user) - - # Call #wiki to make sure the Wiki Repo is initialized - @gollum_wiki.wiki - rescue GollumWiki::CouldNotCreateWikiError => ex - flash[:notice] = "Could not create Wiki Repository at this time. Please try again later." - redirect_to @project - return false - end - - def wiki_params - params[:wiki].slice(:title, :content, :format, :message) - end - - def content - params[:wiki][:content] - end - - def format - params[:wiki][:format] - end - - def message - params[:wiki][:message] - end - end diff --git a/app/decorators/application_decorator.rb b/app/decorators/application_decorator.rb index b805b347..3023699e 100644 --- a/app/decorators/application_decorator.rb +++ b/app/decorators/application_decorator.rb @@ -1,28 +1,27 @@ -class ApplicationDecorator < Draper::Decorator - delegate_all +class ApplicationDecorator < Draper::Base # 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 diff --git a/app/decorators/commit_decorator.rb b/app/decorators/commit_decorator.rb new file mode 100644 index 00000000..69d5b178 --- /dev/null +++ b/app/decorators/commit_decorator.rb @@ -0,0 +1,92 @@ +class CommitDecorator < ApplicationDecorator + decorates :commit + + # Returns a string describing the commit for use in a link title + # + # Example + # + # "Commit: Alex Denisov - Project git clone panel" + def link_title + "Commit: #{author_name} - #{title}" + end + + # Returns the commits title. + # + # Usually, the commit title is the first line of the commit message. + # In case this first line is longer than 80 characters, it is cut off + # after 70 characters and ellipses (`&hellp;`) are appended. + def title + title = safe_message + + return no_commit_message if title.blank? + + title_end = title.index(/\n/) + if (!title_end && title.length > 80) || (title_end && title_end > 80) + title[0..69] << "…".html_safe + else + title.split(/\n/, 2).first + end + end + + # Returns the commits description + # + # cut off, ellipses (`&hellp;`) are prepended to the commit message. + def description + description = safe_message + + title_end = description.index(/\n/) + if (!title_end && description.length > 80) || (title_end && title_end > 80) + "…".html_safe << description[70..-1] + else + description.split(/\n/, 2)[1].try(:chomp) + end + end + + # Returns a link to the commit author. If the author has a matching user and + # is a member of the current @project it will link to the team member page. + # Otherwise it will link to the author email as specified in the commit. + # + # options: + # avatar: true will prepend the avatar image + # size: size of the avatar image in px + def author_link(options = {}) + person_link(options.merge source: :author) + end + + # Just like #author_link but for the committer. + def committer_link(options = {}) + person_link(options.merge source: :committer) + end + + protected + + def no_commit_message + "--no commit message" + end + + # Private: Returns a link to a person. If the person has a matching user and + # is a member of the current @project it will link to the team member page. + # Otherwise it will link to the person email as specified in the commit. + # + # options: + # source: one of :author or :committer + # avatar: true will prepend the avatar image + # size: size of the avatar image in px + def person_link(options = {}) + source_name = send "#{options[:source]}_name".to_sym + source_email = send "#{options[:source]}_email".to_sym + text = if options[:avatar] + avatar = h.image_tag h.gravatar_icon(source_email, options[:size]), class: "avatar #{"s#{options[:size]}" if options[:size]}", width: options[:size] + %Q{#{avatar} #{source_name}} + else + source_name + end + team_member = @project.try(:team_member_by_name_or_email, source_name, source_email) + + if team_member.nil? + h.mail_to source_email, text.html_safe, class: "commit-#{options[:source]}-link" + else + h.link_to text, h.project_team_member_path(@project, team_member), class: "commit-#{options[:source]}-link" + end + end +end diff --git a/app/decorators/tree_decorator.rb b/app/decorators/tree_decorator.rb new file mode 100644 index 00000000..c12227af --- /dev/null +++ b/app/decorators/tree_decorator.rb @@ -0,0 +1,35 @@ +class TreeDecorator < ApplicationDecorator + decorates :tree + + def breadcrumbs(max_links = 2) + if path + part_path = "" + parts = path.split("\/") + + #parts = parts[0...-1] if is_blob? + + yield(h.link_to("..", "#")) if parts.count > max_links + + parts.each do |part| + part_path = File.join(part_path, part) unless part_path.empty? + part_path = part if part_path.empty? + + next unless parts.last(2).include?(part) if parts.count > max_links + yield(h.link_to(h.truncate(part, length: 40), h.project_tree_path(project, h.tree_join(ref, part_path)))) + end + end + end + + def up_dir? + path.present? + end + + def up_dir_path + file = File.join(path, "..") + h.project_tree_path(project, h.tree_join(ref, file)) + end + + def readme + @readme ||= contents.find { |c| c.is_a?(Grit::Blob) and c.name =~ /^readme/i } + end +end diff --git a/app/helpers/admin/teams/members_helper.rb b/app/helpers/admin/teams/members_helper.rb deleted file mode 100644 index 58b9f189..00000000 --- a/app/helpers/admin/teams/members_helper.rb +++ /dev/null @@ -1,5 +0,0 @@ -module Admin::Teams::MembersHelper - def member_since(team, member) - team.user_team_user_relationships.find_by_user_id(member).created_at - end -end diff --git a/app/helpers/admin/teams/projects_helper.rb b/app/helpers/admin/teams/projects_helper.rb deleted file mode 100644 index b97cc403..00000000 --- a/app/helpers/admin/teams/projects_helper.rb +++ /dev/null @@ -1,5 +0,0 @@ -module Admin::Teams::ProjectsHelper - def assigned_since(team, project) - team.user_team_project_relationships.find_by_project_id(project).created_at - end -end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index cb9cb1a3..cba34c96 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -1,5 +1,4 @@ require 'digest/md5' -require 'uri' module ApplicationHelper @@ -31,21 +30,27 @@ module ApplicationHelper args.any? { |v| v.to_s.downcase == action_name } end - def gravatar_icon(user_email = '', size = nil) - size = 40 if size.nil? || size <= 0 - - if !Gitlab.config.gravatar.enabled || user_email.blank? + def gravatar_icon(user_email = '', size = 40) + if Gitlab.config.disable_gravatar? || user_email.blank? 'no_avatar.png' else - gravatar_url = request.ssl? ? Gitlab.config.gravatar.ssl_url : Gitlab.config.gravatar.plain_url + gravatar_prefix = request.ssl? ? "https://secure" : "http://www" user_email.strip! - sprintf gravatar_url, hash: Digest::MD5.hexdigest(user_email.downcase), size: size + "#{gravatar_prefix}.gravatar.com/avatar/#{Digest::MD5.hexdigest(user_email.downcase)}?s=#{size}&d=mm" end end + def request_protocol + request.ssl? ? "https" : "http" + end + + def web_app_url + "#{request_protocol}://#{Gitlab.config.web_host}/" + end + def last_commit(project) if project.repo_exists? - time_ago_in_words(project.repository.commit.committed_date) + " ago" + time_ago_in_words(project.commit.committed_date) + " ago" else "Never" end @@ -54,11 +59,9 @@ module ApplicationHelper end def grouped_options_refs(destination = :tree) - repository = @project.repository - options = [ - ["Branch", repository.branch_names ], - [ "Tag", repository.tag_names ] + ["Branch", @project.branch_names ], + [ "Tag", @project.tag_names ] ] # If reference is commit id - @@ -72,9 +75,7 @@ module ApplicationHelper end def search_autocomplete_source - projects = current_user.authorized_projects.map { |p| { label: "project: #{simple_sanitize(p.name_with_namespace)}", url: project_path(p) } } - groups = current_user.authorized_groups.map { |group| { label: "group: #{simple_sanitize(group.name)}", url: group_path(group) } } - teams = current_user.authorized_teams.map { |team| { label: "team: #{simple_sanitize(team.name)}", url: team_path(team) } } + projects = current_user.projects.map{ |p| { label: p.name, url: project_path(p) } } default_nav = [ { label: "My Profile", url: profile_path }, @@ -84,33 +85,31 @@ module ApplicationHelper ] help_nav = [ - { label: "help: API Help", url: help_api_path }, - { label: "help: Markdown Help", url: help_markdown_path }, - { label: "help: Permissions Help", url: help_permissions_path }, - { label: "help: Public Access Help", url: help_public_access_path }, - { label: "help: Rake Tasks Help", url: help_raketasks_path }, - { label: "help: SSH Keys Help", url: help_ssh_path }, - { label: "help: System Hooks Help", url: help_system_hooks_path }, - { label: "help: Web Hooks Help", url: help_web_hooks_path }, - { label: "help: Workflow Help", url: help_workflow_path }, + { label: "Workflow Help", url: help_workflow_path }, + { label: "Permissions Help", url: help_permissions_path }, + { label: "Web Hooks Help", url: help_web_hooks_path }, + { label: "System Hooks Help", url: help_system_hooks_path }, + { label: "API Help", url: help_api_path }, + { label: "Markdown Help", url: help_markdown_path }, + { label: "SSH Keys Help", url: help_ssh_path }, ] project_nav = [] - if @project && @project.repository.exists? && @project.repository.root_ref + if @project && !@project.new_record? project_nav = [ - { label: "#{simple_sanitize(@project.name_with_namespace)} - Issues", url: project_issues_path(@project) }, - { label: "#{simple_sanitize(@project.name_with_namespace)} - Commits", url: project_commits_path(@project, @ref || @project.repository.root_ref) }, - { label: "#{simple_sanitize(@project.name_with_namespace)} - Merge Requests", url: project_merge_requests_path(@project) }, - { label: "#{simple_sanitize(@project.name_with_namespace)} - Milestones", url: project_milestones_path(@project) }, - { label: "#{simple_sanitize(@project.name_with_namespace)} - Snippets", url: project_snippets_path(@project) }, - { label: "#{simple_sanitize(@project.name_with_namespace)} - Team", url: project_team_index_path(@project) }, - { label: "#{simple_sanitize(@project.name_with_namespace)} - Tree", url: project_tree_path(@project, @ref || @project.repository.root_ref) }, - { label: "#{simple_sanitize(@project.name_with_namespace)} - Wall", url: project_wall_path(@project) }, - { label: "#{simple_sanitize(@project.name_with_namespace)} - Wiki", url: project_wikis_path(@project) }, + { label: "#{@project.name} Issues", url: project_issues_path(@project) }, + { label: "#{@project.name} Commits", url: project_commits_path(@project, @ref || @project.root_ref) }, + { label: "#{@project.name} Merge Requests", url: project_merge_requests_path(@project) }, + { label: "#{@project.name} Milestones", url: project_milestones_path(@project) }, + { label: "#{@project.name} Snippets", url: project_snippets_path(@project) }, + { label: "#{@project.name} Team", url: project_team_index_path(@project) }, + { label: "#{@project.name} Tree", url: project_tree_path(@project, @ref || @project.root_ref) }, + { label: "#{@project.name} Wall", url: wall_project_path(@project) }, + { label: "#{@project.name} Wiki", url: project_wikis_path(@project) }, ] end - [groups, teams, projects, default_nav, project_nav, help_nav].flatten.to_json + [projects, default_nav, project_nav, help_nav].flatten.to_json end def emoji_autocomplete_source @@ -127,22 +126,11 @@ module ApplicationHelper Gitlab::Theme.css_class_by_id(current_user.try(:theme_id)) end - def user_color_scheme_class - case current_user.color_scheme_id - when 1 then 'white' - when 2 then 'black' - when 3 then 'solarized-dark' - else - 'white' - end - end - def show_last_push_widget?(event) event && event.last_push_to_non_root? && !event.rm_ref? && event.project && - event.project.repository && event.project.merge_requests_enabled end @@ -164,21 +152,4 @@ module ApplicationHelper image_tag("authbuttons/#{file_name}", alt: "Sign in with #{provider.to_s.titleize}") end - - def simple_sanitize str - sanitize(str, tags: %w(a span)) - end - - def image_url(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 - - def users_select_tag(id, opts = {}) - css_class = "ajax-users-select" - css_class << " multiselect" if opts[:multiple] - hidden_field_tag(id, '', class: css_class) - end end diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb index 95ca294c..2349888e 100644 --- a/app/helpers/commits_helper.rb +++ b/app/helpers/commits_helper.rb @@ -1,20 +1,4 @@ module CommitsHelper - # Returns a link to the commit author. If the author has a matching user and - # is a member of the current @project it will link to the team member page. - # Otherwise it will link to the author email as specified in the commit. - # - # options: - # avatar: true will prepend the avatar image - # size: size of the avatar image in px - def commit_author_link(commit, options = {}) - commit_person_link(commit, options.merge(source: :author)) - end - - # Just like #author_link but for the committer. - def commit_committer_link(commit, options = {}) - commit_person_link(commit, options.merge(source: :committer)) - end - def identification_type(line) if line[0] == "+" "new" @@ -25,13 +9,11 @@ module CommitsHelper end end - def build_line_anchor(diff, line_new, line_old) - "#{hexdigest(diff.new_path)}_#{line_old}_#{line_new}" + def build_line_anchor(index, line_new, line_old) + "#{index}_#{line_old}_#{line_new}" end - def each_diff_line(diff, index) - diff_arr = diff.diff.lines.to_a - + def each_diff_line(diff_arr, index) line_old = 1 line_new = 1 type = nil @@ -57,7 +39,7 @@ module CommitsHelper next else type = identification_type(line) - line_code = build_line_anchor(diff, line_new, line_old) + line_code = build_line_anchor(index, line_new, line_old) yield(full_line, type, line_code, line_new, line_old) end @@ -73,104 +55,19 @@ module CommitsHelper end end - def each_diff_line_near(diff, index, expected_line_code) - max_number_of_lines = 16 - - prev_match_line = nil - prev_lines = [] - - each_diff_line(diff, index) do |full_line, type, line_code, line_new, line_old| - line = [full_line, type, line_code, line_new, line_old] - if line_code != expected_line_code - if type == "match" - prev_lines.clear - prev_match_line = line - else - prev_lines.push(line) - prev_lines.shift if prev_lines.length >= max_number_of_lines - end - else - yield(prev_match_line) if !prev_match_line.nil? - prev_lines.each { |ln| yield(ln) } - yield(line) - break - end - end - end - def image_diff_class(diff) if diff.deleted_file - "deleted" + "diff_removed" elsif diff.new_file - "added" + "diff_added" else nil end end def commit_to_html commit - escape_javascript(render 'commits/commit', commit: commit) - end - - def diff_line_content(line) - if line.blank? - "  " - else - line - end - end - - # Breadcrumb links for a Project and, if applicable, a tree path - def commits_breadcrumbs - return unless @project && @ref - - # Add the root project link and the arrow icon - crumbs = content_tag(:li) do - content_tag(:span, nil, class: 'arrow') + - link_to(@project.name, project_commits_path(@project, @ref)) - end - - if @path - parts = @path.split('/') - - parts.each_with_index do |part, i| - crumbs += content_tag(:span, '/', class: 'divider') - crumbs += content_tag(:li) do - # The text is just the individual part, but the link needs all the parts before it - link_to part, project_commits_path(@project, tree_join(@ref, parts[0..i].join('/'))) - end - end - end - - crumbs.html_safe - end - - protected - - # Private: Returns a link to a person. If the person has a matching user and - # is a member of the current @project it will link to the team member page. - # Otherwise it will link to the person email as specified in the commit. - # - # options: - # source: one of :author or :committer - # avatar: true will prepend the avatar image - # size: size of the avatar image in px - def commit_person_link(commit, options = {}) - source_name = commit.send "#{options[:source]}_name".to_sym - source_email = commit.send "#{options[:source]}_email".to_sym - text = if options[:avatar] - avatar = image_tag(gravatar_icon(source_email, options[:size]), class: "avatar #{"s#{options[:size]}" if options[:size]}", width: options[:size], alt: "") - %Q{#{avatar} #{source_name}} - else - source_name - end - - user = User.where('name like ? or email like ?', source_name, source_email).first - - if user.nil? - mail_to(source_email, text.html_safe, class: "commit-#{options[:source]}-link") - else - link_to(text.html_safe, user_path(user), class: "commit-#{options[:source]}-link") + if commit.model + escape_javascript(render 'commits/commit', commit: commit) end end end diff --git a/app/helpers/dashboard_helper.rb b/app/helpers/dashboard_helper.rb deleted file mode 100644 index e3be07c9..00000000 --- a/app/helpers/dashboard_helper.rb +++ /dev/null @@ -1,32 +0,0 @@ -module DashboardHelper - def dashboard_filter_path(entity, options={}) - exist_opts = { - status: params[:status], - project_id: params[:project_id], - } - - options = exist_opts.merge(options) - - case entity - when 'issue' then - issues_dashboard_path(options) - when 'merge_request' - merge_requests_dashboard_path(options) - end - end - - def entities_per_project project, entity - items = project.items_for(entity) - - items = case params[:status] - when 'closed' - items.closed - when 'all' - items - else - items.opened - end - - items.cared(current_user).count - end -end diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb index 38374e33..a2548a23 100644 --- a/app/helpers/events_helper.rb +++ b/app/helpers/events_helper.rb @@ -1,9 +1,10 @@ module EventsHelper def link_to_author(event) - author = event.author + project = event.project + tm = project.team_member_by_id(event.author_id) if project - if author - link_to author.name, user_path(author.username) + if tm + link_to event.author_name, project_team_member_path(project, tm) else event.author_name end @@ -19,8 +20,25 @@ module EventsHelper [event.action_name, target].join(" ") end + def event_image event + event_image_path = if event.push? + "event_push.png" + elsif event.merged? + "event_mr_merged.png" + end + + return nil unless event_image_path + + content_tag :div, class: 'event_icon' do + image_tag event_image_path + end + end + def event_filter_link key, tooltip key = key.to_s + + filter = @event_filter.options key + inactive = if @event_filter.active? key nil else @@ -28,18 +46,9 @@ module EventsHelper end content_tag :div, class: "filter_icon #{inactive}" do - link_to dashboard_path, class: 'has_tooltip event_filter_link', id: "#{key}_event_filter", 'data-original-title' => tooltip do - content_tag :i, nil, class: icon_for_event[key] + link_to dashboard_path(event_filter: filter), class: 'has_tooltip', 'data-original-title' => tooltip do + image_tag "event_filter_#{key}.png" end end end - - def icon_for_event - { - EventFilter.push => "icon-upload-alt", - EventFilter.merged => "icon-check", - EventFilter.comments => "icon-comments", - EventFilter.team => "icon-user", - } - end end diff --git a/app/helpers/gitlab_markdown_helper.rb b/app/helpers/gitlab_markdown_helper.rb index 375f8861..111982e9 100644 --- a/app/helpers/gitlab_markdown_helper.rb +++ b/app/helpers/gitlab_markdown_helper.rb @@ -13,13 +13,7 @@ module GitlabMarkdownHelper def link_to_gfm(body, url, html_options = {}) return "" if body.blank? - escaped_body = if body =~ /^\.*?}m) do |match| "#{match}#{link_to("", url, html_options)[0..-5]}" # "".length +1 @@ -49,12 +43,4 @@ module GitlabMarkdownHelper @markdown.render(text).html_safe end - - def render_wiki_content(wiki_page) - if wiki_page.format == :markdown - markdown(wiki_page.content) - else - wiki_page.formatted_content.html_safe - end - end end diff --git a/app/helpers/graph_helper.rb b/app/helpers/graph_helper.rb deleted file mode 100644 index ca7d823a..00000000 --- a/app/helpers/graph_helper.rb +++ /dev/null @@ -1,17 +0,0 @@ -module GraphHelper - def get_refs(commit) - refs = "" - refs += commit.refs.collect{|r|r.name}.join(" ") if commit.refs - - # append note count - notes = @project.notes.for_commit_id(commit.id) - refs += "[#{notes.count}]" if notes.any? - - refs - end - - def parents_zip_spaces(parents, parent_spaces) - ids = parents.map { |p| p.id } - ids.zip(parent_spaces) - end -end diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb deleted file mode 100644 index 283119bc..00000000 --- a/app/helpers/groups_helper.rb +++ /dev/null @@ -1,17 +0,0 @@ -module GroupsHelper - def group_filter_path(entity, options={}) - exist_opts = { - status: params[:status], - project_id: params[:project_id], - } - - options = exist_opts.merge(options) - - case entity - when 'issue' then - issues_group_path(@group, options) - when 'merge_request' - merge_requests_group_path(@group, options) - end - end -end diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index 70ebbdd3..99ea9ef2 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -4,9 +4,31 @@ module IssuesHelper project_issues_path project, params end + def link_to_issue_assignee(issue) + project = issue.project + + tm = project.team_member_by_id(issue.assignee_id) + if tm + link_to issue.assignee_name, project_team_member_path(project, tm), class: "author_link" + else + issue.assignee_name + end + end + + def link_to_issue_author(issue) + project = issue.project + + tm = project.team_member_by_id(issue.author_id) + if tm + link_to issue.author_name, project_team_member_path(project, tm), class: "author_link" + else + issue.author_name + end + end + def issue_css_classes issue classes = "issue" - classes << " closed" if issue.closed? + classes << " closed" if issue.closed classes << " today" if issue.today? classes end @@ -27,65 +49,7 @@ module IssuesHelper all: "all", closed: "closed", to_me: "assigned-to-me", - by_me: "created-by-me", open: "open" } end - - def labels_autocomplete_source - labels = @project.issues_labels.order('count DESC') - labels = labels.map{ |l| { label: l.name, value: l.name } } - labels.to_json - end - - 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 = Gitlab.config.issues_tracker[@project.issues_tracker]["project_url"] - url.gsub(':project_id', @project.id.to_s) - .gsub(':issues_tracker_id', @project.issues_tracker_id.to_s) - end - end - - def url_for_new_issue - return "" if @project.nil? - - if @project.used_default_issues_tracker? - url = new_project_issue_path project_id: @project - else - url = Gitlab.config.issues_tracker[@project.issues_tracker]["new_issue_url"] - url.gsub(':project_id', @project.id.to_s) - .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 = Gitlab.config.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 diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb index 155d03d1..b23c4a8f 100644 --- a/app/helpers/merge_requests_helper.rb +++ b/app/helpers/merge_requests_helper.rb @@ -1,10 +1,32 @@ module MergeRequestsHelper + def link_to_merge_request_assignee(merge_request) + project = merge_request.project + + tm = project.team_member_by_id(merge_request.assignee_id) + if tm + link_to merge_request.assignee_name, project_team_member_path(project, tm), class: "author_link" + else + merge_request.assignee_name + end + end + + def link_to_merge_request_author(merge_request) + project = merge_request.project + + tm = project.team_member_by_id(merge_request.author_id) + if tm + link_to merge_request.author_name, project_team_member_path(project, tm), class: "author_link" + else + merge_request.author_name + end + end + def new_mr_path_from_push_event(event) new_project_merge_request_path( event.project, merge_request: { source_branch: event.branch_name, - target_branch: event.project.repository.root_ref, + target_branch: event.project.root_ref, title: event.branch_name.titleize } ) @@ -12,12 +34,12 @@ module MergeRequestsHelper def mr_css_classes mr classes = "merge_request" - classes << " closed" if mr.closed? + classes << " closed" if mr.closed classes << " merged" if mr.merged? classes end - def ci_build_details_path merge_request - merge_request.project.gitlab_ci_service.build_page(merge_request.last_commit.sha) + def ci_status_path + @project.gitlab_ci_service.commit_badge_path(@merge_request.last_commit.sha) end end diff --git a/app/helpers/namespaces_helper.rb b/app/helpers/namespaces_helper.rb deleted file mode 100644 index a9a6c786..00000000 --- a/app/helpers/namespaces_helper.rb +++ /dev/null @@ -1,27 +0,0 @@ -module NamespacesHelper - def namespaces_options(selected = :current_user, scope = :default) - if current_user.admin - groups = Group.all - users = Namespace.root - else - groups = current_user.owned_groups.select {|n| n.type == 'Group'} - users = current_user.namespaces.reject {|n| n.type == 'Group'} - end - - - global_opts = ["Global", [['/', Namespace.global_id]] ] - group_opts = ["Groups", groups.sort_by(&:human_name).map {|g| [g.human_name, g.id]} ] - users_opts = [ "Users", users.sort_by(&:human_name).map {|u| [u.human_name, u.id]} ] - - options = [] - options << global_opts if current_user.admin - options << group_opts - options << users_opts - - if selected == :current_user && current_user.namespace - selected = current_user.namespace.id - end - - grouped_options_for_select(options, selected) - end -end diff --git a/app/helpers/notes_helper.rb b/app/helpers/notes_helper.rb index fbd0f01e..ffcc7acc 100644 --- a/app/helpers/notes_helper.rb +++ b/app/helpers/notes_helper.rb @@ -1,26 +1,4 @@ module NotesHelper - # Helps to distinguish e.g. commit notes in mr notes list - def note_for_main_target?(note) - (@target_type.camelize == note.noteable_type && !note.for_diff_line?) - end - - def note_target_fields - hidden_field_tag(:target_type, @target_type) + - hidden_field_tag(:target_id, @target_id) - end - - def link_to_commit_diff_line_note(note) - if note.for_commit_diff_line? - link_to "#{note.diff_file_name}:L#{note.diff_new_line}", project_commit_path(@project, note.noteable, anchor: note.line_code) - end - end - - def link_to_merge_request_diff_line_note(note) - if note.for_merge_request_diff_line? and note.diff - link_to "#{note.diff_file_name}:L#{note.diff_new_line}", diffs_project_merge_request_path(note.project, note.noteable_id, anchor: note.line_code) - end - end - def loading_more_notes? params[:loading_more].present? end @@ -28,4 +6,19 @@ module NotesHelper def loading_new_notes? params[:loading_new].present? end + + # Helps to distinguish e.g. commit notes in mr notes list + def note_for_main_target?(note) + !@mixed_targets || @main_target_type == note.noteable_type + end + + def link_to_commit_diff_line_note(note) + commit = note.noteable + diff_index, diff_old_line, diff_new_line = note.line_code.split('_') + + link_file = commit.diffs[diff_index.to_i].new_path + link_line = diff_new_line + + link_to "#{link_file}:L#{link_line}", project_commit_path(@project, commit, anchor: note.line_code) + end end diff --git a/app/helpers/notifications_helper.rb b/app/helpers/notifications_helper.rb deleted file mode 100644 index 7342393a..00000000 --- a/app/helpers/notifications_helper.rb +++ /dev/null @@ -1,2 +0,0 @@ -module NotificationsHelper -end diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index e6427ea8..7c302ef4 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -1,47 +1,18 @@ module ProjectsHelper - def remove_from_project_team_message(project, user) - "You are going to remove #{user.name} from #{project.name} project team. Are you sure?" + def grouper_project_members(project) + @project.users_projects.sort_by(&:project_access).reverse.group_by(&:project_access) + end + + def remove_from_team_message(project, member) + "You are going to remove #{member.user_name} from #{project.name}. Are you sure?" end def link_to_project project - link_to project do - title = content_tag(:strong, project.name) - - if project.namespace - namespace = content_tag(:span, "#{project.namespace.human_name} / ", class: 'tiny') - title = namespace + title - end - - title - end + link_to project.name, project end - def link_to_member(project, author, opts = {}) - default_opts = { avatar: true } - opts = default_opts.merge(opts) - - return "(deleted)" unless author - - author_html = "" - - # Build avatar image tag - author_html << image_tag(gravatar_icon(author.try(:email)), width: 16, class: "lil_av") if opts[:avatar] - - # Build name span tag - author_html << content_tag(:span, sanitize(author.name), class: 'author') - - author_html = author_html.html_safe - - link_to(author_html, user_path(author), class: "author_link").html_safe - end - - def project_title project - if project.group - content_tag :span do - link_to(simple_sanitize(project.group.name), group_path(project.group)) + " / " + project.name - end - else - project.name - end + def tm_path team_member + project_team_member_path(@project, team_member) end end + diff --git a/app/helpers/tab_helper.rb b/app/helpers/tab_helper.rb index d2be4b1a..740700c3 100644 --- a/app/helpers/tab_helper.rb +++ b/app/helpers/tab_helper.rb @@ -39,12 +39,7 @@ module TabHelper # Returns a list item element String def nav_link(options = {}, &block) if path = options.delete(:path) - if path.respond_to?(:each) - c = path.map { |p| p.split('#').first } - a = path.map { |p| p.split('#').last } - else - c, a, _ = path.split('#') - end + c, a, _ = path.split('#') else c = options.delete(:controller) a = options.delete(:action) @@ -73,9 +68,11 @@ module TabHelper end def project_tab_class - return "active" if current_page?(controller: "projects", action: :edit, id: @project) + [:show, :files, :edit, :update].each do |action| + return "active" if current_page?(controller: "projects", action: action, id: @project) + end - if ['services', 'hooks', 'deploy_keys', 'team_members'].include? controller.controller_name + if ['snippets', 'hooks', 'deploy_keys', 'team_members'].include? controller.controller_name "active" end end @@ -87,17 +84,4 @@ module TabHelper 'active' end end - - # Use nav_tab for save controller/action but different params - def nav_tab key, value, &block - o = {} - o[:class] = "" - o[:class] << " active" if params[key] == value - - if block_given? - content_tag(:li, capture(&block), o) - else - content_tag(:li, nil, o) - end - end end diff --git a/app/helpers/tree_helper.rb b/app/helpers/tree_helper.rb index 1cba9476..0f2b695e 100644 --- a/app/helpers/tree_helper.rb +++ b/app/helpers/tree_helper.rb @@ -13,15 +13,13 @@ module TreeHelper tree += render partial: 'tree/tree_item', collection: folders, locals: {type: 'folder'} if folders.present? files.each do |f| - 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? + 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 end tree.html_safe @@ -70,26 +68,28 @@ module TreeHelper end end - def tree_breadcrumbs(tree, max_links = 2) - if tree.path - part_path = "" - parts = tree.path.split("\/") + # Breadcrumb links for a Project and, if applicable, a tree path + def breadcrumbs + return unless @project && @ref - yield('..', nil) if parts.count > max_links + # Add the root project link and the arrow icon + crumbs = content_tag(:li) do + content_tag(:span, nil, class: 'arrow') + + link_to(@project.name, project_commits_path(@project, @ref)) + end - parts.each do |part| - part_path = File.join(part_path, part) unless part_path.empty? - part_path = part if part_path.empty? + if @path + parts = @path.split('/') - next unless parts.last(2).include?(part) if parts.count > max_links - yield(part, tree_join(tree.ref, part_path)) + parts.each_with_index do |part, i| + crumbs += content_tag(:span, '/', class: 'divider') + crumbs += content_tag(:li) do + # The text is just the individual part, but the link needs all the parts before it + link_to part, project_commits_path(@project, tree_join(@ref, parts[0..i].join('/'))) + end end end - end - def up_dir_path tree - file = File.join(tree.path, "..") - tree_join(tree.ref, file) + crumbs.html_safe end - end diff --git a/app/helpers/user_teams_helper.rb b/app/helpers/user_teams_helper.rb deleted file mode 100644 index 2055bb3c..00000000 --- a/app/helpers/user_teams_helper.rb +++ /dev/null @@ -1,26 +0,0 @@ -module UserTeamsHelper - def team_filter_path(entity, options={}) - exist_opts = { - status: params[:status], - project_id: params[:project_id], - } - - options = exist_opts.merge(options) - - case entity - when 'issue' then - issues_team_path(@team, options) - when 'merge_request' - merge_requests_team_path(@team, options) - end - end - - def grouped_user_team_members(team) - team.user_team_user_relationships.sort_by(&:permission).reverse.group_by(&:permission) - end - - def remove_from_user_team_message(team, member) - "You are going to remove #{member.name} from #{team.name}. Are you sure?" - end - -end diff --git a/app/mailers/emails/issues.rb b/app/mailers/emails/issues.rb deleted file mode 100644 index 79731b60..00000000 --- a/app/mailers/emails/issues.rb +++ /dev/null @@ -1,33 +0,0 @@ -module Emails - module Issues - def new_issue_email(recipient_id, issue_id) - @issue = Issue.find(issue_id) - @project = @issue.project - mail(to: recipient(recipient_id), subject: subject("new issue ##{@issue.id}", @issue.title)) - end - - def reassigned_issue_email(recipient_id, issue_id, previous_assignee_id) - @issue = Issue.find(issue_id) - @previous_assignee ||= User.find(previous_assignee_id) - @project = @issue.project - mail(to: recipient(recipient_id), subject: subject("changed issue ##{@issue.id}", @issue.title)) - end - - def closed_issue_email(recipient_id, issue_id, updated_by_user_id) - @issue = Issue.find issue_id - @project = @issue.project - @updated_by = User.find updated_by_user_id - mail(to: recipient(recipient_id), - subject: subject("Closed issue ##{@issue.id}", @issue.title)) - end - - def issue_status_changed_email(recipient_id, issue_id, status, updated_by_user_id) - @issue = Issue.find issue_id - @issue_status = status - @project = @issue.project - @updated_by = User.find updated_by_user_id - mail(to: recipient(recipient_id), - subject: subject("changed issue ##{@issue.id}", @issue.title)) - end - end -end diff --git a/app/mailers/emails/merge_requests.rb b/app/mailers/emails/merge_requests.rb deleted file mode 100644 index 806f1b01..00000000 --- a/app/mailers/emails/merge_requests.rb +++ /dev/null @@ -1,29 +0,0 @@ -module Emails - module MergeRequests - def new_merge_request_email(recipient_id, merge_request_id) - @merge_request = MergeRequest.find(merge_request_id) - @project = @merge_request.project - mail(to: recipient(recipient_id), subject: subject("new merge request !#{@merge_request.id}", @merge_request.title)) - end - - def reassigned_merge_request_email(recipient_id, merge_request_id, previous_assignee_id) - @merge_request = MergeRequest.find(merge_request_id) - @previous_assignee ||= User.find(previous_assignee_id) - @project = @merge_request.project - mail(to: recipient(recipient_id), subject: subject("changed merge request !#{@merge_request.id}", @merge_request.title)) - end - - def closed_merge_request_email(recipient_id, merge_request_id, updated_by_user_id) - @merge_request = MergeRequest.find(merge_request_id) - @project = @merge_request.project - @updated_by = User.find updated_by_user_id - mail(to: recipient(recipient_id), subject: subject("Closed merge request !#{@merge_request.id}", @merge_request.title)) - end - - def merged_merge_request_email(recipient_id, merge_request_id) - @merge_request = MergeRequest.find(merge_request_id) - @project = @merge_request.project - mail(to: recipient(recipient_id), subject: subject("Accepted merge request !#{@merge_request.id}", @merge_request.title)) - end - end -end diff --git a/app/mailers/emails/notes.rb b/app/mailers/emails/notes.rb deleted file mode 100644 index 769b6e0b..00000000 --- a/app/mailers/emails/notes.rb +++ /dev/null @@ -1,30 +0,0 @@ -module Emails - module Notes - def note_commit_email(recipient_id, note_id) - @note = Note.find(note_id) - @commit = @note.noteable - @project = @note.project - mail(to: recipient(recipient_id), subject: subject("note for commit #{@commit.short_id}", @commit.title)) - end - - def note_issue_email(recipient_id, note_id) - @note = Note.find(note_id) - @issue = @note.noteable - @project = @note.project - mail(to: recipient(recipient_id), subject: subject("note for issue ##{@issue.id}")) - end - - def note_merge_request_email(recipient_id, note_id) - @note = Note.find(note_id) - @merge_request = @note.noteable - @project = @note.project - mail(to: recipient(recipient_id), subject: subject("note for merge request !#{@merge_request.id}")) - end - - def note_wall_email(recipient_id, note_id) - @note = Note.find(note_id) - @project = @note.project - mail(to: recipient(recipient_id), subject: subject("note on wall")) - end - end -end diff --git a/app/mailers/emails/projects.rb b/app/mailers/emails/projects.rb deleted file mode 100644 index dcd894bb..00000000 --- a/app/mailers/emails/projects.rb +++ /dev/null @@ -1,18 +0,0 @@ -module Emails - module Projects - def project_access_granted_email(user_project_id) - @users_project = UsersProject.find user_project_id - @project = @users_project.project - mail(to: @users_project.user.email, - subject: subject("access to project was granted")) - end - - - def project_was_moved_email(user_project_id) - @users_project = UsersProject.find user_project_id - @project = @users_project.project - mail(to: @users_project.user.email, - subject: subject("project was moved")) - end - end -end diff --git a/app/mailers/notify.rb b/app/mailers/notify.rb index a5aa97ab..7f3862ad 100644 --- a/app/mailers/notify.rb +++ b/app/mailers/notify.rb @@ -1,35 +1,126 @@ class Notify < ActionMailer::Base - include Emails::Issues - include Emails::MergeRequests - include Emails::Notes - include Emails::Projects - + include Resque::Mailer add_template_helper ApplicationHelper add_template_helper GitlabMarkdownHelper - default_url_options[:host] = Gitlab.config.gitlab.host - default_url_options[:protocol] = Gitlab.config.gitlab.protocol - default_url_options[:port] = Gitlab.config.gitlab.port if Gitlab.config.gitlab_on_non_standard_port? - default_url_options[:script_name] = Gitlab.config.gitlab.relative_url_root + default_url_options[:host] = Gitlab.config.web_host + default_url_options[:protocol] = Gitlab.config.web_protocol + default_url_options[:port] = Gitlab.config.web_port if Gitlab.config.web_custom_port? - default from: Gitlab.config.gitlab.email_from + default from: Gitlab.config.email_from - # Just send email with 3 seconds delay - def self.delay - delay_for(2.seconds) + + + # + # Issue + # + + def new_issue_email(issue_id) + @issue = Issue.find(issue_id) + @project = @issue.project + mail(to: @issue.assignee_email, subject: subject("new issue ##{@issue.id}", @issue.title)) end + def reassigned_issue_email(recipient_id, issue_id, previous_assignee_id) + @issue = Issue.find(issue_id) + @previous_assignee ||= User.find(previous_assignee_id) + @project = @issue.project + mail(to: recipient(recipient_id), subject: subject("changed issue ##{@issue.id}", @issue.title)) + end + + def issue_status_changed_email(recipient_id, issue_id, status, updated_by_user_id) + @issue = Issue.find issue_id + @issue_status = status + @updated_by = User.find updated_by_user_id + mail(to: recipient(recipient_id), + subject: subject("changed issue ##{@issue.id}", @issue.title)) + end + + + + # + # Merge Request + # + + def new_merge_request_email(merge_request_id) + @merge_request = MergeRequest.find(merge_request_id) + @project = @merge_request.project + mail(to: @merge_request.assignee_email, subject: subject("new merge request !#{@merge_request.id}", @merge_request.title)) + end + + def reassigned_merge_request_email(recipient_id, merge_request_id, previous_assignee_id) + @merge_request = MergeRequest.find(merge_request_id) + @previous_assignee ||= User.find(previous_assignee_id) + @project = @merge_request.project + mail(to: recipient(recipient_id), subject: subject("changed merge request !#{@merge_request.id}", @merge_request.title)) + end + + + + # + # Note + # + + def note_commit_email(recipient_id, note_id) + @note = Note.find(note_id) + @commit = @note.noteable + @commit = CommitDecorator.decorate(@commit) + @project = @note.project + mail(to: recipient(recipient_id), subject: subject("note for commit #{@commit.short_id}", @commit.title)) + end + + def note_issue_email(recipient_id, note_id) + @note = Note.find(note_id) + @issue = @note.noteable + @project = @note.project + mail(to: recipient(recipient_id), subject: subject("note for issue ##{@issue.id}")) + end + + def note_merge_request_email(recipient_id, note_id) + @note = Note.find(note_id) + @merge_request = @note.noteable + @project = @note.project + mail(to: recipient(recipient_id), subject: subject("note for merge request !#{@merge_request.id}")) + end + + def note_wall_email(recipient_id, note_id) + @note = Note.find(note_id) + @project = @note.project + mail(to: recipient(recipient_id), subject: subject) + end + + def note_wiki_email(recipient_id, note_id) + @note = Note.find(note_id) + @wiki = @note.noteable + @project = @note.project + mail(to: recipient(recipient_id), subject: subject("note for wiki")) + end + + + + # + # Project + # + + def project_access_granted_email(user_project_id) + @users_project = UsersProject.find user_project_id + @project = @users_project.project + mail(to: @users_project.user.email, + subject: subject("access to project was granted")) + end + + + + # + # User + # + def new_user_email(user_id, password) @user = User.find(user_id) @password = password mail(to: @user.email, subject: subject("Account was created for you")) end - def new_ssh_key_email(key_id) - @key = Key.find(key_id) - @user = @key.user - mail(to: @user.email, subject: subject("SSH key was added to your account")) - end private @@ -57,15 +148,12 @@ class Notify < ActionMailer::Base # >> @project = Project.last # => # # >> subject('Lorem ipsum') - # => "GitLab | Ruby on Rails | Lorem ipsum " + # => "GitLab | Lorem ipsum | Ruby on Rails" # # # Accepts multiple arguments # >> subject('Lorem ipsum', 'Dolor sit amet') # => "GitLab | Lorem ipsum | Dolor sit amet" def subject(*extra) - subject = "GitLab" - subject << (@project ? " | #{@project.name_with_namespace}" : "") - subject << " | " + extra.join(' | ') if extra.present? - subject + "GitLab | " << extra.join(' | ') << (@project ? " | #{@project.name}" : "") end end diff --git a/app/models/ability.rb b/app/models/ability.rb index 5b49104d..c3a212f4 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -1,55 +1,20 @@ class Ability class << self - def allowed(user, subject) - return [] unless user.kind_of?(User) - + def allowed(object, subject) case subject.class.name - when "Project" then project_abilities(user, subject) - when "Issue" then issue_abilities(user, subject) - when "Note" then note_abilities(user, subject) - when "Snippet" then snippet_abilities(user, subject) - when "MergeRequest" then merge_request_abilities(user, subject) - when "Group", "Namespace" then group_abilities(user, subject) - when "UserTeam" then user_team_abilities(user, subject) + when "Project" then project_abilities(object, subject) + when "Issue" then issue_abilities(object, subject) + when "Note" then note_abilities(object, subject) + when "Snippet" then snippet_abilities(object, subject) + when "MergeRequest" then merge_request_abilities(object, subject) else [] - end.concat(global_abilities(user)) - end - - def global_abilities(user) - rules = [] - rules << :create_group if user.can_create_group - rules << :create_team if user.can_create_team - rules + end end def project_abilities(user, project) rules = [] - team = project.team - - # Rules based on role in project - if team.masters.include?(user) - rules << project_master_rules - - elsif team.developers.include?(user) - rules << project_dev_rules - - elsif team.reporters.include?(user) - rules << project_report_rules - - elsif team.guests.include?(user) - rules << project_guest_rules - end - - if project.owner == user || user.admin? - rules << project_admin_rules - end - - rules.flatten - end - - def project_guest_rules - [ + rules << [ :read_project, :read_wiki, :read_issue, @@ -61,80 +26,41 @@ class Ability :write_project, :write_issue, :write_note - ] - end + ] if project.guest_access_for?(user) - def project_report_rules - project_guest_rules + [ + rules << [ :download_code, - :write_snippet - ] - end - - def project_dev_rules - project_report_rules + [ :write_merge_request, + :write_snippet + ] if project.report_access_for?(user) + + rules << [ :write_wiki, :push_code - ] - end + ] if project.dev_access_for?(user) - def project_master_rules - project_dev_rules + [ - :push_code_to_protected_branches, + rules << [ + :push_code_to_protected_branches + ] if project.master_access_for?(user) + + rules << [ :modify_issue, :modify_snippet, :modify_merge_request, + :admin_project, :admin_issue, :admin_milestone, :admin_snippet, :admin_team_member, :admin_merge_request, :admin_note, - :admin_wiki, - :admin_project - ] - end - - def project_admin_rules - project_master_rules + [ - :change_namespace, - :change_public_mode, - :rename_project, - :remove_project - ] - end - - def group_abilities user, group - rules = [] - - # Only group owner and administrators can manage group - if group.owner == user || user.admin? - rules << [ - :manage_group, - :manage_namespace - ] - end + :accept_mr, + :admin_wiki + ] if project.master_access_for?(user) || project.owner == user rules.flatten end - def user_team_abilities user, team - rules = [] - - # Only group owner and administrators can manage team - if team.owner == user || team.admin?(user) || user.admin? - rules << [ :manage_user_team ] - end - - if team.owner == user || user.admin? - rules << [ :admin_user_team ] - end - - rules.flatten - end - - [:issue, :note, :snippet, :merge_request].each do |name| define_method "#{name}_abilities" do |user, subject| if subject.author == user diff --git a/app/models/commit.rb b/app/models/commit.rb index e3363350..5efb20ce 100644 --- a/app/models/commit.rb +++ b/app/models/commit.rb @@ -8,70 +8,146 @@ class Commit # DIFF_SAFE_SIZE = 100 - def self.decorate(commits) - commits.map { |c| self.new(c) } - end + attr_accessor :commit, :head, :refs - attr_accessor :raw + delegate :message, :authored_date, :committed_date, :parents, :sha, + :date, :committer, :author, :message, :diffs, :tree, :id, + :to_patch, to: :commit - def initialize(raw_commit) - raise "Nil as raw commit passed" unless raw_commit + class << self + def find_or_first(repo, commit_id = nil, root_ref) + commit = if commit_id + repo.commit(commit_id) + else + repo.commits(root_ref).first + end - @raw = raw_commit - end + Commit.new(commit) if commit + end - def id - @raw.id - end + def fresh_commits(repo, n = 10) + commits = repo.heads.map do |h| + repo.commits(h.name, n).map { |c| Commit.new(c, h) } + end.flatten.uniq { |c| c.id } - # Returns a string describing the commit for use in a link title - # - # Example - # - # "Commit: Alex Denisov - Project git clone panel" - def link_title - "Commit: #{author_name} - #{title}" - end + commits.sort! do |x, y| + y.committed_date <=> x.committed_date + end - # Returns the commits title. - # - # Usually, the commit title is the first line of the commit message. - # In case this first line is longer than 80 characters, it is cut off - # after 70 characters and ellipses (`&hellp;`) are appended. - def title - title = safe_message + commits[0...n] + end - return no_commit_message if title.blank? + def commits_with_refs(repo, n = 20) + commits = repo.branches.map { |ref| Commit.new(ref.commit, ref) } - title_end = title.index(/\n/) - if (!title_end && title.length > 80) || (title_end && title_end > 80) - title[0..69] << "…".html_safe - else - title.split(/\n/, 2).first + commits.sort! do |x, y| + y.committed_date <=> x.committed_date + end + + commits[0..n] + end + + def commits_since(repo, date) + commits = repo.heads.map do |h| + repo.log(h.name, nil, since: date).each { |c| Commit.new(c, h) } + end.flatten.uniq { |c| c.id } + + commits.sort! do |x, y| + y.committed_date <=> x.committed_date + end + + commits + end + + def commits(repo, ref, path = nil, limit = nil, offset = nil) + if path + repo.log(ref, path, max_count: limit, skip: offset) + elsif limit && offset + repo.commits(ref, limit, offset) + else + repo.commits(ref) + end.map{ |c| Commit.new(c) } + end + + def commits_between(repo, from, to) + repo.commits_between(from, to).map { |c| Commit.new(c) } + end + + def compare(project, from, to) + result = { + commits: [], + diffs: [], + commit: nil, + same: false + } + + return result unless from && to + + first = project.commit(to.try(:strip)) + last = project.commit(from.try(:strip)) + + if first && last + commits = [first, last].sort_by(&:created_at) + younger = commits.first + older = commits.last + + result[:same] = (younger.id == older.id) + result[:commits] = project.repo.commits_between(younger.id, older.id).map {|c| Commit.new(c)} + result[:diffs] = project.repo.diff(younger.id, older.id) rescue [] + result[:commit] = Commit.new(older) + end + + result end end - # Returns the commits description - # - # cut off, ellipses (`&hellp;`) are prepended to the commit message. - def description - description = safe_message - - title_end = description.index(/\n/) - if (!title_end && description.length > 80) || (title_end && title_end > 80) - "…".html_safe << description[70..-1] - else - description.split(/\n/, 2)[1].try(:chomp) - end + def initialize(raw_commit, head = nil) + @commit = raw_commit + @head = head end - def method_missing(m, *args, &block) - @raw.send(m, *args, &block) + def short_id(length = 10) + id.to_s[0..length] end - def respond_to?(method) - return true if @raw.respond_to?(method) + def safe_message + @safe_message ||= message + end - super + def created_at + committed_date + end + + def author_email + author.email + end + + def author_name + author.name + end + + # Was this commit committed by a different person than the original author? + def different_committer? + author_name != committer_name || author_email != committer_email + end + + def committer_name + committer.name + end + + def committer_email + committer.email + end + + def prev_commit + parents.try :first + end + + def prev_commit_id + prev_commit.try :id + end + + def parents_count + parents && parents.count || 0 end end diff --git a/app/models/event.rb b/app/models/event.rb index ae14454c..2b92783c 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -15,20 +15,22 @@ # class Event < ActiveRecord::Base + include PushEvent + attr_accessible :project, :action, :data, :author_id, :project_id, :target_id, :target_type default_scope where("author_id IS NOT NULL") - CREATED = 1 - UPDATED = 2 - CLOSED = 3 - REOPENED = 4 - PUSHED = 5 - COMMENTED = 6 - MERGED = 7 - JOINED = 8 # User joined project - LEFT = 9 # User left project + Created = 1 + Updated = 2 + Closed = 3 + Reopened = 4 + Pushed = 5 + Commented = 6 + Merged = 7 + Joined = 8 # User joined project + Left = 9 # User left project delegate :name, :email, to: :author, prefix: true, allow_nil: true delegate :title, to: :issue, prefix: true, allow_nil: true @@ -42,28 +44,26 @@ class Event < ActiveRecord::Base serialize :data # Scopes - scope :recent, -> { order("created_at DESC") } - scope :code_push, -> { where(action: PUSHED) } + scope :recent, order("created_at DESC") + scope :code_push, where(action: Pushed) scope :in_projects, ->(project_ids) { where(project_id: project_ids).recent } class << self def determine_action(record) if [Issue, MergeRequest].include? record.class - Event::CREATED + Event::Created elsif record.kind_of? Note - Event::COMMENTED + Event::Commented end end end - def proper? - if push? - true - elsif membership_changed? - true - else - (issue? || merge_request? || note? || milestone?) && target - end + # Next events currently enabled for system + # - push + # - new issue + # - merge request + def allowed? + push? || issue? || merge_request? || membership_changed? end def project_name @@ -79,27 +79,19 @@ class Event < ActiveRecord::Base end def push? - action == self.class::PUSHED && valid_push? + action == self.class::Pushed && valid_push? end def merged? - action == self.class::MERGED + action == self.class::Merged end def closed? - action == self.class::CLOSED + action == self.class::Closed end def reopened? - action == self.class::REOPENED - end - - def milestone? - target_type == "Milestone" - end - - def note? - target_type == "Note" + action == self.class::Reopened end def issue? @@ -110,12 +102,32 @@ class Event < ActiveRecord::Base target_type == "MergeRequest" end + def new_issue? + target_type == "Issue" && + action == Created + end + + def new_merge_request? + target_type == "MergeRequest" && + action == Created + end + + def changed_merge_request? + target_type == "MergeRequest" && + [Closed, Reopened].include?(action) + end + + def changed_issue? + target_type == "Issue" && + [Closed, Reopened].include?(action) + end + def joined? - action == JOINED + action == Joined end def left? - action == LEFT + action == Left end def membership_changed? @@ -130,6 +142,10 @@ class Event < ActiveRecord::Base target if target_type == "MergeRequest" end + def author + @author ||= User.find(author_id) + end + def action_name if closed? "closed" @@ -143,143 +159,4 @@ class Event < ActiveRecord::Base "opened" end end - - def valid_push? - data[:ref] - rescue => ex - false - end - - def tag? - data[:ref]["refs/tags"] - end - - def branch? - data[:ref]["refs/heads"] - end - - def new_branch? - commit_from =~ /^00000/ - end - - def new_ref? - commit_from =~ /^00000/ - end - - def rm_ref? - commit_to =~ /^00000/ - end - - def md_ref? - !(rm_ref? || new_ref?) - end - - def commit_from - data[:before] - end - - def commit_to - data[:after] - end - - def ref_name - if tag? - tag_name - else - branch_name - end - end - - def branch_name - @branch_name ||= data[:ref].gsub("refs/heads/", "") - end - - def tag_name - @tag_name ||= data[:ref].gsub("refs/tags/", "") - end - - # Max 20 commits from push DESC - def commits - @commits ||= data[:commits].map { |commit| repository.commit(commit[:id]) }.reverse - end - - def commits_count - data[:total_commits_count] || commits.count || 0 - end - - def ref_type - tag? ? "tag" : "branch" - end - - def push_action_name - if new_ref? - "pushed new" - elsif rm_ref? - "deleted" - else - "pushed to" - end - end - - def repository - project.repository - end - - def parent_commit - repository.commit(commit_from) - rescue => ex - nil - end - - def last_commit - repository.commit(commit_to) - rescue => ex - nil - end - - def push_with_commits? - md_ref? && commits.any? && parent_commit && last_commit - rescue Grit::NoSuchPathError - false - end - - def last_push_to_non_root? - branch? && project.default_branch != branch_name - end - - def note_commit_id - target.commit_id - end - - def note_short_commit_id - note_commit_id[0..8] - end - - def note_commit? - target.noteable_type == "Commit" - end - - def note_target - target.noteable - end - - def note_target_id - if note_commit? - target.commit_id - else - target.noteable_id.to_s - end - end - - def wall_note? - target.noteable_type.blank? - end - - def note_target_type - if target.noteable_type.present? - target.noteable_type.titleize - else - "Wall" - end.downcase - end end diff --git a/app/models/gitlab_ci_service.rb b/app/models/gitlab_ci_service.rb index 4eb39c7e..24b70323 100644 --- a/app/models/gitlab_ci_service.rb +++ b/app/models/gitlab_ci_service.rb @@ -23,27 +23,17 @@ class GitlabCiService < Service after_save :compose_service_hook, if: :activated? + def activated? + active + end + def compose_service_hook hook = service_hook || build_service_hook hook.url = [project_url, "/build", "?token=#{token}"].join("") hook.save end - def commit_status_path sha - project_url + "/builds/#{sha}/status.json?token=#{token}" - end - - def commit_status sha - response = HTTParty.get(commit_status_path(sha)) - - if response.code == 200 and response["status"] - response["status"] - else - :error - end - end - - def build_page sha - project_url + "/builds/#{sha}" + def commit_badge_path sha + project_url + "/status?sha=#{sha}" end end diff --git a/app/models/gollum_wiki.rb b/app/models/gollum_wiki.rb deleted file mode 100644 index 16e801c1..00000000 --- a/app/models/gollum_wiki.rb +++ /dev/null @@ -1,121 +0,0 @@ -class GollumWiki - - MARKUPS = { - "Markdown" => :markdown, - "RDoc" => :rdoc - } - - class CouldNotCreateWikiError < StandardError; end - - # Returns a string describing what went wrong after - # an operation fails. - attr_reader :error_message - - def initialize(project, user = nil) - @project = project - @user = user - end - - def path_with_namespace - @project.path_with_namespace + ".wiki" - end - - def url_to_repo - gitlab_shell.url_to_repo(path_with_namespace) - end - - def ssh_url_to_repo - url_to_repo - end - - def http_url_to_repo - http_url = [Gitlab.config.gitlab.url, "/", path_with_namespace, ".git"].join('') - end - - # Returns the Gollum::Wiki object. - def wiki - @wiki ||= begin - Gollum::Wiki.new(path_to_repo) - rescue Grit::NoSuchPathError - create_repo! - end - end - - # Returns an Array of Gitlab WikiPage instances or an - # empty Array if this Wiki has no pages. - def pages - wiki.pages.map { |page| WikiPage.new(self, page, true) } - end - - # Returns the last 30 Commit objects across the entire - # repository. - def recent_history - Gitlab::Git::Commit.fresh_commits(wiki.repo, 30) - end - - # Finds a page within the repository based on a tile - # or slug. - # - # title - The human readable or parameterized title of - # the page. - # - # Returns an initialized WikiPage instance or nil - def find_page(title, version = nil) - if page = wiki.page(title, version) - WikiPage.new(self, page, true) - else - nil - end - end - - def create_page(title, content, format = :markdown, message = nil) - commit = commit_details(:created, message, title) - - wiki.write_page(title, format, content, commit) - rescue Gollum::DuplicatePageError => e - @error_message = "Duplicate page: #{e.message}" - return false - end - - def update_page(page, content, format = :markdown, message = nil) - commit = commit_details(:updated, message, page.title) - - wiki.update_page(page, page.name, format, content, commit) - end - - def delete_page(page, message = nil) - wiki.delete_page(page, commit_details(:deleted, message, page.title)) - end - - private - - def create_repo! - if init_repo(path_with_namespace) - Gollum::Wiki.new(path_to_repo) - else - raise CouldNotCreateWikiError - end - end - - def init_repo(path_with_namespace) - gitlab_shell.add_repository(path_with_namespace) - end - - def commit_details(action, message = nil, title = nil) - commit_message = message || default_message(action, title) - - {email: @user.email, name: @user.name, message: commit_message} - end - - def default_message(action, title) - "#{@user.username} #{action} page: #{title}" - end - - def gitlab_shell - @gitlab_shell ||= Gitlab::Shell.new - end - - def path_to_repo - @path_to_repo ||= File.join(Gitlab.config.gitlab_shell.repos_path, "#{path_with_namespace}.git") - end -end diff --git a/app/models/group.rb b/app/models/group.rb index 5d838d2b..1ff6872f 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -1,37 +1,36 @@ # == Schema Information # -# Table name: namespaces +# Table name: groups # -# 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) -# description :string(255) default(""), not null +# id :integer not null, primary key +# name :string(255) not null +# code :string(255) not null +# owner_id :integer not null +# created_at :datetime not null +# updated_at :datetime not null # -class Group < Namespace - def add_users_to_project_teams(user_ids, project_access) - UsersProject.add_users_into_projects( - projects.map(&:id), - user_ids, - project_access - ) +class Group < ActiveRecord::Base + attr_accessible :code, :name, :owner_id + + has_many :projects + belongs_to :owner, class_name: "User" + + validates :name, presence: true, uniqueness: true + validates :code, presence: true, uniqueness: true + validates :owner, presence: true + + delegate :name, to: :owner, allow_nil: true, prefix: true + + def self.search query + where("name LIKE :query OR code LIKE :query", query: "%#{query}%") + end + + def to_param + code end def users - users = User.joins(:users_projects).where(users_projects: {project_id: project_ids}) - users = users << owner - users.uniq - end - - def human_name - name - end - - def truncate_teams - UsersProject.truncate_teams(project_ids) + User.joins(:users_projects).where(users_projects: {project_id: project_ids}).uniq end end diff --git a/app/models/issue.rb b/app/models/issue.rb index 54d9af7e..1de9d0f9 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -9,49 +9,25 @@ # project_id :integer # created_at :datetime not null # updated_at :datetime not null +# closed :boolean default(FALSE), not null # position :integer default(0) # branch_name :string(255) # description :text # milestone_id :integer -# state :string(255) # class Issue < ActiveRecord::Base - include Issuable + include IssueCommonality + include Votes - attr_accessible :title, :assignee_id, :position, :description, - :milestone_id, :label_list, :author_id_of_changes, - :state_event + attr_accessible :title, :assignee_id, :closed, :position, :description, + :milestone_id, :label_list, :author_id_of_changes acts_as_taggable_on :labels - class << self - def cared(user) - where('assignee_id = :user', user: user.id) - end + validates :description, length: { within: 0..2000 } - def authored(user) - where('author_id = :user', user: user.id) - end - - def open_for(user) - opened.assigned(user) - end - end - - state_machine :state, initial: :opened do - event :close do - transition [:reopened, :opened] => :closed - end - - event :reopen do - transition closed: :reopened - end - - state :opened - - state :reopened - - state :closed + def self.open_for(user) + opened.assigned(user) end end diff --git a/app/models/key.rb b/app/models/key.rb index 53eee511..5dac1c1c 100644 --- a/app/models/key.rb +++ b/app/models/key.rb @@ -21,10 +21,11 @@ class Key < ActiveRecord::Base attr_accessible :key, :title before_validation :strip_white_space + before_save :set_identifier validates :title, presence: true, length: { within: 0..255 } - validates :key, presence: true, length: { within: 0..5000 }, format: { :with => /ssh-.{3} / }, uniqueness: true - validate :fingerprintable_key + validates :key, presence: true, length: { within: 0..5000 }, format: { :with => /ssh-.{3} / } + validate :unique_key, :fingerprintable_key delegate :name, :email, to: :user, prefix: true @@ -32,9 +33,17 @@ class Key < ActiveRecord::Base self.key = self.key.strip unless self.key.blank? end + def unique_key + query = Key.where(key: key) + query = query.where('(project_id IS NULL OR project_id = ?)', project_id) if project_id + if (query.count > 0) + errors.add :key, 'already exist.' + end + end + def fingerprintable_key return true unless key # Don't test if there is no key. - + # `ssh-keygen -lf /dev/stdin <<< "#{key}"` errors with: redirection unexpected file = Tempfile.new('key_file') begin file.puts key @@ -44,11 +53,19 @@ class Key < ActiveRecord::Base file.close file.unlink # deletes the temp file end - errors.add(:key, "can't be fingerprinted") if $?.exitstatus != 0 + errors.add(:key, "can't be fingerprinted") if fingerprint_output.match("failed") + end + + def set_identifier + if is_deploy_key + self.identifier = "deploy_#{Digest::MD5.hexdigest(key)}" + else + self.identifier = "#{user.identifier}_#{Time.now.to_i}" + end end def is_deploy_key - !!project_id + true if project_id end # projects that has this key @@ -56,11 +73,11 @@ class Key < ActiveRecord::Base if is_deploy_key [project] else - user.authorized_projects + user.projects end end - def shell_id - "key-#{self.id}" + def last_deploy? + Key.where(identifier: identifier).count == 0 end end diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 6ce94417..0766e5ba 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -9,89 +9,61 @@ # author_id :integer # assignee_id :integer # title :string(255) +# closed :boolean default(FALSE), not null # created_at :datetime not null # updated_at :datetime not null # st_commits :text(2147483647) # st_diffs :text(2147483647) +# merged :boolean default(FALSE), not null +# state :integer default(1), not null # milestone_id :integer -# state :string(255) -# merge_status :string(255) # require Rails.root.join("app/models/commit") -require Rails.root.join("lib/static_model") +require Rails.root.join("app/roles/static_model") class MergeRequest < ActiveRecord::Base - include Issuable + include IssueCommonality + include Votes - BROKEN_DIFF = "--broken-diff" - - attr_accessible :title, :assignee_id, :target_branch, :source_branch, :milestone_id, - :author_id_of_changes, :state_event + attr_accessible :title, :assignee_id, :closed, :target_branch, :source_branch, :milestone_id, + :author_id_of_changes attr_accessor :should_remove_source_branch - state_machine :state, initial: :opened do - event :close do - transition [:reopened, :opened] => :closed - end + BROKEN_DIFF = "--broken-diff" - event :merge do - transition [:reopened, :opened] => :merged - end - - event :reopen do - transition closed: :reopened - end - - state :opened - - state :reopened - - state :closed - - state :merged - end - - state_machine :merge_status, initial: :unchecked do - event :mark_as_unchecked do - transition [:can_be_merged, :cannot_be_merged] => :unchecked - end - - event :mark_as_mergeable do - transition unchecked: :can_be_merged - end - - event :mark_as_unmergeable do - transition unchecked: :cannot_be_merged - end - - state :unchecked - - state :can_be_merged - - state :cannot_be_merged - end + UNCHECKED = 1 + CAN_BE_MERGED = 2 + CANNOT_BE_MERGED = 3 serialize :st_commits serialize :st_diffs validates :source_branch, presence: true validates :target_branch, presence: true - validate :validate_branches + validate :validate_branches - scope :merged, -> { with_state(:merged) } - scope :by_branch, ->(branch_name) { where("source_branch LIKE :branch OR target_branch LIKE :branch", branch: branch_name) } - scope :cared, ->(user) { where('assignee_id = :user OR author_id = :user', user: user.id) } - scope :by_milestone, ->(milestone) { where(milestone_id: milestone) } + def self.find_all_by_branch(branch_name) + where("source_branch LIKE :branch OR target_branch LIKE :branch", branch: branch_name) + end - # Closed scope for merge request should return - # both merged and closed mr's - scope :closed, -> { with_states(:closed, :merged) } + def self.find_all_by_milestone(milestone) + where("milestone_id = :milestone_id", milestone_id: milestone) + end + + def human_state + states = { + CAN_BE_MERGED => "can_be_merged", + CANNOT_BE_MERGED => "cannot_be_merged", + UNCHECKED => "unchecked" + } + states[self.state] + end def validate_branches if target_branch == source_branch - errors.add :branch_conflict, "You can not use same branch for source and target branches" + errors.add :base, "You can not use same branch for source and target branches" end end @@ -100,12 +72,26 @@ class MergeRequest < ActiveRecord::Base self.reloaded_diffs end + def unchecked? + state == UNCHECKED + end + + def mark_as_unchecked + self.state = UNCHECKED + self.save + end + + def can_be_merged? + state == CAN_BE_MERGED + end + def check_if_can_be_merged - if Gitlab::Satellite::MergeAction.new(self.author, self).can_be_merged? - mark_as_mergeable - else - mark_as_unmergeable - end + self.state = if Gitlab::Satellite::MergeAction.new(self.author, self).can_be_merged? + CAN_BE_MERGED + else + CANNOT_BE_MERGED + end + self.save end def diffs @@ -113,7 +99,7 @@ class MergeRequest < ActiveRecord::Base end def reloaded_diffs - if opened? && unmerged_diffs.any? + if open? && unmerged_diffs.any? self.st_diffs = unmerged_diffs self.save end @@ -143,41 +129,44 @@ class MergeRequest < ActiveRecord::Base commits.first end + def merged? + merged && merge_event + end + def merge_event - self.project.events.where(target_id: self.id, target_type: "MergeRequest", action: Event::MERGED).last + self.project.events.where(target_id: self.id, target_type: "MergeRequest", action: Event::Merged).last end def closed_event - self.project.events.where(target_id: self.id, target_type: "MergeRequest", action: Event::CLOSED).last + self.project.events.where(target_id: self.id, target_type: "MergeRequest", action: Event::Closed).last end def commits - if st_commits.present? - # check if merge request commits are valid - if st_commits.first.respond_to?(:short_id) - st_commits - else - # if commits are invalid - simply reload it from repo - reloaded_commits - end - else - [] - end + st_commits || [] end def probably_merged? unmerged_commits.empty? && - commits.any? && opened? + commits.any? && open? + end + + def open? + !closed + end + + def mark_as_merged! + self.merged = true + self.closed = true + save + end + + def mark_as_unmergable + self.state = CANNOT_BE_MERGED + self.save end def reloaded_commits - if opened? && unmerged_commits.any? - # we need to reset st_commits field first - # in order to prevent internal rails comparison - self.st_commits = [] - save - - # Then we can safely write unmerged commits + if open? && unmerged_commits.any? self.st_commits = unmerged_commits save end @@ -185,15 +174,22 @@ class MergeRequest < ActiveRecord::Base end def unmerged_commits - self.project.repository. + self.project.repo. commits_between(self.target_branch, self.source_branch). + map {|c| Commit.new(c)}. sort_by(&:created_at). reverse end def merge!(user_id) - self.author_id_of_changes = user_id - self.merge + self.mark_as_merged! + Event.create( + project: self.project, + action: Event::Merged, + target_id: self.id, + target_type: "MergeRequest", + author_id: user_id + ) end def automerge!(current_user) @@ -202,30 +198,24 @@ class MergeRequest < ActiveRecord::Base true end rescue - mark_as_unmergeable + self.mark_as_unmergable false end + def to_raw + FileUtils.mkdir_p(Rails.root.join("tmp", "patches")) + patch_path = Rails.root.join("tmp", "patches", "merge_request_#{self.id}.patch") + + from = commits.last.id + to = source_branch + + project.repo.git.run('', "format-patch" , " > #{patch_path.to_s}", {}, ["#{from}..#{to}", "--stdout"]) + + patch_path + end + def mr_and_commit_notes commit_ids = commits.map(&:id) - Note.where("(noteable_type = 'MergeRequest' AND noteable_id = :mr_id) OR (noteable_type = 'Commit' AND commit_id IN (:commit_ids))", mr_id: id, commit_ids: commit_ids) - end - - # Returns the raw diff for this merge request - # - # see "git diff" - def to_diff - project.repo.git.native(:diff, {timeout: 30, raise: true}, "#{target_branch}...#{source_branch}") - end - - # Returns the commit as a series of email patches. - # - # see "git format-patch" - def to_patch - project.repo.git.format_patch({timeout: 30, raise: true, stdout: true}, "#{target_branch}..#{source_branch}") - end - - def last_commit_short_sha - @last_commit_short_sha ||= last_commit.sha[0..10] + Note.where("(noteable_type = 'MergeRequest' AND noteable_id = :mr_id) OR (noteable_type = 'Commit' AND noteable_id IN (:commit_ids))", mr_id: id, commit_ids: commit_ids) end end diff --git a/app/models/milestone.rb b/app/models/milestone.rb index 2a9b9e44..a50831a2 100644 --- a/app/models/milestone.rb +++ b/app/models/milestone.rb @@ -7,45 +7,24 @@ # project_id :integer not null # description :text # due_date :date +# closed :boolean default(FALSE), not null # created_at :datetime not null # updated_at :datetime not null -# state :string(255) # class Milestone < ActiveRecord::Base - attr_accessible :title, :description, :due_date, :state_event, :author_id_of_changes - attr_accessor :author_id_of_changes + attr_accessible :title, :description, :due_date, :closed belongs_to :project has_many :issues has_many :merge_requests - scope :active, -> { with_state(:active) } - scope :closed, -> { with_state(:closed) } - validates :title, presence: true validates :project, presence: true + validates :closed, inclusion: { in: [true, false] } - state_machine :state, initial: :active do - event :close do - transition active: :closed - end - - event :activate do - transition closed: :active - end - - state :closed - - state :active - end - - def expired? - if due_date - due_date.past? - else - false - end + def self.active + where("due_date > ? OR due_date IS NULL", Date.today) end def participants @@ -71,24 +50,6 @@ class Milestone < ActiveRecord::Base end def expires_at - if due_date - if due_date.past? - "expired at #{due_date.stamp("Aug 21, 2011")}" - else - "expires at #{due_date.stamp("Aug 21, 2011")}" - end - end - end - - def can_be_closed? - active? && issues.opened.count.zero? - end - - def is_empty? - total_items_count.zero? - end - - def author_id - author_id_of_changes + "expires at #{due_date.stamp("Aug 21, 2011")}" if due_date end end diff --git a/app/models/namespace.rb b/app/models/namespace.rb deleted file mode 100644 index cb7164ea..00000000 --- a/app/models/namespace.rb +++ /dev/null @@ -1,89 +0,0 @@ -# == Schema Information -# -# 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) -# description :string(255) default(""), not null -# - -class Namespace < ActiveRecord::Base - include Gitlab::ShellAdapter - - attr_accessible :name, :description, :path - - has_many :projects, dependent: :destroy - belongs_to :owner, class_name: "User" - - validates :owner, presence: true - validates :name, presence: true, uniqueness: true, - 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" } - - delegate :name, to: :owner, allow_nil: true, prefix: true - - after_create :ensure_dir_exist - after_update :move_dir, if: :path_changed? - after_destroy :rm_dir - - scope :root, -> { where('type IS NULL') } - - def self.search query - where("name LIKE :query OR path LIKE :query", query: "%#{query}%") - end - - def self.global_id - 'GLN' - end - - def to_param - path - end - - def human_name - owner_name - end - - def ensure_dir_exist - gitlab_shell.add_namespace(path) - end - - def rm_dir - gitlab_shell.rm_namespace(path) - end - - def move_dir - if gitlab_shell.mv_namespace(path_was, path) - # If repositories moved successfully we need to remove old satellites - # and send update instructions to users. - # However we cannot allow rollback since we moved namespace dir - # So we basically we mute exceptions in next actions - begin - gitlab_shell.rm_satellites(path_was) - send_update_instructions - rescue - # Returning false does not rolback after_* transaction but gives - # us information about failing some of tasks - false - end - else - # if we cannot move namespace directory we should rollback - # db changes in order to prevent out of sync between db and fs - raise Exception.new('namespace directory cannot be moved') - end - end - - def send_update_instructions - projects.each(&:send_move_instructions) - end -end diff --git a/app/models/network/commit.rb b/app/models/network/commit.rb deleted file mode 100644 index 3cd0c015..00000000 --- a/app/models/network/commit.rb +++ /dev/null @@ -1,39 +0,0 @@ -require "grit" - -module Network - class Commit - include ActionView::Helpers::TagHelper - - attr_reader :refs - attr_accessor :time, :spaces, :parent_spaces - - def initialize(raw_commit, refs) - @commit = Gitlab::Git::Commit.new(raw_commit) - @time = -1 - @spaces = [] - @parent_spaces = [] - @refs = refs || [] - end - - def method_missing(m, *args, &block) - @commit.send(m, *args, &block) - end - - def space - if @spaces.size > 0 - @spaces.first - else - 0 - end - end - - def parents(map) - @commit.parents.map do |p| - if map.include?(p.id) - map[p.id] - end - end - .compact - end - end -end diff --git a/app/models/network/graph.rb b/app/models/network/graph.rb deleted file mode 100644 index 2957adbf..00000000 --- a/app/models/network/graph.rb +++ /dev/null @@ -1,286 +0,0 @@ -require "grit" - -module Network - class Graph - attr_reader :days, :commits, :map - - def self.max_count - @max_count ||= 650 - end - - def initialize project, ref, commit - @project = project - @ref = ref - @commit = commit - @repo = project.repo - - @commits = collect_commits - @days = index_commits - end - - protected - - # Get commits from repository - # - def collect_commits - refs_cache = build_refs_cache - - find_commits(count_to_display_commit_in_center) - .map do |commit| - # Decorate with app/model/network/commit.rb - Network::Commit.new(commit, refs_cache[commit.id]) - end - end - - # Method is adding time and space on the - # list of commits. As well as returns date list - # corelated with time set on commits. - # - # @return [Array] list of commit dates corelated with time on commits - def index_commits - days = [] - @map = {} - @reserved = {} - - @commits.each_with_index do |c,i| - c.time = i - days[i] = c.committed_date - @map[c.id] = c - @reserved[i] = [] - end - - commits_sort_by_ref.each do |commit| - place_chain(commit) - end - - # find parent spaces for not overlap lines - @commits.each do |c| - c.parent_spaces.concat(find_free_parent_spaces(c)) - end - - days - end - - # Skip count that the target commit is displayed in center. - def count_to_display_commit_in_center - offset = -1 - skip = 0 - while offset == -1 - tmp_commits = find_commits(skip) - if tmp_commits.size > 0 - index = tmp_commits.index do |c| - c.id == @commit.id - end - - if index - # Find the target commit - offset = index + skip - else - skip += self.class.max_count - end - else - # Cant't find the target commit in the repo. - offset = 0 - end - end - - if self.class.max_count / 2 < offset then - # get max index that commit is displayed in the center. - offset - self.class.max_count / 2 - else - 0 - end - end - - def find_commits(skip = 0) - Grit::Commit.find_all( - @repo, - nil, - { - date_order: true, - max_count: self.class.max_count, - skip: skip - } - ) - 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) - spaces = [] - - commit.parents(@map).each do |parent| - range = commit.time..parent.time - - space = if commit.space >= parent.space then - find_free_parent_space(range, parent.space, -1, commit.space) - else - find_free_parent_space(range, commit.space, -1, parent.space) - end - - mark_reserved(range, space) - spaces << space - end - - spaces - end - - def find_free_parent_space(range, space_base, space_step, space_default) - if is_overlap?(range, space_default) then - find_free_space(range, space_step, space_base, space_default) - else - space_default - end - end - - def is_overlap?(range, overlap_space) - range.each do |i| - if i != range.first && - i != range.last && - @commits[i].spaces.include?(overlap_space) then - - return true; - end - end - - false - end - - # Add space mark on commit and its parents - # - # @param [::Commit] the commit object. - def place_chain(commit, parent_time = nil) - leaves = take_left_leaves(commit) - if leaves.empty? - return - end - - time_range = leaves.first.time..leaves.last.time - space_base = get_space_base(leaves) - space = find_free_space(time_range, 2, space_base) - leaves.each do |l| - l.spaces << space - # Also add space to parent - l.parents(@map).each do |parent| - if parent.space > 0 - parent.spaces << space - end - end - end - - # and mark it as reserved - if parent_time.nil? - min_time = leaves.first.time - else - min_time = parent_time + 1 - end - - max_time = leaves.last.time - leaves.last.parents(@map).each do |parent| - if max_time < parent.time - max_time = parent.time - end - end - mark_reserved(min_time..max_time, space) - - # Visit branching chains - leaves.each do |l| - parents = l.parents(@map).select{|p| p.space.zero?} - for p in parents - place_chain(p, l.time) - end - end - end - - def get_space_base(leaves) - space_base = 1 - parents = leaves.last.parents(@map) - if parents.size > 0 - if parents.first.space > 0 - space_base = parents.first.space - 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 [::Commit] the commit object. - # - # @return [Array] list of branch commits - def take_left_leaves(raw_commit) - commit = @map[raw_commit.id] - leaves = [] - leaves.push(commit) if commit.space.zero? - - while true - return leaves if commit.parents(@map).count.zero? - - commit = commit.parents(@map).first - - return leaves unless commit.space.zero? - - leaves.push(commit) - end - end - - def build_refs_cache - refs_cache = {} - @repo.refs.each do |ref| - refs_cache[ref.commit.id] = [] unless refs_cache.include?(ref.commit.id) - refs_cache[ref.commit.id] << ref - end - refs_cache - end - end -end diff --git a/app/models/note.rb b/app/models/note.rb index f26420ca..60846e04 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -4,6 +4,7 @@ # # id :integer not null, primary key # note :text +# noteable_id :string(255) # noteable_type :string(255) # author_id :integer # created_at :datetime not null @@ -11,16 +12,18 @@ # project_id :integer # attachment :string(255) # line_code :string(255) -# commit_id :string(255) -# noteable_id :integer # require 'carrierwave/orm/activerecord' require 'file_size_validator' class Note < ActiveRecord::Base + attr_accessible :note, :noteable, :noteable_id, :noteable_type, :project_id, - :attachment, :line_code, :commit_id + :attachment, :line_code + + attr_accessor :notify + attr_accessor :notify_author belongs_to :project belongs_to :noteable, polymorphic: true @@ -29,21 +32,17 @@ class Note < ActiveRecord::Base delegate :name, to: :project, prefix: true delegate :name, :email, to: :author, prefix: true - validates :note, :project, presence: true - validates :line_code, format: { with: /\A[a-z0-9]+_\d+_\d+\Z/ }, allow_blank: true + validates :project, presence: true + validates :note, presence: true, length: { within: 0..5000 } validates :attachment, file_size: { maximum: 10.megabytes.to_i } - validates :noteable_id, presence: true, if: ->(n) { n.noteable_type.present? && n.noteable_type != 'Commit' } - validates :commit_id, presence: true, if: ->(n) { n.noteable_type == 'Commit' } - - mount_uploader :attachment, AttachmentUploader + mount_uploader :attachment, AttachmentUploader # Scopes - scope :for_commit_id, ->(commit_id) { where(noteable_type: "Commit", commit_id: commit_id) } - scope :inline, -> { where("line_code IS NOT NULL") } - scope :not_inline, -> { where("line_code IS NULL") } - - scope :common, ->{ where(noteable_type: ["", nil]) } + scope :common, ->{ where(noteable_id: nil) } + scope :today, ->{ where("created_at >= :date", date: Date.today) } + scope :last_week, ->{ where("created_at >= :date", date: (Date.today - 7.days)) } + scope :since, ->(day) { where("created_at >= :date", date: (day)) } scope :fresh, ->{ order("created_at ASC, id ASC") } scope :inc_author_project, ->{ includes(:project, :author) } scope :inc_author, ->{ includes(:author) } @@ -57,6 +56,53 @@ class Note < ActiveRecord::Base }, without_protection: true) end + def notify + @notify ||= false + end + + def notify_author + @notify_author ||= false + end + + # override to return commits, which are not active record + def noteable + if for_commit? + project.commit(noteable_id) + else + super + end + # Temp fix to prevent app crash + # if note commit id doesnt exist + rescue + nil + end + + # Check if we can notify commit author + # with email about our comment + # + # If commit author email exist in project + # and commit author is not passed user we can + # send email to him + # + # params: + # user - current user + # + # return: + # Boolean + # + def notify_only_author?(user) + for_commit? && commit_author && + commit_author.email != user.email + end + + def for_commit? + noteable_type == "Commit" + end + + def for_diff_line? + line_code.present? + end + def commit_author @commit_author ||= project.users.find_by_email(noteable.author_email) || @@ -65,98 +111,15 @@ class Note < ActiveRecord::Base nil end - def diff - if noteable.diffs.present? - noteable.diffs.select do |d| - if d.b_path - Digest::SHA1.hexdigest(d.b_path) == diff_file_index - end - end.first - end - end - - def diff_file_index - line_code.split('_')[0] - end - - def diff_file_name - diff.b_path - end - - def diff_new_line - line_code.split('_')[2].to_i - end - - def discussion_id - @discussion_id ||= [:discussion, noteable_type.try(:underscore), noteable_id, line_code].join("-").to_sym + # Returns true if this is an upvote note, + # otherwise false is returned + def upvote? + note.start_with?('+1') || note.start_with?(':+1:') end # Returns true if this is a downvote note, # otherwise false is returned def downvote? - votable? && (note.start_with?('-1') || - note.start_with?(':-1:') - ) - end - - def for_commit? - noteable_type == "Commit" - end - - def for_commit_diff_line? - for_commit? && for_diff_line? - end - - def for_diff_line? - line_code.present? - end - - def for_issue? - noteable_type == "Issue" - end - - def for_merge_request? - noteable_type == "MergeRequest" - end - - def for_merge_request_diff_line? - for_merge_request? && for_diff_line? - end - - def for_wall? - noteable_type.blank? - end - - # override to return commits, which are not active record - def noteable - if for_commit? - project.repository.commit(commit_id) - else - super - end - # Temp fix to prevent app crash - # if note commit id doesn't exist - rescue - nil - end - - # Returns true if this is an upvote note, - # otherwise false is returned - def upvote? - votable? && (note.start_with?('+1') || - note.start_with?(':+1:') - ) - end - - def votable? - for_issue? || (for_merge_request? && !for_diff_line?) - end - - def noteable_type_name - if noteable_type.present? - noteable_type.downcase - else - "wall" - end + note.start_with?('-1') || note.start_with?(':-1:') end end diff --git a/app/models/notification.rb b/app/models/notification.rb deleted file mode 100644 index bfd1e2cf..00000000 --- a/app/models/notification.rb +++ /dev/null @@ -1,30 +0,0 @@ -class Notification - # - # Notification levels - # - N_DISABLED = 0 - N_PARTICIPATING = 1 - N_WATCH = 2 - - attr_accessor :user - - def self.notification_levels - [N_DISABLED, N_PARTICIPATING, N_WATCH] - end - - def initialize(user) - @user = user - end - - def disabled? - user.notification_level == N_DISABLED - end - - def participating? - user.notification_level == N_PARTICIPATING - end - - def watch? - user.notification_level == N_WATCH - end -end diff --git a/app/models/project.rb b/app/models/project.rb index 0263ffef..3cbc9417 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -8,127 +8,100 @@ # description :text # created_at :datetime not null # updated_at :datetime not null -# creator_id :integer +# private_flag :boolean default(TRUE), not null +# code :string(255) +# owner_id :integer # default_branch :string(255) # issues_enabled :boolean default(TRUE), not null # wall_enabled :boolean default(TRUE), not null # merge_requests_enabled :boolean default(TRUE), not null # wiki_enabled :boolean default(TRUE), not null -# namespace_id :integer -# public :boolean default(FALSE), not null -# issues_tracker :string(255) default("gitlab"), not null -# issues_tracker_id :string(255) -# snippets_enabled :boolean default(TRUE), not null +# group_id :integer # require "grit" class Project < ActiveRecord::Base - include Gitlab::ShellAdapter - extend Enumerize + include Repository + include PushObserver + include Authority + include Team - attr_accessible :name, :path, :description, :default_branch, :issues_tracker, - :issues_enabled, :wall_enabled, :merge_requests_enabled, :snippets_enabled, :issues_tracker_id, - :wiki_enabled, :public, :import_url, as: [:default, :admin] - - attr_accessible :namespace_id, :creator_id, as: :admin - - attr_accessor :import_url + attr_accessible :name, :path, :description, :code, :default_branch, :issues_enabled, + :wall_enabled, :merge_requests_enabled, :wiki_enabled + attr_accessor :error_code # Relations - belongs_to :creator, foreign_key: "creator_id", class_name: "User" - belongs_to :group, foreign_key: "namespace_id", conditions: "type = 'Group'" - belongs_to :namespace - + belongs_to :group + belongs_to :owner, class_name: "User" + has_many :users, through: :users_projects + has_many :events, dependent: :destroy + has_many :merge_requests, dependent: :destroy + has_many :issues, dependent: :destroy, order: "closed, created_at DESC" + has_many :milestones, dependent: :destroy + has_many :users_projects, dependent: :destroy + has_many :notes, dependent: :destroy + has_many :snippets, dependent: :destroy + has_many :deploy_keys, dependent: :destroy, foreign_key: "project_id", class_name: "Key" + has_many :hooks, dependent: :destroy, class_name: "ProjectHook" + has_many :wikis, dependent: :destroy + has_many :protected_branches, dependent: :destroy has_one :last_event, class_name: 'Event', order: 'events.created_at DESC', foreign_key: 'project_id' has_one :gitlab_ci_service, dependent: :destroy - has_many :events, dependent: :destroy - has_many :merge_requests, dependent: :destroy - 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 - has_many :snippets, dependent: :destroy - has_many :deploy_keys, dependent: :destroy, class_name: "Key", foreign_key: "project_id" - has_many :hooks, dependent: :destroy, class_name: "ProjectHook" - has_many :wikis, dependent: :destroy - has_many :protected_branches, dependent: :destroy - has_many :user_team_project_relationships, dependent: :destroy - - has_many :users, through: :users_projects - has_many :user_teams, through: :user_team_project_relationships - has_many :user_team_user_relationships, through: :user_teams - has_many :user_teams_members, through: :user_team_user_relationships - delegate :name, to: :owner, allow_nil: true, prefix: true # Validations - validates :creator, presence: true + validates :owner, presence: true validates :description, length: { within: 0..2000 } - validates :name, presence: true, length: { within: 0..255 }, - format: { with: Gitlab::Regex.project_name_regex, - message: "only letters, digits, spaces & '_' '-' '.' allowed. Letter should be first" } - validates :path, presence: true, length: { within: 0..255 }, - format: { with: Gitlab::Regex.path_regex, + validates :name, uniqueness: true, presence: true, length: { within: 0..255 } + validates :path, uniqueness: true, presence: true, length: { within: 0..255 }, + format: { with: /\A[a-zA-Z][a-zA-Z0-9_\-\.]*\z/, + message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" } + validates :code, presence: true, uniqueness: true, length: { within: 1..255 }, + format: { with: /\A[a-zA-Z][a-zA-Z0-9_\-\.]*\z/, 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 - - validates :import_url, - format: { with: URI::regexp(%w(http https)), message: "should be a valid url" }, - if: :import? - validate :check_limit, :repo_name # Scopes - scope :without_user, ->(user) { where("id NOT IN (:ids)", ids: user.authorized_projects.map(&:id) ) } + scope :public_only, where(private_flag: false) + scope :without_user, ->(user) { where("id NOT IN (:ids)", ids: user.projects.map(&:id) ) } scope :not_in_group, ->(group) { where("id NOT IN (:ids)", ids: group.project_ids ) } - scope :without_team, ->(team) { team.projects.present? ? where("id NOT IN (:ids)", ids: team.projects.map(&:id)) : scoped } - scope :in_team, ->(team) { where("id IN (:ids)", ids: team.projects.map(&:id)) } - scope :in_namespace, ->(namespace) { where(namespace_id: namespace.id) } - scope :sorted_by_activity, ->() { order("(SELECT max(events.created_at) FROM events WHERE events.project_id = projects.id) DESC") } - scope :personal, ->(user) { where(namespace_id: user.namespace_id) } - 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'). - group('project_id'). - having('latest_date < ?', 6.months.ago).map(&:project_id) - - where(id: project_ids) - end - - def with_push - includes(:events).where('events.action = ?', Event::PUSHED) - end - def active joins(:issues, :notes, :merge_requests).order("issues.created_at, notes.created_at, merge_requests.created_at DESC") end def search query - where("projects.name LIKE :query OR projects.path LIKE :query", query: "%#{query}%") + where("name LIKE :query OR code LIKE :query OR path LIKE :query", query: "%#{query}%") end - def find_with_namespace(id) - if id.include?("/") - id = id.split("/") - namespace = Namespace.find_by_path(id.first) - return nil unless namespace + def create_by_user(params, user) + project = Project.new params - where(namespace_id: namespace.id).find_by_path(id.second) - else - where(path: id, namespace_id: nil).last + Project.transaction do + project.owner = user + project.save! + + # Add user as project master + project.users_projects.create!(project_access: UsersProject::MASTER, user: user) + + # when project saved no team member exist so + # project repository should be updated after first user add + project.update_repository end + + project + rescue Gitlab::Gitolite::AccessDenied => ex + project.error_code = :gitolite + project + rescue => ex + project.error_code = :db + project.errors.add(:base, "Can't save project. Please try again later") + project end def access_options @@ -136,32 +109,24 @@ class Project < ActiveRecord::Base end end - def team - @team ||= ProjectTeam.new(self) - end - - def repository - @repository ||= Repository.new(path_with_namespace, default_branch) + def git_error? + error_code == :gitolite end def saved? - id && persisted? - end - - def import? - import_url.present? + id && valid? end def check_limit - unless creator.can_create_project? - errors[:limit_reached] << ("Your own projects limit is #{creator.projects_limit}! Please contact administrator to increase it") + unless owner.can_create_project? + errors[:base] << ("Your own projects limit is #{owner.projects_limit}! Please contact administrator to increase it") end rescue errors[:base] << ("Can't check your ability to create project") end def repo_name - denied_paths = %w(admin dashboard groups help profile projects search) + denied_paths = %w(gitolite-admin groups projects dashboard) if denied_paths.include?(path) errors.add(:path, "like #{path} is not allowed") @@ -169,19 +134,35 @@ class Project < ActiveRecord::Base end def to_param - if namespace - namespace.path + "/" + path - else - path - end + code end def web_url - [Gitlab.config.gitlab.url, path_with_namespace].join("/") + [Gitlab.config.url, code].join("/") + end + + def common_notes + notes.where(noteable_type: ["", nil]).inc_author_project end def build_commit_note(commit) - notes.new(commit_id: commit.id, noteable_type: "Commit") + notes.new(noteable_id: commit.id, noteable_type: "Commit") + end + + def commit_notes(commit) + notes.where(noteable_id: commit.id, noteable_type: "Commit", line_code: nil) + end + + def commit_line_notes(commit) + notes.where(noteable_id: commit.id, noteable_type: "Commit").where("line_code IS NOT NULL") + end + + def public? + !private_flag + end + + def private? + private_flag end def last_activity @@ -192,6 +173,10 @@ class Project < ActiveRecord::Base last_event.try(:created_at) || updated_at end + def wiki_notes + Note.where(noteable_id: wikis.pluck(:id), noteable_type: 'Wiki', project_id: self.id) + end + def project_id self.id end @@ -200,22 +185,6 @@ 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 @@ -223,180 +192,4 @@ class Project < ActiveRecord::Base def gitlab_ci? gitlab_ci_service && gitlab_ci_service.active end - - # For compatibility with old code - def code - path - end - - def items_for entity - case entity - when 'issue' then - issues - when 'merge_request' then - merge_requests - end - end - - def send_move_instructions - self.users_projects.each do |member| - Notify.delay.project_was_moved_email(member.id) - end - end - - def owner - if namespace - namespace_owner - else - creator - end - end - - def team_member_by_name_or_email(name = nil, email = nil) - user = users.where("name like ? or email like ?", name, email).first - users_projects.where(user: user) if user - end - - # Get Team Member record by user id - def team_member_by_id(user_id) - users_projects.find_by_user_id(user_id) - end - - def name_with_namespace - @name_with_namespace ||= begin - if namespace - namespace.human_name + " / " + name - else - name - end - end - end - - def namespace_owner - namespace.try(:owner) - end - - def path_with_namespace - if namespace - namespace.path + '/' + path - else - path - end - end - - def transfer(new_namespace) - ProjectTransferService.new.transfer(self, new_namespace) - end - - def execute_hooks(data) - hooks.each { |hook| hook.async_execute(data) } - end - - def execute_services(data) - services.each do |service| - - # Call service hook only if it is active - service.execute(data) if service.active - end - end - - def discover_default_branch - # Discover the default branch, but only if it hasn't already been set to - # something else - if repository && default_branch.nil? - update_attributes(default_branch: self.repository.discover_default_branch) - end - end - - def update_merge_requests(oldrev, newrev, ref, user) - return true unless ref =~ /heads/ - branch_name = ref.gsub("refs/heads/", "") - c_ids = self.repository.commits_between(oldrev, newrev).map(&:id) - - # Update code for merge requests - mrs = self.merge_requests.opened.by_branch(branch_name).all - mrs.each { |merge_request| merge_request.reload_code; merge_request.mark_as_unchecked } - - # Close merge requests - mrs = self.merge_requests.opened.where(target_branch: branch_name).all - mrs = mrs.select(&:last_commit).select { |mr| c_ids.include?(mr.last_commit.id) } - mrs.each { |merge_request| merge_request.merge!(user.id) } - - true - end - - def valid_repo? - repository.exists? - rescue - errors.add(:path, "Invalid repository path") - false - end - - def empty_repo? - !repository.exists? || repository.empty? - end - - def ensure_satellite_exists - self.satellite.create unless self.satellite.exists? - end - - def satellite - @satellite ||= Gitlab::Satellite::Satellite.new(self) - end - - def repo - repository.raw - end - - def url_to_repo - gitlab_shell.url_to_repo(path_with_namespace) - end - - def namespace_dir - namespace.try(:path) || '' - end - - def repo_exists? - @repo_exists ||= repository.exists? - rescue - @repo_exists = false - end - - def open_branches - all_branches = repository.branches - - if protected_branches.present? - all_branches.reject! do |branch| - protected_branches_names.include?(branch.name) - end - end - - all_branches - end - - def protected_branches_names - @protected_branches_names ||= protected_branches.map(&:name) - end - - def root_ref?(branch) - repository.root_ref == branch - end - - def ssh_url_to_repo - url_to_repo - end - - def http_url_to_repo - http_url = [Gitlab.config.gitlab.url, "/", path_with_namespace, ".git"].join('') - end - - def project_access_human(member) - project_user_relation = self.users_projects.find_by_user_id(member.id) - self.class.access_options.key(project_user_relation.project_access) - end - - # Check if current branch name is marked as protected in the system - def protected_branch? branch_name - protected_branches_names.include?(branch_name) - end end diff --git a/app/models/project_team.rb b/app/models/project_team.rb deleted file mode 100644 index f3e5c0e5..00000000 --- a/app/models/project_team.rb +++ /dev/null @@ -1,99 +0,0 @@ -class ProjectTeam - attr_accessor :project - - def initialize(project) - @project = project - end - - # Shortcut to add users - # - # Use: - # @team << [@user, :master] - # @team << [@users, :master] - # - def << args - users = args.first - - if users.respond_to?(:each) - add_users(users, args.second) - else - add_user(users, args.second) - end - end - - def get_tm user_id - project.users_projects.find_by_user_id(user_id) - end - - def add_user(user, access) - add_users_ids([user.id], access) - end - - def add_users(users, access) - add_users_ids(users.map(&:id), access) - end - - def add_users_ids(user_ids, access) - UsersProject.add_users_into_projects( - [project.id], - user_ids, - access - ) - end - - # Remove all users from project team - def truncate - UsersProject.truncate_team(project) - end - - def members - project.users_projects - end - - def guests - members.guests.map(&:user) - end - - def reporters - members.reporters.map(&:user) - end - - def developers - members.developers.map(&:user) - end - - def masters - members.masters.map(&:user) - end - - def import(source_project) - target_project = project - - source_team = source_project.users_projects.all - target_team = target_project.users_projects.all - target_user_ids = target_team.map(&:user_id) - - source_team.reject! do |tm| - # Skip if user already present in team - target_user_ids.include?(tm.user_id) - end - - source_team.map! do |tm| - new_tm = tm.dup - new_tm.id = nil - new_tm.project_id = target_project.id - new_tm.skip_git = true - new_tm - end - - UsersProject.transaction do - source_team.each do |tm| - tm.save - end - end - - true - rescue - false - end -end diff --git a/app/models/protected_branch.rb b/app/models/protected_branch.rb index 16379720..c54aa3ce 100644 --- a/app/models/protected_branch.rb +++ b/app/models/protected_branch.rb @@ -10,7 +10,7 @@ # class ProtectedBranch < ActiveRecord::Base - include Gitlab::ShellAdapter + include GitHost attr_accessible :name @@ -18,7 +18,14 @@ class ProtectedBranch < ActiveRecord::Base validates :name, presence: true validates :project, presence: true + after_save :update_repository + after_destroy :update_repository + + def update_repository + git_host.update_repository(project) + end + def commit - project.repository.commit(self.name) + project.commit(self.name) end end diff --git a/app/models/repository.rb b/app/models/repository.rb deleted file mode 100644 index ed600e29..00000000 --- a/app/models/repository.rb +++ /dev/null @@ -1,45 +0,0 @@ -class Repository - attr_accessor :raw_repository - - def initialize(path_with_namespace, default_branch) - @raw_repository = Gitlab::Git::Repository.new(path_with_namespace, default_branch) - rescue Gitlab::Git::Repository::NoRepository - nil - end - - def exists? - raw_repository - end - - def empty? - raw_repository.empty? - end - - def commit(id = nil) - commit = raw_repository.commit(id) - commit = Commit.new(commit) if commit - commit - end - - def commits(ref, path = nil, limit = nil, offset = nil) - commits = raw_repository.commits(ref, path, limit, offset) - commits = Commit.decorate(commits) if commits.present? - commits - end - - def commits_between(target, source) - commits = raw_repository.commits_between(target, source) - commits = Commit.decorate(commits) if commits.present? - commits - end - - def method_missing(m, *args, &block) - raw_repository.send(m, *args, &block) - end - - def respond_to?(method) - return true if raw_repository.respond_to?(method) - - super - end -end diff --git a/app/models/service.rb b/app/models/service.rb index d3486d29..17a7a656 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -20,8 +20,4 @@ class Service < ActiveRecord::Base has_one :service_hook validates :project_id, presence: true - - def activated? - active - end end diff --git a/app/models/snippet.rb b/app/models/snippet.rb index c4ee35e0..997c19bd 100644 --- a/app/models/snippet.rb +++ b/app/models/snippet.rb @@ -22,18 +22,18 @@ class Snippet < ActiveRecord::Base belongs_to :author, class_name: "User" has_many :notes, as: :noteable, dependent: :destroy - delegate :name, :email, to: :author, prefix: true, allow_nil: true + delegate :name, :email, to: :author, prefix: true validates :author, presence: true validates :project, presence: true validates :title, presence: true, length: { within: 0..255 } validates :file_name, presence: true, length: { within: 0..255 } - validates :content, presence: true + validates :content, presence: true, length: { within: 0..10000 } # Scopes - scope :fresh, -> { order("created_at DESC") } - scope :non_expired, -> { where(["expires_at IS NULL OR expires_at > ?", Time.current]) } - scope :expired, -> { where(["expires_at IS NOT NULL AND expires_at < ?", Time.current]) } + scope :fresh, order("created_at DESC") + scope :non_expired, where(["expires_at IS NULL OR expires_at > ?", Time.current]) + scope :expired, where(["expires_at IS NOT NULL AND expires_at < ?", Time.current]) def self.content_types [ diff --git a/app/models/system_hook.rb b/app/models/system_hook.rb index 5cdf0466..2ae5b131 100644 --- a/app/models/system_hook.rb +++ b/app/models/system_hook.rb @@ -12,4 +12,13 @@ # class SystemHook < WebHook + def self.all_hooks_fire(data) + SystemHook.all.each do |sh| + sh.async_execute data + end + end + + def async_execute(data) + Resque.enqueue(SystemHookWorker, id, data) + end end diff --git a/app/models/tree.rb b/app/models/tree.rb index 4b6c5b13..c3dfd4c7 100644 --- a/app/models/tree.rb +++ b/app/models/tree.rb @@ -1,13 +1,12 @@ class Tree include Linguist::BlobHelper - - attr_accessor :path, :tree, :ref + attr_accessor :path, :tree, :project, :ref delegate :contents, :basename, :name, :data, :mime_type, :mode, :size, :text?, :colorize, to: :tree - def initialize(raw_tree, ref = nil, path = nil) - @ref, @path = ref, path + def initialize(raw_tree, project, ref = nil, path = nil) + @project, @ref, @path = project, ref, path @tree = if path.present? raw_tree / path else @@ -26,12 +25,4 @@ class Tree def empty? data.blank? end - - def up_dir? - path.present? - end - - def readme - @readme ||= contents.find { |c| c.is_a?(Grit::Blob) and c.name =~ /^readme/i } - end end diff --git a/app/models/user.rb b/app/models/user.rb index dcbf5812..6d539c1f 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -22,145 +22,56 @@ # linkedin :string(255) default(""), not null # twitter :string(255) default(""), not null # authentication_token :string(255) +# dark_scheme :boolean default(FALSE), not null # theme_id :integer default(1), not null # bio :string(255) +# blocked :boolean default(FALSE), not null # failed_attempts :integer default(0) # locked_at :datetime # extern_uid :string(255) # provider :string(255) -# username :string(255) -# can_create_group :boolean default(TRUE), not null -# can_create_team :boolean default(TRUE), not null -# state :string(255) -# color_scheme_id :integer default(1), not null -# notification_level :integer default(1), not null # class User < ActiveRecord::Base - devise :database_authenticatable, :token_authenticatable, :lockable, - :recoverable, :rememberable, :trackable, :validatable, :omniauthable, :registerable + include Account - attr_accessible :email, :password, :password_confirmation, :remember_me, :bio, :name, :username, - :skype, :linkedin, :twitter, :color_scheme_id, :theme_id, :force_random_password, - :extern_uid, :provider, as: [:default, :admin] - attr_accessible :projects_limit, :can_create_team, :can_create_group, as: :admin + devise :database_authenticatable, :token_authenticatable, :lockable, + :recoverable, :rememberable, :trackable, :validatable, :omniauthable + + attr_accessible :email, :password, :password_confirmation, :remember_me, :bio, :name, + :skype, :linkedin, :twitter, :dark_scheme, :theme_id, :force_random_password, + :extern_uid, :provider, :as => [:default, :admin] + attr_accessible :projects_limit, :as => :admin attr_accessor :force_random_password - # Virtual attribute for authenticating by either username or email - attr_accessor :login - - # Add login to attr_accessible - attr_accessible :login - - - # - # Relations - # - - # Namespace for personal projects - 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 - - # 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 - has_many :merge_requests, dependent: :destroy, foreign_key: :author_id - 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 :users_projects, dependent: :destroy + has_many :issues, foreign_key: :author_id, dependent: :destroy + has_many :notes, foreign_key: :author_id, dependent: :destroy + has_many :merge_requests, foreign_key: :author_id, dependent: :destroy + has_many :my_own_projects, class_name: "Project", foreign_key: :owner_id + has_many :events, class_name: "Event", foreign_key: :author_id, dependent: :destroy + has_many :recent_events, class_name: "Event", foreign_key: :author_id, order: "id DESC" + has_many :assigned_issues, class_name: "Issue", foreign_key: :assignee_id, dependent: :destroy + has_many :assigned_merge_requests, class_name: "MergeRequest", foreign_key: :assignee_id, dependent: :destroy - 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 } - validates :extern_uid, allow_blank: true, uniqueness: {scope: :provider} + validates :extern_uid, :allow_blank => true, :uniqueness => {:scope => :provider} validates :projects_limit, presence: true, numericality: {greater_than_or_equal_to: 0} - validates :username, presence: true, uniqueness: true, - format: { with: Gitlab::Regex.username_regex, - message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" } - - validates :notification_level, - inclusion: { in: Notification.notification_levels }, - presence: true - - validate :namespace_uniq, if: ->(user) { user.username_changed? } before_validation :generate_password, on: :create before_save :ensure_authentication_token alias_attribute :private_token, :authentication_token - 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, -> { 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) } - scope :potential_team_members, ->(team) { team.members.any? ? active.not_in_team(team) : active } + scope :not_in_project, ->(project) { where("id not in (:ids)", ids: project.users.map(&:id) ) } + scope :admins, where(admin: true) + scope :blocked, where(blocked: true) + scope :active, where(blocked: false) - # - # Class methods - # class << self - # Devise method overriden to allow sing in with email or username - def find_for_database_authentication(warden_conditions) - conditions = warden_conditions.dup - if login = conditions.delete(:login) - where(conditions).where(["lower(username) = :value OR lower(email) = :value", { value: login.downcase }]).first - else - where(conditions).first - end - end - def filter filter_name case filter_name when "admins"; self.admins @@ -171,14 +82,6 @@ class User < ActiveRecord::Base end end - def not_in_project(project) - if project.users.present? - where("id not in (:ids)", ids: project.users.map(&:id) ) - else - scoped - end - end - def without_projects where('id NOT IN (SELECT DISTINCT(user_id) FROM users_projects)') end @@ -200,173 +103,13 @@ class User < ActiveRecord::Base end def search query - where("name LIKE :query OR email LIKE :query OR username LIKE :query", query: "%#{query}%") + where("name LIKE :query or email LIKE :query", query: "%#{query}%") end end - # - # Instance methods - # - - def to_param - username - end - - def notification - @notification ||= Notification.new(self) - end - def generate_password if self.force_random_password self.password = self.password_confirmation = Devise.friendly_token.first(8) end end - - def namespace_uniq - namespace_name = self.username - if Namespace.find_by_path(namespace_name) - self.errors.add :username, "already exist" - end - end - - # Namespaces user has access to - def namespaces - namespaces = [] - - # Add user account namespace - namespaces << self.namespace if self.namespace - - # Add groups you can manage - namespaces += groups.all - - namespaces - end - - # Groups where user is an owner - def owned_groups - groups - end - - # Groups user has access to - def authorized_groups - @authorized_groups ||= begin - groups = Group.where(id: self.authorized_projects.pluck(:namespace_id)).all - groups = groups + self.groups - groups.uniq - end - end - - - # Projects user has access to - def authorized_projects - project_ids = users_projects.pluck(:project_id) - project_ids = project_ids | owned_projects.pluck(:id) - Project.where(id: project_ids) - end - - # Projects in user namespace - def personal_projects - Project.personal(self) - end - - # Projects where user is an owner - def owned_projects - Project.where("(projects.namespace_id IN (:namespaces)) OR - (projects.namespace_id IS NULL AND projects.creator_id = :user_id)", - namespaces: namespaces.map(&:id), user_id: self.id) - end - - # Team membership in authorized projects - def tm_in_authorized_projects - UsersProject.where(project_id: authorized_projects.map(&:id), user_id: self.id) - end - - def is_admin? - admin - end - - def require_ssh_key? - keys.count == 0 - end - - def can_change_username? - Gitlab.config.gitlab.username_changing_enabled - end - - def can_create_project? - projects_limit > owned_projects.count - end - - def can_create_group? - can?(:create_group, nil) - end - - def abilities - @abilities ||= begin - abilities = Six.new - abilities << Ability - abilities - end - end - - def can_select_namespace? - several_namespaces? || admin - end - - def can? action, subject - abilities.allowed?(self, action, subject) - end - - def first_name - name.split.first unless name.blank? - end - - def cared_merge_requests - MergeRequest.cared(self) - end - - def projects_limit_percent - return 100 if projects_limit.zero? - (personal_projects.count.to_f / projects_limit) * 100 - end - - def recent_push project_id = nil - # Get push events not earlier than 2 hours ago - events = recent_events.code_push.where("created_at > ?", Time.now - 2.hours) - events = events.where(project_id: project_id) if project_id - - # Take only latest one - events = events.recent.limit(1).first - end - - def projects_sorted_by_activity - authorized_projects.sorted_by_activity - end - - def several_namespaces? - namespaces.size > 1 - end - - def namespace_id - namespace.try :id - end - - def authorized_teams - @authorized_teams ||= begin - ids = [] - ids << UserTeam.with_member(self).pluck('user_teams.id') - ids << UserTeam.created_by(self).pluck('user_teams.id') - ids.flatten - - UserTeam.where(id: ids) - end - end - - def owned_teams - UserTeam.where(owner_id: self.id) - end - - def name_with_username - "#{name} (#{username})" - end end diff --git a/app/models/user_team.rb b/app/models/user_team.rb deleted file mode 100644 index 5de2ac6a..00000000 --- a/app/models/user_team.rb +++ /dev/null @@ -1,117 +0,0 @@ -# == Schema Information -# -# Table name: user_teams -# -# id :integer not null, primary key -# name :string(255) -# path :string(255) -# owner_id :integer -# created_at :datetime not null -# updated_at :datetime not null -# description :string(255) default(""), not null -# - -class UserTeam < ActiveRecord::Base - attr_accessible :name, :description, :owner_id, :path - - belongs_to :owner, class_name: User - - has_many :user_team_project_relationships, dependent: :destroy - has_many :user_team_user_relationships, dependent: :destroy - - has_many :projects, through: :user_team_project_relationships - has_many :members, through: :user_team_user_relationships, source: :user - - validates :owner, presence: true - validates :name, presence: true, uniqueness: true, - 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" } - - scope :with_member, ->(user){ joins(:user_team_user_relationships).where(user_team_user_relationships: {user_id: user.id}) } - scope :with_project, ->(project){ joins(:user_team_project_relationships).where(user_team_project_relationships: {project_id: project})} - scope :without_project, ->(project){ where("user_teams.id NOT IN (:ids)", ids: (a = with_project(project); a.blank? ? 0 : a))} - scope :created_by, ->(user){ where(owner_id: user) } - - class << self - def search query - where("name LIKE :query OR path LIKE :query", query: "%#{query}%") - end - - def global_id - 'GLN' - end - - def access_roles - UsersProject.access_roles - end - end - - def to_param - path - end - - def assign_to_projects(projects, access) - projects.each do |project| - assign_to_project(project, access) - end - end - - def assign_to_project(project, access) - Gitlab::UserTeamManager.assign(self, project, access) - end - - def resign_from_project(project) - Gitlab::UserTeamManager.resign(self, project) - end - - def add_members(users, access, group_admin) - # reject existing users - users.reject! { |id| member_ids.include?(id.to_i) } - - users.each do |user| - add_member(user, access, group_admin) - end - end - - def add_member(user, access, group_admin) - Gitlab::UserTeamManager.add_member_into_team(self, user, access, group_admin) - end - - def remove_member(user) - Gitlab::UserTeamManager.remove_member_from_team(self, user) - end - - def update_membership(user, options) - Gitlab::UserTeamManager.update_team_user_membership(self, user, options) - end - - def update_project_access(project, permission) - Gitlab::UserTeamManager.update_project_greates_access(self, project, permission) - end - - def max_project_access(project) - user_team_project_relationships.find_by_project_id(project).greatest_access - end - - def human_max_project_access(project) - self.class.access_roles.invert[max_project_access(project)] - end - - def default_projects_access(member) - user_team_user_relationships.find_by_user_id(member).permission - end - - def human_default_projects_access(member) - self.class.access_roles.invert[default_projects_access(member)] - end - - def admin?(member) - user_team_user_relationships.with_user(member).first.group_admin? - end - -end diff --git a/app/models/user_team_project_relationship.rb b/app/models/user_team_project_relationship.rb deleted file mode 100644 index 991510be..00000000 --- a/app/models/user_team_project_relationship.rb +++ /dev/null @@ -1,44 +0,0 @@ -# == Schema Information -# -# Table name: user_team_project_relationships -# -# id :integer not null, primary key -# project_id :integer -# user_team_id :integer -# greatest_access :integer -# created_at :datetime not null -# updated_at :datetime not null -# - -class UserTeamProjectRelationship < ActiveRecord::Base - attr_accessible :greatest_access, :project_id, :user_team_id - - belongs_to :user_team - belongs_to :project - - validates :project, presence: true - validates :user_team, presence: true - validate :check_greatest_access - - scope :with_project, ->(project){ where(project_id: project.id) } - - def team_name - user_team.name - end - - def human_max_access - UserTeam.access_roles.key(greatest_access) - end - - private - - def check_greatest_access - errors.add(:base, :incorrect_access_code) unless correct_access? - end - - def correct_access? - return false if greatest_access.blank? - return true if UsersProject.access_roles.has_value?(greatest_access) - false - end -end diff --git a/app/models/user_team_user_relationship.rb b/app/models/user_team_user_relationship.rb deleted file mode 100644 index 1f7e2625..00000000 --- a/app/models/user_team_user_relationship.rb +++ /dev/null @@ -1,32 +0,0 @@ -# == Schema Information -# -# Table name: user_team_user_relationships -# -# id :integer not null, primary key -# user_id :integer -# user_team_id :integer -# group_admin :boolean -# permission :integer -# created_at :datetime not null -# updated_at :datetime not null -# - -class UserTeamUserRelationship < ActiveRecord::Base - attr_accessible :group_admin, :permission, :user_id, :user_team_id - - belongs_to :user_team - belongs_to :user - - validates :user_team, presence: true - validates :user, presence: true - - scope :with_user, ->(user) { where(user_id: user.id) } - - def user_name - user.name - end - - def access_human - UsersProject.access_roles.invert[permission] - end -end diff --git a/app/models/users_project.rb b/app/models/users_project.rb index c32edf38..6231088f 100644 --- a/app/models/users_project.rb +++ b/app/models/users_project.rb @@ -11,7 +11,7 @@ # class UsersProject < ActiveRecord::Base - include Gitlab::ShellAdapter + include GitHost GUEST = 10 REPORTER = 20 @@ -23,94 +23,86 @@ class UsersProject < ActiveRecord::Base belongs_to :user belongs_to :project - attr_accessor :skip_git + after_save :update_repository + after_destroy :update_repository validates :user, presence: true - validates :user_id, uniqueness: { scope: [:project_id], message: "already exists in project" } - validates :project_access, inclusion: { in: [GUEST, REPORTER, DEVELOPER, MASTER] }, presence: true + validates :user_id, uniqueness: { :scope => [:project_id], message: "already exists in project" } validates :project, presence: true - delegate :name, :username, :email, to: :user, prefix: true - - scope :guests, -> { where(project_access: GUEST) } - scope :reporters, -> { where(project_access: REPORTER) } - scope :developers, -> { where(project_access: DEVELOPER) } - scope :masters, -> { where(project_access: MASTER) } - - scope :in_project, ->(project) { where(project_id: project.id) } - scope :in_projects, ->(projects) { where(project_id: projects.map { |p| p.id }) } - scope :with_user, ->(user) { where(user_id: user.id) } + delegate :name, :email, to: :user, prefix: true class << self + def import_team(source_project, target_project) + UsersProject.without_repository_callback do + UsersProject.transaction do + team = source_project.users_projects.all - # Add users to project teams with passed access option - # - # access can be an integer representing a access code - # or symbol like :master representing role - # - # Ex. - # add_users_into_projects( - # project_ids, - # user_ids, - # UsersProject::MASTER - # ) - # - # add_users_into_projects( - # project_ids, - # user_ids, - # :master - # ) - # - def add_users_into_projects(project_ids, user_ids, access) - project_access = if roles_hash.has_key?(access) - roles_hash[access] - elsif roles_hash.values.include?(access.to_i) - access - else - raise "Non valid access" - end + team.each do |tm| + # Skip if user already present in team + next if target_project.users.include?(tm.user) - UsersProject.transaction do - project_ids.each do |project_id| - user_ids.each do |user_id| - users_project = UsersProject.new(project_access: project_access, user_id: user_id) - users_project.project_id = project_id - users_project.skip_git = true - users_project.save + new_tm = tm.dup + new_tm.id = nil + new_tm.project_id = target_project.id + new_tm.save end end end + target_project.update_repository true rescue false end - def truncate_teams(project_ids) + def without_repository_callback + UsersProject.skip_callback(:destroy, :after, :update_repository) + yield + UsersProject.set_callback(:destroy, :after, :update_repository) + end + + def bulk_delete(project, user_ids) UsersProject.transaction do - users_projects = UsersProject.where(project_id: project_ids) - users_projects.each do |users_project| - users_project.skip_git = true + UsersProject.where(:user_id => user_ids, :project_id => project.id).each do |users_project| users_project.destroy end end - - true - rescue - false end - def truncate_team project - truncate_teams [project.id] + def bulk_update(project, user_ids, project_access) + UsersProject.transaction do + UsersProject.where(:user_id => user_ids, :project_id => project.id).each do |users_project| + users_project.project_access = project_access + users_project.save + end + end end - def roles_hash - { - guest: GUEST, - reporter: REPORTER, - developer: DEVELOPER, - master: MASTER - } + def bulk_import(project, user_ids, project_access) + UsersProject.transaction do + user_ids.each do |user_id| + users_project = UsersProject.new( + project_access: project_access, + user_id: user_id + ) + users_project.project = project + users_project.save + end + end + end + + def user_bulk_import(user, project_ids, project_access) + UsersProject.transaction do + project_ids.each do |project_id| + users_project = UsersProject.new( + project_access: project_access, + ) + users_project.project_id = project_id + users_project.user_id = user.id + users_project.save + end + end end def access_roles @@ -123,6 +115,14 @@ class UsersProject < ActiveRecord::Base end end + def role_access + project_access + end + + def update_repository + git_host.update_repository(project) + end + def project_access_human Project.access_options.key(self.project_access) end @@ -130,8 +130,4 @@ class UsersProject < ActiveRecord::Base def repo_access_human self.class.access_roles.invert[self.project_access] end - - def skip_git? - !!@skip_git - end end diff --git a/app/models/web_hook.rb b/app/models/web_hook.rb index 3f22b108..df58fa93 100644 --- a/app/models/web_hook.rb +++ b/app/models/web_hook.rb @@ -28,18 +28,10 @@ class WebHook < ActiveRecord::Base WebHook.post(url, body: data.to_json, headers: { "Content-Type" => "application/json" }) else post_url = url.gsub("#{parsed_url.userinfo}@", "") - auth = { - username: URI.decode(parsed_url.user), - password: URI.decode(parsed_url.password), - } WebHook.post(post_url, body: data.to_json, headers: {"Content-Type" => "application/json"}, - basic_auth: auth) + basic_auth: {username: parsed_url.user, password: parsed_url.password}) end end - - def async_execute(data) - Sidekiq::Client.enqueue(ProjectWebHookWorker, id, data) - end end diff --git a/app/models/wiki.rb b/app/models/wiki.rb index 7f488ca7..252a97e8 100644 --- a/app/models/wiki.rb +++ b/app/models/wiki.rb @@ -25,8 +25,6 @@ class Wiki < ActiveRecord::Base before_update :set_slug - scope :ordered, order("created_at DESC") - def to_param slug end @@ -52,4 +50,5 @@ class Wiki < ActiveRecord::Base def set_slug self.slug = self.title.parameterize end + end diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb deleted file mode 100644 index 497d69e8..00000000 --- a/app/models/wiki_page.rb +++ /dev/null @@ -1,181 +0,0 @@ -class WikiPage - include ActiveModel::Validations - include ActiveModel::Conversion - include StaticModel - extend ActiveModel::Naming - - def self.primary_key - 'slug' - end - - def self.model_name - ActiveModel::Name.new(self, nil, 'wiki') - end - - def to_key - [:slug] - end - - validates :title, presence: true - validates :content, presence: true - - # The Gitlab GollumWiki instance. - attr_reader :wiki - - # The raw Gollum::Page instance. - attr_reader :page - - # The attributes Hash used for storing and validating - # new Page values before writing to the Gollum repository. - attr_accessor :attributes - - def initialize(wiki, page = nil, persisted = false) - @wiki = wiki - @page = page - @persisted = persisted - @attributes = {}.with_indifferent_access - - set_attributes if persisted? - end - - # The escaped URL path of this page. - def slug - @attributes[:slug] - end - - alias :to_param :slug - - # The formatted title of this page. - def title - @attributes[:title] || "" - end - - # Sets the title of this page. - def title=(new_title) - @attributes[:title] = new_title - end - - # The raw content of this page. - def content - @attributes[:content] - end - - # The processed/formatted content of this page. - def formatted_content - @attributes[:formatted_content] - end - - # The markup format for the page. - def format - @attributes[:format] || :markdown - end - - # The commit message for this page version. - def message - version.try(:message) - end - - # The Gitlab Commit instance for this page. - def version - return nil unless persisted? - - @version ||= Commit.new(Gitlab::Git::Commit.new(@page.version)) - end - - # Returns an array of Gitlab Commit instances. - def versions - return [] unless persisted? - - @page.versions.map { |v| Commit.new(Gitlab::Git::Commit.new(v)) } - end - - # Returns the Date that this latest version was - # created on. - def created_at - @page.version.date - end - - # Returns boolean True or False if this instance - # is an old version of the page. - def historical? - @page.historical? - end - - # Returns boolean True or False if this instance - # has been fully saved to disk or not. - def persisted? - @persisted == true - end - - # Creates a new Wiki Page. - # - # attr - Hash of attributes to set on the new page. - # :title - The title for the new page. - # :content - The raw markup content. - # :format - Optional symbol representing the - # content format. Can be any type - # listed in the GollumWiki::MARKUPS - # Hash. - # :message - Optional commit message to set on - # the new page. - # - # Returns the String SHA1 of the newly created page - # or False if the save was unsuccessful. - def create(attr = {}) - @attributes.merge!(attr) - - save :create_page, title, content, format, message - end - - # Updates an existing Wiki Page, creating a new version. - # - # new_content - The raw markup content to replace the existing. - # format - Optional symbol representing the content format. - # See GollumWiki::MARKUPS Hash for available formats. - # message - Optional commit message to set on the new version. - # - # Returns the String SHA1 of the newly created page - # or False if the save was unsuccessful. - def update(new_content = "", format = :markdown, message = nil) - @attributes[:content] = new_content - @attributes[:format] = format - - save :update_page, @page, content, format, message - end - - # Destroys the WIki Page. - # - # Returns boolean True or False. - def delete - if wiki.delete_page(@page) - true - else - false - end - end - - private - - def set_attributes - attributes[:slug] = @page.escaped_url_path - attributes[:title] = @page.title - attributes[:content] = @page.raw_data - attributes[:formatted_content] = @page.formatted_data - attributes[:format] = @page.format - end - - def save(method, *args) - if valid? && wiki.send(method, *args) - @page = wiki.wiki.paged(title) - - set_attributes - - @persisted = true - else - errors.add(:base, wiki.error_message) if wiki.error_message - @persisted = false - end - @persisted - end - -end diff --git a/app/observers/activity_observer.rb b/app/observers/activity_observer.rb index ee3e4629..48351bac 100644 --- a/app/observers/activity_observer.rb +++ b/app/observers/activity_observer.rb @@ -1,59 +1,25 @@ -class ActivityObserver < BaseObserver - observe :issue, :merge_request, :note, :milestone +class ActivityObserver < ActiveRecord::Observer + observe :issue, :merge_request def after_create(record) - event_author_id = record.author_id + Event.create( + project: record.project, + target_id: record.id, + target_type: record.class.name, + action: Event.determine_action(record), + author_id: record.author_id + ) + end - if record.kind_of?(Note) - # Skip system status notes like 'status changed to close' - return true if record.note.include?("_Status changed to ") - - # Skip wall notes to prevent spaming of dashboard - return true if record.noteable_type.blank? - end - - if event_author_id + def after_save(record) + if record.changed.include?("closed") Event.create( project: record.project, target_id: record.id, target_type: record.class.name, - action: Event.determine_action(record), - author_id: event_author_id + action: (record.closed ? Event::Closed : Event::Reopened), + author_id: record.author_id_of_changes ) end end - - def after_close(record, transition) - Event.create( - project: record.project, - target_id: record.id, - target_type: record.class.name, - action: Event::CLOSED, - author_id: record.author_id_of_changes - ) - end - - def after_reopen(record, transition) - Event.create( - project: record.project, - target_id: record.id, - target_type: record.class.name, - action: Event::REOPENED, - author_id: record.author_id_of_changes - ) - end - - def after_merge(record, transition) - # Since MR can be merged via sidekiq - # to prevent event duplication do this check - return true if record.merge_event - - Event.create( - project: record.project, - target_id: record.id, - target_type: record.class.name, - action: Event::MERGED, - author_id: record.author_id_of_changes - ) - end end diff --git a/app/observers/base_observer.rb b/app/observers/base_observer.rb deleted file mode 100644 index 182d3b7b..00000000 --- a/app/observers/base_observer.rb +++ /dev/null @@ -1,9 +0,0 @@ -class BaseObserver < ActiveRecord::Observer - def notification - NotificationService.new - end - - def log_info message - Gitlab::AppLogger.info message - end -end diff --git a/app/observers/issue_observer.rb b/app/observers/issue_observer.rb index 03ce4b95..62fd9bf8 100644 --- a/app/observers/issue_observer.rb +++ b/app/observers/issue_observer.rb @@ -1,30 +1,33 @@ -class IssueObserver < BaseObserver +class IssueObserver < ActiveRecord::Observer cattr_accessor :current_user def after_create(issue) - notification.new_issue(issue, current_user) - end - - def after_close(issue, transition) - notification.close_issue(issue, current_user) - - create_note(issue) - end - - def after_reopen(issue, transition) - create_note(issue) + if issue.assignee && issue.assignee != current_user + Notify.new_issue_email(issue.id).deliver + end end def after_update(issue) - if issue.is_being_reassigned? - notification.reassigned_issue(issue, current_user) + send_reassigned_email(issue) if issue.is_being_reassigned? + + status = nil + status = 'closed' if issue.is_being_closed? + status = 'reopened' if issue.is_being_reopened? + if status + Note.create_status_change_note(issue, current_user, status) + [issue.author, issue.assignee].compact.each do |recipient| + Notify.issue_status_changed_email(recipient.id, issue.id, status, current_user) + end end end protected - # Create issue note with service comment like 'Status changed to closed' - def create_note(issue) - Note.create_status_change_note(issue, current_user, issue.state) + def send_reassigned_email(issue) + recipient_ids = [issue.assignee_id, issue.assignee_id_was].keep_if {|id| id && id != current_user.id } + + recipient_ids.each do |recipient_id| + Notify.reassigned_issue_email(recipient_id, issue.id, issue.assignee_id_was).deliver + end end end diff --git a/app/observers/key_observer.rb b/app/observers/key_observer.rb index 28fef55a..a3f17bde 100644 --- a/app/observers/key_observer.rb +++ b/app/observers/key_observer.rb @@ -1,19 +1,12 @@ -class KeyObserver < BaseObserver - def after_save(key) - GitlabShellWorker.perform_async( - :add_key, - key.shell_id, - key.key - ) +class KeyObserver < ActiveRecord::Observer + include GitHost - notification.new_key(key) + def after_save(key) + git_host.set_key(key.identifier, key.key, key.projects) end def after_destroy(key) - GitlabShellWorker.perform_async( - :remove_key, - key.shell_id, - key.key, - ) + return if key.is_deploy_key && !key.last_deploy? + git_host.remove_key(key.identifier, key.projects) end end diff --git a/app/observers/merge_request_observer.rb b/app/observers/merge_request_observer.rb index d0dfad88..c4040f15 100644 --- a/app/observers/merge_request_observer.rb +++ b/app/observers/merge_request_observer.rb @@ -1,25 +1,31 @@ -class MergeRequestObserver < BaseObserver +class MergeRequestObserver < ActiveRecord::Observer cattr_accessor :current_user def after_create(merge_request) - notification.new_merge_request(merge_request, current_user) - end - - def after_close(merge_request, transition) - Note.create_status_change_note(merge_request, current_user, merge_request.state) - - notification.close_mr(merge_request, current_user) - end - - def after_merge(merge_request, transition) - notification.merge_mr(merge_request) - end - - def after_reopen(merge_request, transition) - Note.create_status_change_note(merge_request, current_user, merge_request.state) + if merge_request.assignee && merge_request.assignee != current_user + Notify.new_merge_request_email(merge_request.id).deliver + end end def after_update(merge_request) - notification.reassigned_merge_request(merge_request, current_user) if merge_request.is_being_reassigned? + send_reassigned_email(merge_request) if merge_request.is_being_reassigned? + + status = nil + status = 'closed' if merge_request.is_being_closed? + status = 'reopened' if merge_request.is_being_reopened? + if status + Note.create_status_change_note(merge_request, current_user, status) + end + end + + protected + + def send_reassigned_email(merge_request) + recipients_ids = merge_request.assignee_id_was, merge_request.assignee_id + recipients_ids.delete current_user.id + + recipients_ids.each do |recipient_id| + Notify.reassigned_merge_request_email(recipient_id, merge_request.id, merge_request.assignee_id_was).deliver + end end end diff --git a/app/observers/note_observer.rb b/app/observers/note_observer.rb index 7b79161c..083aa705 100644 --- a/app/observers/note_observer.rb +++ b/app/observers/note_observer.rb @@ -1,5 +1,37 @@ -class NoteObserver < BaseObserver +class NoteObserver < ActiveRecord::Observer + def after_create(note) - notification.new_note(note) + send_notify_mails(note) + end + + protected + + def send_notify_mails(note) + if note.notify + notify_team(note) + elsif note.notify_author + # Notify only author of resource + Notify.note_commit_email(note.commit_author.id, note.id).deliver + else + # Otherwise ignore it + nil + end + end + + # Notifies the whole team except the author of note + def notify_team(note) + # Note: wall posts are not "attached" to anything, so fall back to "Wall" + noteable_type = note.noteable_type || "Wall" + notify_method = "note_#{noteable_type.underscore}_email".to_sym + + if Notify.respond_to? notify_method + team_without_note_author(note).map do |u| + Notify.send(notify_method, u.id, note.id).deliver + end + end + end + + def team_without_note_author(note) + note.project.users.reject { |u| u.id == note.author.id } end end diff --git a/app/observers/project_observer.rb b/app/observers/project_observer.rb index 7d7ecdd3..03a61709 100644 --- a/app/observers/project_observer.rb +++ b/app/observers/project_observer.rb @@ -1,30 +1,21 @@ -class ProjectObserver < BaseObserver - def after_create(project) - GitlabShellWorker.perform_async( - :add_repository, - project.path_with_namespace - ) - - log_info("#{project.owner.name} created a new project \"#{project.name_with_namespace}\"") - end - - def after_update(project) - project.send_move_instructions if project.namespace_id_changed? +class ProjectObserver < ActiveRecord::Observer + def after_save(project) + project.update_repository end def after_destroy(project) - GitlabShellWorker.perform_async( - :remove_repository, - project.path_with_namespace - ) - - GitlabShellWorker.perform_async( - :remove_repository, - project.path_with_namespace + ".wiki" - ) - - project.satellite.destroy - log_info("Project \"#{project.name}\" was removed") + + project.destroy_repository + end + + def after_create project + log_info("#{project.owner.name} created a new project \"#{project.name}\"") + end + + protected + + def log_info message + Gitlab::AppLogger.info message end end diff --git a/app/observers/system_hook_observer.rb b/app/observers/system_hook_observer.rb index 3a649fd5..312cd2b3 100644 --- a/app/observers/system_hook_observer.rb +++ b/app/observers/system_hook_observer.rb @@ -1,11 +1,67 @@ -class SystemHookObserver < BaseObserver +class SystemHookObserver < ActiveRecord::Observer observe :user, :project, :users_project - + def after_create(model) - SystemHooksService.execute_hooks_for(model, :create) + if model.kind_of? Project + SystemHook.all_hooks_fire({ + event_name: "project_create", + name: model.name, + path: model.path, + project_id: model.id, + owner_name: model.owner.name, + owner_email: model.owner.email, + created_at: model.created_at + }) + elsif model.kind_of? User + SystemHook.all_hooks_fire({ + event_name: "user_create", + name: model.name, + email: model.email, + created_at: model.created_at + }) + + elsif model.kind_of? UsersProject + SystemHook.all_hooks_fire({ + event_name: "user_add_to_team", + project_name: model.project.name, + project_path: model.project.path, + project_id: model.project_id, + user_name: model.user.name, + user_email: model.user.email, + project_access: model.repo_access_human, + created_at: model.created_at + }) + + end end def after_destroy(model) - SystemHooksService.execute_hooks_for(model, :destroy) + if model.kind_of? Project + SystemHook.all_hooks_fire({ + event_name: "project_destroy", + name: model.name, + path: model.path, + project_id: model.id, + owner_name: model.owner.name, + owner_email: model.owner.email, + }) + elsif model.kind_of? User + SystemHook.all_hooks_fire({ + event_name: "user_destroy", + name: model.name, + email: model.email + }) + + elsif model.kind_of? UsersProject + SystemHook.all_hooks_fire({ + event_name: "user_remove_from_team", + project_name: model.project.name, + project_path: model.project.path, + project_id: model.project_id, + user_name: model.user.name, + user_email: model.user.email, + project_access: model.repo_access_human + }) + end end end diff --git a/app/observers/user_observer.rb b/app/observers/user_observer.rb index 6bb3c471..654621f7 100644 --- a/app/observers/user_observer.rb +++ b/app/observers/user_observer.rb @@ -1,21 +1,17 @@ -class UserObserver < BaseObserver +class UserObserver < ActiveRecord::Observer def after_create(user) log_info("User \"#{user.name}\" (#{user.email}) was created") - notification.new_user(user) + Notify.new_user_email(user.id, user.password).deliver end def after_destroy user log_info("User \"#{user.name}\" (#{user.email}) was removed") end - def after_save user - if user.username_changed? - if user.namespace - user.namespace.update_attributes(path: user.username) - else - user.create_namespace!(path: user.username, name: user.username) - end - end + protected + + def log_info message + Gitlab::AppLogger.info message end end diff --git a/app/observers/users_project_observer.rb b/app/observers/users_project_observer.rb index ca9649c7..0c9c2b26 100644 --- a/app/observers/users_project_observer.rb +++ b/app/observers/users_project_observer.rb @@ -1,26 +1,21 @@ -class UsersProjectObserver < BaseObserver +class UsersProjectObserver < ActiveRecord::Observer def after_commit(users_project) return if users_project.destroyed? + Notify.project_access_granted_email(users_project.id).deliver end def after_create(users_project) Event.create( project_id: users_project.project.id, - action: Event::JOINED, + action: Event::Joined, author_id: users_project.user.id ) - - notification.new_team_member(users_project) - end - - def after_update(users_project) - notification.update_team_member(users_project) end def after_destroy(users_project) Event.create( project_id: users_project.project.id, - action: Event::LEFT, + action: Event::Left, author_id: users_project.user.id ) end diff --git a/app/roles/account.rb b/app/roles/account.rb new file mode 100644 index 00000000..b80fbba0 --- /dev/null +++ b/app/roles/account.rb @@ -0,0 +1,73 @@ +module Account + # Returns a string for use as a Gitolite user identifier + # + # Note that Gitolite 2.x requires the following pattern for users: + # + # ^@?[0-9a-zA-Z][0-9a-zA-Z._\@+-]*$ + def identifier + # Replace non-word chars with underscores, then make sure it starts with + # valid chars + email.gsub(/\W/, '_').gsub(/\A([\W\_])+/, '') + end + + def is_admin? + admin + end + + def require_ssh_key? + keys.count == 0 + end + + def can_create_project? + projects_limit > my_own_projects.count + end + + def can_create_group? + is_admin? + end + + def last_activity_project + projects.first + end + + def first_name + name.split.first unless name.blank? + end + + def cared_merge_requests + MergeRequest.where("author_id = :id or assignee_id = :id", id: self.id).opened + end + + def project_ids + projects.map(&:id) + 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? + (my_own_projects.count.to_f / projects_limit) * 100 + end + + def recent_push project_id = nil + # Get push events not earlier than 2 hours ago + events = recent_events.code_push.where("created_at > ?", Time.now - 2.hours) + events = events.where(project_id: project_id) if project_id + + # Take only latest one + events = events.recent.limit(1).first + end + + def projects_sorted_by_activity + projects.order("(SELECT max(events.created_at) FROM events WHERE events.project_id = projects.id) DESC") + end +end diff --git a/app/roles/authority.rb b/app/roles/authority.rb new file mode 100644 index 00000000..e0796d5f --- /dev/null +++ b/app/roles/authority.rb @@ -0,0 +1,58 @@ +module Authority + # Compatible with all access rights + # Should be rewrited for new access rights + def add_access(user, *access) + access = if access.include?(:admin) + { project_access: UsersProject::MASTER } + elsif access.include?(:write) + { project_access: UsersProject::DEVELOPER } + else + { project_access: UsersProject::REPORTER } + end + opts = { user: user } + opts.merge!(access) + users_projects.create(opts) + end + + def reset_access(user) + users_projects.where(project_id: self.id, user_id: user.id).destroy if self.id + end + + def repository_readers + keys = Key.joins({user: :users_projects}). + where("users_projects.project_id = ? AND users_projects.project_access = ?", id, UsersProject::REPORTER) + keys.map(&:identifier) + deploy_keys.map(&:identifier) + end + + def repository_writers + keys = Key.joins({user: :users_projects}). + where("users_projects.project_id = ? AND users_projects.project_access = ?", id, UsersProject::DEVELOPER) + keys.map(&:identifier) + end + + def repository_masters + keys = Key.joins({user: :users_projects}). + where("users_projects.project_id = ? AND users_projects.project_access = ?", id, UsersProject::MASTER) + keys.map(&:identifier) + end + + def allow_read_for?(user) + !users_projects.where(user_id: user.id).empty? + end + + def guest_access_for?(user) + !users_projects.where(user_id: user.id).empty? + end + + def report_access_for?(user) + !users_projects.where(user_id: user.id, project_access: [UsersProject::REPORTER, UsersProject::DEVELOPER, UsersProject::MASTER]).empty? + end + + def dev_access_for?(user) + !users_projects.where(user_id: user.id, project_access: [UsersProject::DEVELOPER, UsersProject::MASTER]).empty? + end + + def master_access_for?(user) + !users_projects.where(user_id: user.id, project_access: [UsersProject::MASTER]).empty? + end +end diff --git a/app/roles/git_host.rb b/app/roles/git_host.rb new file mode 100644 index 00000000..aa620f77 --- /dev/null +++ b/app/roles/git_host.rb @@ -0,0 +1,5 @@ +module GitHost + def git_host + Gitlab::Gitolite.new + end +end diff --git a/app/models/concerns/issuable.rb b/app/roles/issue_commonality.rb similarity index 58% rename from app/models/concerns/issuable.rb rename to app/roles/issue_commonality.rb index 85337583..79831cdc 100644 --- a/app/models/concerns/issuable.rb +++ b/app/roles/issue_commonality.rb @@ -1,10 +1,5 @@ -# == Issuable concern -# # Contains common functionality shared between Issues and MergeRequests -# -# Used by Issue, MergeRequest -# -module Issuable +module IssueCommonality extend ActiveSupport::Concern included do @@ -17,13 +12,13 @@ module Issuable validates :project, presence: true validates :author, presence: true validates :title, presence: true, length: { within: 0..255 } + validates :closed, inclusion: { in: [true, false] } - scope :opened, -> { with_state(:opened) } - scope :closed, -> { with_state(:closed) } + scope :opened, where(closed: false) + scope :closed, where(closed: true) scope :of_group, ->(group) { where(project_id: group.project_ids) } - scope :of_user_team, ->(team) { where(project_id: team.project_ids, assignee_id: team.member_ids) } scope :assigned, ->(u) { where(assignee_id: u.id)} - scope :recent, -> { order("created_at DESC") } + scope :recent, order("created_at DESC") delegate :name, :email, @@ -61,38 +56,12 @@ module Issuable assignee_id_changed? end - # - # Votes - # - - # Return the number of -1 comments (downvotes) - def downvotes - notes.select(&:downvote?).size + def is_being_closed? + closed_changed? && closed end - def downvotes_in_percent - if votes_count.zero? - 0 - else - 100.0 - upvotes_in_percent - end + def is_being_reopened? + closed_changed? && !closed end - # Return the number of +1 comments (upvotes) - def upvotes - notes.select(&:upvote?).size - end - - def upvotes_in_percent - if votes_count.zero? - 0 - else - 100.0 / votes_count * upvotes - end - end - - # Return the total number of votes - def votes_count - upvotes + downvotes - end end diff --git a/app/roles/push_event.rb b/app/roles/push_event.rb new file mode 100644 index 00000000..8ce71b54 --- /dev/null +++ b/app/roles/push_event.rb @@ -0,0 +1,100 @@ +module PushEvent + def valid_push? + data[:ref] + rescue => ex + false + end + + def tag? + data[:ref]["refs/tags"] + end + + def branch? + data[:ref]["refs/heads"] + end + + def new_branch? + commit_from =~ /^00000/ + end + + def new_ref? + commit_from =~ /^00000/ + end + + def rm_ref? + commit_to =~ /^00000/ + end + + def md_ref? + !(rm_ref? || new_ref?) + end + + def commit_from + data[:before] + end + + def commit_to + data[:after] + end + + def ref_name + if tag? + tag_name + else + branch_name + end + end + + def branch_name + @branch_name ||= data[:ref].gsub("refs/heads/", "") + end + + def tag_name + @tag_name ||= data[:ref].gsub("refs/tags/", "") + end + + # Max 20 commits from push DESC + def commits + @commits ||= data[:commits].map { |commit| project.commit(commit[:id]) }.reverse + end + + def commits_count + data[:total_commits_count] || commits.count || 0 + end + + def ref_type + tag? ? "tag" : "branch" + end + + def push_action_name + if new_ref? + "pushed new" + elsif rm_ref? + "deleted" + else + "pushed to" + end + end + + def parent_commit + project.commit(commit_from) + rescue => ex + nil + end + + def last_commit + project.commit(commit_to) + rescue => ex + nil + end + + def push_with_commits? + md_ref? && commits.any? && parent_commit && last_commit + rescue Grit::NoSuchPathError + false + end + + def last_push_to_non_root? + branch? && project.default_branch != branch_name + end +end diff --git a/app/roles/push_observer.rb b/app/roles/push_observer.rb new file mode 100644 index 00000000..2ee60646 --- /dev/null +++ b/app/roles/push_observer.rb @@ -0,0 +1,144 @@ +# Includes methods for handling Git Push events +# +# Triggered by PostReceive job +module PushObserver + # This method will be called after each post receive and only if the provided + # user is present in GitLab. + # + # All callbacks for post receive should be placed here. + def trigger_post_receive(oldrev, newrev, ref, user) + data = post_receive_data(oldrev, newrev, ref, user) + + # Create push event + self.observe_push(data) + + if push_to_branch? ref, oldrev + # Close merged MR + self.update_merge_requests(oldrev, newrev, ref, user) + + # Execute web hooks + self.execute_hooks(data.dup) + + # Execute project services + self.execute_services(data.dup) + end + + # Create satellite + self.satellite.create unless self.satellite.exists? + + # Discover the default branch, but only if it hasn't already been set to + # something else + if default_branch.nil? + update_attributes(default_branch: discover_default_branch) + end + end + + def push_to_branch? ref, oldrev + ref_parts = ref.split('/') + + # Return if this is not a push to a branch (e.g. new commits) + !(ref_parts[1] !~ /heads/ || oldrev == "00000000000000000000000000000000") + end + + def observe_push(data) + Event.create( + project: self, + action: Event::Pushed, + data: data, + author_id: data[:user_id] + ) + end + + def execute_hooks(data) + hooks.each { |hook| hook.execute(data) } + end + + def execute_services(data) + services.each do |service| + + # Call service hook only if it is active + service.execute(data) if service.active + end + end + + # Produce a hash of post-receive data + # + # data = { + # before: String, + # after: String, + # ref: String, + # user_id: String, + # user_name: String, + # repository: { + # name: String, + # url: String, + # description: String, + # homepage: String, + # }, + # commits: Array, + # total_commits_count: Fixnum + # } + # + def post_receive_data(oldrev, newrev, ref, user) + + push_commits = commits_between(oldrev, newrev) + + # Total commits count + push_commits_count = push_commits.size + + # Get latest 20 commits ASC + push_commits_limited = push_commits.last(20) + + # Hash to be passed as post_receive_data + data = { + before: oldrev, + after: newrev, + ref: ref, + user_id: user.id, + user_name: user.name, + repository: { + name: name, + url: web_url, + description: description, + homepage: web_url, + }, + commits: [], + total_commits_count: push_commits_count + } + + # For perfomance purposes maximum 20 latest commits + # will be passed as post receive hook data. + # + push_commits_limited.each do |commit| + data[:commits] << { + id: commit.id, + message: commit.safe_message, + timestamp: commit.date.xmlschema, + url: "#{Gitlab.config.url}/#{code}/commits/#{commit.id}", + author: { + name: commit.author_name, + email: commit.author_email + } + } + end + + data + end + + def update_merge_requests(oldrev, newrev, ref, user) + return true unless ref =~ /heads/ + branch_name = ref.gsub("refs/heads/", "") + c_ids = self.commits_between(oldrev, newrev).map(&:id) + + # Update code for merge requests + mrs = self.merge_requests.opened.find_all_by_branch(branch_name).all + mrs.each { |merge_request| merge_request.reload_code; merge_request.mark_as_unchecked } + + # Close merge requests + mrs = self.merge_requests.opened.where(target_branch: branch_name).all + mrs = mrs.select(&:last_commit).select { |mr| c_ids.include?(mr.last_commit.id) } + mrs.each { |merge_request| merge_request.merge!(user.id) } + + true + end +end diff --git a/app/roles/repository.rb b/app/roles/repository.rb new file mode 100644 index 00000000..88468117 --- /dev/null +++ b/app/roles/repository.rb @@ -0,0 +1,191 @@ +module Repository + include GitHost + + def valid_repo? + repo + rescue + errors.add(:path, "Invalid repository path") + false + end + + def empty_repo? + !repo_exists? || !has_commits? + end + + def commit(commit_id = nil) + Commit.find_or_first(repo, commit_id, root_ref) + end + + def fresh_commits(n = 10) + Commit.fresh_commits(repo, n) + end + + def commits_with_refs(n = 20) + Commit.commits_with_refs(repo, n) + end + + def commits_since(date) + Commit.commits_since(repo, date) + end + + def commits(ref, path = nil, limit = nil, offset = nil) + Commit.commits(repo, ref, path, limit, offset) + end + + def last_commit_for(ref, path = nil) + commits(ref, path, 1).first + end + + def commits_between(from, to) + Commit.commits_between(repo, from, to) + end + + def satellite + @satellite ||= Gitlab::Satellite::Satellite.new(self) + end + + def has_post_receive_file? + hook_file = File.join(path_to_repo, 'hooks', 'post-receive') + File.exists?(hook_file) + end + + # Returns an Array of branch names + def branch_names + repo.branches.collect(&:name).sort + end + + # Returns an Array of Branches + def branches + repo.branches.sort_by(&:name) + end + + # Returns an Array of tag names + def tag_names + repo.tags.collect(&:name).sort.reverse + end + + # Returns an Array of Tags + def tags + repo.tags.sort_by(&:name).reverse + end + + # Returns an Array of branch and tag names + def ref_names + [branch_names + tag_names].flatten + end + + def repo + @repo ||= Grit::Repo.new(path_to_repo) + end + + def url_to_repo + git_host.url_to_repo(path) + end + + def path_to_repo + File.join(Gitlab.config.git_base_path, "#{path}.git") + end + + def update_repository + git_host.update_repository(self) + end + + def destroy_repository + git_host.remove_repository(self) + end + + def repo_exists? + @repo_exists ||= (repo && !repo.branches.empty?) + rescue + @repo_exists = false + end + + def heads + @heads ||= repo.heads + end + + def tree(fcommit, path = nil) + fcommit = commit if fcommit == :head + tree = fcommit.tree + path ? (tree / path) : tree + end + + def open_branches + if protected_branches.empty? + self.repo.heads + else + pnames = protected_branches.map(&:name) + self.repo.heads.reject { |h| pnames.include?(h.name) } + end.sort_by(&:name) + end + + # Discovers the default branch based on the repository's available branches + # + # - If no branches are present, returns nil + # - If one branch is present, returns its name + # - If two or more branches are present, returns the one that has a name + # matching root_ref (default_branch or 'master' if default_branch is nil) + def discover_default_branch + if branch_names.length == 0 + nil + elsif branch_names.length == 1 + branch_names.first + else + branch_names.select { |v| v == root_ref }.first + end + end + + def has_commits? + !!commit + rescue Grit::NoSuchPathError + false + end + + def root_ref + default_branch || "master" + end + + def root_ref?(branch) + root_ref == branch + end + + # Archive Project to .tar.gz + # + # Already packed repo archives stored at + # app_root/tmp/repositories/project_name/project_name-commit-id.tag.gz + # + def archive_repo(ref) + ref = ref || self.root_ref + commit = self.commit(ref) + return nil unless commit + + # Build file path + file_name = self.code + "-" + commit.id.to_s + ".tar.gz" + storage_path = Rails.root.join("tmp", "repositories", self.code) + file_path = File.join(storage_path, file_name) + + # Put files into a directory before archiving + prefix = self.code + "/" + + # Create file if not exists + unless File.exists?(file_path) + FileUtils.mkdir_p storage_path + file = self.repo.archive_to_file(ref, prefix, file_path) + end + + file_path + end + + def ssh_url_to_repo + url_to_repo + end + + def http_url_to_repo + http_url = [Gitlab.config.url, "/", path, ".git"].join('') + end + + # Check if current branch name is marked as protected in the system + def protected_branch? branch_name + protected_branches.map(&:name).include?(branch_name) + end +end diff --git a/lib/static_model.rb b/app/roles/static_model.rb similarity index 95% rename from lib/static_model.rb rename to app/roles/static_model.rb index 185921d8..5b64be1f 100644 --- a/lib/static_model.rb +++ b/app/roles/static_model.rb @@ -38,7 +38,7 @@ module StaticModel end def ==(other) - if other.is_a? ::StaticModel + if other.is_a? StaticModel id == other.id else super diff --git a/app/roles/team.rb b/app/roles/team.rb new file mode 100644 index 00000000..a7ba0588 --- /dev/null +++ b/app/roles/team.rb @@ -0,0 +1,52 @@ +module Team + def team_member_by_name_or_email(name = nil, email = nil) + user = users.where("name like ? or email like ?", name, email).first + users_projects.where(user: user) if user + end + + # Get Team Member record by user id + def team_member_by_id(user_id) + users_projects.find_by_user_id(user_id) + end + + # Add user to project + # with passed access role + def add_user_to_team(user, access_role) + add_user_id_to_team(user.id, access_role) + end + + # Add multiple users to project + # with same access role + def add_users_to_team(users, access_role) + add_users_ids_to_team(users.map(&:id), access_role) + end + + # Add user to project + # with passed access role by user id + def add_user_id_to_team(user_id, access_role) + users_projects.create( + user_id: user_id, + project_access: access_role + ) + end + + # Add multiple users to project + # with same access role by user ids + def add_users_ids_to_team(users_ids, access_role) + UsersProject.bulk_import(self, users_ids, access_role) + self.update_repository + end + + # Update multiple project users + # to same access role by user ids + def update_users_ids_to_role(users_ids, access_role) + UsersProject.bulk_update(self, users_ids, access_role) + self.update_repository + end + + # Delete multiple users from project by user ids + def delete_users_ids_from_team(users_ids) + UsersProject.bulk_delete(self, users_ids) + self.update_repository + end +end diff --git a/app/roles/votes.rb b/app/roles/votes.rb new file mode 100644 index 00000000..043a6feb --- /dev/null +++ b/app/roles/votes.rb @@ -0,0 +1,32 @@ +module Votes + # Return the number of +1 comments (upvotes) + def upvotes + notes.select(&:upvote?).size + end + + def upvotes_in_percent + if votes_count.zero? + 0 + else + 100.0 / votes_count * upvotes + end + end + + # Return the number of -1 comments (downvotes) + def downvotes + notes.select(&:downvote?).size + end + + def downvotes_in_percent + if votes_count.zero? + 0 + else + 100.0 - upvotes_in_percent + end + end + + # Return the total number of votes + def votes_count + upvotes + downvotes + end +end diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb deleted file mode 100644 index 383e6398..00000000 --- a/app/services/git_push_service.rb +++ /dev/null @@ -1,125 +0,0 @@ -class GitPushService - attr_accessor :project, :user, :push_data - - # This method will be called after each git update - # and only if the provided user and project is present in GitLab. - # - # All callbacks for post receive action should be placed here. - # - # Now this method do next: - # 1. Ensure project satellite exists - # 2. Update merge requests - # 3. Execute project web hooks - # 4. Execute project services - # 5. Create Push Event - # - def execute(project, user, oldrev, newrev, ref) - @project, @user = project, user - - # 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 - project.repository.expire_cache - - if push_to_branch?(ref, oldrev) - project.update_merge_requests(oldrev, newrev, ref, @user) - project.execute_hooks(@push_data.dup) - project.execute_services(@push_data.dup) - end - end - - # This method provide a sample data - # generated with post_receive_data method - # for given project - # - def sample_data(project, user) - @project, @user = project, user - commits = project.repository.commits(project.default_branch, nil, 3) - post_receive_data(commits.last.id, commits.first.id, "refs/heads/#{project.default_branch}") - end - - protected - - def create_push_event - Event.create( - project: project, - action: Event::PUSHED, - data: push_data, - author_id: push_data[:user_id] - ) - end - - # Produce a hash of post-receive data - # - # data = { - # before: String, - # after: String, - # ref: String, - # user_id: String, - # user_name: String, - # repository: { - # name: String, - # url: String, - # description: String, - # homepage: String, - # }, - # commits: Array, - # total_commits_count: Fixnum - # } - # - def post_receive_data(oldrev, newrev, ref) - push_commits = project.repository.commits_between(oldrev, newrev) - - # Total commits count - push_commits_count = push_commits.size - - # Get latest 20 commits ASC - push_commits_limited = push_commits.last(20) - - # Hash to be passed as post_receive_data - data = { - before: oldrev, - after: newrev, - ref: ref, - user_id: user.id, - user_name: user.name, - repository: { - name: project.name, - url: project.url_to_repo, - description: project.description, - homepage: project.web_url, - }, - commits: [], - total_commits_count: push_commits_count - } - - # For performance purposes maximum 20 latest commits - # will be passed as post receive hook data. - # - push_commits_limited.each do |commit| - data[:commits] << { - id: commit.id, - message: commit.safe_message, - timestamp: commit.date.xmlschema, - url: "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/commit/#{commit.id}", - author: { - name: commit.author_name, - email: commit.author_email - } - } - end - - data - end - - def push_to_branch? ref, oldrev - ref_parts = ref.split('/') - - # Return if this is not a push to a branch (e.g. new commits) - !(ref_parts[1] !~ /heads/ || oldrev == "00000000000000000000000000000000") - end -end diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb deleted file mode 100644 index f8779fd5..00000000 --- a/app/services/notification_service.rb +++ /dev/null @@ -1,197 +0,0 @@ -# NotificationService class -# -# Used for notifing users with emails about different events -# -# Ex. -# NotificationService.new.new_issue(issue, current_user) -# -class NotificationService - # Always notify user about ssh key added - # only if ssh key is not deploy key - # - # This is security email so it will be sent - # even if user disabled notifications - def new_key(key) - if key.user - Notify.delay.new_ssh_key_email(key.id) - end - end - - # When create an issue we should send next emails: - # - # * issue assignee if his notification level is not Disabled - # * project team members with notification level higher then Participating - # - def new_issue(issue, current_user) - new_resource_email(issue, 'new_issue_email') - end - - # When we close an issue we should send next emails: - # - # * issue author if his notification level is not Disabled - # * issue assignee if his notification level is not Disabled - # * project team members with notification level higher then Participating - # - def close_issue(issue, current_user) - close_resource_email(issue, current_user, 'closed_issue_email') - end - - # When we reassign an issue we should send next emails: - # - # * issue old assignee if his notification level is not Disabled - # * issue new assignee if his notification level is not Disabled - # - def reassigned_issue(issue, current_user) - reassign_resource_email(issue, current_user, 'reassigned_issue_email') - end - - - # When create a merge request we should send next emails: - # - # * mr assignee if his notification level is not Disabled - # - def new_merge_request(merge_request, current_user) - new_resource_email(merge_request, 'new_merge_request_email') - end - - # When we reassign a merge_request we should send next emails: - # - # * merge_request old assignee if his notification level is not Disabled - # * merge_request assignee if his notification level is not Disabled - # - def reassigned_merge_request(merge_request, current_user) - reassign_resource_email(merge_request, current_user, 'reassigned_merge_request_email') - end - - # When we close a merge request we should send next emails: - # - # * merge_request author if his notification level is not Disabled - # * merge_request assignee if his notification level is not Disabled - # * project team members with notification level higher then Participating - # - def close_mr(merge_request, current_user) - close_resource_email(merge_request, current_user, 'closed_merge_request_email') - end - - # When we merge a merge request we should send next emails: - # - # * merge_request author if his notification level is not Disabled - # * merge_request assignee if his notification level is not Disabled - # * project team members with notification level higher then Participating - # - def merge_mr(merge_request) - recipients = reject_muted_users([merge_request.author, merge_request.assignee]) - recipients = recipients.concat(project_watchers(merge_request.project)).uniq - - recipients.each do |recipient| - Notify.delay.merged_merge_request_email(recipient.id, merge_request.id) - end - end - - # Notify new user with email after creation - def new_user(user) - # Dont email omniauth created users - Notify.delay.new_user_email(user.id, user.password) unless user.extern_uid? - end - - # Notify users on new note in system - # - # TODO: split on methods and refactor - # - def new_note(note) - # ignore wall messages - return true unless note.noteable_type.present? - - opts = { noteable_type: note.noteable_type, project_id: note.project_id } - - if note.commit_id.present? - opts.merge!(commit_id: note.commit_id) - recipients = [note.commit_author] - else - opts.merge!(noteable_id: note.noteable_id) - target = note.noteable - recipients = [] - recipients << target.assignee if target.respond_to?(:assignee) - recipients << target.author if target.respond_to?(:author) - end - - # Get users who left comment in thread - recipients = recipients.concat(User.where(id: Note.where(opts).pluck(:author_id))) - - # Merge project watchers - recipients = recipients.concat(project_watchers(note.project)).compact.uniq - - # Reject mutes users - recipients = reject_muted_users(recipients) - - # Reject author - recipients.delete(note.author) - - # build notify method like 'note_commit_email' - notify_method = "note_#{note.noteable_type.underscore}_email".to_sym - - recipients.each do |recipient| - Notify.delay.send(notify_method, recipient.id, note.id) - end - end - - def new_team_member(users_project) - Notify.delay.project_access_granted_email(users_project.id) - end - - def update_team_member(users_project) - Notify.delay.project_access_granted_email(users_project.id) - end - - protected - - # Get project users with WATCH notification level - def project_watchers(project) - project.users.where(notification_level: Notification::N_WATCH) - end - - # Remove users with disabled notifications from array - # Also remove duplications and nil recipients - def reject_muted_users(users) - users.compact.uniq.reject do |user| - user.notification.disabled? - end - end - - def new_resource_email(target, method) - recipients = reject_muted_users([target.assignee]) - recipients = recipients.concat(project_watchers(target.project)).uniq - recipients.delete(target.author) - - recipients.each do |recipient| - Notify.delay.send(method, recipient.id, target.id) - end - end - - def close_resource_email(target, current_user, method) - recipients = reject_muted_users([target.author, target.assignee]) - recipients = recipients.concat(project_watchers(target.project)).uniq - recipients.delete(current_user) - - recipients.each do |recipient| - Notify.delay.send(method, recipient.id, target.id, current_user.id) - end - end - - def reassign_resource_email(target, current_user, method) - recipients = User.where(id: [target.assignee_id, target.assignee_id_was]) - - # Add watchers to email list - recipients = recipients.concat(project_watchers(target.project)) - - # reject users with disabled notifications - recipients = reject_muted_users(recipients) - - # Reject me from recipients if I reassign an item - recipients.delete(current_user) - - recipients.each do |recipient| - Notify.delay.send(method, recipient.id, target.id, target.assignee_id_was) - end - end -end diff --git a/app/services/project_transfer_service.rb b/app/services/project_transfer_service.rb deleted file mode 100644 index 3b8c4847..00000000 --- a/app/services/project_transfer_service.rb +++ /dev/null @@ -1,36 +0,0 @@ -# ProjectTransferService class -# -# Used for transfer project to another namespace -# -class ProjectTransferService - include Gitlab::ShellAdapter - - class TransferError < StandardError; end - - attr_accessor :project - - def transfer(project, new_namespace) - Project.transaction do - old_path = project.path_with_namespace - new_path = File.join(new_namespace.try(:path) || '', project.path) - - if Project.where(path: project.path, namespace_id: new_namespace.try(:id)).present? - raise TransferError.new("Project with same path in target namespace already exists") - end - - project.namespace = new_namespace - project.save! - - # Move main repository - unless gitlab_shell.mv_repository(old_path, new_path) - raise TransferError.new('Cannot move project') - end - - # Move wiki repo also if present - gitlab_shell.mv_repository("#{old_path}.wiki", "#{new_path}.wiki") - - true - end - end -end - diff --git a/app/services/system_hooks_service.rb b/app/services/system_hooks_service.rb deleted file mode 100644 index 132bb14a..00000000 --- a/app/services/system_hooks_service.rb +++ /dev/null @@ -1,59 +0,0 @@ -class SystemHooksService - def self.execute_hooks_for(model, event) - execute_hooks(build_event_data(model, event)) - end - - private - - def self.execute_hooks(data) - SystemHook.all.each do |sh| - async_execute_hook sh, data - end - end - - def self.async_execute_hook(hook, data) - Sidekiq::Client.enqueue(SystemHookWorker, hook.id, data) - end - - def self.build_event_data(model, event) - data = { - event_name: build_event_name(model, event), - created_at: model.created_at - } - - case model - when Project - data.merge!({ - name: model.name, - path: model.path, - project_id: model.id, - owner_name: model.owner.name, - owner_email: model.owner.email - }) - when User - data.merge!({ - name: model.name, - email: model.email - }) - when UsersProject - data.merge!({ - project_name: model.project.name, - project_path: model.project.path, - project_id: model.project_id, - user_name: model.user.name, - user_email: model.user.email, - project_access: model.repo_access_human - }) - end - end - - def self.build_event_name(model, event) - case model - when UsersProject - return "user_add_to_team" if event == :create - return "user_remove_from_team" if event == :destroy - else - "#{model.class.name.downcase}_#{event.to_s}" - end - end -end diff --git a/app/uploaders/attachment_uploader.rb b/app/uploaders/attachment_uploader.rb index 200700b8..bb7dc0da 100644 --- a/app/uploaders/attachment_uploader.rb +++ b/app/uploaders/attachment_uploader.rb @@ -1,30 +1,49 @@ # encoding: utf-8 class AttachmentUploader < CarrierWave::Uploader::Base - storage :file + # Include RMagick or ImageScience support: + # include CarrierWave::RMagick + # include CarrierWave::MiniMagick + # include CarrierWave::ImageScience + + # Choose what kind of storage to use for this uploader: + storage :file + # storage :fog + + # Override the directory where uploaded files will be stored. + # This is a sensible default for uploaders that are meant to be mounted: def store_dir "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}" end - def image? - img_ext = %w(png jpg jpeg) - if file.respond_to?(:extension) - img_ext.include?(file.extension) - else - # Not all CarrierWave storages respond to :extension - ext = file.path.split('.').last - img_ext.include?(ext) - end - rescue - false - end + # Provide a default URL as a default if there hasn't been a file uploaded: + # def default_url + # "/images/fallback/" + [version_name, "default.png"].compact.join('_') + # end + + # Process files as they are uploaded: + # process scale: [200, 300] + # + # def scale(width, height) + # # do something + # end + + # Create different versions of your uploaded files: + # version :thumb do + # process scale: [50, 50] + # end + + # Add a white list of extensions which are allowed to be uploaded. + # For images you might use something like this: + # def extension_white_list + # %w(jpg jpeg gif png) + # end + + # Override the filename of the uploaded files: + # Avoid using model.id or version_name here, see uploader/store.rb for details. + # def filename + # "something.jpg" if original_filename + # end - def secure_url - if self.class.storage == CarrierWave::Storage::File - "/files/#{model.class.to_s.underscore}/#{model.id}/#{file.filename}" - else - url - end - end end diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml index 46a87629..b0b59a46 100644 --- a/app/views/admin/dashboard/index.html.haml +++ b/app/views/admin/dashboard/index.html.haml @@ -1,75 +1,55 @@ .admin_dash.row .span4 .ui-box - %h5.title Projects + %h5 + Resque Workers + .data.padded + - if @resque_accessible + = link_to admin_resque_path do + %h1{class: @workers.present? ? "cgreen" : "cred"} + = @workers.count + %hr + %p + %strong{class: @pending_jobs > 0 ? "cred" : "cgreen"} + #{@pending_jobs} post receive jobs waiting + - else + = link_to admin_resque_path do + %h1.cdark ? + %hr + %p + %strong Resque status unknown + + + .span4 + .ui-box + %h5 Projects .data.padded = link_to admin_projects_path do %h1= Project.count %hr - = link_to 'New Project', new_project_path, class: "btn btn-small" + = link_to 'New Project', new_admin_project_path, class: "btn small" .span4 .ui-box - %h5.title Groups - .data.padded - = link_to admin_groups_path do - %h1= Group.count - %hr - = link_to 'New Group', new_admin_group_path, class: "btn btn-small" - .span4 - .ui-box - %h5.title Users + %h5 Users .data.padded = link_to admin_users_path do %h1= User.count %hr - = link_to 'New User', new_admin_user_path, class: "btn btn-small" + = link_to 'New User', new_admin_user_path, class: "btn small" + .row - .span4 - %h4 Latest projects + .span6 + %h3 Latest projects %hr - @projects.each do |project| %p - = link_to project.name_with_namespace, [:admin, project] - %span.light.pull-right - = time_ago_in_words project.created_at - ago - - .span4 - %h4 Latest users + = link_to project.name, [:admin, project] + .span6 + %h3 Latest users %hr - @users.each do |user| %p = link_to [:admin, user] do = user.name - %span.light.pull-right - = time_ago_in_words user.created_at - ago - - .span4 - %h4 Stats - %hr - %p - Issues - %span.light.pull-right - = Issue.count - %p - Merge Requests - %span.light.pull-right - = MergeRequest.count - %p - Notes - %span.light.pull-right - = Note.count - %p - Snippets - %span.light.pull-right - = Snippet.count - %p - SSH Keys - %span.light.pull-right - = Key.count - %p - Milestones - %span.light.pull-right - = Milestone.count + %small= user.email diff --git a/app/views/admin/groups/_form.html.haml b/app/views/admin/groups/_form.html.haml new file mode 100644 index 00000000..e85cce66 --- /dev/null +++ b/app/views/admin/groups/_form.html.haml @@ -0,0 +1,19 @@ += form_for [:admin, @group] do |f| + - if @group.errors.any? + .alert-message.block-message.error + %span= @group.errors.full_messages.first + .clearfix.group_name_holder + = f.label :name do + Group name is + .input + = f.text_field :name, placeholder: "Example Group", class: "xxlarge" + .clearfix + = f.label :code do + URL + .input + .input-prepend + %span.add-on= web_app_url + 'groups/' + = f.text_field :code, placeholder: "example" + + .form-actions + = f.submit 'Save group', class: "btn save-btn" diff --git a/app/views/admin/groups/edit.html.haml b/app/views/admin/groups/edit.html.haml index bb1398f6..9904122c 100644 --- a/app/views/admin/groups/edit.html.haml +++ b/app/views/admin/groups/edit.html.haml @@ -1,31 +1,3 @@ %h3.page_title Edit Group -%hr -= form_for [:admin, @group] do |f| - - if @group.errors.any? - .alert.alert-error - %span= @group.errors.full_messages.first - .clearfix.group_name_holder - = f.label :name do - Group name is - .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 - %span.cred Group path is - .input - = f.text_field :path, placeholder: "example-group", class: "xxlarge danger" - %ul.cred - %li Changing group path can have unintended side effects. - %li Renaming group path will rename directory for all related projects - %li It will change web url for access group and group projects. - %li It will change the git path to repositories under this group. - - .form-actions - = f.submit 'Edit group', class: "btn btn-remove" - = link_to 'Cancel', admin_groups_path, class: "btn btn-cancel" +%br += render 'form' diff --git a/app/views/admin/groups/index.html.haml b/app/views/admin/groups/index.html.haml index 0029cc78..6a0794cf 100644 --- a/app/views/admin/groups/index.html.haml +++ b/app/views/admin/groups/index.html.haml @@ -1,37 +1,29 @@ += render 'admin/shared/projects_head' %h3.page_title Groups %small allows you to keep projects organized. Use groups for uniting related projects. - = link_to 'New Group', new_admin_group_path, class: "btn btn-small pull-right" + = link_to 'New Group', new_admin_group_path, class: "btn small right" %br = form_tag admin_groups_path, method: :get, class: 'form-inline' do = text_field_tag :name, params[:name], class: "xlarge" - = submit_tag "Search", class: "btn submit btn-primary" + = submit_tag "Search", class: "btn submit primary" %table %thead - %tr - %th - Name - %i.icon-sort-down - %th Description - %th Path - %th Projects - %th Owner - %th.cred Danger Zone! + %th Name + %th Code + %th Projects + %th Edit + %th.cred Danger Zone! - @groups.each do |group| %tr - %td - %strong= link_to group.name, [:admin, group] - %td= truncate group.description - %td= group.path + %td= link_to group.name, [:admin, group] + %td= group.code %td= group.projects.count - %td - = link_to group.owner_name, admin_user_path(group.owner) - %td.bgred - = 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" + %td= link_to 'Edit', edit_admin_group_path(group), id: "edit_#{dom_id(group)}", class: "btn small" + %td.bgred= link_to 'Destroy', [:admin, group], confirm: "REMOVE #{group.name}? Are you sure?", method: :delete, class: "btn small danger" = paginate @groups, theme: "admin" diff --git a/app/views/admin/groups/new.html.haml b/app/views/admin/groups/new.html.haml index 3fa63e1b..d6b6ea15 100644 --- a/app/views/admin/groups/new.html.haml +++ b/app/views/admin/groups/new.html.haml @@ -1,27 +1,3 @@ %h3.page_title New Group -%hr -= form_for [:admin, @group] do |f| - - if @group.errors.any? - .alert.alert-error - %span= @group.errors.full_messages.first - .clearfix - = f.label :name do - Group name is - .input - = f.text_field :name, placeholder: "Ex. OpenSource", class: "xxlarge left" - .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 - %li Group is kind of directory for several projects - %li All created groups are private - %li People within a group see only projects they have access to - %li All projects of group will be stored in group directory - %li You will be able to move existing projects into group +%br += render 'form' diff --git a/app/views/admin/groups/show.html.haml b/app/views/admin/groups/show.html.haml index 63ea78fd..309a10e5 100644 --- a/app/views/admin/groups/show.html.haml +++ b/app/views/admin/groups/show.html.haml @@ -1,5 +1,9 @@ += render 'admin/shared/projects_head' %h3.page_title Group: #{@group.name} + = link_to edit_admin_group_path(@group), class: "btn right" do + %i.icon-edit + Edit %br %table.zebra-striped @@ -13,121 +17,36 @@ Name: %td = @group.name -   - = link_to edit_admin_group_path(@group), class: "btn btn-small pull-right" do - %i.icon-edit - Edit %tr %td %b - Description: + Code: %td - = @group.description - %tr - %td - %b - Path: - %td - %span.monospace= File.join(Gitlab.config.gitlab_shell.repos_path, @group.path) + = @group.code %tr %td %b Owner: %td = @group.owner_name - .pull-right - = link_to "#", class: "btn btn-small change-owner-link" do - %i.icon-edit - Change owner - - %tr.change-owner-holder.hide - %td.bgred - %b.cred - New Owner: - %td.bgred - = form_for [:admin, @group] do |f| - = f.select :owner_id, User.all.map { |user| [user.name, user.id] }, {}, {class: 'chosen'} - %div - = f.submit 'Change Owner', class: "btn btn-remove" - = link_to "Cancel", "#", class: "btn change-owner-cancel-link" - -- if @group.projects.any? - %fieldset - %legend Projects (#{@group.projects.count}) - %table - %thead - %tr - %th Project name - %th Path - %th Users - %th.cred Danger Zone! - - @group.projects.each do |project| - %tr - %td - = link_to project.name_with_namespace, [:admin, project] - %td - %span.monospace= project.path_with_namespace + ".git" - %td= project.users.count - %td.bgred - = link_to 'Transfer project to global namespace', remove_project_admin_group_path(@group, project_id: project.id), confirm: 'Remove project from group and move to global namespace. Are you sure?', method: :delete, class: "btn btn-remove small" - - = form_tag project_teams_update_admin_group_path(@group), id: "new_team_member", class: "bulk_import", method: :put do - %table.zebra-striped - %thead - %tr - %th Users - %th Project Access: - - - @group.users.each do |user| - - next unless user - %tr{class: "user_#{user.id}"} - %td.name= link_to user.name, admin_user_path(user) - %td.projects_access - - user.authorized_projects.in_namespace(@group).each do |project| - - u_p = user.users_projects.in_project(project).first - - next unless u_p - %span - = project.name_with_namespace - = link_to "(#{ u_p.project_access_human })", edit_admin_project_member_path(project, user) - %tr - %td.input= select_tag :user_ids, options_from_collection_for_select(@users , :id, :name), multiple: true, data: {placeholder: 'Select users'}, class: 'chosen span5' - %td= select_tag :project_access, options_for_select(Project.access_options), {class: "project-access-select chosen span3"} - - %tr - %td= submit_tag 'Add user to projects in group', class: "btn btn-primary" - %td - Read more about project permissions - %strong= link_to "here", help_permissions_path, class: "vlink" - -- else - %fieldset - %legend Group is empty +.ui-box + %h5 + Projects + %small + (#{@group.projects.count}) + %ul.unstyled + - @group.projects.each do |project| + %li.wll + %strong + = link_to project.name, [:admin, project] + .right + = link_to 'Remove from group', remove_project_admin_group_path(@group, project_id: project.id), confirm: 'Are you sure?', method: :delete, class: "btn danger small" + .clearfix +%br +%h3 Add new project +%br = form_tag project_update_admin_group_path(@group), class: "bulk_import", method: :put do - %fieldset - %legend Move projects to group - .alert - You can move only projects with existing repos - %br - Group projects will be moved in group directory and will not be accessible by old path - .clearfix - = label_tag :project_ids do - Projects - .input - = select_tag :project_ids, options_from_collection_for_select(@projects , :id, :name_with_namespace), multiple: true, data: {placeholder: 'Select projects'}, class: 'chosen span5' - .form-actions - = submit_tag 'Add', class: "btn btn-primary" - -:javascript - $(function(){ - var modal = $('.change-owner-holder'); - $('.change-owner-link').bind("click", function(){ - $(this).hide(); - modal.show(); - }); - $('.change-owner-cancel-link').bind("click", function(){ - modal.hide(); - $('.change-owner-link').show(); - }) - }) - + = select_tag :project_ids, options_from_collection_for_select(@projects , :id, :name), multiple: true, data: {placeholder: 'Select projects'}, class: 'chosen span5' + .form-actions + = submit_tag 'Add', class: "btn primary" diff --git a/app/views/admin/hooks/index.html.haml b/app/views/admin/hooks/index.html.haml index acbf7a10..f17355fb 100644 --- a/app/views/admin/hooks/index.html.haml +++ b/app/views/admin/hooks/index.html.haml @@ -7,7 +7,7 @@ = form_for @hook, as: :hook, url: admin_hooks_path, html: { class: 'form-inline' } do |f| -if @hook.errors.any? - .alert.alert-error + .alert-message.block-message.error - @hook.errors.full_messages.each do |msg| %p= msg .clearfix @@ -15,7 +15,7 @@ .input = f.text_field :url, class: "text_field xxlarge"   - = f.submit "Add System Hook", class: "btn btn-primary" + = f.submit "Add System Hook", class: "btn primary" %hr -if @hooks.any? @@ -33,7 +33,7 @@ %td = link_to admin_hook_path(hook) do %strong= hook.url - = link_to 'Test Hook', admin_hook_test_path(hook), class: "btn btn-small pull-right" + = link_to 'Test Hook', admin_hook_test_path(hook), class: "btn small right" %td POST %td - = link_to 'Remove', admin_hook_path(hook), confirm: 'Are you sure?', method: :delete, class: "btn btn-remove btn-small pull-right" + = link_to 'Remove', admin_hook_path(hook), confirm: 'Are you sure?', method: :delete, class: "danger btn small right" diff --git a/app/views/admin/logs/show.html.haml b/app/views/admin/logs/show.html.haml index fef72186..0efe6db7 100644 --- a/app/views/admin/logs/show.html.haml +++ b/app/views/admin/logs/show.html.haml @@ -3,22 +3,12 @@ = link_to "githost.log", "#githost", 'data-toggle' => 'tab' %li = link_to "application.log", "#application", 'data-toggle' => 'tab' - %li - = link_to "production.log", "#production", 'data-toggle' => 'tab' - %li - = link_to "sidekiq.log", "#sidekiq", 'data-toggle' => 'tab' - -%p.light To prevent performance issues admin logs output the last 2000 lines .tab-content .tab-pane.active#githost .file_holder#README .file_title %i.icon-file githost.log - .pull-right - = link_to '#', class: 'log-bottom' do - %i.icon-arrow-down - Scroll down .file_content.logs %ol - Gitlab::GitLogger.read_latest.each do |line| @@ -29,40 +19,8 @@ .file_title %i.icon-file application.log - .pull-right - = link_to '#', class: 'log-bottom' do - %i.icon-arrow-down - Scroll down .file_content.logs %ol - Gitlab::AppLogger.read_latest.each do |line| %li %p= line - .tab-pane#production - .file_holder#README - .file_title - %i.icon-file - production.log - .pull-right - = link_to '#', class: 'log-bottom' do - %i.icon-arrow-down - Scroll down - .file_content.logs - %ol - - Gitlab::Logger.read_latest_for('production.log').each do |line| - %li - %p= line - .tab-pane#sidekiq - .file_holder#README - .file_title - %i.icon-file - sidekiq.log - .pull-right - = link_to '#', class: 'log-bottom' do - %i.icon-arrow-down - Scroll down - .file_content.logs - %ol - - Gitlab::Logger.read_latest_for('sidekiq.log').each do |line| - %li - %p= line diff --git a/app/views/admin/projects/_form.html.haml b/app/views/admin/projects/_form.html.haml new file mode 100644 index 00000000..4848e739 --- /dev/null +++ b/app/views/admin/projects/_form.html.haml @@ -0,0 +1,74 @@ += form_for [:admin, project] do |f| + -if project.errors.any? + .alert-message.block-message.error + %ul + - project.errors.full_messages.each do |msg| + %li= msg + + .clearfix.project_name_holder + = f.label :name do + Project name is + .input + = f.text_field :name, placeholder: "Example Project", class: "xxlarge" + + %hr + .adv_settings + %h6 Advanced settings: + .clearfix + = f.label :path do + Path + .input + .input-prepend + %strong + = text_field_tag :ppath, @admin_project.path_to_repo, class: "xlarge", disabled: true + .clearfix + = f.label :code do + URL + .input + .input-prepend + %span.add-on= web_app_url + = f.text_field :code, placeholder: "example" + + - unless project.new_record? + .clearfix + = f.label :owner_id + .input= f.select :owner_id, User.all.map { |user| [user.name, user.id] }, {}, {class: 'chosen'} + + - if project.repo_exists? + .clearfix + = f.label :default_branch, "Default Branch" + .input= f.select(:default_branch, project.heads.map(&:name), {}, style: "width:210px;") + + - unless project.new_record? + %hr + .adv_settings + %h6 Features: + + .clearfix + = f.label :issues_enabled, "Issues" + .input= f.check_box :issues_enabled + + .clearfix + = f.label :merge_requests_enabled, "Merge Requests" + .input= f.check_box :merge_requests_enabled + + .clearfix + = f.label :wall_enabled, "Wall" + .input= f.check_box :wall_enabled + + .clearfix + = f.label :wiki_enabled, "Wiki" + .input= f.check_box :wiki_enabled + + - unless project.new_record? + .actions + = f.submit 'Save Project', class: "btn save-btn" + = link_to 'Cancel', admin_projects_path, class: "btn cancel-btn" + + + +:javascript + $(function(){ + new Projects(); + }) + diff --git a/app/views/admin/projects/_new_form.html.haml b/app/views/admin/projects/_new_form.html.haml new file mode 100644 index 00000000..d793b6f3 --- /dev/null +++ b/app/views/admin/projects/_new_form.html.haml @@ -0,0 +1,29 @@ += form_for [:admin, @admin_project] do |f| + - if @admin_project.errors.any? + .alert-message.block-message.error + %span= @admin_project.errors.full_messages.first + .clearfix.project_name_holder + = f.label :name do + Project name is + .input + = f.text_field :name, placeholder: "Example Project", class: "xxlarge" + = f.submit 'Create project', class: "btn primary project-submit" + + %hr + %div.adv_settings + %h6 Advanced settings: + .clearfix + = f.label :path do + Git Clone + .input + .input-prepend + %span.add-on= Gitlab.config.ssh_path + = f.text_field :path, placeholder: "example_project", disabled: !@admin_project.new_record? + %span.add-on= ".git" + .clearfix + = f.label :code do + URL + .input + .input-prepend + %span.add-on= web_app_url + = f.text_field :code, placeholder: "example" diff --git a/app/views/admin/projects/edit.html.haml b/app/views/admin/projects/edit.html.haml new file mode 100644 index 00000000..b5255671 --- /dev/null +++ b/app/views/admin/projects/edit.html.haml @@ -0,0 +1,3 @@ +%h3.page_title #{@admin_project.name} → Edit project +%hr += render 'form', project: @admin_project diff --git a/app/views/admin/projects/index.html.haml b/app/views/admin/projects/index.html.haml index 82d5fdcd..3335fce0 100644 --- a/app/views/admin/projects/index.html.haml +++ b/app/views/admin/projects/index.html.haml @@ -1,61 +1,27 @@ += render 'admin/shared/projects_head' %h3.page_title Projects - = link_to 'New Project', new_project_path, class: "btn btn-small pull-right" + = link_to 'New Project', new_admin_project_path, class: "btn small right" +%br += form_tag admin_projects_path, method: :get, class: 'form-inline' do + = text_field_tag :name, params[:name], class: "xlarge" + = submit_tag "Search", class: "btn submit primary" -%hr +%table + %thead + %th Name + %th Path + %th Team Members + %th Last Commit + %th Edit + %th.cred Danger Zone! -.row - .span4 - .admin-filter - = form_tag admin_projects_path, method: :get, class: 'form-inline' do - .control-group - = label_tag :name, 'Name:', class: 'control-label' - .controls - = text_field_tag :name, params[:name], class: "span2" - - .control-group - = label_tag :namespace_id, 'Namespace:', class: 'control-label' - .controls - = select_tag :namespace_id, namespaces_options(params[:namespace_id], :all), class: "chosen span2", prompt: "Any" - .control-group - = label_tag :public_only, 'Public Only', class: 'control-label' - .controls - = check_box_tag :public_only, 1, params[:public_only] - .control-group - = label_tag :with_push, 'Not empty', class: 'control-label' - .controls - = check_box_tag :with_push, 1, params[:with_push] -   - %span.light Projects with push events - .control-group - = label_tag :abandoned, 'Abandoned', class: 'control-label' - .controls - = check_box_tag :abandoned, 1, params[:abandoned] -   - %span.light No activity over 6 month - - - - .form-actions - = submit_tag "Search", class: "btn submit btn-primary" - = link_to "Reset", admin_projects_path, class: "btn" - .span8 - .ui-box - %h5.title - Projects (#{@projects.total_count}) - %ul.well-list - - @projects.each do |project| - %li - - if project.public - %i.icon-share - - else - %i.icon-lock.cgreen - = link_to project.name_with_namespace, [:admin, project] - .pull-right - = link_to 'Edit', edit_project_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-small" - = link_to 'Destroy', [project], confirm: "REMOVE #{project.name}? Are you sure?", method: :delete, class: "btn btn-small btn-remove" - - if @projects.blank? - %p.nothing_here_message 0 projects matches - - else - %li.bottom - = paginate @projects, theme: "gitlab" + - @admin_projects.each do |project| + %tr + %td= link_to project.name, [:admin, project] + %td= project.path + %td= project.users_projects.count + %td= last_commit(project) + %td= link_to 'Edit', edit_admin_project_path(project), id: "edit_#{dom_id(project)}", class: "btn small" + %td.bgred= link_to 'Destroy', [:admin, project], confirm: "REMOVE #{project.name}? Are you sure?", method: :delete, class: "btn small danger" += paginate @admin_projects, theme: "admin" diff --git a/app/views/admin/projects/members/_form.html.haml b/app/views/admin/projects/members/_form.html.haml deleted file mode 100644 index 80412029..00000000 --- a/app/views/admin/projects/members/_form.html.haml +++ /dev/null @@ -1,16 +0,0 @@ -= form_for @team_member_relation, as: :team_member, url: admin_project_member_path(@project, @member) do |f| - -if @team_member_relation.errors.any? - .alert.alert-error - %ul - - @team_member_relation.errors.full_messages.each do |msg| - %li= msg - - .clearfix - %label Project Access: - .input - = f.select :project_access, options_for_select(Project.access_options, @team_member_relation.project_access), {}, class: "project-access-select chosen span3" - - %br - .actions - = f.submit 'Save', class: "btn btn-primary" - = link_to 'Cancel', :back, class: "btn" diff --git a/app/views/admin/projects/members/edit.html.haml b/app/views/admin/projects/members/edit.html.haml deleted file mode 100644 index 2d76deb2..00000000 --- a/app/views/admin/projects/members/edit.html.haml +++ /dev/null @@ -1,8 +0,0 @@ -%p.slead - Edit access for - = link_to @member.name, admin_user_path(@member) - in - = link_to @project.name_with_namespace, admin_project_path(@project) - -%hr -= render 'form' diff --git a/app/views/admin/projects/new.html.haml b/app/views/admin/projects/new.html.haml new file mode 100644 index 00000000..933cb671 --- /dev/null +++ b/app/views/admin/projects/new.html.haml @@ -0,0 +1,12 @@ +.project_new_holder + %h3.page_title + New Project + %hr + = render 'new_form' +%div.save-project-loader.hide + %center + = image_tag "ajax_loader.gif" + %h3 Creating project & repository. Please wait a few minutes + +:javascript + $(function(){ new Projects(); }); diff --git a/app/views/admin/projects/show.html.haml b/app/views/admin/projects/show.html.haml index 92b89601..78df8f2d 100644 --- a/app/views/admin/projects/show.html.haml +++ b/app/views/admin/projects/show.html.haml @@ -1,90 +1,93 @@ += render 'admin/shared/projects_head' %h3.page_title - Project: #{@project.name_with_namespace} - = link_to edit_project_path(@project), class: "btn pull-right" do + Project: #{@admin_project.name} + = link_to edit_admin_project_path(@admin_project), class: "btn right" do %i.icon-edit Edit -%hr -.row - .span6 - .ui-box - %h5.title - Project info: - %ul.well-list - %li - %span.light Name: - %strong= @project.name - %li - %span.light Namespace: - %strong - - if @project.namespace - = link_to @project.namespace.human_name, [:admin, @project.group || @project.owner] - - else - Global - %li - %span.light Owned by: - %strong - - if @project.owner - = link_to @project.owner_name, admin_user_path(@project.owner) - - else - (deleted) - %li - %span.light Created by: - %strong - = @project.creator.try(:name) || '(deleted)' +- if !@admin_project.has_post_receive_file? && @admin_project.has_commits? + %br + .alert.alert-error + %span + %strong Important! + Project has commits but missing post-receive file. + %br + If you exported project manually - copy post-receive hook to bare repository - %li - %span.light Created at: - %strong - = @project.created_at.stamp("March 1, 1999") +%br +%table.zebra-striped + %thead + %tr + %th Project + %th + %tr + %td + %b + Name: + %td + = @admin_project.name + %tr + %td + %b + Code: + %td + = @admin_project.code + %tr + %td + %b + Path: + %td + = @admin_project.path + %tr + %td + %b + Owner: + %td + = @admin_project.owner_name || '(deleted)' + %tr + %td + %b + Post Receive File: + %td + = check_box_tag :post_receive_file, 1, @admin_project.has_post_receive_file?, disabled: true +%br +%h3 + Team + %small + (#{@admin_project.users_projects.count}) +%br +%table.zebra-striped + %thead + %tr + %th Name + %th Project Access + %th Repository Access + %th - %li - %span.light http: - %strong - = link_to @project.http_url_to_repo - %li - %span.light ssh: - %strong - = link_to @project.ssh_url_to_repo - - if @project.repository.exists? - %li - %span.light fs: - %strong - = @repository.path_to_repo + - @admin_project.users_projects.each do |tm| + %tr + %td + = link_to tm.user_name, admin_user_path(tm.user) + %td= tm.project_access_human + %td= link_to 'Edit Access', edit_admin_team_member_path(tm), class: "btn small" + %td= link_to 'Remove from team', admin_team_member_path(tm), confirm: 'Are you sure?', method: :delete, class: "btn danger small" - %li - %span.light last commit: - %strong - = last_commit(@project) - - else - %li - %span.light repository: - %strong.cred - does not exist +%br +%h3 Add new team member +%br += form_tag team_update_admin_project_path(@admin_project), class: "bulk_import", method: :put do + %table.zebra-striped + %thead + %tr + %th Users + %th Project Access: - %li - %span.light access: - %strong - - if @project.public - %span.cblue - %i.icon-share - Public - - else - %span.cgreen - %i.icon-lock - Private - .span6 - .ui-box - %h5.title - Team - %small - (#{@project.users.count}) - = link_to project_team_index_path(@project), class: "btn btn-tiny" do - %i.icon-edit - Edit Team - %ul.well-list.team_members - - @project.users.each do |tm| - %li - %strong - = link_to tm.name, admin_user_path(tm) - %span.pull-right.light= @project.project_access_human(tm) + %tr + %td= select_tag :user_ids, options_from_collection_for_select(@users , :id, :name), multiple: true, data: {placeholder: 'Select users'}, class: 'chosen span5' + %td= select_tag :project_access, options_for_select(Project.access_options), {class: "project-access-select chosen span3"} + + %tr + %td= submit_tag 'Add', class: "btn primary" + %td + Read more about project permissions + %strong= link_to "here", help_permissions_path, class: "vlink" diff --git a/app/views/admin/projects/team.html.haml b/app/views/admin/projects/team.html.haml new file mode 100644 index 00000000..e69de29b diff --git a/app/views/admin/resque/show.html.haml b/app/views/admin/resque/show.html.haml index 499738f9..41254a6b 100644 --- a/app/views/admin/resque/show.html.haml +++ b/app/views/admin/resque/show.html.haml @@ -1,4 +1,4 @@ -%h3.page_title Background Jobs +%h3.page_title Resque %br .ui-box - %iframe{src: sidekiq_path, width: '100%', height: 900, style: "border: none"} + %iframe{src: resque_path, width: '100%', height: 600, style: "border: none"} diff --git a/app/views/admin/shared/_projects_head.html.haml b/app/views/admin/shared/_projects_head.html.haml new file mode 100644 index 00000000..3f5c34c5 --- /dev/null +++ b/app/views/admin/shared/_projects_head.html.haml @@ -0,0 +1,5 @@ +%ul.nav.nav-tabs + = nav_link(controller: :projects) do + = link_to 'Projects', admin_projects_path, class: "tab" + = nav_link(controller: :groups) do + = link_to 'Groups', admin_groups_path, class: "tab" diff --git a/app/views/admin/team_members/_form.html.haml b/app/views/admin/team_members/_form.html.haml new file mode 100644 index 00000000..9cd94fdd --- /dev/null +++ b/app/views/admin/team_members/_form.html.haml @@ -0,0 +1,16 @@ += form_for @admin_team_member, as: :team_member, url: admin_team_member_path(@admin_team_member) do |f| + -if @admin_team_member.errors.any? + .alert-message.block-message.error + %ul + - @admin_team_member.errors.full_messages.each do |msg| + %li= msg + + .clearfix + %label Project Access: + .input + = f.select :project_access, options_for_select(Project.access_options, @admin_team_member.project_access), {}, class: "project-access-select chosen span3" + + %br + .actions + = f.submit 'Save', class: "btn primary" + = link_to 'Cancel', :back, class: "btn" diff --git a/app/views/admin/team_members/edit.html.haml b/app/views/admin/team_members/edit.html.haml new file mode 100644 index 00000000..431387be --- /dev/null +++ b/app/views/admin/team_members/edit.html.haml @@ -0,0 +1,19 @@ +%h3 + Edit access + %small + = @admin_team_member.project.name + – + = @admin_team_member.user_name + +%hr +%table.zebra-striped + %tr + %td User: + %td= @admin_team_member.user_name + %tr + %td Project: + %td= @admin_team_member.project.name + %tr + %td Since: + %td= @admin_team_member.updated_at.stamp("Nov 11, 2010") += render 'form' diff --git a/app/views/admin/teams/edit.html.haml b/app/views/admin/teams/edit.html.haml deleted file mode 100644 index 0a3d993b..00000000 --- a/app/views/admin/teams/edit.html.haml +++ /dev/null @@ -1,28 +0,0 @@ -%h3.page_title Edit Team -%hr -= form_for @team, url: admin_team_path(@team), method: :put do |f| - - if @team.errors.any? - .alert.alert-error - %span= @team.errors.full_messages.first - .clearfix.team_name_holder - = f.label :name do - Team name is - .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 - .input - = f.text_field :path, placeholder: "example-team", class: "xxlarge danger" - %ul.cred - %li It will change web url for access team and team projects. - - .form-actions - = f.submit 'Edit team', class: "btn btn-remove" - = link_to 'Cancel', admin_teams_path, class: "btn btn-cancel" diff --git a/app/views/admin/teams/index.html.haml b/app/views/admin/teams/index.html.haml deleted file mode 100644 index 3690d6d9..00000000 --- a/app/views/admin/teams/index.html.haml +++ /dev/null @@ -1,43 +0,0 @@ -%h3.page_title - Teams - %small - simple Teams description - - = link_to 'New Team', new_admin_team_path, class: "btn btn-small pull-right" - %br - -= form_tag admin_teams_path, method: :get, class: 'form-inline' do - = text_field_tag :name, params[:name], class: "xlarge" - = submit_tag "Search", class: "btn submit btn-primary" - -%table - %thead - %tr - %th - Name - %i.icon-sort-down - %th Description - %th Path - %th Projects - %th Members - %th Owner - %th.cred Danger Zone! - - - @teams.each do |team| - %tr - %td - %strong= link_to team.name, admin_team_path(team) - %td= truncate team.description - %td= team.path - %td= team.projects.count - %td= team.members.count - %td - - if team.owner - = link_to team.owner.name, admin_user_path(team.owner) - - else - (deleted) - %td.bgred - = link_to 'Edit', edit_admin_team_path(team), id: "edit_#{dom_id(team)}", class: "btn btn-small" - = link_to 'Destroy', admin_team_path(team), confirm: "REMOVE #{team.name}? Are you sure?", method: :delete, class: "btn btn-small btn-remove" - -= paginate @teams, theme: "admin" diff --git a/app/views/admin/teams/members/_form.html.haml b/app/views/admin/teams/members/_form.html.haml deleted file mode 100644 index f1388aab..00000000 --- a/app/views/admin/teams/members/_form.html.haml +++ /dev/null @@ -1,20 +0,0 @@ -= form_tag admin_team_member_path(@team, @member), method: :put do - -if @member.errors.any? - .alert.alert-error - %ul - - @member.errors.full_messages.each do |msg| - %li= msg - - .clearfix - %label Default access for Team projects: - .input - = select_tag :default_project_access, options_for_select(UserTeam.access_roles, @team.default_projects_access(@member)), class: "project-access-select chosen span3" - .clearfix - %label Team admin? - .input - = check_box_tag :group_admin, true, @team.admin?(@member) - - %br - .actions - = submit_tag 'Save', class: "btn btn-primary" - = link_to 'Cancel', :back, class: "btn" diff --git a/app/views/admin/teams/members/edit.html.haml b/app/views/admin/teams/members/edit.html.haml deleted file mode 100644 index a82847ee..00000000 --- a/app/views/admin/teams/members/edit.html.haml +++ /dev/null @@ -1,16 +0,0 @@ -%h3 - Edit access #{@member.name} in #{@team.name} team - -%hr -%table.zebra-striped - %tr - %td User: - %td= @member.name - %tr - %td Team: - %td= @team.name - %tr - %td Since: - %td= member_since(@team, @member).stamp("Nov 11, 2010") - -= render 'form' diff --git a/app/views/admin/teams/members/new.html.haml b/app/views/admin/teams/members/new.html.haml deleted file mode 100644 index d3929cb7..00000000 --- a/app/views/admin/teams/members/new.html.haml +++ /dev/null @@ -1,29 +0,0 @@ -%h3.page_title - Team: #{@team.name} - -%fieldset - %legend Members (#{@team.members.count}) - = form_tag admin_team_members_path(@team), id: "team_members", class: "bulk_import", method: :post do - %table#members_list - %thead - %tr - %th User name - %th Default project access - %th Team access - %th - - @team.members.each do |member| - %tr.member - %td - = link_to [:admin, member] do - = member.name - %small= "(#{member.email})" - %td= @team.human_default_projects_access(member) - %td= @team.admin?(member) ? "Admin" : "Member" - %td - %tr - %td= select_tag :user_ids, options_from_collection_for_select(@users , :id, :name_with_username), multiple: true, data: {placeholder: 'Select users'}, class: 'chosen span5' - %td= select_tag :default_project_access, options_for_select(Project.access_options), {class: "project-access-select chosen span3" } - %td - %span= check_box_tag :group_admin - %span Admin? - %td= submit_tag 'Add', class: "btn btn-primary", id: :add_members_to_team diff --git a/app/views/admin/teams/new.html.haml b/app/views/admin/teams/new.html.haml deleted file mode 100644 index 1c90cb20..00000000 --- a/app/views/admin/teams/new.html.haml +++ /dev/null @@ -1,26 +0,0 @@ -%h3.page_title New Team -%hr -= form_for @team, url: admin_teams_path do |f| - - if @team.errors.any? - .alert.alert-error - %span= @team.errors.full_messages.first - .clearfix - = f.label :name do - Team name is - .input - = f.text_field :name, placeholder: "Ex. OpenSource", class: "xxlarge left" - - .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 - %li All created teams are public (users can view who enter into team and which project are assigned for this team) - %li People within a team see only projects they have access to - %li You will be able to assign existing projects for team diff --git a/app/views/admin/teams/projects/_form.html.haml b/app/views/admin/teams/projects/_form.html.haml deleted file mode 100644 index 5b79d518..00000000 --- a/app/views/admin/teams/projects/_form.html.haml +++ /dev/null @@ -1,16 +0,0 @@ -= form_tag admin_team_project_path(@team, @project), method: :put do - -if @project.errors.any? - .alert.alert-error - %ul - - @project.errors.full_messages.each do |msg| - %li= msg - - .clearfix - %label Max access for Team members: - .input - = select_tag :greatest_project_access, options_for_select(UserTeam.access_roles, @team.max_project_access(@project)), class: "project-access-select chosen span3" - - %br - .actions - = submit_tag 'Save', class: "btn btn-primary" - = link_to 'Cancel', :back, class: "btn" diff --git a/app/views/admin/teams/projects/edit.html.haml b/app/views/admin/teams/projects/edit.html.haml deleted file mode 100644 index b91a4982..00000000 --- a/app/views/admin/teams/projects/edit.html.haml +++ /dev/null @@ -1,16 +0,0 @@ -%h3 - Edit max access in #{@project.name} for #{@team.name} team - -%hr -%table.zebra-striped - %tr - %td Project: - %td= @project.name - %tr - %td Team: - %td= @team.name - %tr - %td Since: - %td= assigned_since(@team, @project).stamp("Nov 11, 2010") - -= render 'form' diff --git a/app/views/admin/teams/projects/new.html.haml b/app/views/admin/teams/projects/new.html.haml deleted file mode 100644 index b60dad35..00000000 --- a/app/views/admin/teams/projects/new.html.haml +++ /dev/null @@ -1,23 +0,0 @@ -%h3.page_title - Team: #{@team.name} - -%fieldset - %legend Projects (#{@team.projects.count}) - = form_tag admin_team_projects_path(@team), id: "assign_projects", class: "bulk_import", method: :post do - %table#projects_list - %thead - %tr - %th Project name - %th Max access - %th - - @team.projects.each do |project| - %tr.project - %td - = link_to project.name_with_namespace, [:admin, project] - %td - %span= @team.human_max_project_access(project) - %td - %tr - %td= select_tag :project_ids, options_from_collection_for_select(@projects , :id, :name_with_namespace), multiple: true, data: {placeholder: 'Select projects'}, class: 'chosen span5' - %td= select_tag :greatest_project_access, options_for_select(Project.access_options), {class: "project-access-select chosen span3" } - %td= submit_tag 'Add', class: "btn btn-primary", id: :assign_projects_to_team diff --git a/app/views/admin/teams/show.html.haml b/app/views/admin/teams/show.html.haml deleted file mode 100644 index abdfada8..00000000 --- a/app/views/admin/teams/show.html.haml +++ /dev/null @@ -1,107 +0,0 @@ -%h3.page_title - Team: #{@team.name} - -%br -%table.zebra-striped - %thead - %tr - %th Team - %th - %tr - %td - %b - Name: - %td - = @team.name -   - = link_to edit_admin_team_path(@team), class: "btn btn-small pull-right" do - %i.icon-edit - Edit - %tr - %td - %b - Description: - %td - = @team.description - %tr - %td - %b - Owner: - %td - = @team.owner.name - .pull-right - = link_to "#", class: "btn btn-small change-owner-link" do - %i.icon-edit - Change owner - - %tr.change-owner-holder.hide - %td.bgred - %b.cred - New Owner: - %td.bgred - = form_for @team, url: admin_team_path(@team) do |f| - = f.select :owner_id, User.all.map { |user| [user.name, user.id] }, {}, {class: 'chosen'} - %div - = f.submit 'Change Owner', class: "btn btn-remove" - = link_to "Cancel", "#", class: "btn change-owner-cancel-link" - -%fieldset - %legend - Members (#{@team.members.count}) - %span= link_to 'Add members', new_admin_team_member_path(@team), class: "btn btn-primary btn-small pull-right", id: :add_members_to_team - - if @team.members.any? - %table#members_list - %thead - %tr - %th User name - %th Default project access - %th Team access - %th.cred.span3 Danger Zone! - - @team.members.each do |member| - %tr.member{ class: "user_#{member.id}"} - %td - = link_to [:admin, member] do - = member.name - %small= "(#{member.email})" - %td= @team.human_default_projects_access(member) - %td= @team.admin?(member) ? "Admin" : "Member" - %td.bgred - = link_to 'Edit', edit_admin_team_member_path(@team, member), class: "btn btn-small" -   - = link_to 'Remove', admin_team_member_path(@team, member), confirm: 'Remove member from team. Are you sure?', method: :delete, class: "btn btn-remove btn-small", id: "remove_member_#{member.id}" - -%fieldset - %legend - Projects (#{@team.projects.count}) - %span= link_to 'Add projects', new_admin_team_project_path(@team), class: "btn btn-primary btn-small pull-right", id: :assign_projects_to_team - - if @team.projects.any? - %table#projects_list - %thead - %tr - %th Project name - %th Max access - %th.cred.span3 Danger Zone! - - @team.projects.each do |project| - %tr.project - %td - = link_to project.name_with_namespace, [:admin, project] - %td - %span= @team.human_max_project_access(project) - %td.bgred - = link_to 'Edit', edit_admin_team_project_path(@team, project), class: "btn btn-small" -   - = link_to 'Relegate', admin_team_project_path(@team, project), confirm: 'Remove project from team. Are you sure?', method: :delete, class: "btn btn-remove small", id: "relegate_project_#{project.id}" - -:javascript - $(function(){ - var modal = $('.change-owner-holder'); - $('.change-owner-link').bind("click", function(){ - $(this).hide(); - modal.show(); - }); - $('.change-owner-cancel-link').bind("click", function(){ - modal.hide(); - $('.change-owner-link').show(); - }) - }) - diff --git a/app/views/admin/users/_form.html.haml b/app/views/admin/users/_form.html.haml index 1d1fe341..7010c272 100644 --- a/app/views/admin/users/_form.html.haml +++ b/app/views/admin/users/_form.html.haml @@ -6,82 +6,75 @@ - @admin_user.errors.full_messages.each do |msg| %li= msg - %fieldset - %legend Account - .clearfix - = f.label :name - .input - = f.text_field :name, required: true, autocomplete: "off" - %span.help-inline * required - .clearfix - = f.label :username - .input - = f.text_field :username, required: true, autocomplete: "off" - %span.help-inline * required - .clearfix - = f.label :email - .input - = f.text_field :email, required: true, autocomplete: "off" - %span.help-inline * required - - %fieldset - %legend Password - .clearfix - = f.label :password - .input= f.password_field :password, disabled: f.object.force_random_password - .clearfix - = f.label :password_confirmation - .input= f.password_field :password_confirmation, disabled: f.object.force_random_password - -if f.object.new_record? - .clearfix - = f.label :force_random_password do - %span Generate random password - .input= f.check_box :force_random_password, {}, true, nil - - %fieldset - %legend Access - .row - .span8 + .row + .span7 + .ui-box + %br + .clearfix + = f.label :name + .input + = f.text_field :name + %span.help-inline * required + .clearfix + = f.label :email + .input + = f.text_field :email + %span.help-inline * required + %hr + -if f.object.new_record? + .clearfix + = f.label :force_random_password do + %span Generate random password + .input= f.check_box :force_random_password, {}, true, nil + + %div.password-fields + .clearfix + = f.label :password + .input= f.password_field :password, disabled: f.object.force_random_password + .clearfix + = f.label :password_confirmation + .input= f.password_field :password_confirmation, disabled: f.object.force_random_password + %hr + .clearfix + = f.label :skype + .input= f.text_field :skype + .clearfix + = f.label :linkedin + .input= f.text_field :linkedin + .clearfix + = f.label :twitter + .input= f.text_field :twitter + .span5 + .ui-box + %br .clearfix = f.label :projects_limit .input= f.number_field :projects_limit - .clearfix - = f.label :can_create_group - .input= f.check_box :can_create_group - - .clearfix - = f.label :can_create_team - .input= f.check_box :can_create_team - .clearfix = f.label :admin do %strong.cred Administrator .input= f.check_box :admin - .span4 - unless @admin_user.new_record? - .alert.alert-error - - 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" + %hr + .padded.cred + - if @admin_user.blocked + %span + This user is blocked and is not able to login to GitLab + .clearfix + = link_to 'Unblock User', unblock_admin_user_path(@admin_user), method: :put, class: "btn small right" - else - %p Blocked users will be removed from all projects & will not be able to login to GitLab. - = link_to 'Block User', block_admin_user_path(@admin_user), confirm: 'USER WILL BE BLOCKED! Are you sure?', method: :put, class: "btn btn-small btn-remove" - %fieldset - %legend Profile - .clearfix - = f.label :skype - .input= f.text_field :skype - .clearfix - = f.label :linkedin - .input= f.text_field :linkedin - .clearfix - = f.label :twitter - .input= f.text_field :twitter + %span + Blocked users will be removed from all projects & will not be able to login to GitLab. + .clearfix + = link_to 'Block User', block_admin_user_path(@admin_user), confirm: 'USER WILL BE BLOCKED! Are you sure?', method: :put, class: "btn small right danger" + .row + .span6 + .span6 .actions - = f.submit 'Save', class: "btn btn-save" + = f.submit 'Save', class: "btn save-btn" - if @admin_user.new_record? - = link_to 'Cancel', admin_users_path, class: "btn btn-cancel" + = link_to 'Cancel', admin_users_path, class: "btn cancel-btn" - else - = link_to 'Cancel', admin_user_path(@admin_user), class: "btn btn-cancel" + = link_to 'Cancel', admin_user_path(@admin_user), class: "btn cancel-btn" diff --git a/app/views/admin/users/edit.html.haml b/app/views/admin/users/edit.html.haml index f8ff77b8..032e3cfa 100644 --- a/app/views/admin/users/edit.html.haml +++ b/app/views/admin/users/edit.html.haml @@ -1,6 +1,3 @@ -%h3.page_title - #{@admin_user.name} → - %i.icon-edit - Edit user +%h3.page_title #{@admin_user.name} → Edit user %hr = render 'form' diff --git a/app/views/admin/users/index.html.haml b/app/views/admin/users/index.html.haml index 9da2871e..5ef94ef5 100644 --- a/app/views/admin/users/index.html.haml +++ b/app/views/admin/users/index.html.haml @@ -1,62 +1,46 @@ %h3.page_title Users - = link_to 'New User', new_admin_user_path, class: "btn btn-small pull-right" + = link_to 'New User', new_admin_user_path, class: "btn small right" %br -.row - .span3 - .admin-filter - = form_tag admin_users_path, method: :get, class: 'form-inline' do - = search_field_tag :name, params[:name], placeholder: 'Name, email or username', class: 'search-text-input span2' - = button_tag type: 'submit', class: 'btn' do - %i.icon-search - %ul.nav.nav-pills.nav-stacked - %li{class: "#{'active' unless params[:filter]}"} - = link_to admin_users_path do - Active - %small.pull-right= User.active.count - %li{class: "#{'active' if params[:filter] == "admins"}"} - = link_to admin_users_path(filter: "admins") do - Admins - %small.pull-right= User.admins.count - %li{class: "#{'active' if params[:filter] == "blocked"}"} - = link_to admin_users_path(filter: "blocked") do - Blocked - %small.pull-right= User.blocked.count - %li{class: "#{'active' if params[:filter] == "wop"}"} - = link_to admin_users_path(filter: "wop") do - Without projects - %small.pull-right= User.without_projects.count - %hr - = link_to 'Reset', admin_users_path, class: "btn btn-cancel" += form_tag admin_users_path, method: :get, class: 'form-inline' do + = text_field_tag :name, params[:name], class: "xlarge" + = submit_tag "Search", class: "btn submit primary" +%ul.nav.nav-pills + %li{class: "#{'active' unless params[:filter]}"} + = link_to "Active", admin_users_path + %li{class: "#{'active' if params[:filter] == "admins"}"} + = link_to admin_users_path(filter: "admins") do + Admins + %li{class: "#{'active' if params[:filter] == "blocked"}"} + = link_to admin_users_path(filter: "blocked") do + Blocked + %li{class: "#{'active' if params[:filter] == "wop"}"} + = link_to admin_users_path(filter: "wop") do + Without projects - .span9 - .ui-box - %h5.title - Users (#{@admin_users.total_count}) - %ul.well-list - - @admin_users.each do |user| - %li - - if user.blocked? - %i.icon-lock.cred - - else - %i.icon-user.cgreen - = link_to user.name, [:admin, user] - - if user.admin? - %strong.cred (Admin) - - if user == current_user - %span.cred It's you! - .pull-right - %span.light - %i.icon-envelope - = mail_to user.email, user.email, class: 'light' -   - = link_to 'Edit', edit_admin_user_path(user), id: "edit_#{dom_id(user)}", class: "btn btn-small" - - unless user == current_user - - 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" - = link_to 'Destroy', [:admin, user], confirm: "USER #{user.name} WILL BE REMOVED! Are you sure?", method: :delete, class: "btn btn-small btn-remove" - %li.bottom - = paginate @admin_users, theme: "gitlab" +%table + %thead + %th Admin + %th Name + %th Email + %th Projects + %th Edit + %th Blocked + %th.cred Danger Zone! + + - @admin_users.each do |user| + %tr + %td= check_box_tag "admin", 1, user.admin, disabled: :disabled + %td= link_to user.name, [:admin, user] + %td= user.email + %td= user.users_projects.count + %td= link_to 'Edit', edit_admin_user_path(user), id: "edit_#{dom_id(user)}", class: "btn small" + %td + - if user.blocked + = link_to 'Unblock', unblock_admin_user_path(user), method: :put, class: "btn small success" + - else + = link_to 'Block', block_admin_user_path(user), confirm: 'USER WILL BE BLOCKED! Are you sure?', method: :put, class: "btn small danger" + %td.bgred= link_to 'Destroy', [:admin, user], confirm: "USER #{user.name} WILL BE REMOVED! Are you sure?", method: :delete, class: "btn small danger" + += paginate @admin_users, theme: "admin" diff --git a/app/views/admin/users/new.html.haml b/app/views/admin/users/new.html.haml index 1e82b249..70ead0d3 100644 --- a/app/views/admin/users/new.html.haml +++ b/app/views/admin/users/new.html.haml @@ -1,5 +1,3 @@ -%h3.page_title - %i.icon-plus - New user -%hr +%h3.page_title New user +%br = render 'form' diff --git a/app/views/admin/users/show.html.haml b/app/views/admin/users/show.html.haml index b813f859..e73f4d10 100644 --- a/app/views/admin/users/show.html.haml +++ b/app/views/admin/users/show.html.haml @@ -1,94 +1,101 @@ -.row - .span6 - %h3.page_title - = image_tag gravatar_icon(@admin_user.email, 90), class: "avatar s90" - = @admin_user.name - - if @admin_user.blocked? - %span.cred (Blocked) - - if @admin_user.admin - %span.cred (Admin) - .pull-right - = link_to edit_admin_user_path(@admin_user), class: "btn pull-right" do - %i.icon-edit - Edit - %br - %small @#{@admin_user.username} - %br - %small member since #{@admin_user.created_at.stamp("Nov 12, 2031")} - .clearfix - %hr - %p - %span.btn.btn-small - %i.icon-envelope - = mail_to @admin_user.email - - unless @admin_user == current_user - - if @admin_user.blocked? - = link_to 'Unblock', unblock_admin_user_path(@admin_user), method: :put, class: "btn btn-small success" - - else - = link_to 'Block', block_admin_user_path(@admin_user), confirm: 'USER WILL BE BLOCKED! Are you sure?', method: :put, class: "btn btn-small btn-remove" - = link_to 'Destroy', [:admin, @admin_user], confirm: "USER #{@admin_user.name} WILL BE REMOVED! Are you sure?", method: :delete, class: "btn btn-small btn-remove" - %hr - %h5 - Add User to Projects - %small +%h3.page_title + User: #{@admin_user.name} + - if @admin_user.blocked + %small Blocked + - if @admin_user.admin + %small Administrator + = link_to edit_admin_user_path(@admin_user), class: "btn right" do + %i.icon-edit + Edit + +%br + +%table.zebra-striped + %thead + %tr + %th Profile + %th + %tr + %td + %b + Email: + %td + = @admin_user.email + %tr + %td + %b + Admin: + %td= check_box_tag "admin", 1, @admin_user.admin, disabled: :disabled + %tr + %td + %b + Blocked: + %td= check_box_tag "blocked", 1, @admin_user.blocked, disabled: :disabled + %tr + %td + %b + Projects limit: + %td + = @admin_user.projects_limit + - unless @admin_user.skype.empty? + %tr + %td + %b + Skype: + %td + = @admin_user.skype + - unless @admin_user.linkedin.empty? + %tr + %td + %b + Linkedin: + %td + = @admin_user.linkedin + - unless @admin_user.twitter.empty? + %tr + %td + %b + Twitter: + %td + = @admin_user.twitter + +%br +%h3 Add User to Projects +%br += form_tag team_update_admin_user_path(@admin_user), class: "bulk_import", method: :put do + %table + %thead + %tr + %th Projects + %th Project Access: + + %tr + %td= select_tag :project_ids, options_from_collection_for_select(@projects , :id, :name), multiple: true, data: {placeholder: 'Select projects'}, class: 'chosen span5' + %td= select_tag :project_access, options_for_select(Project.access_options), class: "project-access-select chosen span3" + + %tr + %td= submit_tag 'Add', class: "btn primary" + %td Read more about project permissions %strong= link_to "here", help_permissions_path, class: "vlink" - %br - = form_tag team_update_admin_user_path(@admin_user), class: "bulk_import", method: :put do - .control-group - = label_tag :project_ids, "Projects", class: 'control-label' - .controls - = select_tag :project_ids, options_from_collection_for_select(@not_in_projects , :id, :name_with_namespace), multiple: true, data: {placeholder: 'Select projects'}, class: 'chosen span3' - .control-group - = label_tag :project_access, "Project Access", class: 'control-label' - .controls - = select_tag :project_access, options_for_select(Project.access_options), class: "project-access-select chosen span3" +%br - .form-actions - = submit_tag 'Add', class: "btn btn-create" - .pull-right - %br +- if @admin_user.projects.present? + %h3 Projects + %br - - if @admin_user.owned_groups.present? - .ui-box - %h5.title Owned groups: - %ul.well-list - - @admin_user.groups.each do |group| - %li - %strong= link_to group.name, admin_group_path(group) + %table.zebra-striped + %thead + %tr + %th Name + %th Project Access + %th + %th - - if @admin_user.owned_teams.present? - .ui-box - %h5.title Owned teams: - %ul.well-list - - @admin_user.owned_teams.each do |team| - %li - %strong= link_to team.name, admin_team_path(team) - - - .span6 - = render 'users/profile', user: @admin_user - .ui-box - %h5.title Projects (#{@projects.count}) - %ul.well-list - - @projects.each do |project| - %li - = link_to admin_project_path(project), class: dom_class(project) do - - if project.namespace - = project.namespace.human_name - \/ - %strong.well-title - = truncate(project.name, length: 45) - %span.pull-right.light - - if project.owner == @admin_user - %i.icon-wrench - - tm = project.team.get_tm(@admin_user.id) - - if tm - = tm.project_access_human - = link_to edit_admin_project_member_path(project, tm.user), class: "btn btn-small" do - %i.icon-edit - = link_to admin_project_member_path(project, tm.user), confirm: 'Are you sure?', method: :delete, class: "btn btn-small btn-remove" do - %i.icon-remove - %p.light - %i.icon-wrench - – user is a project owner + - @admin_user.users_projects.each do |tm| + - project = tm.project + %tr + %td= link_to project.name, admin_project_path(project) + %td= tm.project_access_human + %td= link_to 'Edit Access', edit_admin_team_member_path(tm), class: "btn small" + %td= link_to 'Remove from team', admin_team_member_path(tm), confirm: 'Are you sure?', method: :delete, class: "btn small danger" diff --git a/app/views/blame/_head.html.haml b/app/views/blame/_head.html.haml index 3a883829..85da1805 100644 --- a/app/views/blame/_head.html.haml +++ b/app/views/blame/_head.html.haml @@ -1,2 +1,7 @@ -%div.tree-ref-holder - = render 'shared/ref_switcher', destination: 'tree', path: params[:path] +%ul.nav.nav-tabs + %li + = render partial: 'shared/ref_switcher', locals: {destination: 'tree', path: params[:path]} + = nav_link(controller: :refs) do + = link_to 'Source', project_tree_path(@project, @ref) + %li.right + = render "shared/clone_panel" diff --git a/app/views/blame/show.html.haml b/app/views/blame/show.html.haml index 96d153e6..c5192c53 100644 --- a/app/views/blame/show.html.haml +++ b/app/views/blame/show.html.haml @@ -3,10 +3,10 @@ #tree-holder.tree-holder %ul.breadcrumb %li - %i.icon-angle-right + %span.arrow = link_to project_tree_path(@project, @ref) do = @project.name - - tree_breadcrumbs(@tree, 6) do |link| + - @tree.breadcrumbs(6) do |link| \/ %li= link .clear @@ -20,29 +20,16 @@ %span.options= render "tree/blob_actions" .file_content.blame %table - - current_line = 1 - @blame.each do |commit, lines| - commit = Commit.new(commit) + - commit = CommitDecorator.decorate(commit) %tr - %td.blame-commit - %span.commit - = link_to commit.short_id(8), project_commit_path(@project, commit), class: "commit_short_id" -   - = commit_author_link(commit, avatar: true, size: 16) -   - = link_to_gfm truncate(commit.title, length: 20), project_commit_path(@project, commit.id), class: "row_title" - %td.lines.blame-numbers - %pre - - if lines.empty? - = current_line - - current_line += 1 - - else - - lines.each do |line| - = current_line - \ - - current_line += 1 + %td.author= commit.author_link avatar: true, size: 16 + %td.blame_commit +   + %code= link_to commit.short_id, project_commit_path(@project, commit) + = link_to_gfm truncate(commit.title, length: 30), project_commit_path(@project, commit), class: "row_title" rescue "--broken encoding" %td.lines - %pre - - lines.each do |line| - = line - \ + = preserve do + %pre + = lines.join("\n") diff --git a/app/views/commit/_commit_box.html.haml b/app/views/commit/_commit_box.html.haml deleted file mode 100644 index 64679177..00000000 --- a/app/views/commit/_commit_box.html.haml +++ /dev/null @@ -1,50 +0,0 @@ -.ui-box.ui-box-show - .ui-box-head - .pull-right - - if @notes_count > 0 - %span.btn.disabled.grouped - %i.icon-comment - = @notes_count - .left.btn-group - %a.btn.grouped.dropdown-toggle{ data: {toggle: :dropdown} } - %i.icon-download-alt - Download as - %span.caret - %ul.dropdown-menu - %li= link_to "Email Patches", project_commit_path(@project, @commit, format: :patch) - %li= link_to "Plain Diff", project_commit_path(@project, @commit, format: :diff) - = link_to project_tree_path(@project, @commit), class: "btn btn-primary grouped" do - %span Browse Code » - %h3.commit-title.page_title - = gfm escape_once(@commit.title) - - if @commit.description.present? - %pre.commit-description - = gfm escape_once(@commit.description) - .ui-box-body - .row - .span5 - .author - = commit_author_link(@commit, avatar: true, size: 32) - authored - %time{title: @commit.authored_date.stamp("Aug 21, 2011 9:23pm")} - #{time_ago_in_words(@commit.authored_date)} ago - - if @commit.different_committer? - .committer - → - = commit_committer_link(@commit) - committed - %time{title: @commit.committed_date.stamp("Aug 21, 2011 9:23pm")} - #{time_ago_in_words(@commit.committed_date)} ago - .span6.pull-right - .pull-right - .sha-block - %span.cgray commit - %span.label_commit= @commit.id - .clearfix - .pull-right - .sha-block - %span.cgray= pluralize(@commit.parents.count, "parent") - - @commit.parents.each do |parent| - = link_to parent.id[0...10], project_commit_path(@project, parent) - - diff --git a/app/views/commit/huge_commit.html.haml b/app/views/commit/huge_commit.html.haml index 7f0bcf38..ba97a7c5 100644 --- a/app/views/commit/huge_commit.html.haml +++ b/app/views/commit/huge_commit.html.haml @@ -1,3 +1,3 @@ = render "commits/commit_box" -.alert.alert-error +.alert-message.block-message.error %h4 Commit diffs are too big to be displayed diff --git a/app/views/commit/show.html.haml b/app/views/commit/show.html.haml index 48fb44a9..432d55b1 100644 --- a/app/views/commit/show.html.haml +++ b/app/views/commit/show.html.haml @@ -1,18 +1,25 @@ -= render "commit_box" - -- unless @commit.has_zero_stats? - %p.pull-right.cgray - This commit has - %span.cgreen #{@commit.stats.additions} additions - and - %span.cred #{@commit.stats.deletions} deletions - += render "commits/commit_box" = render "commits/diffs", diffs: @commit.diffs -= render "notes/notes_with_form" += render "notes/notes_with_form", tid: @commit.id, tt: "commit" += render "notes/per_line_form" + :javascript $(function(){ - $('.files .file').each(function(){ - new CommitFile(this); + PerLineNotes.init(); + var w, h; + $('.diff_file').each(function(){ + $('.image.diff_removed img', this).on('load', $.proxy(function(event){ + var w = event.currentTarget.naturalWidth + , h = event.currentTarget.naturalHeight; + $('.image.diff_removed .image-info', this).append(' | W: ' + w + 'px | H: ' + h + 'px'); + }, this)); + $('.image.diff_added img', this).on('load', $.proxy(function(event){ + var w = event.currentTarget.naturalWidth + , h = event.currentTarget.naturalHeight; + $('.image.diff_added .image-info', this).append(' | W: ' + w + 'px | H: ' + h + 'px'); + }, this)); + }); + }); diff --git a/app/views/commit/show.patch.erb b/app/views/commit/show.patch.erb new file mode 100644 index 00000000..ce1c3d02 --- /dev/null +++ b/app/views/commit/show.patch.erb @@ -0,0 +1 @@ +<%= @commit.to_patch %> diff --git a/app/views/commits/_commit.html.haml b/app/views/commits/_commit.html.haml index 65d92030..156ff1e9 100644 --- a/app/views/commits/_commit.html.haml +++ b/app/views/commits/_commit.html.haml @@ -4,18 +4,18 @@ %strong= link_to "Browse Code »", project_tree_path(@project, commit), class: "right" %p = link_to commit.short_id(8), project_commit_path(@project, commit), class: "commit_short_id" - = commit_author_link(commit, avatar: true, size: 24) + = commit.author_link avatar: true, size: 24   - = link_to_gfm truncate(commit.title, length: 70), project_commit_path(@project, commit.id), class: "row_title" + = link_to_gfm truncate(commit.title, length: 50), project_commit_path(@project, commit.id), class: "row_title" - %time.committed_ago{ datetime: commit.committed_date, title: commit.committed_date.stamp("Aug 21, 2011 9:23pm") } + %span.committed_ago = time_ago_in_words(commit.committed_date) ago   %span.notes_count - - notes = @project.notes.for_commit_id(commit.id) + - notes = @project.commit_notes(commit) + @project.commit_line_notes(commit) - if notes.any? - %span.btn.disabled.grouped + %span.btn.small.disabled.grouped %i.icon-comment = notes.count diff --git a/app/views/commits/_commit_box.html.haml b/app/views/commits/_commit_box.html.haml new file mode 100644 index 00000000..26753a14 --- /dev/null +++ b/app/views/commits/_commit_box.html.haml @@ -0,0 +1,42 @@ +.commit-box{class: @commit.parents_count > 1 ? "merge-commit" : ""} + .commit-head + .right + - if @notes_count > 0 + %span.btn.disabled.grouped + %i.icon-comment + = @notes_count + = link_to project_commit_path(@project, @commit, format: :patch), class: "btn small grouped" do + %i.icon-download-alt + Get Patch + = link_to project_tree_path(@project, @commit), class: "browse-button primary grouped" do + %strong Browse Code » + %h3.commit-title.page_title + = gfm escape_once(@commit.title) + - if @commit.description.present? + %pre.commit-description + = gfm escape_once(@commit.description) + .commit-info + .row + .span5 + .author + %strong= @commit.author_link avatar: true, size: 40 + authored + %time{title: @commit.authored_date.stamp("Aug 21, 2011 9:23pm")} + #{time_ago_in_words(@commit.authored_date)} ago + - if @commit.different_committer? + .committer + → + %strong= @commit.committer_link + committed + %time{title: @commit.committed_date.stamp("Aug 21, 2011 9:23pm")} + #{time_ago_in_words(@commit.committed_date)} ago + .span6.right + .sha-block + %span.cgray commit + %code.label_commit= @commit.id + .sha-block + %span.cgray= pluralize(@commit.parents.count, "parent") + - @commit.parents.each do |parent| + = link_to parent.id[0...10], project_commit_path(@project, parent) + + diff --git a/app/views/commits/_commits.html.haml b/app/views/commits/_commits.html.haml index 869d1f9c..c3c7d49c 100644 --- a/app/views/commits/_commits.html.haml +++ b/app/views/commits/_commits.html.haml @@ -1,6 +1,6 @@ -- @commits.group_by { |c| c.committed_date.to_date }.sort.reverse.each do |day, commits| +- @commits.group_by { |c| c.committed_date.to_date }.each do |day, commits| %div.ui-box - %h5.title + %h5.small %i.icon-calendar - %span= day.stamp("28 Aug, 2010") - %ul.well-list= render commits + = day.stamp("28 Aug, 2010") + %ul.unstyled= render commits diff --git a/app/views/commits/_diffs.html.haml b/app/views/commits/_diffs.html.haml index b2da4796..53c2319f 100644 --- a/app/views/commits/_diffs.html.haml +++ b/app/views/commits/_diffs.html.haml @@ -1,7 +1,7 @@ - if @suppress_diff - .alert.alert-block + .alert-message.block-message %p - %strong Warning! Large commit with more than #{Commit::DIFF_SAFE_SIZE} files changed. + %strong Warning! Large commit with more then #{Commit::DIFF_SAFE_SIZE} files changed. %p To prevent performance issue we rejected diff information. %p But if you still want to see diff @@ -12,38 +12,50 @@ .file-stats = render "commits/diff_head", diffs: diffs -.files - - unless @suppress_diff - - diffs.each_with_index do |diff, i| - - next if diff.diff.empty? - - file = (@commit.tree / diff.new_path) - - file = (@commit.prev_commit.tree / diff.old_path) unless file - - next unless file - .file{id: "diff-#{i}"} - .header - - if diff.deleted_file - %span= diff.old_path +- unless @suppress_diff + - diffs.each_with_index do |diff, i| + - next if diff.diff.empty? + - file = (@commit.tree / diff.new_path) + - file = (@commit.prev_commit.tree / diff.old_path) unless file + - next unless file + .diff_file{id: "diff-#{i}"} + .diff_file_header + - if diff.deleted_file + %span= diff.old_path - - if @commit.prev_commit - = link_to project_tree_path(@project, tree_join(@commit.prev_commit_id, diff.new_path)), {:class => 'btn btn-tiny pull-right view-file'} do - View file @ - %span.commit-short-id= @commit.short_id(6) - - else - %span= diff.new_path - - if diff.a_mode && diff.b_mode && diff.a_mode != diff.b_mode - %span.file-mode= "#{diff.a_mode} → #{diff.b_mode}" - - = link_to project_tree_path(@project, tree_join(@commit.id, diff.new_path)), {:class => 'btn btn-tiny pull-right view-file'} do + - if @commit.prev_commit + = link_to project_tree_path(@project, tree_join(@commit.prev_commit_id, diff.new_path)), {:class => 'btn right view-commit'} do View file @ %span.commit-short-id= @commit.short_id(6) + - else + %span= diff.new_path + - if diff.a_mode && diff.b_mode && diff.a_mode != diff.b_mode + %span.file-mode= "#{diff.a_mode} → #{diff.b_mode}" - .content - -# Skipp all non non-supported blobs - - next unless file.respond_to?('text?') - - if file.text? - = render "commits/text_file", diff: diff, index: i - - elsif file.image? - - old_file = (@commit.prev_commit.tree / diff.old_path) if !@commit.prev_commit.nil? - = render "commits/image", diff: diff, old_file: old_file, file: file, index: i + = link_to project_tree_path(@project, tree_join(@commit.id, diff.new_path)), {:class => 'btn very_small right view-commit'} do + View file @ + %span.commit-short-id= @commit.short_id(6) + + %br/ + .diff_file_content + -# Skipp all non non-supported blobs + - next unless file.respond_to?('text?') + - if file.text? + = render "commits/text_file", diff: diff, index: i + - elsif file.image? + - old_file = (@commit.prev_commit.tree / diff.old_path) + - if diff.renamed_file || diff.new_file || diff.deleted_file + .diff_file_content_image + .image{class: image_diff_class(diff)} + %img{src: "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"} + %div.image-info= "#{number_to_human_size file.size}" - else - %p.nothing_here_message No preview for this file type + .diff_file_content_image.img_compared + .image.diff_removed + %img{src: "data:#{file.mime_type};base64,#{Base64.encode64(old_file.data)}"} + %div.image-info= "#{number_to_human_size file.size}" + .image.diff_added + %img{src: "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"} + %div.image-info= "#{number_to_human_size file.size}" + - else + %p.nothing_here_message No preview for this file type diff --git a/app/views/commits/_head.html.haml b/app/views/commits/_head.html.haml index 20b9195c..2ec1d24b 100644 --- a/app/views/commits/_head.html.haml +++ b/app/views/commits/_head.html.haml @@ -2,19 +2,19 @@ %li= render partial: 'shared/ref_switcher', locals: {destination: 'commits'} = nav_link(controller: [:commit, :commits]) do - = link_to 'Commits', project_commits_path(@project, @repository.root_ref) + = link_to 'Commits', project_commits_path(@project, @project.root_ref) = nav_link(controller: :compare) do = link_to 'Compare', project_compare_index_path(@project) = nav_link(html_options: {class: branches_tab_class}) do = link_to project_repository_path(@project) do Branches - %span.badge= @repository.branches.length + %span.badge= @project.branches.length = nav_link(controller: :repositories, action: :tags) do = link_to tags_project_repository_path(@project) do Tags - %span.badge= @repository.tags.length + %span.badge= @project.tags.length = nav_link(controller: :repositories, action: :stats) do = link_to stats_project_repository_path(@project) do @@ -22,6 +22,7 @@ - if current_controller?(:commits) && current_user.private_token - %li.pull-right - = link_to project_commits_path(@project, @ref, {format: :atom, private_token: current_user.private_token}), title: "Feed" do - %i.icon-rss + %li.right + %span.rss-icon + = link_to project_commits_path(@project, @ref, {format: :atom, private_token: current_user.private_token}), title: "Feed" do + = image_tag "rss_ui.png", title: "feed" diff --git a/app/views/commits/_image.html.haml b/app/views/commits/_image.html.haml deleted file mode 100644 index db02fa33..00000000 --- a/app/views/commits/_image.html.haml +++ /dev/null @@ -1,63 +0,0 @@ -- if diff.renamed_file || diff.new_file || diff.deleted_file - .image - %span.wrap - .frame{class: image_diff_class(diff)} - %img{src: "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"} - %p.image-info= "#{number_to_human_size file.size}" -- else - .image - %div.two-up.view - %span.wrap - .frame.deleted - %a{href: project_tree_path(@project, tree_join(@commit.id, diff.old_path))} - %img{src: "data:#{old_file.mime_type};base64,#{Base64.encode64(old_file.data)}"} - %p.image-info.hide - %span.meta-filesize= "#{number_to_human_size old_file.size}" - | - %b W: - %span.meta-width - | - %b H: - %span.meta-height - %span.wrap - .frame.added - %a{href: project_tree_path(@project, tree_join(@commit.id, diff.new_path))} - %img{src: "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"} - %p.image-info.hide - %span.meta-filesize= "#{number_to_human_size file.size}" - | - %b W: - %span.meta-width - | - %b H: - %span.meta-height - - %div.swipe.view.hide - .swipe-frame - .frame.deleted - %img{src: "data:#{old_file.mime_type};base64,#{Base64.encode64(old_file.data)}"} - .swipe-wrap - .frame.added - %img{src: "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"} - %span.swipe-bar - %span.top-handle - %span.bottom-handle - - %div.onion-skin.view.hide - .onion-skin-frame - .frame.deleted - %img{src: "data:#{old_file.mime_type};base64,#{Base64.encode64(old_file.data)}"} - .frame.added - %img{src: "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"} - .controls - .transparent - .drag-track - .dragger{:style => "left: 0px;"} - .opaque - - - .view-modes.hide - %ul.view-modes-menu - %li.two-up{data: {mode: 'two-up'}} 2-up - %li.swipe{data: {mode: 'swipe'}} Swipe - %li.onion-skin{data: {mode: 'onion-skin'}} Onion skin \ No newline at end of file diff --git a/app/views/commits/_text_file.html.haml b/app/views/commits/_text_file.html.haml index 760fd07e..02117386 100644 --- a/app/views/commits/_text_file.html.haml +++ b/app/views/commits/_text_file.html.haml @@ -2,8 +2,8 @@ - if too_big %a.supp_diff_link Diff suppressed. Click to show -%table.text-file{class: "#{'hide' if too_big}"} - - each_diff_line(diff, index) do |line, type, line_code, line_new, line_old| +%table{class: "#{'hide' if too_big}"} + - each_diff_line(diff.diff.lines.to_a, index) do |line, type, line_code, line_new, line_old| %tr.line_holder{ id: line_code } - if type == "match" %td.old_line= "..." @@ -13,11 +13,11 @@ %td.old_line = link_to raw(type == "new" ? " " : line_old), "##{line_code}", id: line_code - if @comments_allowed - = render "notes/diff_note_link", line_code: line_code + = render "notes/per_line_note_link", line_code: line_code %td.new_line= link_to raw(type == "old" ? " " : line_new) , "##{line_code}", id: line_code - %td.line_content{class: "noteable_line #{type} #{line_code}", "line_code" => line_code}= raw diff_line_content(line) + %td.line_content{class: "noteable_line #{type} #{line_code}", "line_code" => line_code}= raw "#{line}  " - - if @reply_allowed - - comments = @line_notes.select { |n| n.line_code == line_code }.sort_by(&:created_at) - - unless comments.empty? - = render "notes/diff_notes_with_reply", notes: comments + - if @comments_allowed + - comments = @line_notes.select { |n| n.line_code == line_code }.sort_by(&:created_at) + - unless comments.empty? + = render "notes/per_line_notes_with_reply", notes: comments diff --git a/app/views/commits/show.html.haml b/app/views/commits/show.html.haml index 586b21df..9451a038 100644 --- a/app/views/commits/show.html.haml +++ b/app/views/commits/show.html.haml @@ -2,10 +2,10 @@ - if @path.present? %ul.breadcrumb - = commits_breadcrumbs + = breadcrumbs %div{id: dom_id(@project)} - #commits-list= render "commits" + #commits_list= render "commits" .clear .loading{ style: "display:none;"} diff --git a/app/views/compare/_form.html.haml b/app/views/compare/_form.html.haml index ef80cd4a..07f1c818 100644 --- a/app/views/compare/_form.html.haml +++ b/app/views/compare/_form.html.haml @@ -1,34 +1,27 @@ %div - - unless params[:to] - %p.slead - Fill input field with commit id like - %code.label-branch 4eedf23 - or branch/tag name like - %code.label-branch master - and press compare button for commits list, code diff. + %p.slead + Fill input field with commit id like + %code.label_branch 4eedf23 + or branch/tag name like + %code.label_branch master + and press compare button for commits list, code diff. - %br + %br = form_tag project_compare_index_path(@project), method: :post do .clearfix - .pull-left - - if params[:to] && params[:from] - = link_to 'switch', {from: params[:to], to: params[:from]}, {class: 'commits-compare-switch has_tooltip', title: 'Switch base of comparison'} - = text_field_tag :from, params[:from], placeholder: "master", class: "xlarge" - = "..." - = text_field_tag :to, params[:to], placeholder: "aa8b4ef", class: "xlarge" - .pull-left -   - = submit_tag "Compare", class: "btn btn-primary wide commits-compare-btn" + = text_field_tag :from, params[:from], placeholder: "master", class: "xlarge" + = "..." + = text_field_tag :to, params[:to], placeholder: "aa8b4ef", class: "xlarge" - if @refs_are_same .alert %span Refs are the same - - + .actions + = submit_tag "Compare", class: "btn primary wide commits-compare-btn" :javascript $(function() { - var availableTags = #{@project.repository.ref_names.to_json}; + var availableTags = #{@project.ref_names.to_json}; $("#from, #to").autocomplete({ source: availableTags, diff --git a/app/views/compare/show.html.haml b/app/views/compare/show.html.haml index 56c4a113..528c8b44 100644 --- a/app/views/compare/show.html.haml +++ b/app/views/compare/show.html.haml @@ -6,17 +6,10 @@ = render "form" -- if @commits.size > 100 - .alert.alert-block - %p - %strong Warning! This comparison include 100+ commits. - %p To prevent performance issue we dont show diff information. - - if @commits.present? %div.ui-box - %h5.title - Commits (#{@commits.count}) - %ul.well-list= render Commit.decorate(@commits) + %h5.small Commits (#{@commits.count}) + %ul.unstyled= render @commits - unless @diffs.empty? %h4 Diff diff --git a/app/views/dashboard/_activities.html.haml b/app/views/dashboard/_activities.html.haml deleted file mode 100644 index 2b7d23c2..00000000 --- a/app/views/dashboard/_activities.html.haml +++ /dev/null @@ -1,13 +0,0 @@ -= render "events/event_last_push", event: @last_push - -.event_filter - = event_filter_link EventFilter.push, 'Push events' - = event_filter_link EventFilter.merged, 'Merge events' - = event_filter_link EventFilter.comments, 'Comments' - = event_filter_link EventFilter.team, 'Team' - -- if @events.any? - .content_list -- else - %p.nothing_here_message Projects activity will be displayed here -.loading.hide diff --git a/app/views/dashboard/_filter.html.haml b/app/views/dashboard/_filter.html.haml deleted file mode 100644 index df4447cd..00000000 --- a/app/views/dashboard/_filter.html.haml +++ /dev/null @@ -1,33 +0,0 @@ -= form_tag dashboard_filter_path(entity), method: 'get' do - %fieldset.dashboard-search-filter - = search_field_tag "search", params[:search], { id: 'filter_search', placeholder: 'Search', class: 'search-text-input' } - = button_tag type: 'submit', class: 'btn' do - %i.icon-search - - %fieldset - %legend Status: - %ul.nav.nav-pills.nav-stacked - %li{class: ("active" if !params[:status])} - = link_to dashboard_filter_path(entity, status: nil) do - Open - %li{class: ("active" if params[:status] == 'closed')} - = link_to dashboard_filter_path(entity, status: 'closed') do - Closed - %li{class: ("active" if params[:status] == 'all')} - = link_to dashboard_filter_path(entity, status: 'all') do - All - - %fieldset - %legend Projects: - %ul.nav.nav-pills.nav-stacked - - @projects.each do |project| - - unless entities_per_project(project, entity).zero? - %li{class: ("active" if params[:project_id] == project.id.to_s)} - = link_to dashboard_filter_path(entity, project_id: project.id) do - = project.name_with_namespace - %small.pull-right= entities_per_project(project, entity) - - %fieldset - %hr - = link_to "Reset", dashboard_filter_path(entity), class: 'btn pull-right' - diff --git a/app/views/dashboard/_groups.html.haml b/app/views/dashboard/_groups.html.haml index 2fedf87a..7c5e9f3f 100644 --- a/app/views/dashboard/_groups.html.haml +++ b/app/views/dashboard/_groups.html.haml @@ -1,24 +1,20 @@ -.ui-box - %h5.title +.groups_box + %h5 Groups - %span.light + %small (#{groups.count}) - if current_user.can_create_group? - %span.pull-right - = link_to new_group_path, class: "btn btn-small" do + %span.right + = link_to new_admin_group_path, class: "btn very_small info" do %i.icon-plus New Group - %ul.well-list + %ul.unstyled - groups.each do |group| - %li - = link_to group_path(id: group.path), class: dom_class(group) do - %strong.well-title - = truncate(group.name, length: 35) + %li.wll + = link_to group_path(id: group.code), class: dom_class(group) do + %strong.group_name= truncate(group.name, length: 25) %span.arrow → %span.last_activity - %strong Owner: - %span= group.owner_name - - if groups.blank? - %li - %h3.nothing_here_message You have no groups yet. + %strong Projects: + %span= group.projects.count diff --git a/app/views/dashboard/_projects.html.haml b/app/views/dashboard/_projects.html.haml index a106e83e..00f19ccd 100644 --- a/app/views/dashboard/_projects.html.haml +++ b/app/views/dashboard/_projects.html.haml @@ -1,31 +1,21 @@ -.ui-box - %h5.title +.projects_box + %h5 Projects - %span.light - (#{@projects_count}) + %small + (#{projects.total_count}) - if current_user.can_create_project? - %span.pull-right - = link_to new_project_path, class: "btn btn-small" do + %span.right + = link_to new_project_path, class: "btn very_small info" do %i.icon-plus New Project - - %ul.well-list + %ul.unstyled - projects.each do |project| - %li + %li.wll = link_to project_path(project), class: dom_class(project) do - - if project.namespace - = project.namespace.human_name - \/ - %strong.well-title - = truncate(project.name, length: 25) + %strong.project_name= truncate(project.name, length: 25) %span.arrow → %span.last_activity %strong Last activity: %span= project_last_activity(project) - - if projects.blank? - %li - %h3.nothing_here_message There are no projects here. - - if @projects_count > 20 - %li.bottom - %strong= link_to "show all projects", projects_dashboard_path + .bottom= paginate projects, theme: "gitlab" diff --git a/app/views/dashboard/_sidebar.html.haml b/app/views/dashboard/_sidebar.html.haml deleted file mode 100644 index 876a5b61..00000000 --- a/app/views/dashboard/_sidebar.html.haml +++ /dev/null @@ -1,28 +0,0 @@ -%ul.nav.nav-tabs.dash-sidebar-tabs - %li.active - = link_to 'Projects', '#projects', 'data-toggle' => 'tab', id: 'sidebar-projects-tab' - %li - = link_to 'Groups', '#groups', 'data-toggle' => 'tab', id: 'sidebar-groups-tab' - %li - = link_to 'Teams', '#teams', 'data-toggle' => 'tab', id: 'sidebar-teams-tab' - -.tab-content - .tab-pane.active#projects - = render "projects", projects: @projects - .tab-pane#groups - = render "groups", groups: @groups - .tab-pane#teams - = render "teams", teams: @teams - -.prepend-top-20 - %span.rss-icon - = link_to dashboard_path(:atom, { private_token: current_user.private_token }) do - %strong - %i.icon-rss - News Feed - -%hr -.gitlab-promo - = link_to "Homepage", "http://gitlab.org" - = link_to "Blog", "http://blog.gitlab.org" - = link_to "@gitlabhq", "https://twitter.com/gitlabhq" diff --git a/app/views/dashboard/_teams.html.haml b/app/views/dashboard/_teams.html.haml deleted file mode 100644 index 95d87f50..00000000 --- a/app/views/dashboard/_teams.html.haml +++ /dev/null @@ -1,23 +0,0 @@ -.ui-box.teams-box - %h5.title - Teams - %span.light - (#{teams.count}) - %span.pull-right - = link_to new_team_path, class: "btn btn-small" do - %i.icon-plus - New Team - %ul.well-list - - teams.each do |team| - %li - = link_to team_path(id: team.path), class: dom_class(team) do - %strong.well-title= truncate(team.name, length: 35) - %span.pull-right.light - - if team.owner == current_user - %i.icon-wrench - - tm = current_user.user_team_user_relationships.find_by_user_team_id(team.id) - - if tm - = tm.access_human - - if teams.blank? - %li - %h3.nothing_here_message You have no teams yet. diff --git a/app/views/dashboard/_zero_authorized_projects.html.haml b/app/views/dashboard/_zero_authorized_projects.html.haml deleted file mode 100644 index 4b0d0d68..00000000 --- a/app/views/dashboard/_zero_authorized_projects.html.haml +++ /dev/null @@ -1,12 +0,0 @@ -%h3.nothing_here_message - There are no projects you have access to. - %br - - if current_user.can_create_project? - You can create up to - = current_user.projects_limit - projects. Click on button below to add a new one - .link_holder - = link_to new_project_path, class: "btn btn-primary" do - New Project » - - else - If you will be added to project - it will be displayed here diff --git a/app/views/dashboard/show.atom.builder b/app/views/dashboard/index.atom.builder similarity index 85% rename from app/views/dashboard/show.atom.builder rename to app/views/dashboard/index.atom.builder index 29b2e4a2..ffa15258 100644 --- a/app/views/dashboard/show.atom.builder +++ b/app/views/dashboard/index.atom.builder @@ -1,13 +1,13 @@ xml.instruct! xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do xml.title "Dashboard feed#{" - #{current_user.name}" if current_user.name.present?}" - xml.link :href => dashboard_url(:atom), :rel => "self", :type => "application/atom+xml" - xml.link :href => dashboard_url, :rel => "alternate", :type => "text/html" + xml.link :href => projects_url(:atom), :rel => "self", :type => "application/atom+xml" + xml.link :href => projects_url, :rel => "alternate", :type => "text/html" xml.id projects_url xml.updated @events.maximum(:updated_at).strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any? @events.each do |event| - if event.proper? + if event.allowed? event = EventDecorator.decorate(event) xml.entry do event_link = event.feed_url diff --git a/app/views/dashboard/index.html.haml b/app/views/dashboard/index.html.haml new file mode 100644 index 00000000..d0882c6d --- /dev/null +++ b/app/views/dashboard/index.html.haml @@ -0,0 +1,51 @@ +- if @projects.any? + .projects + .activities.span8 + = render "events/event_last_push", event: @last_push + = render 'shared/no_ssh' + + .event_filter + = event_filter_link EventFilter.push, 'Push events' + = event_filter_link EventFilter.merged, 'Merge events' + = event_filter_link EventFilter.comments, 'Comments' + = event_filter_link EventFilter.team, 'Team' + + - if @events.any? + .content_list= render @events + - else + %p.nothing_here_message Projects activity will be displayed here + .loading.hide + .side + - if @groups.present? + = render "groups", groups: @groups + = render "projects", projects: @projects + %div + %span.rss-icon + = link_to dashboard_path(:atom, { private_token: current_user.private_token }) do + = image_tag "rss_ui.png", title: "feed" + %strong News Feed + + %hr + .gitlab-promo + = link_to "Homepage", "http://gitlabhq.com" + = link_to "Blog", "http://blog.gitlabhq.com" + = link_to "@gitlabhq", "https://twitter.com/gitlabhq" + + +- else + %h3.nothing_here_message There are no projects you have access to. + %br + %h4.nothing_here_message + - if current_user.can_create_project? + You can create up to + = current_user.projects_limit + projects. Click on button below to add a new one + .link_holder + = link_to new_project_path, class: "btn primary" do + New Project » + - else + If you will be added to project - it will be displayed here + + +:javascript + $(function(){ Pager.init(20); }); diff --git a/app/views/dashboard/show.js.haml b/app/views/dashboard/index.js.haml similarity index 100% rename from app/views/dashboard/show.js.haml rename to app/views/dashboard/index.js.haml diff --git a/app/views/dashboard/issues.atom.builder b/app/views/dashboard/issues.atom.builder index 0f0f3466..5bd07bcd 100644 --- a/app/views/dashboard/issues.atom.builder +++ b/app/views/dashboard/issues.atom.builder @@ -1,9 +1,9 @@ xml.instruct! xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do - xml.title "#{current_user.name} issues" - xml.link :href => issues_dashboard_url(:atom, :private_token => current_user.private_token), :rel => "self", :type => "application/atom+xml" - xml.link :href => issues_dashboard_url(:private_token => current_user.private_token), :rel => "alternate", :type => "text/html" - xml.id issues_dashboard_url(:private_token => current_user.private_token) + xml.title "#{@user.name} issues" + xml.link :href => dashboard_issues_url(:atom, :private_token => @user.private_token), :rel => "self", :type => "application/atom+xml" + xml.link :href => dashboard_issues_url(:private_token => @user.private_token), :rel => "alternate", :type => "text/html" + xml.id dashboard_issues_url(:private_token => @user.private_token) xml.updated @issues.first.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") if @issues.any? @issues.each do |issue| diff --git a/app/views/dashboard/issues.html.haml b/app/views/dashboard/issues.html.haml index 539c5765..cc488d57 100644 --- a/app/views/dashboard/issues.html.haml +++ b/app/views/dashboard/issues.html.haml @@ -1,24 +1,19 @@ %h3.page_title Issues %small (assigned to you) - %small.pull-right #{@issues.total_count} issues + %small.right #{@issues.total_count} issues -%hr - -.row - .span3 - = render 'filter', entity: 'issue' - .span9 - - if @issues.any? - - @issues.group_by(&:project).each do |group| - %div.ui-box - - project = group[0] - %h5.title - = link_to_project project - %ul.well-list.issues_table - - group[1].each do |issue| - = render issue - %hr - = paginate @issues, theme: "gitlab" - - else - %p.nothing_here_message Nothing to show here +%br +.clearfix +- if @issues.any? + - @issues.group_by(&:project).each do |group| + %div.ui-box + - @project = group[0] + %h5= @project.name + %ul.unstyled.issues_table + - group[1].each do |issue| + = render(partial: 'issues/show', locals: {issue: issue}) + %hr + = paginate @issues, theme: "gitlab" +- else + %h3.nothing_here_message Nothing to show here diff --git a/app/views/dashboard/merge_requests.html.haml b/app/views/dashboard/merge_requests.html.haml index a311729d..23a7e722 100644 --- a/app/views/dashboard/merge_requests.html.haml +++ b/app/views/dashboard/merge_requests.html.haml @@ -1,11 +1,18 @@ %h3.page_title Merge Requests %small (authored by or assigned to you) - %small.pull-right #{@merge_requests.total_count} merge requests + %small.right #{@merge_requests.total_count} merge requests -%hr -.row - .span3 - = render 'filter', entity: 'merge_request' - .span9 - = render 'shared/merge_requests' +%br +- if @merge_requests.any? + - @merge_requests.group_by(&:project).each do |group| + %ul.unstyled.ui-box + - @project = group[0] + %h5= @project.name + - group[1].each do |merge_request| + = render(partial: 'merge_requests/merge_request', locals: {merge_request: merge_request}) + %hr + = paginate @merge_requests, theme: "gitlab" + +- else + %h3.nothing_here_message Nothing to show here diff --git a/app/views/dashboard/projects.html.haml b/app/views/dashboard/projects.html.haml deleted file mode 100644 index 29a16d61..00000000 --- a/app/views/dashboard/projects.html.haml +++ /dev/null @@ -1,63 +0,0 @@ -%h3.page_title - Projects - %span - (#{@projects.total_count}) - - if current_user.can_create_project? - %span.pull-right - = link_to new_project_path, class: "btn btn-tiny info" do - %i.icon-plus - New Project - - -%hr -.row - .span3 - %ul.nav.nav-pills.nav-stacked - = nav_tab :scope, nil do - = link_to "All", projects_dashboard_path - = nav_tab :scope, 'personal' do - = link_to "Personal", projects_dashboard_path(scope: 'personal') - = nav_tab :scope, 'joined' do - = link_to "Joined", projects_dashboard_path(scope: 'joined') - - .span9 - = form_tag projects_dashboard_path, method: 'get' do - %fieldset.dashboard-search-filter - = hidden_field_tag "scope", params[:scope] - = search_field_tag "search", params[:search], { id: 'dashboard_projects_search', placeholder: 'Search', class: 'left input-xxlarge'} - = button_tag type: 'submit', class: 'btn' do - %i.icon-search - - %ul.well-list - - @projects.each do |project| - %li.clearfix - .clearfix - %h5 - = link_to project_path(project), class: dom_class(project) do - - if project.namespace - = project.namespace.human_name - \/ - %strong - = truncate(project.name, length: 45) - .pull-right.light - - if project.owner == current_user - %i.icon-wrench - - tm = project.team.get_tm(current_user.id) - - if tm - %strong= tm.project_access_human - .clearfix - .left - - if project.description.present? - %span.light= project.description - - .pull-right.light - %small.light - Last activity #{project_last_activity(project)} - - - if @projects.blank? - %li - %h3.nothing_here_message There are no projects here. - .bottom - %hr - = paginate @projects, theme: "gitlab" - diff --git a/app/views/dashboard/show.html.haml b/app/views/dashboard/show.html.haml deleted file mode 100644 index 1a66ba4f..00000000 --- a/app/views/dashboard/show.html.haml +++ /dev/null @@ -1,12 +0,0 @@ -- if @has_authorized_projects - .projects - .activities.span8 - = render 'activities' - .side.span4 - = render 'sidebar' - -- else - = render "zero_authorized_projects" - -:javascript - dashboardPage(); diff --git a/app/views/deploy_keys/_form.html.haml b/app/views/deploy_keys/_form.html.haml index 5fb83021..6beba562 100644 --- a/app/views/deploy_keys/_form.html.haml +++ b/app/views/deploy_keys/_form.html.haml @@ -1,7 +1,7 @@ %div = form_for [@project, @key], url: project_deploy_keys_path do |f| -if @key.errors.any? - .alert.alert-error + .alert-message.block-message.error %ul - @key.errors.full_messages.each do |msg| %li= msg @@ -18,6 +18,6 @@ = link_to "here", help_ssh_path .actions - = f.submit 'Save', class: "btn-save btn" - = link_to "Cancel", project_deploy_keys_path(@project), class: "btn btn-cancel" + = f.submit 'Save', class: "save-btn btn" + = link_to "Cancel", project_deploy_keys_path(@project), class: "btn cancel-btn" diff --git a/app/views/deploy_keys/_show.html.haml b/app/views/deploy_keys/_show.html.haml index 63505435..a5314ae9 100644 --- a/app/views/deploy_keys/_show.html.haml +++ b/app/views/deploy_keys/_show.html.haml @@ -8,5 +8,5 @@ = time_ago_in_words(key.created_at) ago %td - = link_to 'Remove', project_deploy_key_path(key.project, key), confirm: 'Are you sure?', method: :delete, class: "btn btn-remove delete-key btn-small pull-right" + = link_to 'Remove', project_deploy_key_path(key.project, key), confirm: 'Are you sure?', method: :delete, class: "danger btn delete-key small right" diff --git a/app/views/deploy_keys/index.html.haml b/app/views/deploy_keys/index.html.haml index 80d30e1c..b9c654a1 100644 --- a/app/views/deploy_keys/index.html.haml +++ b/app/views/deploy_keys/index.html.haml @@ -1,10 +1,10 @@ -= render "projects/settings_nav" += render "repositories/head" %p.slead Deploy keys allow read-only access to repository. It matches perfectly for CI, staging or production servers. - if can? current_user, :admin_project, @project - = link_to new_project_deploy_key_path(@project), class: "btn btn-small", title: "New Deploy Key" do + = link_to new_project_deploy_key_path(@project), class: "btn small", title: "New Deploy Key" do Add Deploy Key - if @keys.any? %table diff --git a/app/views/deploy_keys/new.html.haml b/app/views/deploy_keys/new.html.haml index 0bbea1eb..e973cb7d 100644 --- a/app/views/deploy_keys/new.html.haml +++ b/app/views/deploy_keys/new.html.haml @@ -1,4 +1,4 @@ -= render "projects/settings_nav" += render "repositories/head" %h3.page_title New Deploy key %hr diff --git a/app/views/deploy_keys/show.html.haml b/app/views/deploy_keys/show.html.haml index 0a9f376d..c94cf10d 100644 --- a/app/views/deploy_keys/show.html.haml +++ b/app/views/deploy_keys/show.html.haml @@ -1,5 +1,4 @@ -= render "projects/settings_nav" - += render "repositories/head" %h3.page_title Deploy key: = @key.title @@ -11,5 +10,5 @@ ← To keys list %hr %pre= @key.key -.pull-right - = link_to 'Remove', project_deploy_key_path(@key.project, @key), confirm: 'Are you sure?', method: :delete, class: "btn-remove btn delete-key" +.right + = link_to 'Remove', project_deploy_key_path(@key.project, @key), confirm: 'Are you sure?', method: :delete, class: "danger btn delete-key" diff --git a/app/views/devise/passwords/edit.html.haml b/app/views/devise/passwords/edit.html.haml index e5800025..31d35567 100644 --- a/app/views/devise/passwords/edit.html.haml +++ b/app/views/devise/passwords/edit.html.haml @@ -8,5 +8,5 @@ %div = f.password_field :password_confirmation, class: "text bottom", placeholder: "Confirm new password" %div - = f.submit "Change my password", class: "btn btn-primary" - .pull-right= render partial: "devise/shared/links" + = f.submit "Change my password", class: "btn primary" + .right= render partial: "devise/shared/links" diff --git a/app/views/devise/passwords/new.html.erb b/app/views/devise/passwords/new.html.erb index 0e39f318..cf56c5d2 100644 --- a/app/views/devise/passwords/new.html.erb +++ b/app/views/devise/passwords/new.html.erb @@ -1,9 +1,9 @@ -<%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { class: "login-box", method: :post }) do |f| %> - <%= image_tag "login-logo.png", width: "304", height: "66", class: "login-logo", alt: "Login Logo" %> +<%= form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :class => "login-box", :method => :post }) do |f| %> + <%= image_tag "login-logo.png", :width => "304", :height => "66", :class => "login-logo", :alt => "Login Logo" %> <%= devise_error_messages! %> - <%= f.email_field :email, placeholder: "Email", class: "text" %> + <%= f.email_field :email, :placeholder => "Email", :class => "text" %>

    - <%= f.submit "Reset password", class: "btn-primary btn" %> -
    <%= link_to "Sign in", new_session_path(resource_name), class: "btn" %>
    + <%= f.submit "Reset password", :class => "primary btn" %> +
    <%= render :partial => "devise/shared/links" %>
    <% end %> diff --git a/app/views/devise/registrations/new.html.erb b/app/views/devise/registrations/new.html.erb new file mode 100644 index 00000000..4ac617c5 --- /dev/null +++ b/app/views/devise/registrations/new.html.erb @@ -0,0 +1,18 @@ +

    Sign up

    + +<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f| %> + <%= devise_error_messages! %> + +
    <%= f.label :email %>
    + <%= f.email_field :email %>
    + +
    <%= f.label :password %>
    + <%= f.password_field :password %>
    + +
    <%= f.label :password_confirmation %>
    + <%= f.password_field :password_confirmation %>
    + +
    <%= f.submit "Sign up", :class => "input_button" %>
    +<% end %> + +<%= render :partial => "devise/shared/links" %> diff --git a/app/views/devise/registrations/new.html.haml b/app/views/devise/registrations/new.html.haml deleted file mode 100644 index 12b04382..00000000 --- a/app/views/devise/registrations/new.html.haml +++ /dev/null @@ -1,18 +0,0 @@ -= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { class: "login-box" }) do |f| - = image_tag "login-logo.png", width: "304", height: "66", class: "login-logo", alt: "Login Logo" - = devise_error_messages! - %div - = f.text_field :name, class: "text top", placeholder: "Name", required: true - %div - = f.text_field :username, class: "text middle", placeholder: "Username", required: true - %div - = f.email_field :email, class: "text middle", placeholder: "Email", required: true - %div - = f.password_field :password, class: "text middle", placeholder: "Password", required: true - %div - = f.password_field :password_confirmation, class: "text bottom", placeholder: "Confirm password", required: true - %div - = f.submit "Sign up", class: "btn-create btn" - %hr - = link_to "Sign in", new_session_path(resource_name) - = link_to "Forgot your password?", new_password_path(resource_name), class: "pull-right" diff --git a/app/views/devise/sessions/_new_ldap.html.haml b/app/views/devise/sessions/_new_ldap.html.haml index eb8c5194..4233aa61 100644 --- a/app/views/devise/sessions/_new_ldap.html.haml +++ b/app/views/devise/sessions/_new_ldap.html.haml @@ -1,13 +1,13 @@ = form_tag(user_omniauth_callback_path(:ldap), :class => "login-box", :id => 'new_ldap_user' ) do = image_tag "login-logo.png", :width => "304", :height => "66", :class => "login-logo", :alt => "Login Logo" - = text_field_tag :username, nil, {:class => "text top", :placeholder => "LDAP Login", :autofocus => "autofocus"} + = text_field_tag :username, nil, {:class => "text top", :placeholder => "LDAP Login"} = password_field_tag :password, nil, {:class => "text bottom", :placeholder => "Password"} %br/ - = submit_tag "LDAP Sign in", :class => "btn-primary btn" + = submit_tag "LDAP Sign in", :class => "primary btn" - if devise_mapping.omniauthable? - (resource_class.omniauth_providers - [:ldap]).each do |provider| %hr/ - = link_to "Sign in with #{provider.to_s.titleize}", omniauth_authorize_path(resource_name, provider), :class => "btn btn-primary" + = link_to "Sign in with #{provider.to_s.titleize}", omniauth_authorize_path(resource_name, provider), :class => "btn primary" %br/ %hr/ %a#other_form_toggle{:href => "#", :onclick => "javascript:$('#new_user').toggle();"} Other Sign in @@ -24,6 +24,6 @@ = f.check_box :remember_me %span Remember me %br/ - = f.submit "Sign in", :class => "btn-primary btn" - .pull-right + = f.submit "Sign in", :class => "primary btn" + .right = render :partial => "devise/shared/links" diff --git a/app/views/devise/sessions/new.html.haml b/app/views/devise/sessions/new.html.haml index 5e93ab18..38192d71 100644 --- a/app/views/devise/sessions/new.html.haml +++ b/app/views/devise/sessions/new.html.haml @@ -1,28 +1,22 @@ - if ldap_enable? - = render partial: 'devise/sessions/new_ldap' + = render :partial => 'devise/sessions/new_ldap' - else - = form_for(resource, as: resource_name, url: session_path(resource_name), html: { class: "login-box" }) do |f| - = image_tag "login-logo.png", width: "304", height: "66", class: "login-logo", alt: "Login Logo" - = f.text_field :login, class: "text top", placeholder: "Username or Email", autofocus: "autofocus" - = f.password_field :password, class: "text bottom", placeholder: "Password" + = form_for(resource, :as => resource_name, :url => session_path(resource_name), :html => { :class => "login-box" }) do |f| + = image_tag "login-logo.png", :width => "304", :height => "66", :class => "login-logo", :alt => "Login Logo" + = f.text_field :email, :class => "text top", :placeholder => "Email" + = f.password_field :password, :class => "text bottom", :placeholder => "Password" - if devise_mapping.rememberable? .clearfix.inputs-list - %label.checkbox.remember_me{for: "user_remember_me"} + %label.checkbox.remember_me{:for => "user_remember_me"} = f.check_box :remember_me %span Remember me %br/ - = f.submit "Sign in", class: "btn-create btn" - .pull-right - = link_to "Forgot your password?", new_password_path(resource_name), class: "btn" - %br/ - - if Gitlab.config.gitlab.signup_enabled - %hr/ - Don't have an account? - = link_to "Sign up", new_registration_path(resource_name) + = f.submit "Sign in", :class => "primary btn wide" + .right + = render :partial => "devise/shared/links" + .clearfix - if devise_mapping.omniauthable? && resource_class.omniauth_providers.present? - %hr %div - %span Sign in with:   - resource_class.omniauth_providers.each do |provider| %span = link_to authbutton(provider, 32), omniauth_authorize_path(resource_name, provider) diff --git a/app/views/errors/access_denied.html.haml b/app/views/errors/access_denied.html.haml index f2d082cb..3b60ed8b 100644 --- a/app/views/errors/access_denied.html.haml +++ b/app/views/errors/access_denied.html.haml @@ -1,5 +1,4 @@ -%h1.http_status_code 403 -%h3.page_title Access Denied +%h1 Access Denied %hr -%p You are not allowed to access this page. +%h2 You are not allowed to access this page. %p Read more about project permissions #{link_to "here", help_permissions_path, class: "vlink"} diff --git a/app/views/errors/encoding.html.haml b/app/views/errors/encoding.html.haml index a0aa6306..d7b5e68e 100644 --- a/app/views/errors/encoding.html.haml +++ b/app/views/errors/encoding.html.haml @@ -1,4 +1,3 @@ -%h1.http_status_code 500 -%h3.page_title Encoding Error +%h1 Encoding Error %hr %p Page can't be loaded because of an encoding error. diff --git a/app/views/errors/git_not_found.html.haml b/app/views/errors/git_not_found.html.haml index 5c9c4953..cd01ea1b 100644 --- a/app/views/errors/git_not_found.html.haml +++ b/app/views/errors/git_not_found.html.haml @@ -1,6 +1,6 @@ -%h1.http_status_code 404 -%h3.page_title Git Resource Not found +%h1 404 %hr +%h2 Git Resource Not found %p Application can't get access to some branch or commit in your repository. It may have been moved. diff --git a/app/views/errors/gitolite.html.haml b/app/views/errors/gitolite.html.haml new file mode 100644 index 00000000..699e6984 --- /dev/null +++ b/app/views/errors/gitolite.html.haml @@ -0,0 +1,25 @@ +%h1 Git Error +%hr +%h2 GitLab was unable to access your Gitolite system. + +.git_error_tips + %h4 Tips for Administrator: + %ol + %li + %p + Check git logs in admin area + %li + %p + Check config/gitlab.yml for correct settings. + %li + %p + Diagnostic tool: + %pre + bundle exec rake gitlab:app:status RAILS_ENV=production + %li + %p + Permissions: + %pre + = preserve do + sudo chmod -R 770 #{Gitlab.config.git_base_path} + sudo chown -R git:git #{Gitlab.config.git_base_path} diff --git a/app/views/errors/not_found.html.haml b/app/views/errors/not_found.html.haml index ee23d219..a4e8d020 100644 --- a/app/views/errors/not_found.html.haml +++ b/app/views/errors/not_found.html.haml @@ -1,4 +1,4 @@ -%h1.http_status_code 404 -%h3.page_title The resource you were looking for doesn't exist. +%h1 404 %hr +%h2 The resource you were looking for doesn't exist. %p You may have mistyped the address or the page may have moved. diff --git a/app/views/events/_commit.html.haml b/app/views/events/_commit.html.haml index f2f2d47e..ea417aa9 100644 --- a/app/views/events/_commit.html.haml +++ b/app/views/events/_commit.html.haml @@ -1,3 +1,4 @@ +- commit = CommitDecorator.decorate(commit) %li.commit %p = link_to commit.short_id(8), project_commit_path(project, commit), class: "commit_short_id" diff --git a/app/views/events/_event.html.haml b/app/views/events/_event.html.haml index e2bf54ea..0d91a67a 100644 --- a/app/views/events/_event.html.haml +++ b/app/views/events/_event.html.haml @@ -1,16 +1,15 @@ -- if event.proper? +- if event.allowed? %div.event-item - %span.cgray.pull-right - #{time_ago_in_words(event.created_at)} ago. + = event_image(event) + = image_tag gravatar_icon(event.author_email), class: "avatar" - = cache event do - = image_tag gravatar_icon(event.author_email), class: "avatar s24" - - - if event.push? - = render "events/event/push", event: event - .clearfix - - elsif event.note? - = render "events/event/note", event: event - - else - = render "events/event/common", event: event + - if event.push? + = render "events/event/push", event: event + - else + = render "events/event/common", event: event + .clearfix + %span.cgray.right + = time_ago_in_words(event.created_at) + ago. + .clearfix diff --git a/app/views/events/_event_last_push.html.haml b/app/views/events/_event_last_push.html.haml index 2c2f270c..e15f1ac0 100644 --- a/app/views/events/_event_last_push.html.haml +++ b/app/views/events/_event_last_push.html.haml @@ -1,10 +1,12 @@ - if show_last_push_widget?(event) .event_lp + = image_tag "event_push.png" +   %span You pushed to = link_to project_commits_path(event.project, event.ref_name) do %strong= truncate(event.ref_name, length: 28) at - %strong= link_to_project event.project + %strong= link_to event.project.name, event.project %span = time_ago_in_words(event.created_at) ago. diff --git a/app/views/events/event/_common.html.haml b/app/views/events/event/_common.html.haml index 53cbe1c9..dcabd1a9 100644 --- a/app/views/events/event/_common.html.haml +++ b/app/views/events/event/_common.html.haml @@ -2,9 +2,10 @@ %span.author_name= link_to_author event %span.event_label{class: event.action_name}= event_action_name(event) - if event.target - %strong= link_to_gfm truncate(event.target_title), [event.project, event.target] + = link_to [event.project, event.target] do + %strong= truncate event.target_title - else - %strong= gfm truncate(event.target_title) + %strong= truncate event.target_title at - if event.project = link_to_project event.project diff --git a/app/views/events/event/_note.html.haml b/app/views/events/event/_note.html.haml deleted file mode 100644 index 8bcfa95f..00000000 --- a/app/views/events/event/_note.html.haml +++ /dev/null @@ -1,35 +0,0 @@ -.event-title - %span.author_name= link_to_author event - %span.event_label commented on - - if event.note_target - - if event.note_commit? - = event.note_target_type - = link_to event.note_short_commit_id, project_commit_path(event.project, event.note_commit_id), class: "commit_short_id" - - else - = link_to [event.project, event.note_target] do - %strong - #{event.note_target_type} ##{truncate event.note_target_id} - - - elsif event.wall_note? - = link_to 'wall', project_wall_path(event.project) - - else - %strong (deleted) - at - - if event.project - = link_to_project event.project - - else - = event.project_name - -.event-body - .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 - - if note.attachment.image? - = image_tag note.attachment.url, class: 'note-image-attach' - - else - %i.icon-paper-clip - = note.attachment_identifier diff --git a/app/views/events/event/_push.html.haml b/app/views/events/event/_push.html.haml index 119b8e82..869321ed 100644 --- a/app/views/events/event/_push.html.haml +++ b/app/views/events/event/_push.html.haml @@ -7,12 +7,12 @@ = link_to project_commits_path(event.project, event.ref_name) do %strong= event.ref_name at - %strong= link_to_project event.project + %strong= link_to event.project.name, event.project - if event.push_with_commits? - project = event.project .event-body - %ul.well-list.event_commits + %ul.unstyled.event_commits - few_commits = event.commits[0...2] - few_commits.each do |commit| = render "events/commit", commit: commit, project: project diff --git a/app/views/graph/_head.html.haml b/app/views/graph/_head.html.haml deleted file mode 100644 index fba9a958..00000000 --- a/app/views/graph/_head.html.haml +++ /dev/null @@ -1,16 +0,0 @@ -%h3.page_title Project Network Graph -%hr - -.clearfix - .pull-left - = render partial: 'shared/ref_switcher', locals: {destination: 'graph', path: @path} - - .search.pull-right - = form_tag project_graph_path(@project, params[:id]), method: :get do |f| - .control-group - = label_tag :search , "Looking for commit:", class: 'control-label light' - .controls - = text_field_tag :q, @q, placeholder: "Input SHA", class: "search-input xlarge" - = button_tag type: 'submit', class: 'btn vtop' do - %i.icon-search - diff --git a/app/views/graph/show.html.haml b/app/views/graph/show.html.haml deleted file mode 100644 index e45aca1d..00000000 --- a/app/views/graph/show.html.haml +++ /dev/null @@ -1,17 +0,0 @@ -= render "head" -.graph_holder - %h4 - %small You can move around the graph by using the arrow keys. - #holder.graph - .loading.loading-gray - -:javascript - var branch_graph; - $(function(){ - branch_graph = new BranchGraph($("#holder"), { - url: '#{project_graph_path(@project, @ref, q: @q, format: :json)}', - commit_url: '#{project_commit_path(@project, 'ae45ca32').gsub("ae45ca32", "%s")}', - ref: '#{@ref}', - commit_id: '#{@commit.id}' - }); - }); diff --git a/app/views/graph/show.json.erb b/app/views/graph/show.json.erb deleted file mode 100644 index 529d5849..00000000 --- a/app/views/graph/show.json.erb +++ /dev/null @@ -1,23 +0,0 @@ -<% self.formats = ["html"] %> - -<%= raw( - { - days: @graph.days.compact.map { |d| [d.day, d.strftime("%b")] }, - commits: @graph.commits.map do |c| - { - parents: parents_zip_spaces(c.parents(@graph.map), c.parent_spaces), - author: { - name: c.author.name, - email: c.author.email, - icon: gravatar_icon(c.author.email, 20) - }, - time: c.time, - space: c.spaces.first, - refs: get_refs(c), - id: c.sha, - date: c.date, - message: c.message, - } - end - }.to_json -) %> diff --git a/app/views/groups/_filter.html.haml b/app/views/groups/_filter.html.haml deleted file mode 100644 index 5c66f977..00000000 --- a/app/views/groups/_filter.html.haml +++ /dev/null @@ -1,35 +0,0 @@ -= form_tag group_filter_path(entity), method: 'get' do - %fieldset.dashboard-search-filter - = search_field_tag "search", params[:search], { placeholder: 'Search', class: 'search-text-input' } - = button_tag type: 'submit', class: 'btn' do - %i.icon-search - - %fieldset - %legend Status: - %ul.nav.nav-pills.nav-stacked - %li{class: ("active" if !params[:status])} - = link_to group_filter_path(entity, status: nil) do - Open - %li{class: ("active" if params[:status] == 'closed')} - = link_to group_filter_path(entity, status: 'closed') do - Closed - %li{class: ("active" if params[:status] == 'all')} - = link_to group_filter_path(entity, status: 'all') do - All - - %fieldset - %legend Projects: - %ul.nav.nav-pills.nav-stacked - - @projects.each do |project| - - unless entities_per_project(project, entity).zero? - %li{class: ("active" if params[:project_id] == project.id.to_s)} - = link_to group_filter_path(entity, project_id: project.id) do - = project.name_with_namespace - %small.pull-right= entities_per_project(project, entity) - - if @projects.blank? - %p.nothing_here_message This group has no projects yet - - %fieldset - %hr - = link_to "Reset", group_filter_path(entity), class: 'btn pull-right' - diff --git a/app/views/groups/_new_group_member.html.haml b/app/views/groups/_new_group_member.html.haml deleted file mode 100644 index 9cdbea60..00000000 --- a/app/views/groups/_new_group_member.html.haml +++ /dev/null @@ -1,18 +0,0 @@ -= form_for @team_member, as: :team_member, url: team_members_group_path(@group) do |f| - %fieldset - %legend= "New Team member(s) for projects in #{@group.name}" - - %h6 1. Choose people you want in the team - .clearfix - = f.label :user_ids, "People" - .input= select_tag(:user_ids, options_from_collection_for_select(User.active.alphabetically, :id, :name), {data: {placeholder: "Select users"}, class: "chosen xxlarge", multiple: true}) - - %h6 2. Set access level for them - .clearfix - = f.label :project_access, "Project Access" - .input= select_tag :project_access, options_for_select(Project.access_options, @team_member.project_access), class: "project-access-select chosen" - - .form-actions - = hidden_field_tag :redirect_to, people_group_path(@group) - = f.submit 'Add', class: "btn btn-save" - diff --git a/app/views/groups/_new_member.html.haml b/app/views/groups/_new_member.html.haml deleted file mode 100644 index b3424b01..00000000 --- a/app/views/groups/_new_member.html.haml +++ /dev/null @@ -1,18 +0,0 @@ -= form_for @team_member, as: :team_member, url: project_team_members_path(@project, @team_member) do |f| - %fieldset - %legend= "New Team member(s) for #{@project.name}" - - %h6 1. Choose people you want in the team - .clearfix - = f.label :user_ids, "People" - .input= select_tag(:user_ids, options_from_collection_for_select(User.not_in_project(@project).alphabetically, :id, :name), {data: {placeholder: "Select users"}, class: "chosen xxlarge", multiple: true}) - - %h6 2. Set access level for them - .clearfix - = f.label :project_access, "Project Access" - .input= select_tag :project_access, options_for_select(Project.access_options, @team_member.project_access), class: "project-access-select chosen" - - .form-actions - = hidden_field_tag :redirect_to, people_group_path(@group, project_id: @project.id) - = f.submit 'Add', class: "btn btn-save" - diff --git a/app/views/groups/_people_filter.html.haml b/app/views/groups/_people_filter.html.haml deleted file mode 100644 index ee63743e..00000000 --- a/app/views/groups/_people_filter.html.haml +++ /dev/null @@ -1,16 +0,0 @@ -= form_tag people_group_path(@group), method: 'get' do - %fieldset - %legend Projects: - %ul.nav.nav-pills.nav-stacked - - @projects.each do |project| - %li{class: ("active" if params[:project_id] == project.id.to_s)} - = link_to people_group_path(@group, project_id: project.id) do - = project.name_with_namespace - %small.pull-right= project.users.count - - if @projects.blank? - %p.nothing_here_message This group has no projects yet - - %fieldset - %hr - = link_to "Reset", people_group_path(@group), class: 'btn pull-right' - diff --git a/app/views/groups/_projects.html.haml b/app/views/groups/_projects.html.haml index bf1a624b..b565dad3 100644 --- a/app/views/groups/_projects.html.haml +++ b/app/views/groups/_projects.html.haml @@ -1,20 +1,13 @@ -.ui-box - %h5.title +.projects_box + %h5 Projects %small (#{projects.count}) - - if can? current_user, :manage_group, @group - %span.pull-right - = link_to new_project_path(namespace_id: @group.id), class: "btn btn-tiny info" do - %i.icon-plus - New Project - %ul.well-list - - if projects.blank? - %p.nothing_here_message This groups has no projects yet + %ul.unstyled - projects.each do |project| - %li + %li.wll = link_to project_path(project), class: dom_class(project) do - %strong.well-title= truncate(project.name, length: 25) + %strong.project_name= truncate(project.name, length: 25) %span.arrow → %span.last_activity diff --git a/app/views/groups/edit.html.haml b/app/views/groups/edit.html.haml deleted file mode 100644 index eb4f324b..00000000 --- a/app/views/groups/edit.html.haml +++ /dev/null @@ -1,75 +0,0 @@ -.row - .span3 - %ul.nav.nav-pills.nav-stacked - %li.active - = link_to 'Projects', '#tab-projects', 'data-toggle' => 'tab' - %li - = link_to 'Edit Group', '#tab-edit', 'data-toggle' => 'tab' - %li - = link_to 'Transfer', '#tab-transfer', 'data-toggle' => 'tab' - %li - = link_to 'Remove', '#tab-remove', 'data-toggle' => 'tab' - - .span9 - .tab-content - .tab-pane.active#tab-projects - .ui-box - %h5.title Projects - %ul.well-list - - @group.projects.each do |project| - %li - - if project.public - %i.icon-share - - else - %i.icon-lock.cgreen - = link_to project.name_with_namespace, project - .pull-right - = link_to 'Team', project_team_index_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-small" - = link_to 'Edit', edit_project_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-small" - = link_to 'Remove', project, confirm: "REMOVE #{project.name}? Are you sure?", method: :delete, class: "btn btn-small btn-remove" - - if @group.projects.blank? - %p.nothing_here_message This group has no projects yet - - .tab-pane#tab-edit - .ui-box - %h5.title Edit Group - %div.form-holder - = form_for @group do |f| - - if @group.errors.any? - .alert.alert-error - %span= @group.errors.full_messages.first - .clearfix - = f.label :name do - Group name is - .input - = f.text_field :name, placeholder: "Ex. OpenSource", class: "xxlarge left" - - .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" - - .tab-pane#tab-transfer - .ui-box.ui-box-danger - %h5.title Transfer group - .ui-box-body - %p - Transferring group will cause loss of admin control over group and all child projects - = form_for @group do |f| - = users_select_tag(:'group[owner_id]') - %hr - = f.submit 'Transfer group', class: "btn btn-small btn-remove" - - .tab-pane#tab-remove - .ui-box.ui-box-danger - %h5.title Remove group - .ui-box-body - %p - Remove of group will cause removing all child projects and resources. - %p - %strong Removed group can not be restored! - - = link_to 'Remove Group', @group, confirm: 'Removed group can not be restored! Are you sure?', method: :delete, class: "btn btn-remove btn-small" diff --git a/app/views/groups/issues.atom.builder b/app/views/groups/issues.atom.builder index 701747bd..5bd07bcd 100644 --- a/app/views/groups/issues.atom.builder +++ b/app/views/groups/issues.atom.builder @@ -1,9 +1,9 @@ xml.instruct! xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do xml.title "#{@user.name} issues" - xml.link :href => issues_dashboard_url(:atom, :private_token => @user.private_token), :rel => "self", :type => "application/atom+xml" - xml.link :href => issues_dashboard_url(:private_token => @user.private_token), :rel => "alternate", :type => "text/html" - xml.id issues_dashboard_url(:private_token => @user.private_token) + xml.link :href => dashboard_issues_url(:atom, :private_token => @user.private_token), :rel => "self", :type => "application/atom+xml" + xml.link :href => dashboard_issues_url(:private_token => @user.private_token), :rel => "alternate", :type => "text/html" + xml.id dashboard_issues_url(:private_token => @user.private_token) xml.updated @issues.first.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") if @issues.any? @issues.each do |issue| diff --git a/app/views/groups/issues.html.haml b/app/views/groups/issues.html.haml index 96aa2a16..cc488d57 100644 --- a/app/views/groups/issues.html.haml +++ b/app/views/groups/issues.html.haml @@ -1,23 +1,19 @@ %h3.page_title Issues %small (assigned to you) - %small.pull-right #{@issues.total_count} issues + %small.right #{@issues.total_count} issues -%hr -.row - .span3 - = render 'filter', entity: 'issue' - .span9 - - if @issues.any? - - @issues.group_by(&:project).each do |group| - %div.ui-box - - project = group[0] - %h5.title - = link_to_project project - %ul.well-list.issues_table - - group[1].each do |issue| - = render issue - %hr - = paginate @issues, theme: "gitlab" - - else - %p.nothing_here_message Nothing to show here +%br +.clearfix +- if @issues.any? + - @issues.group_by(&:project).each do |group| + %div.ui-box + - @project = group[0] + %h5= @project.name + %ul.unstyled.issues_table + - group[1].each do |issue| + = render(partial: 'issues/show', locals: {issue: issue}) + %hr + = paginate @issues, theme: "gitlab" +- else + %h3.nothing_here_message Nothing to show here diff --git a/app/views/groups/merge_requests.html.haml b/app/views/groups/merge_requests.html.haml index a311729d..23a7e722 100644 --- a/app/views/groups/merge_requests.html.haml +++ b/app/views/groups/merge_requests.html.haml @@ -1,11 +1,18 @@ %h3.page_title Merge Requests %small (authored by or assigned to you) - %small.pull-right #{@merge_requests.total_count} merge requests + %small.right #{@merge_requests.total_count} merge requests -%hr -.row - .span3 - = render 'filter', entity: 'merge_request' - .span9 - = render 'shared/merge_requests' +%br +- if @merge_requests.any? + - @merge_requests.group_by(&:project).each do |group| + %ul.unstyled.ui-box + - @project = group[0] + %h5= @project.name + - group[1].each do |merge_request| + = render(partial: 'merge_requests/merge_request', locals: {merge_request: merge_request}) + %hr + = paginate @merge_requests, theme: "gitlab" + +- else + %h3.nothing_here_message Nothing to show here diff --git a/app/views/groups/new.html.haml b/app/views/groups/new.html.haml deleted file mode 100644 index 36ee4922..00000000 --- a/app/views/groups/new.html.haml +++ /dev/null @@ -1,28 +0,0 @@ -%h3.page_title New Group -%hr -= form_for @group do |f| - - if @group.errors.any? - .alert.alert-error - %span= @group.errors.full_messages.first - .clearfix - = f.label :name do - Group name is - .input - = f.text_field :name, placeholder: "Ex. OpenSource", class: "xxlarge left" - - .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 - %li All created groups are private - %li People within a group see only projects they have access to - %li All projects of group will be stored in group directory - %li You will be able to move existing projects into group diff --git a/app/views/groups/people.html.haml b/app/views/groups/people.html.haml index 3e4eb082..25810808 100644 --- a/app/views/groups/people.html.haml +++ b/app/views/groups/people.html.haml @@ -1,20 +1,12 @@ -.row - .span3 - = render 'people_filter' - .span9 - - if can?(current_user, :manage_group, @group) - = render (@project ? "new_member" : "new_group_member") - .ui-box - %h5.title - Team - %small - (#{@users.size}) - %ul.well-list - - @users.each do |user| - %li - = image_tag gravatar_icon(user.email, 16), class: "avatar s16" - %strong= user.name - %span.cgray= user.email - - if @group.owner == user - %span.btn.btn-small.disabled.pull-right Group Owner +.ui-box + %h5 + People + %small + (#{@users.size}) + %ul.unstyled + - @users.each do |user| + %li.wll + = image_tag gravatar_icon(user.email, 16), class: "avatar s16" + %strong= user.name + %span.cgray= user.email diff --git a/app/views/groups/search.html.haml b/app/views/groups/search.html.haml new file mode 100644 index 00000000..6ca5630f --- /dev/null +++ b/app/views/groups/search.html.haml @@ -0,0 +1,75 @@ += form_tag search_group_path(@group), method: :get, class: 'form-inline' do |f| + .padded + = label_tag :search do + %strong Looking for + .input + = search_field_tag :search, params[:search], placeholder: "issue 143", class: "input-xxlarge search-text-input", id: "dashboard_search" + = submit_tag 'Search', class: "btn primary wide" +- if params[:search].present? + %br + %h3 + Search results + %small (#{@projects.count + @merge_requests.count + @issues.count}) + %hr + .search_results + .row + .span6 + %table + %thead + %tr + %th Projects + %tbody + - @projects.each do |project| + %tr + %td + = link_to project do + %strong.term= project.name + %small.cgray + last activity at + = project.last_activity_date.stamp("Aug 25, 2011") + - if @projects.blank? + %tr + %td + %h4.nothing_here_message No Projects + %br + %table + %thead + %tr + %th Merge Requests + %tbody + - @merge_requests.each do |merge_request| + %tr + %td + = link_to [merge_request.project, merge_request] do + %span.badge.badge-info ##{merge_request.id} + – + %strong.term= truncate merge_request.title, length: 50 + %strong.right + %span.label= merge_request.project.name + - if @merge_requests.blank? + %tr + %td + %h4.nothing_here_message No Merge Requests + .span6 + %table + %thead + %tr + %th Issues + %tbody + - @issues.each do |issue| + %tr + %td + = link_to [issue.project, issue] do + %span.badge.badge-info ##{issue.id} + – + %strong.term= truncate issue.title, length: 40 + %strong.right + %span.label= issue.project.name + - if @issues.blank? + %tr + %td + %h4.nothing_here_message No Issues + :javascript + $(function() { + $(".search_results .term").highlight("#{params[:search]}"); + }) diff --git a/app/views/groups/show.atom.builder b/app/views/groups/show.atom.builder index 5f2999c3..fa3bfade 100644 --- a/app/views/groups/show.atom.builder +++ b/app/views/groups/show.atom.builder @@ -1,13 +1,13 @@ xml.instruct! xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do - xml.title "Group feed - #{@group.name}" - xml.link :href => group_path(@group, :atom), :rel => "self", :type => "application/atom+xml" - xml.link :href => group_path(@group), :rel => "alternate", :type => "text/html" + xml.title "Dashboard feed#{" - #{current_user.name}" if current_user.name.present?}" + xml.link :href => projects_url(:atom), :rel => "self", :type => "application/atom+xml" + xml.link :href => projects_url, :rel => "alternate", :type => "text/html" xml.id projects_url xml.updated @events.maximum(:updated_at).strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any? @events.each do |event| - if event.proper? + if event.allowed? event = EventDecorator.decorate(event) xml.entry do event_link = event.feed_url diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index adf249f6..72d7ad9a 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -1,26 +1,24 @@ .projects .activities.span8 = render "events/event_last_push", event: @last_push - = link_to dashboard_path, class: 'btn btn-tiny' do + = link_to dashboard_path, class: 'btn very_small' do ← To dashboard   - %span.cgray You will only see events from projects in this group + %span.cgray Events and projects are filtered in scope of group %hr + = render 'shared/no_ssh' - if @events.any? - .content_list + .content_list= render @events - else - %p.nothing_here_message Project activity will be displayed here + %h4.nothing_here_message Projects activity will be displayed here .loading.hide - .side.span4 - - if @group.description.present? - .description.well.light - = @group.description + .side = render "projects", projects: @projects - .prepend-top-20 - = link_to group_path(@group, { format: :atom, private_token: current_user.private_token }), title: "Feed" do - %strong - %i.icon-rss - News Feed + %div + %span.rss-icon + = link_to dashboard_path(:atom, { private_token: current_user.private_token }) do + = image_tag "rss_ui.png", title: "feed" + %strong News Feed %hr .gitlab-promo @@ -29,4 +27,4 @@ = link_to "@gitlabhq", "https://twitter.com/gitlabhq" :javascript - $(function(){ Pager.init(20, true); }); + $(function(){ Pager.init(20); }); diff --git a/app/views/help/_layout.html.haml b/app/views/help/_layout.html.haml deleted file mode 100644 index fa5e3a30..00000000 --- a/app/views/help/_layout.html.haml +++ /dev/null @@ -1,34 +0,0 @@ -.row - .span3{:"data-spy" => 'affix'} - .ui-box - .title - %h5 Help - %ul.well-list - %li - %strong= link_to "Workflow", help_workflow_path - %li - %strong= link_to "SSH keys", help_ssh_path - - %li - %strong= link_to "GitLab Markdown", help_markdown_path - - %li - %strong= link_to "Permissions", help_permissions_path - - %li - %strong= link_to "API", help_api_path - - %li - %strong= link_to "Web Hooks", help_web_hooks_path - - %li - %strong= link_to "Rake Tasks", help_raketasks_path - - %li - %strong= link_to "System Hooks", help_system_hooks_path - - %li - %strong= link_to "Public Access", help_public_access_path - - .span9.pull-right - = yield diff --git a/app/views/help/api.html.haml b/app/views/help/api.html.haml index 0c502ada..00085166 100644 --- a/app/views/help/api.html.haml +++ b/app/views/help/api.html.haml @@ -1,116 +1,96 @@ -= render layout: 'help/layout' do - %h3.page_title API - %br +%h3.page_title API +.back_link + = link_to help_path do + ← to index +%br - %ul.nav.nav-tabs.log-tabs.nav-small-tabs - %li.active - = link_to "README", "#README", 'data-toggle' => 'tab' - %li - = link_to "Projects", "#projects", 'data-toggle' => 'tab' - %li - = link_to "Snippets", "#snippets", 'data-toggle' => 'tab' - %li - = link_to "Repositories", "#repositories", 'data-toggle' => 'tab' - %li - = link_to "Users", "#users", 'data-toggle' => 'tab' - %li - = link_to "Session", "#session", 'data-toggle' => 'tab' - %li - = link_to "Issues", "#issues", 'data-toggle' => 'tab' - %li - = link_to "Milestones", "#milestones", 'data-toggle' => 'tab' - %li - = link_to "Notes", "#notes", 'data-toggle' => 'tab' - %li - = link_to "System Hooks", "#system_hooks", 'data-toggle' => 'tab' +%ul.nav.nav-tabs.log-tabs + %li.active + = link_to "README", "#README", 'data-toggle' => 'tab' + %li + = link_to "Projects", "#projects", 'data-toggle' => 'tab' + %li + = link_to "Snippets", "#snippets", 'data-toggle' => 'tab' + %li + = link_to "Repositories", "#repositories", 'data-toggle' => 'tab' + %li + = link_to "Users", "#users", 'data-toggle' => 'tab' + %li + = link_to "Session", "#session", 'data-toggle' => 'tab' + %li + = link_to "Issues", "#issues", 'data-toggle' => 'tab' + %li + = link_to "Milestones", "#milestones", 'data-toggle' => 'tab' - .tab-content - .tab-pane.active#README - .file_holder - .file_title - %i.icon-file - README - .file_content.wiki - = preserve do - = markdown File.read(Rails.root.join("doc", "api", "README.md")) +.tab-content + .tab-pane.active#README + .file_holder + .file_title + %i.icon-file + README + .file_content.wiki + = preserve do + = markdown File.read(Rails.root.join("doc", "api", "README.md")) - .tab-pane#projects - .file_holder - .file_title - %i.icon-file - Projects - .file_content.wiki - = preserve do - = markdown File.read(Rails.root.join("doc", "api", "projects.md")) + .tab-pane#projects + .file_holder + .file_title + %i.icon-file + Projects + .file_content.wiki + = preserve do + = markdown File.read(Rails.root.join("doc", "api", "projects.md")) - .tab-pane#snippets - .file_holder - .file_title - %i.icon-file - Projects Snippets - .file_content.wiki - = preserve do - = markdown File.read(Rails.root.join("doc", "api", "snippets.md")) + .tab-pane#snippets + .file_holder + .file_title + %i.icon-file + Projects Snippets + .file_content.wiki + = preserve do + = markdown File.read(Rails.root.join("doc", "api", "snippets.md")) - .tab-pane#repositories - .file_holder - .file_title - %i.icon-file - Projects - .file_content.wiki - = preserve do - = markdown File.read(Rails.root.join("doc", "api", "repositories.md")) + .tab-pane#repositories + .file_holder + .file_title + %i.icon-file + Projects + .file_content.wiki + = preserve do + = markdown File.read(Rails.root.join("doc", "api", "repositories.md")) - .tab-pane#users - .file_holder - .file_title - %i.icon-file - Users - .file_content.wiki - = preserve do - = markdown File.read(Rails.root.join("doc", "api", "users.md")) + .tab-pane#users + .file_holder + .file_title + %i.icon-file + Users + .file_content.wiki + = preserve do + = markdown File.read(Rails.root.join("doc", "api", "users.md")) - .tab-pane#session - .file_holder - .file_title - %i.icon-file - Session - .file_content.wiki - = preserve do - = markdown File.read(Rails.root.join("doc", "api", "session.md")) + .tab-pane#session + .file_holder + .file_title + %i.icon-file + Session + .file_content.wiki + = preserve do + = markdown File.read(Rails.root.join("doc", "api", "session.md")) - .tab-pane#issues - .file_holder - .file_title - %i.icon-file - Issues - .file_content.wiki - = preserve do - = markdown File.read(Rails.root.join("doc", "api", "issues.md")) + .tab-pane#issues + .file_holder + .file_title + %i.icon-file + Issues + .file_content.wiki + = preserve do + = markdown File.read(Rails.root.join("doc", "api", "issues.md")) - .tab-pane#milestones - .file_holder - .file_title - %i.icon-file - Milestones - .file_content.wiki - = preserve do - = markdown File.read(Rails.root.join("doc", "api", "milestones.md")) - - .tab-pane#notes - .file_holder - .file_title - %i.icon-file - Notes - .file_content.wiki - = preserve do - = markdown File.read(Rails.root.join("doc", "api", "notes.md")) - - .tab-pane#system_hooks - .file_holder - .file_title - %i.icon-file - System Hooks - .file_content.wiki - = preserve do - = markdown File.read(Rails.root.join("doc", "api", "system_hooks.md")) + .tab-pane#milestones + .file_holder + .file_title + %i.icon-file + Milestones + .file_content.wiki + = preserve do + = markdown File.read(Rails.root.join("doc", "api", "milestones.md")) diff --git a/app/views/help/index.html.haml b/app/views/help/index.html.haml index ffea654d..962f2175 100644 --- a/app/views/help/index.html.haml +++ b/app/views/help/index.html.haml @@ -1,78 +1,36 @@ %h3.page_title GITLAB - .pull-right - %span= Gitlab::VERSION - %small= Gitlab::REVISION + .right + %span= Gitlab::Version + %small= Gitlab::Revision %hr %p.lead Self Hosted Git Management %br - Fast, secure and stable solution based on Ruby on Rails. + Fast, secure and stable solution based on Ruby on Rails & Gitolite. -%br +%hr -.row - .span4 - .ui-box - .title - %h5 Quick help - %ul.well-list - %li - Email your - = mail_to Gitlab.config.gitlab.support_email, "support contact" - %li - Use the - = link_to "search bar", '#', onclick: "$('#search').focus();" - on the top of this page - %li - Ask in our - = link_to "support forum", "https://groups.google.com/forum/#!forum/gitlabhq" - %li - Browse our - = link_to "issue tracker", "https://github.com/gitlabhq/gitlabhq/issues" - .span4 - .ui-box - .title - %h5 User documentation - %ul.well-list - %li - %strong= link_to "Workflow", help_workflow_path - %p Learn how to use Git and GitLab together. +%h3 Help - %li - %strong= link_to "SSH keys", help_ssh_path - %p Setup secure access to your projects. +%ol + %li + %h5= link_to "Workflow", help_workflow_path - %li - %strong= link_to "GitLab Markdown", help_markdown_path - %p Learn what you can do with GitLab's advanced formatting system. + %li + %h5= link_to "Permissions", help_permissions_path - %li - %strong= link_to "Permissions", help_permissions_path - %p Get familiar with GitLab's permission levels. + %li + %h5= link_to "Web Hooks", help_web_hooks_path - %li - %strong= link_to "API", help_api_path - %p Explore how you can access GitLab via a simple and powerful API. + %li + %h5= link_to "System Hooks", help_system_hooks_path - %li - %strong= link_to "Web Hooks", help_web_hooks_path - %p Let GitLab notify you when new code has been pushed to your project. + %li + %h5= link_to "API", help_api_path - .span4 - .ui-box - .title - %h5 Admin documentation - %ul.well-list + %li + %h5= link_to "GitLab Markdown", help_markdown_path - %li - %strong= link_to "Rake Tasks", help_raketasks_path - %p Explore what GitLab has in store for you to make administration easier. - - %li - %strong= link_to "System Hooks", help_system_hooks_path - %p Let GitLab notify you when certain management tasks need to be carried out. - - %li - %strong= link_to "Public Access", help_public_access_path - %p Learn how you can allow public access to a project. + %li + %h5= link_to "SSH keys", help_ssh_path diff --git a/app/views/help/markdown.html.haml b/app/views/help/markdown.html.haml index 92c1e49b..aa608ed6 100644 --- a/app/views/help/markdown.html.haml +++ b/app/views/help/markdown.html.haml @@ -1,127 +1,129 @@ -= render layout: 'help/layout' do - %h3.page_title GitLab Flavored Markdown - %br +%h3.page_title GitLab Flavored Markdown +.back_link + = link_to help_path do + ← to index +%hr - .row - .span8 +.row + .span8 + %p + For GitLab we developed something we call "GitLab Flavored Markdown" (GFM). + It extends the standard Markdown in a few significant ways adds some useful functionality. + + %p You can use GFM in: + %ul + %li commit messages + %li comments + %li wall posts + %li issues + %li merge requests + %li milestones + %li wiki pages + + .span4 + .alert.alert-info %p - For GitLab we developed something we call "GitLab Flavored Markdown" (GFM). - It extends the standard Markdown in a few significant ways adds some useful functionality. + If you're not already familiar with Markdown, you should spend 15 minutes and go over the excellent + %strong= link_to "Markdown Syntax Guide", "http://daringfireball.net/projects/markdown/syntax" + at Daring Fireball. - %p You can use GFM in: - %ul - %li commit messages - %li comments - %li wall posts - %li issues - %li merge requests - %li milestones - %li wiki pages +.row + .span8 + %h3 Differences from traditional Markdown - .span4 - .alert.alert-info - %p - If you're not already familiar with Markdown, you should spend 15 minutes and go over the excellent - %strong= link_to "Markdown Syntax Guide", "http://daringfireball.net/projects/markdown/syntax" - at Daring Fireball. + %h4 Newlines - .row - .span8 - %h3 Differences from traditional Markdown + %p + The biggest difference that GFM introduces is in the handling of linebreaks. + With traditional Markdown you can hard wrap paragraphs of text and they will be combined into a single paragraph. We find this to be the cause of a huge number of unintentional formatting errors. + GFM treats newlines in paragraph-like content as real line breaks, which is probably what you intended. - %h4 Newlines + %p The next paragraph contains two phrases separated by a single newline character: + %pre= "Roses are red\nViolets are blue" + %p becomes + = markdown "Roses are red\nViolets are blue" + + %h4 Multiple underscores in words + + %p + It is not reasonable to italicize just part of a word, especially when you're dealing with code and names often appear with multiple underscores. + Therefore, GFM ignores multiple underscores in words. + + %pre= "perform_complicated_task\ndo_this_and_do_that_and_another_thing" + %p becomes + = markdown "perform_complicated_task\ndo_this_and_do_that_and_another_thing" + + %h4 URL autolinking + + %p + GFM will autolink standard URLs you copy and paste into your text. + So if you want to link to a URL (instead of a textual link), you can simply put the URL in verbatim and it will be turned into a link to that URL. + + %h4 Fenced code blocks + + %p + Markdown converts text with four spaces at the front of each line to code blocks. + GFM supports that, but we also support fenced blocks. + Just wrap your code blocks in ``` and you won't need to indent manually to trigger a code block. + + %pre= %Q{```ruby\nrequire 'redcarpet'\nmarkdown = Redcarpet.new("Hello World!")\nputs markdown.to_html\n```} + %p becomes + = markdown %Q{```ruby\nrequire 'redcarpet'\nmarkdown = Redcarpet.new("Hello World!")\nputs markdown.to_html\n```} + + %h4 Emoji + +.row + .span8 + :ruby + puts markdown %Q{Sometimes you want to be :cool: and add some :sparkles: to your :speech_balloon:. Well we have a :gift: for you: + + :exclamation: You can use emoji anywhere GFM is supported. :sunglasses: + + You can use it to point out a :bug: or warn about :monkey:patches. And if someone improves your really :snail: code, send them a :bouquet: or some :candy:. People will :heart: you for that. + + If you are :new: to this, don't be :fearful:. You can easily join the emoji :circus_tent:. All you need to do is to :book: up on the supported codes. + } + + .span4 + .alert.alert-info %p - The biggest difference that GFM introduces is in the handling of linebreaks. - With traditional Markdown you can hard wrap paragraphs of text and they will be combined into a single paragraph. We find this to be the cause of a huge number of unintentional formatting errors. - GFM treats newlines in paragraph-like content as real line breaks, which is probably what you intended. + Consult the + %strong= link_to "Emoji Cheat Sheet", "http://www.emoji-cheat-sheet.com/" + for a list of all supported emoji codes. +.row + .span8 + %h4 Special GitLab references - %p The next paragraph contains two phrases separated by a single newline character: - %pre= "Roses are red\nViolets are blue" - %p becomes - = markdown "Roses are red\nViolets are blue" + %p + GFM recognizes special references. + You can easily reference e.g. a team member, an issue or a commit within a project. + GFM will turn that reference into a link so you can navigate between them easily. - %h4 Multiple underscores in words + %p GFM will recognize the following references: + %ul + %li + %code @foo + for team members + %li + %code #123 + for issues + %li + %code !123 + for merge request + %li + %code $123 + for snippets + %li + %code 1234567 + for commits - %p - It is not reasonable to italicize just part of a word, especially when you're dealing with code and names often appear with multiple underscores. - Therefore, GFM ignores multiple underscores in words. - - %pre= "perform_complicated_task\ndo_this_and_do_that_and_another_thing" - %p becomes - = markdown "perform_complicated_task\ndo_this_and_do_that_and_another_thing" - - %h4 URL autolinking - - %p - GFM will autolink standard URLs you copy and paste into your text. - So if you want to link to a URL (instead of a textual link), you can simply put the URL in verbatim and it will be turned into a link to that URL. - - %h4 Fenced code blocks - - %p - Markdown converts text with four spaces at the front of each line to code blocks. - GFM supports that, but we also support fenced blocks. - Just wrap your code blocks in ``` and you won't need to indent manually to trigger a code block. - - %pre= %Q{```ruby\nrequire 'redcarpet'\nmarkdown = Redcarpet.new("Hello World!")\nputs markdown.to_html\n```} - %p becomes - = markdown %Q{```ruby\nrequire 'redcarpet'\nmarkdown = Redcarpet.new("Hello World!")\nputs markdown.to_html\n```} - - %h4 Emoji - - .row - .span8 - :ruby - puts markdown %Q{Sometimes you want to be :cool: and add some :sparkles: to your :speech_balloon:. Well we have a :gift: for you: - - :exclamation: You can use emoji anywhere GFM is supported. :sunglasses: - - You can use it to point out a :bug: or warn about :monkey:patches. And if someone improves your really :snail: code, send them a :bouquet: or some :candy:. People will :heart: you for that. - - If you are :new: to this, don't be :fearful:. You can easily join the emoji :circus_tent:. All you need to do is to :book: up on the supported codes. - } - - .span4 - .alert.alert-info - %p - Consult the - %strong= link_to "Emoji Cheat Sheet", "http://www.emoji-cheat-sheet.com/" - for a list of all supported emoji codes. - - .row - .span8 - %h4 Special GitLab references - - %p - GFM recognizes special references. - You can easily reference e.g. a team member, an issue or a commit within a project. - GFM will turn that reference into a link so you can navigate between them easily. - - %p GFM will recognize the following references: - %ul - %li - %code @foo - for team members - %li - %code #123 - for issues - %li - %code !123 - for merge request - %li - %code $123 - for snippets - %li - %code 1234567 - for commits - - -# this example will only be shown if the user has a project with at least one issue - - if @project = current_user.authorized_projects.first - - if issue = @project.issues.first - %p For example in your #{link_to @project.name, project_path(@project)} project, writing: - %pre= "This is related to ##{issue.id}. @#{current_user.username} is working on solving it." - %p becomes: - = markdown "This is related to ##{issue.id}. @#{current_user.username} is working on solving it." - - @project = nil # Prevent this from bubbling up to page title + -# this example will only be shown if the user has a project with at least one issue + - if @project = current_user.projects.first + - if issue = @project.issues.first + %p For example in your #{link_to @project.name, project_path(@project)} project, writing: + %pre= "This is related to ##{issue.id}. @#{current_user.name} is working on solving it." + %p becomes: + = markdown "This is related to ##{issue.id}. @#{current_user.name} is working on solving it." + - @project = nil # Prevent this from bubbling up to page title diff --git a/app/views/help/permissions.html.haml b/app/views/help/permissions.html.haml index 2075753e..f9287fa0 100644 --- a/app/views/help/permissions.html.haml +++ b/app/views/help/permissions.html.haml @@ -1,28 +1,32 @@ -= render layout: 'help/layout' do - %h3.page_title Permissions - %br +%h3.page_title Permissions +.back_link + = link_to help_path do + ← to index +%hr - %fieldset - %legend Guest - %ul +.row + .ui-box.span2 + %h5 Guest + %ul.unstyled %li Create new issue %li Leave comments %li Write on project wall - %fieldset - %legend Reporter - %ul + .ui-box.span3 + %h5 Reporter + %ul.unstyled %li Create new issue %li Leave comments %li Write on project wall %li Pull project code %li Download project + %li Create new merge request %li Create a code snippets - %fieldset - %legend Developer - %ul + .ui-box.span3 + %h5 Developer + %ul.unstyled %li Create new issue %li Leave comments %li Write on project wall @@ -36,9 +40,9 @@ %li Add tags %li Write a wiki - %fieldset - %legend Master - %ul + .ui-box.span3 + %h5 Master + %ul.unstyled %li Create new issue %li Leave comments %li Write on project wall @@ -58,9 +62,3 @@ %li Edit project %li Add Deploy Keys to project %li Configure Project Hooks - - %fieldset - %legend Owner - %ul - %li Transfer project to another namespace - %li Remove project diff --git a/app/views/help/public_access.html.haml b/app/views/help/public_access.html.haml deleted file mode 100644 index 66de17a3..00000000 --- a/app/views/help/public_access.html.haml +++ /dev/null @@ -1,16 +0,0 @@ -= render layout: 'help/layout' do - %h3.page_title Public Access - %br - - %p - GitLab allows you to open selected projects to be accessed publicly. - These projects will be clonable - %em without any - authentication. - Also they will be listed on the #{link_to "public access directory", public_root_path}. - - %ol - %li Go to your project dashboard - %li Click on the "Edit" tab - %li Select "Public clone access" - diff --git a/app/views/help/raketasks.html.haml b/app/views/help/raketasks.html.haml deleted file mode 100644 index bcc874fc..00000000 --- a/app/views/help/raketasks.html.haml +++ /dev/null @@ -1,64 +0,0 @@ -= render layout: 'help/layout' do - %h3.page_title GitLab Rake Tasks - %br - - %p.slead - GitLab provides some specific rake tasks to enable special features or perform maintenance tasks. - - %ul.nav.nav-tabs.log-tabs - %li.active - = link_to "Features", "#features", 'data-toggle' => 'tab' - %li - = link_to "Maintenance", "#maintenance", 'data-toggle' => 'tab' - %li - = link_to "User Management", "#user_management", 'data-toggle' => 'tab' - %li - = link_to "Backup & Restore", "#backup_restore", 'data-toggle' => 'tab' - %li - = link_to "Cleanup", "#cleanup", 'data-toggle' => 'tab' - - .tab-content - .tab-pane.active#features - .file_holder - .file_title - %i.icon-file - Features - .file_content.wiki - = preserve do - = markdown File.read(Rails.root.join("doc", "raketasks", "features.md")) - - .tab-pane#maintenance - .file_holder - .file_title - %i.icon-file - Maintenance - .file_content.wiki - = preserve do - = markdown File.read(Rails.root.join("doc", "raketasks", "maintenance.md")) - - .tab-pane#user_management - .file_holder - .file_title - %i.icon-file - User Management - .file_content.wiki - = preserve do - = markdown File.read(Rails.root.join("doc", "raketasks", "user_management.md")) - - .tab-pane#cleanup - .file_holder - .file_title - %i.icon-file - Cleanup - .file_content.wiki - = preserve do - = markdown File.read(Rails.root.join("doc", "raketasks", "cleanup.md")) - - .tab-pane#backup_restore - .file_holder - .file_title - %i.icon-file - Backup & Restore - .file_content.wiki - = preserve do - = markdown File.read(Rails.root.join("doc", "raketasks", "backup_restore.md")) diff --git a/app/views/help/ssh.html.haml b/app/views/help/ssh.html.haml index 11441597..3f082333 100644 --- a/app/views/help/ssh.html.haml +++ b/app/views/help/ssh.html.haml @@ -1,23 +1,25 @@ -= render layout: 'help/layout' do - %h3.page_title SSH Keys - %br +%h3.page_title SSH Keys +.back_link + = link_to help_path do + ← to index +%hr - %p.slead - SSH key allows you to establish a secure connection between your computer and GitLab +%p.slead + SSH key allows you to establish a secure connection between your computer and GitLab - %p.slead - To generate a new SSH key just open your terminal and use code below. +%p.slead + To generate a new SSH key just open your terminal and use code below. - %pre.dark - ssh-keygen -t rsa -C "#{current_user.email}" +%pre.dark + ssh-keygen -t rsa -C "#{current_user.email}" - \# Creates a new ssh key using the provided email - \# Generating public/private rsa key pair... + \# Creates a new ssh key using the provided email + \# Generating public/private rsa key pair... - %p.slead - Next just use code below to dump your public key and add to GitLab SSH Keys +%p.slead + Next just use code below to dump your public key and add to GitLab SSH Keys - %pre.dark - cat ~/.ssh/id_rsa.pub +%pre.dark + cat ~/.ssh/id_rsa.pub - \# ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC6eNtGpNGwstc.... + \# ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC6eNtGpNGwstc.... diff --git a/app/views/help/system_hooks.html.haml b/app/views/help/system_hooks.html.haml index c49011a2..c25b60de 100644 --- a/app/views/help/system_hooks.html.haml +++ b/app/views/help/system_hooks.html.haml @@ -1,12 +1,14 @@ -= render layout: 'help/layout' do - %h3.page_title System hooks - %br +%h3 System hooks +.back_link + = link_to :back do + ← back +%hr - %p.slead - Your GitLab instance can perform HTTP POST requests on the following events: create_project, delete_project, create_user, delete_user, change_team_member. - %br - %br - System Hooks can be used, e.g. for logging or changing information in a LDAP server. - %br - %h5 Hooks request example: - = render "admin/hooks/data_ex" +%p.slead + Your GitLab instance can perform HTTP POST requests on the following events: create_project, delete_project, create_user, delete_user, change_team_member. + %br + %br + System Hooks can be used, e.g. for logging or changing information in a LDAP server. + %br +%h5 Hooks request example: += render "admin/hooks/data_ex" diff --git a/app/views/help/web_hooks.html.haml b/app/views/help/web_hooks.html.haml index 09745f73..65036613 100644 --- a/app/views/help/web_hooks.html.haml +++ b/app/views/help/web_hooks.html.haml @@ -1,13 +1,15 @@ -= render layout: 'help/layout' do - %h3.page_title Web hooks +%h3.page_title Web hooks +.back_link + = link_to help_path do + ← to index +%hr + +%p.slead + Every GitLab project can trigger a web server whenever the repo is pushed to. %br - - %p.slead - Every GitLab project can trigger a web server whenever the repo is pushed to. - %br - Web Hooks can be used to update an external issue tracker, trigger CI builds, update a backup mirror, or even deploy to your production server. - %br - GitLab will send POST request with commits information on every push. - %h5 Hooks request example: - = render "hooks/data_ex" + Web Hooks can be used to update an external issue tracker, trigger CI builds, update a backup mirror, or even deploy to your production server. + %br + GitLab will send POST request with commits information on every push. +%h5 Hooks request example: += render "hooks/data_ex" diff --git a/app/views/help/workflow.html.haml b/app/views/help/workflow.html.haml index 495b7c6e..6062ca09 100644 --- a/app/views/help/workflow.html.haml +++ b/app/views/help/workflow.html.haml @@ -1,38 +1,40 @@ -= render layout: 'help/layout' do - %h3.page_title Workflow - %br +%h3.page_title Workflow +.back_link + = link_to help_path do + ← to index +%hr - %ol.help - %li - %p Clone project - .bash - %pre.dark - git clone git@example.com:project-name.git +%ol.help + %li + %p Clone project + .bash + %pre.dark + git clone git@example.com:project-name.git - %li - %p Create branch with your feature - .bash - %pre.dark - git checkout -b $feature_name + %li + %p Create branch with your feature + .bash + %pre.dark + git checkout -b $feature_name - %li - %p Write code. Commit changes - .bash - %pre.dark - git commit -am "My feature is ready" + %li + %p Write code. Commit changes + .bash + %pre.dark + git commit -am "My feature is ready" - %li - %p Push your branch to GitLab - .bash - %pre.dark - git push origin $feature_name + %li + %p Push your branch to GitLab + .bash + %pre.dark + git push origin $feature_name - %li - %p Review your code on Commits page + %li + %p Review your code on Commits page - %li - %p Create a merge request + %li + %p Create a merge request - %li - %p Your team lead will review code & merge it to main branch + %li + %p Your team lead will review code & merge it to main branch diff --git a/app/views/hooks/_data_ex.html.erb b/app/views/hooks/_data_ex.html.erb index b4281fa1..7dd6b9e0 100644 --- a/app/views/hooks/_data_ex.html.erb +++ b/app/views/hooks/_data_ex.html.erb @@ -1,43 +1,45 @@ <% data_ex_str = < "95790bf891e76fee5e1747ab589903a6a1f80f22", + :after => "da1560886d4f094c3e6c9ef40349f7d38b5d27d7", + :ref => "refs/heads/master", + :user_id => 4, + :user_name => "John Smith", + :repository => { + :name => "Diaspora", + :url => "localhost/diaspora", + :description => "", + :homepage => "localhost/diaspora", + :private => true }, - // ... - { - "id": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7", - "message": "fixed readme", - "timestamp": "2012-01-03T23:36:29+02:00", - "url": "http://localhost/diaspora/commits/da1560886d4f094c3e6c9ef40349f7d38b5d27d7", - "author": { - "name": "GitLab dev user", - "email": "gitlabdev@dv6700.(none)", - }, - }, - ], - "total_commits_count": 4, -}; + :commits => [ + [0] { + :id => "450d0de7532f8b663b9c5cce183b...", + :message => "Update Catalan translation to e38cb41.", + :timestamp => "2011-12-12T14:27:31+02:00", + :url => "http://localhost/diaspora/commits/450d0de7532f...", + :author => { + :name => "Jordi Mallach", + :email => "jordi@softcatala.org" + } + }, + + .... + + [3] { + :id => "da1560886d4f094c3e6c9ef40349...", + :message => "fixed readme", + :timestamp => "2012-01-03T23:36:29+02:00", + :url => "http://localhost/diaspora/commits/da1560886d...", + :author => { + :name => "GitLab dev user", + :email => "gitlabdev@dv6700.(none)" + } + } + ], + total_commits_count => 4 +} eos %> -
    - <%= raw Pygments::Lexer[:js].highlight(data_ex_str) %> -
    +<% js_lexer = Pygments::Lexer[:js] %> +<%= raw js_lexer.highlight(data_ex_str) %> diff --git a/app/views/hooks/index.html.haml b/app/views/hooks/index.html.haml index 3155dd32..1b59c8e8 100644 --- a/app/views/hooks/index.html.haml +++ b/app/views/hooks/index.html.haml @@ -1,4 +1,4 @@ -= render "projects/settings_nav" += render "projects/project_head" - if can? current_user, :admin_project, @project .alert.alert-info @@ -10,7 +10,7 @@ = form_for [@project, @hook], as: :hook, url: project_hooks_path(@project), html: { class: 'form-inline' } do |f| -if @hook.errors.any? - .alert.alert-error + .alert-message.block-message.error - @hook.errors.full_messages.each do |msg| %p= msg .clearfix @@ -18,25 +18,26 @@ .input = f.text_field :url, class: "text_field xxlarge"   - = f.submit "Add Web Hook", class: "btn btn-create" + = f.submit "Add Web Hook", class: "btn primary" %hr -if @hooks.any? - %h3.page_title - Hooks (#{@hooks.count}) + %h3 + Hooks + %small (#{@hooks.count}) %br %table %thead %tr %th URL + %th Method %th - @hooks.each do |hook| %tr %td - %span.badge.badge-info POST - → - %span.monospace= hook.url + = link_to project_hook_path(@project, hook) do + %strong= hook.url + = link_to 'Test Hook', test_project_hook_path(@project, hook), class: "btn small right" + %td POST %td - .pull-right - = link_to 'Test Hook', test_project_hook_path(@project, hook), class: "btn btn-small grouped" - = link_to 'Remove', project_hook_path(@project, hook), confirm: 'Are you sure?', method: :delete, class: "btn btn-remove btn-small grouped" + = link_to 'Remove', project_hook_path(@project, hook), confirm: 'Are you sure?', method: :delete, class: "danger btn small right" diff --git a/app/views/issues/_filter.html.haml b/app/views/issues/_filter.html.haml deleted file mode 100644 index b621f11b..00000000 --- a/app/views/issues/_filter.html.haml +++ /dev/null @@ -1,23 +0,0 @@ -= form_tag project_issues_path(@project), method: 'get' do - %fieldset - %ul.nav.nav-pills.nav-stacked - %li{class: ("active" if !params[:status])} - = link_to project_issues_path(@project, status: nil) do - Open - %li{class: ("active" if params[:status] == 'assigned-to-me')} - = link_to project_issues_path(@project, status: 'assigned-to-me') do - 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 - %li{class: ("active" if params[:status] == 'all')} - = link_to project_issues_path(@project, status: 'all') do - All - - %fieldset - %hr - = link_to "Reset", project_issues_path(@project), class: 'btn pull-right' - diff --git a/app/views/issues/_form.html.haml b/app/views/issues/_form.html.haml index 6d7613a7..670b4e05 100644 --- a/app/views/issues/_form.html.haml +++ b/app/views/issues/_form.html.haml @@ -1,32 +1,31 @@ %div.issue-form-holder %h3.page_title= @issue.new_record? ? "New Issue" : "Edit Issue ##{@issue.id}" - = form_for [@project, @issue] do |f| + = form_for [@project, @issue], remote: request.xhr? do |f| -if @issue.errors.any? - .alert.alert-error - - @issue.errors.full_messages.each do |msg| - %span= msg - %br - .ui-box.ui-box-show - .ui-box-head + .alert-message.block-message.error + %ul + - @issue.errors.full_messages.each do |msg| + %li= msg + .issue_form_box + .issue_title .clearfix = f.label :title do %strong= "Subject *" .input - = f.text_field :title, maxlength: 255, class: "xxlarge js-gfm-input", autofocus: true, required: true - .ui-box-body - .clearfix - .issue_assignee.pull-left - = f.label :assignee_id do - %i.icon-user - Assign to - .input= f.select(:assignee_id, @project.users.all.collect {|p| [ p.name, p.id ] }, { include_blank: "Select a user" }, {class: 'chosen'}) - .issue_milestone.pull-left - = f.label :milestone_id do - %i.icon-time - Milestone - .input= f.select(:milestone_id, @project.milestones.active.all.collect {|p| [ p.title, p.id ] }, { include_blank: "Select milestone" }, {class: 'chosen'}) + = f.text_field :title, maxlength: 255, class: "xxlarge js-gfm-input", autofocus: true + .issue_middle_block + .issue_assignee + = f.label :assignee_id do + %i.icon-user + Assign to + .input= f.select(:assignee_id, @project.users.all.collect {|p| [ p.name, p.id ] }, { include_blank: "Select a user" }, {class: 'chosen'}) + .issue_milestone + = f.label :milestone_id do + %i.icon-time + Milestone + .input= f.select(:milestone_id, @project.milestones.active.all.collect {|p| [ p.title, p.id ] }, { include_blank: "Select milestone" }, {class: 'chosen'}) - .ui-box-bottom + .issue_description .clearfix = f.label :label_list do %i.icon-tag @@ -44,42 +43,15 @@ .actions - if @issue.new_record? - = f.submit 'Submit new issue', class: "btn btn-create" + = f.submit 'Submit new issue', class: "btn save-btn" -else - = f.submit 'Save changes', class: "btn-save btn" - - - cancel_path = @issue.new_record? ? project_issues_path(@project) : project_issue_path(@project, @issue) - = link_to "Cancel", cancel_path, class: 'btn btn-cancel' - - - - -:javascript - $(function(){ - $("#issue_label_list") - .bind( "keydown", function( event ) { - if ( event.keyCode === $.ui.keyCode.TAB && - $( this ).data( "autocomplete" ).menu.active ) { - event.preventDefault(); - } - }) - .autocomplete({ - minLength: 0, - source: function( request, response ) { - response( $.ui.autocomplete.filter( - #{raw labels_autocomplete_source}, extractLast( request.term ) ) ); - }, - focus: function() { - return false; - }, - select: function(event, ui) { - var terms = split( this.value ); - terms.pop(); - terms.push( ui.item.value ); - terms.push( "" ); - this.value = terms.join( ", " ); - return false; - } - }); - }); + = f.submit 'Save changes', class: "save-btn btn" + - cancel_class = 'btn cancel-btn' + - if request.xhr? + = link_to "Cancel", "#back", onclick: "backToIssues();", class: cancel_class + - else + - if @issue.new_record? + = link_to "Cancel", project_issues_path(@project), class: cancel_class + - else + = link_to "Cancel", project_issue_path(@project, @issue), class: cancel_class diff --git a/app/views/issues/_head.html.haml b/app/views/issues/_head.html.haml index 44d14d5c..4294503c 100644 --- a/app/views/issues/_head.html.haml +++ b/app/views/issues/_head.html.haml @@ -5,6 +5,7 @@ = link_to 'Milestones', project_milestones_path(@project), class: "tab" = nav_link(controller: :labels) do = link_to 'Labels', project_labels_path(@project), class: "tab" - %li.pull-right - = link_to project_issues_path(@project, :atom, { private_token: current_user.private_token }) do - %i.icon-rss + %li.right + %span.rss-icon + = link_to project_issues_path(@project, :atom, { private_token: current_user.private_token }) do + = image_tag "rss_ui.png", title: "feed" diff --git a/app/views/issues/_issues.html.haml b/app/views/issues/_issues.html.haml index dc7db906..f82ae8bd 100644 --- a/app/views/issues/_issues.html.haml +++ b/app/views/issues/_issues.html.haml @@ -1,11 +1,14 @@ -= render @issues +- @issues.each do |issue| + = render(partial: 'issues/show', locals: {issue: issue}) - if @issues.present? %li.bottom - .left= paginate @issues, remote: true, theme: "gitlab" - .pull-right - %span.issue_counter #{@issues.total_count} - issues for this filter + .row + .span7= paginate @issues, remote: true, theme: "gitlab" + .span3.right + %span.cgray.right + %span.issue_counter #{@issues.total_count} + issues for this filter - else %li %h4.nothing_here_message Nothing to show here diff --git a/app/views/issues/_issue.html.haml b/app/views/issues/_show.html.haml similarity index 62% rename from app/views/issues/_issue.html.haml rename to app/views/issues/_show.html.haml index 3d1ecd43..8aa92ebf 100644 --- a/app/views/issues/_issue.html.haml +++ b/app/views/issues/_show.html.haml @@ -1,18 +1,22 @@ -%li{ id: dom_id(issue), class: issue_css_classes(issue), url: project_issue_path(issue.project, issue) } +%li.wll{ id: dom_id(issue), class: issue_css_classes(issue), url: project_issue_path(issue.project, issue) } - if controller.controller_name == 'issues' .issue_check = check_box_tag dom_id(issue,"selected"), nil, false, 'data-id' => issue.id, class: "selected_issue", disabled: !can?(current_user, :modify_issue, issue) - .pull-right + .right + - issue.labels.each do |label| + %span.label.label-tag.grouped + %i.icon-tag + = label.name - if issue.notes.any? - %span.btn.btn-small.disabled.grouped + %span.btn.small.disabled.grouped %i.icon-comment = issue.notes.count - if can? current_user, :modify_issue, issue - - if issue.closed? - = link_to 'Reopen', project_issue_path(issue.project, issue, issue: {state_event: :reopen }, status_only: true), method: :put, class: "btn btn-small grouped reopen_issue", remote: true + - if issue.closed + = link_to 'Reopen', project_issue_path(issue.project, issue, issue: {closed: false }, status_only: true), method: :put, class: "btn small grouped reopen_issue", remote: true - else - = link_to 'Close', project_issue_path(issue.project, issue, issue: {state_event: :close }, status_only: true), method: :put, class: "btn btn-small grouped close_issue", remote: true - = link_to edit_project_issue_path(issue.project, issue), class: "btn btn-small edit-issue-link grouped" do + = link_to 'Close', project_issue_path(issue.project, issue, issue: {closed: true }, status_only: true), method: :put, class: "btn small grouped close_issue", remote: true + = link_to edit_project_issue_path(issue.project, issue), class: "btn small edit-issue-link grouped", remote: true do %i.icon-edit Edit @@ -24,7 +28,7 @@ %p= link_to_gfm truncate(issue.title, length: 100), project_issue_path(issue.project, issue), class: "row_title" %span.update-author - %span.cdark= "##{issue.id}" + %small.cdark= "##{issue.id}" - if issue.assignee assigned to #{issue.assignee_name} - else @@ -32,8 +36,3 @@ - if issue.votes_count > 0 = render 'votes/votes_inline', votable: issue - %span - - issue.labels.each do |label| - %span.label - %i.icon-tag - = label.name diff --git a/app/views/issues/create.js.haml b/app/views/issues/create.js.haml new file mode 100644 index 00000000..d90cbf0d --- /dev/null +++ b/app/views/issues/create.js.haml @@ -0,0 +1,10 @@ +- if @issue.valid? + :plain + switchFromNewIssue(); + $("#issues-table").prepend("#{escape_javascript(render(partial: 'show', locals: {issue: @issue}))}"); + $.ajax({type: "GET", url: location.href, dataType: "script"}); +- else + :plain + $("#new_issue_dialog").empty(); + $("#new_issue_dialog").append("#{escape_javascript(render('form'))}"); + $('select#issue_assignee_id').chosen(); diff --git a/app/views/issues/edit.js.haml b/app/views/issues/edit.js.haml new file mode 100644 index 00000000..a994572f --- /dev/null +++ b/app/views/issues/edit.js.haml @@ -0,0 +1,4 @@ +:plain + $("#edit_issue_dialog").html("#{escape_javascript(render('form'))}"); + switchToEditIssue(); + diff --git a/app/views/issues/index.html.haml b/app/views/issues/index.html.haml index 875f29e2..d89b183d 100644 --- a/app/views/issues/index.html.haml +++ b/app/views/issues/index.html.haml @@ -2,47 +2,64 @@ .issues_content %h3.page_title Issues - %span (#{@issues.total_count}) - .pull-right + %small (#{@issues.total_count}) + .right .span5 - if can? current_user, :write_issue, @project - = link_to new_project_issue_path(@project, issue: { assignee_id: params[:assignee_id], milestone_id: params[:milestone_id]}), class: "btn btn-primary pull-right", title: "New Issue", id: "new_issue_link" do + = link_to new_project_issue_path(@project), class: "right btn", title: "New Issue", remote: true, id: "new_issue_link" do %i.icon-plus New Issue - = form_tag search_project_issues_path(@project), method: :get, remote: true, id: "issue_search_form", class: 'pull-right' do + = form_tag search_project_issues_path(@project), method: :get, remote: true, id: "issue_search_form", class: :right do = hidden_field_tag :project_id, @project.id, { id: 'project_id' } - = hidden_field_tag :status, params[:status] - = search_field_tag :issue_search, nil, { placeholder: 'Search', class: 'issue_search span3 pull-right neib search-text-input' } + = hidden_field_tag :status, params[:f] + = search_field_tag :issue_search, nil, { placeholder: 'Search', class: 'issue_search span3 right neib search-text-input' } .clearfix -.row - .span3 - = render 'filter', entity: 'issue' - .span9 - %div#issues-table-holder.ui-box - .title - = check_box_tag "check_all_issues", nil, false, class: "check_all_issues left" - .clearfix - .issues_bulk_update.hide - = form_tag bulk_update_project_issues_path(@project), method: :post do - %span.update_issues_text Update selected issues with   - .left - = select_tag('update[status]', options_for_select(['open', 'closed']), prompt: "Status") - = select_tag('update[assignee_id]', options_from_collection_for_select(@project.users.all, "id", "name", params[:assignee_id]), prompt: "Assignee") - = select_tag('update[milestone_id]', options_from_collection_for_select(issues_active_milestones, "id", "title", params[:milestone_id]), prompt: "Milestone") - = hidden_field_tag 'update[issues_ids]', [] - = hidden_field_tag :status, params[:status] - = button_tag "Save", class: "btn update_selected_issues btn-small btn-save" - .issues_filters - = form_tag project_issues_path(@project), method: :get do - = select_tag(:label_name, options_for_select(issue_tags, params[:label_name]), prompt: "Labels") - = select_tag(:assignee_id, options_from_collection_for_select([unassigned_filter] + @project.users.all, "id", "name", params[:assignee_id]), prompt: "Assignee") - = select_tag(:milestone_id, options_from_collection_for_select([unassigned_filter] + issues_active_milestones, "id", "title", params[:milestone_id]), prompt: "Milestone") - = hidden_field_tag :status, params[:status] + %div#issues-table-holder.ui-box + .title + = check_box_tag "check_all_issues", nil, false, class: "check_all_issues left" - %ul#issues-table.well-list.issues_table - = render "issues" + + .issues_bulk_update.hide + = form_tag bulk_update_project_issues_path(@project), method: :post do + %span.update_issues_text Update selected issues with   + .left + = select_tag('update[status]', options_for_select(['open', 'closed']), prompt: "Status") + = select_tag('update[assignee_id]', options_from_collection_for_select(@project.users.all, "id", "name", params[:assignee_id]), prompt: "Assignee") + = select_tag('update[milestone_id]', options_from_collection_for_select(@project.milestones.order("id desc").all, "id", "title", params[:milestone_id]), prompt: "Milestone") + = hidden_field_tag 'update[issues_ids]', [] + = hidden_field_tag :f, params[:f] + = button_tag "Save", class: "btn update_selected_issues" + .issues_filters + .left + %ul.nav.nav-pills.left + %li{class: ("active" if (params[:f] == issues_filter[:open] || !params[:f]))} + = link_to project_issues_path(@project, f: issues_filter[:open], milestone_id: params[:milestone_id]) do + Open + %li{class: ("active" if params[:f] == issues_filter[:closed])} + = link_to project_issues_path(@project, f: issues_filter[:closed], milestone_id: params[:milestone_id]) do + Closed + %li{class: ("active" if params[:f] == issues_filter[:to_me])} + = link_to project_issues_path(@project, f: issues_filter[:to_me], milestone_id: params[:milestone_id]) do + To Me + %li{class: ("active" if params[:f] == issues_filter[:all])} + = link_to project_issues_path(@project, f: issues_filter[:all], milestone_id: params[:milestone_id]) do + All + + .right + = form_tag project_issues_path(@project), method: :get, class: :right do + = select_tag(:label_name, options_for_select(issue_tags, params[:label_name]), prompt: "Labels") + = select_tag(:assignee_id, options_from_collection_for_select([unassigned_filter] + @project.users.all, "id", "name", params[:assignee_id]), prompt: "Assignee") + = select_tag(:milestone_id, options_from_collection_for_select([unassigned_filter] + @project.milestones.order("id desc").all, "id", "title", params[:milestone_id]), prompt: "Milestone") + = hidden_field_tag :f, params[:f] + .clearfix + + %ul#issues-table.unstyled.issues_table + = render "issues" + +#new_issue_dialog +#edit_issue_dialog :javascript $(function(){ diff --git a/app/views/issues/new.js.haml b/app/views/issues/new.js.haml new file mode 100644 index 00000000..4cbcc563 --- /dev/null +++ b/app/views/issues/new.js.haml @@ -0,0 +1,3 @@ +:plain + $("#new_issue_dialog").html("#{escape_javascript(render('form'))}"); + switchToNewIssue(); diff --git a/app/views/issues/show.html.haml b/app/views/issues/show.html.haml index 70f94e52..9114febd 100644 --- a/app/views/issues/show.html.haml +++ b/app/views/issues/show.html.haml @@ -5,18 +5,18 @@ created at = @issue.created_at.stamp("Aug 21, 2011") - %span.pull-right - - if can?(current_user, :modify_issue, @issue) - - if @issue.closed? - = link_to 'Reopen', project_issue_path(@project, @issue, issue: {state_event: :reopen }, status_only: true), method: :put, class: "btn grouped reopen_issue" + %span.right + - if can?(current_user, :admin_project, @project) || @issue.author == current_user + - if @issue.closed + = link_to 'Reopen', project_issue_path(@project, @issue, issue: {closed: false }, status_only: true), method: :put, class: "btn grouped reopen_issue" - else - = link_to 'Close', project_issue_path(@project, @issue, issue: {state_event: :close }, status_only: true), method: :put, class: "btn grouped close_issue", title: "Close Issue" - + = link_to 'Close', project_issue_path(@project, @issue, issue: {closed: true }, status_only: true), method: :put, class: "btn grouped close_issue", title: "Close Issue" + - if can?(current_user, :admin_project, @project) || @issue.author == current_user = link_to edit_project_issue_path(@project, @issue), class: "btn grouped" do %i.icon-edit Edit -.pull-right +.right .span3#votes= render 'votes/votes_block', votable: @issue .back_link @@ -24,42 +24,41 @@ ← To issues list -.ui-box.ui-box-show - .ui-box-head - %h4.box-title - - if @issue.closed? - .error.status_info Closed +.main_box + .top_box_content + %h4 + - if @issue.closed + .alert-message.error.status_info Closed + - else + .alert-message.success.status_info Open = gfm escape_once(@issue.title) - .ui-box-body - %cite.cgray - Created by #{link_to_member(@project, @issue.author)} - - if @issue.assignee - \ and currently assigned to #{link_to_member(@project, @issue.assignee)} + .middle_box_content + %cite.cgray Created by + = image_tag gravatar_icon(@issue.author_email), width: 16, class: "lil_av" + %strong.author= link_to_issue_author(@issue) + + - if @issue.assignee + %cite.cgray and currently assigned to + = image_tag gravatar_icon(@issue.assignee_email), width: 16, class: "lil_av" + %strong.author= link_to_issue_assignee(@issue) - if @issue.milestone - milestone = @issue.milestone %cite.cgray and attached to milestone %strong= link_to_gfm truncate(milestone.title, length: 20), project_milestone_path(milestone.project, milestone) - .pull-right + .right - @issue.labels.each do |label| - %span.label + %span.label.label-issue %i.icon-tag = label.name   - if @issue.description.present? - .ui-box-bottom - .wiki - = preserve do - = markdown @issue.description + .bottom_box_content + = preserve do + = markdown @issue.description -- content_for :note_actions do - - if can?(current_user, :modify_issue, @issue) - - if @issue.closed? - = link_to 'Reopen Issue', project_issue_path(@project, @issue, issue: {state_event: :reopen }, status_only: true), method: :put, class: "btn grouped reopen_issue" - - else - = link_to 'Close Issue', project_issue_path(@project, @issue, issue: {state_event: :close }, status_only: true), method: :put, class: "btn grouped close_issue", title: "Close Issue" -.voting_notes#notes= render "notes/notes_with_form" +.issue_notes.voting_notes#notes= render "notes/notes_with_form", tid: @issue.id, tt: "issue" diff --git a/app/views/issues/update.js.haml b/app/views/issues/update.js.haml index 7f66022a..44722895 100644 --- a/app/views/issues/update.js.haml +++ b/app/views/issues/update.js.haml @@ -2,3 +2,13 @@ - if @issue.valid? :plain $("##{dom_id(@issue)}").fadeOut(); +- else + - if @issue.valid? + :plain + updatePage(); + switchFromEditIssue(); + - else + :plain + $("#edit_issue_dialog").empty(); + $("#edit_issue_dialog").append("#{escape_javascript(render('form'))}"); + $('select#issue_assignee_id').chosen(); diff --git a/app/views/kaminari/admin/_gap.html.haml b/app/views/kaminari/admin/_gap.html.haml index 3ffd12f8..f82f185a 100644 --- a/app/views/kaminari/admin/_gap.html.haml +++ b/app/views/kaminari/admin/_gap.html.haml @@ -4,6 +4,5 @@ -# num_pages: total number of pages -# per_page: number of items to fetch per page -# remote: data-remote -%li{class: "page"} - %span.page.gap - = raw(t 'views.pagination.truncate') +%span.page.gap + = raw(t 'views.pagination.truncate') diff --git a/app/views/keys/_form.html.haml b/app/views/keys/_form.html.haml index fe26216b..26700803 100644 --- a/app/views/keys/_form.html.haml +++ b/app/views/keys/_form.html.haml @@ -1,7 +1,7 @@ %div = form_for @key do |f| -if @key.errors.any? - .alert.alert-error + .alert-message.block-message.error %ul - @key.errors.full_messages.each do |msg| %li= msg @@ -19,6 +19,6 @@ .actions - = f.submit 'Save', class: "btn btn-save" - = link_to "Cancel", keys_path, class: "btn btn-cancel" + = f.submit 'Save', class: "btn save-btn" + = link_to "Cancel", keys_path, class: "btn cancel-btn" diff --git a/app/views/keys/_show.html.haml b/app/views/keys/_show.html.haml index 52bbea6f..9d4485cf 100644 --- a/app/views/keys/_show.html.haml +++ b/app/views/keys/_show.html.haml @@ -8,5 +8,5 @@ = time_ago_in_words(key.created_at) ago %td - = link_to 'Remove', key, confirm: 'Are you sure?', method: :delete, class: "btn btn-small btn-remove delete-key pull-right" + = link_to 'Remove', key, confirm: 'Are you sure?', method: :delete, class: "btn small danger delete-key right" diff --git a/app/views/keys/create.js.haml b/app/views/keys/create.js.haml new file mode 100644 index 00000000..1dccf6fd --- /dev/null +++ b/app/views/keys/create.js.haml @@ -0,0 +1,9 @@ +- if @key.valid? + :plain + $("#new_key_dialog").dialog("close"); + $("#keys-table .data").append("#{escape_javascript(render(partial: 'show', locals: {key: @key}))}"); + $("#no_ssh_key_defined").hide(); +- else + :plain + $("#new_key_dialog").empty(); + $("#new_key_dialog").append("#{escape_javascript(render('form'))}"); diff --git a/app/views/keys/index.html.haml b/app/views/keys/index.html.haml index 7730b344..f5a8283a 100644 --- a/app/views/keys/index.html.haml +++ b/app/views/keys/index.html.haml @@ -1,6 +1,6 @@ %h3.page_title SSH Keys - = link_to "Add new", new_key_path, class: "btn pull-right" + = link_to "Add new", new_key_path, class: "btn right" %hr %p.slead diff --git a/app/views/keys/new.js.haml b/app/views/keys/new.js.haml new file mode 100644 index 00000000..86e9db03 --- /dev/null +++ b/app/views/keys/new.js.haml @@ -0,0 +1,11 @@ +:plain + var new_key_dialog = $("
    "); + new_key_dialog.html("#{escape_javascript(render('form'))}"); + $(new_key_dialog).dialog({ + width: 350, + resizable: false, + draggable: false, + title: "Add new public key", + close: function(event, ui) { $("#new_key_dialog").remove();}, + modal: true + }); diff --git a/app/views/keys/show.html.haml b/app/views/keys/show.html.haml index 059fe5e5..a8cba6c8 100644 --- a/app/views/keys/show.html.haml +++ b/app/views/keys/show.html.haml @@ -10,5 +10,5 @@ %hr %pre= @key.key -.pull-right - = link_to 'Remove', @key, confirm: 'Are you sure?', method: :delete, class: "btn btn-remove delete-key" +.right + = link_to 'Remove', @key, confirm: 'Are you sure?', method: :delete, class: "btn danger delete-key" diff --git a/app/views/labels/_label.html.haml b/app/views/labels/_label.html.haml index 027b041d..8a465a9e 100644 --- a/app/views/labels/_label.html.haml +++ b/app/views/labels/_label.html.haml @@ -1,8 +1,8 @@ -%li +%li.wll %strong %i.icon-tag = label.name - .pull-right + .right = link_to project_issues_path(label_name: label.name) do %strong = pluralize(label.count, 'issue') diff --git a/app/views/labels/index.html.haml b/app/views/labels/index.html.haml index 6eb2c00e..4e41d375 100644 --- a/app/views/labels/index.html.haml +++ b/app/views/labels/index.html.haml @@ -4,7 +4,7 @@ Labels %br %div.ui-box - %ul.well-list.labels-table + %ul.unstyled.labels-table - @labels.each do |label| = render 'label', label: label diff --git a/app/views/layouts/_flash.html.haml b/app/views/layouts/_flash.html.haml index a3bed593..86564ad7 100644 --- a/app/views/layouts/_flash.html.haml +++ b/app/views/layouts/_flash.html.haml @@ -1,8 +1,17 @@ -.flash-container - - if alert - .alert - %span= alert +- if alert || notice + - text = alert || notice + %div{style: "display:none", id: "flash_container"} + %center + %h4= text + :javascript + $(function(){ + $("#flash_container").slideDown("slow"); + $("#flash_container").click(function(){ + $(this).slideUp("slow"); + }); + setTimeout("hideFlash()",3000); + }); - - elsif notice - .alert.alert-info - %span= notice + function hideFlash(){ + $("#flash_container").slideUp("slow"); + } diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml index eb83fd2f..25fe9d80 100644 --- a/app/views/layouts/_head.html.haml +++ b/app/views/layouts/_head.html.haml @@ -1,20 +1,17 @@ %head %meta{charset: "utf-8"} %title - = "#{title} | " if defined?(title) GitLab + = " > #{@project.name}" if @project && !@project.new_record? = favicon_link_tag 'favicon.ico' = stylesheet_link_tag "application" = javascript_include_tag "application" - = csrf_meta_tags - = include_gon - -# Atom feed - - if current_user - - if controller_name == 'projects' && action_name == 'index' - = auto_discovery_link_tag :atom, projects_url(:atom, private_token: current_user.private_token), title: "Dashboard feed" - - if @project && !@project.new_record? - - if current_controller?(:tree, :commits) - = auto_discovery_link_tag(:atom, project_commits_url(@project, @ref, format: :atom, private_token: current_user.private_token), title: "Recent commits to #{@project.name}:#{@ref}") - - if current_controller?(:issues) - = auto_discovery_link_tag(:atom, project_issues_url(@project, :atom, private_token: current_user.private_token), title: "#{@project.name} issues") + - if controller_name == 'projects' && action_name == 'index' + = auto_discovery_link_tag :atom, projects_url(:atom, private_token: current_user.private_token), title: "Dashboard feed" + - if @project && !@project.new_record? + - if current_controller?(:tree, :commits) + = auto_discovery_link_tag(:atom, project_commits_url(@project, @ref, format: :atom, private_token: current_user.private_token), title: "Recent commits to #{@project.name}:#{@ref}") + - if current_controller?(:issues) + = auto_discovery_link_tag(:atom, project_issues_url(@project, :atom, private_token: current_user.private_token), title: "#{@project.name} issues") + = csrf_meta_tags diff --git a/app/views/layouts/_head_panel.html.haml b/app/views/layouts/_head_panel.html.haml index 8f6873e1..38e1d7f0 100644 --- a/app/views/layouts/_head_panel.html.haml +++ b/app/views/layouts/_head_panel.html.haml @@ -2,17 +2,11 @@ .navbar-inner .container %div.app_logo - %span.separator - = link_to root_path, class: "home has_bottom_tooltip", title: "Dashboard" do + = link_to root_path, class: "home", title: "Home" do %h1 GITLAB %span.separator %h1.project_name= title %ul.nav - %li - = render "layouts/search" - %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 @@ -22,14 +16,22 @@ = link_to new_project_path, title: "Create New Project", class: 'has_bottom_tooltip', 'data-original-title' => 'New project' do %i.icon-plus %li - = link_to profile_path, title: "My Profile", class: 'has_bottom_tooltip', 'data-original-title' => 'Your profile' do + = link_to profile_path, title: "Your Profile", class: 'has_bottom_tooltip', 'data-original-title' => 'Your profile' do %i.icon-user + %span.separator %li - = link_to destroy_user_session_path, class: "logout", method: :delete, title: "Logout", class: 'has_bottom_tooltip', 'data-original-title' => 'Logout' do - %i.icon-signout + = render "layouts/search" %li - = link_to current_user, class: "profile-pic" do - = image_tag gravatar_icon(current_user.email, 26) + .account-box + = link_to profile_path, class: "pic" do + = image_tag gravatar_icon(current_user.email) + .account-links + = link_to profile_path, class: "username" do + %i.icon-user.icon-white + My profile + = link_to destroy_user_session_path, class: "logout", method: :delete do + %i.icon-signout.icon-white + Logout = render "layouts/init_auto_complete" diff --git a/app/views/layouts/_init_auto_complete.html.haml b/app/views/layouts/_init_auto_complete.html.haml index 8f8c7d88..502f289e 100644 --- a/app/views/layouts/_init_auto_complete.html.haml +++ b/app/views/layouts/_init_auto_complete.html.haml @@ -1,6 +1,6 @@ :javascript $(function() { - GitLab.GfmAutoComplete.Members.url = "#{ "/api/v3/projects/#{@project.id}/members" if @project }"; + GitLab.GfmAutoComplete.Members.url = "#{ "/api/v2/projects/#{@project.code}/members" if @project }"; GitLab.GfmAutoComplete.Members.params.private_token = "#{current_user.private_token}"; GitLab.GfmAutoComplete.Emoji.data = #{raw emoji_autocomplete_source}; diff --git a/app/views/layouts/_search.html.haml b/app/views/layouts/_search.html.haml index c484af04..7ea90798 100644 --- a/app/views/layouts/_search.html.haml +++ b/app/views/layouts/_search.html.haml @@ -1,8 +1,6 @@ .search = form_tag search_path, method: :get, class: 'navbar-form pull-left' do |f| = text_field_tag "search", nil, placeholder: "Search", class: "search-input" - = hidden_field_tag :group_id, @group.try(:id) - = hidden_field_tag :project_id, @project.try(:id) :javascript $(function(){ diff --git a/app/views/layouts/admin.html.haml b/app/views/layouts/admin.html.haml index abe3f2ea..582f86ba 100644 --- a/app/views/layouts/admin.html.haml +++ b/app/views/layouts/admin.html.haml @@ -1,11 +1,22 @@ !!! 5 %html{ lang: "en"} - = render "layouts/head", title: "Admin area" + = render "layouts/head" %body{class: "#{app_theme} admin"} - = render "layouts/head_panel", title: "Admin area" = render "layouts/flash" - %nav.main-nav - .container= render 'layouts/nav/admin' - + = render "layouts/head_panel", title: "Admin area" .container + %ul.main_menu + = nav_link(controller: :dashboard, html_options: {class: 'home'}) do + = link_to "Stats", admin_root_path + = nav_link(controller: [:projects, :groups]) do + = link_to "Projects", admin_projects_path + = nav_link(controller: :users) do + = link_to "Users", admin_users_path + = nav_link(controller: :logs) do + = link_to "Logs", admin_logs_path + = nav_link(controller: :hooks) do + = link_to "Hooks", admin_hooks_path + = nav_link(controller: :resque) do + = link_to "Resque", admin_resque_path + .content= yield diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 4e683140..40f4f88c 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -1,11 +1,24 @@ !!! 5 %html{ lang: "en"} - = render "layouts/head", title: "Dashboard" + = render "layouts/head" %body{class: "#{app_theme} application"} - = render "layouts/head_panel", title: "Dashboard" = render "layouts/flash" - %nav.main-nav - .container= render 'layouts/nav/dashboard' - + = render "layouts/head_panel", title: "Dashboard" .container + %ul.main_menu + = nav_link(path: 'dashboard#index', html_options: {class: 'home'}) do + = link_to "Home", root_path, title: "Home" + = nav_link(path: 'dashboard#issues') do + = link_to dashboard_issues_path do + Issues + %span.count= current_user.assigned_issues.opened.count + = nav_link(path: 'dashboard#merge_requests') do + = link_to dashboard_merge_requests_path do + Merge Requests + %span.count= current_user.cared_merge_requests.count + = nav_link(path: 'search#show') do + = link_to "Search", search_path + = nav_link(path: 'help#index') do + = link_to "Help", help_path + .content= yield diff --git a/app/views/layouts/devise.html.haml b/app/views/layouts/devise.html.haml index a9758f19..70c4f007 100644 --- a/app/views/layouts/devise.html.haml +++ b/app/views/layouts/devise.html.haml @@ -2,7 +2,5 @@ %html{ lang: "en"} = render "layouts/head" %body.ui_basic.login-page - = render "layouts/flash" - .container - .content - = yield + = render partial: "layouts/flash" + .container= yield diff --git a/app/views/layouts/errors.html.haml b/app/views/layouts/errors.html.haml index b9395873..1f5c03bd 100644 --- a/app/views/layouts/errors.html.haml +++ b/app/views/layouts/errors.html.haml @@ -1,9 +1,9 @@ !!! 5 %html{ lang: "en"} - = render "layouts/head", title: "Error" + = render "layouts/head" %body{class: "#{app_theme} application"} - = render "layouts/head_panel", title: "" = render "layouts/flash" + = render "layouts/head_panel", title: "" .container .content %center.padded.prepend-top-20 diff --git a/app/views/layouts/group.html.haml b/app/views/layouts/group.html.haml index 8296b8ae..985200e2 100644 --- a/app/views/layouts/group.html.haml +++ b/app/views/layouts/group.html.haml @@ -1,11 +1,24 @@ !!! 5 %html{ lang: "en"} - = render "layouts/head", title: "#{@group.name}" + = render "layouts/head" %body{class: "#{app_theme} application"} - = render "layouts/head_panel", title: "group: #{@group.name}" = render "layouts/flash" - %nav.main-nav - .container= render 'layouts/nav/group' - + = render "layouts/head_panel", title: "#{@group.name}" .container + %ul.main_menu + = nav_link(path: 'groups#show', html_options: {class: 'home'}) do + = link_to "Home", group_path(@group), title: "Home" + = nav_link(path: 'groups#issues') do + = link_to issues_group_path(@group) do + Issues + %span.count= current_user.assigned_issues.opened.of_group(@group).count + = nav_link(path: 'groups#merge_requests') do + = link_to merge_requests_group_path(@group) do + Merge Requests + %span.count= current_user.cared_merge_requests.of_group(@group).count + = nav_link(path: 'groups#search') do + = link_to "Search", search_group_path(@group) + = nav_link(path: 'groups#people') do + = link_to "People", people_group_path(@group) + .content= yield diff --git a/app/views/layouts/nav/_admin.html.haml b/app/views/layouts/nav/_admin.html.haml deleted file mode 100644 index ca77c26e..00000000 --- a/app/views/layouts/nav/_admin.html.haml +++ /dev/null @@ -1,19 +0,0 @@ -%ul - = nav_link(controller: :dashboard, html_options: {class: 'home'}) do - = link_to admin_root_path, title: "Stats" do - %i.icon-home - = nav_link(controller: :projects) do - = link_to "Projects", admin_projects_path - = nav_link(controller: :teams) do - = link_to "Teams", admin_teams_path - = nav_link(controller: :groups) do - = link_to "Groups", admin_groups_path - = nav_link(controller: :users) do - = link_to "Users", admin_users_path - = nav_link(controller: :logs) do - = link_to "Logs", admin_logs_path - = nav_link(controller: :hooks) do - = link_to "Hooks", admin_hooks_path - = nav_link(controller: :resque) do - = link_to "Background Jobs", admin_resque_path - diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml deleted file mode 100644 index 2ac35050..00000000 --- a/app/views/layouts/nav/_dashboard.html.haml +++ /dev/null @@ -1,20 +0,0 @@ -%ul - = nav_link(path: 'dashboard#show', html_options: {class: 'home'}) do - = link_to root_path, title: "Home" do - %i.icon-home - = nav_link(path: 'dashboard#projects') do - = link_to projects_dashboard_path do - Projects - = nav_link(path: 'dashboard#issues') do - = link_to issues_dashboard_path do - Issues - %span.count= current_user.assigned_issues.opened.count - = nav_link(path: 'dashboard#merge_requests') do - = link_to merge_requests_dashboard_path do - Merge Requests - %span.count= current_user.cared_merge_requests.opened.count - = nav_link(path: 'search#show') do - = link_to "Search", search_path - = nav_link(controller: :help) do - = link_to "Help", help_path - diff --git a/app/views/layouts/nav/_group.html.haml b/app/views/layouts/nav/_group.html.haml deleted file mode 100644 index f3cdb5ac..00000000 --- a/app/views/layouts/nav/_group.html.haml +++ /dev/null @@ -1,20 +0,0 @@ -%ul - = nav_link(path: 'groups#show', html_options: {class: 'home'}) do - = link_to group_path(@group), title: "Home" do - %i.icon-home - = nav_link(path: 'groups#issues') do - = link_to issues_group_path(@group) do - Issues - %span.count= current_user.assigned_issues.opened.of_group(@group).count - = nav_link(path: 'groups#merge_requests') do - = link_to merge_requests_group_path(@group) do - Merge Requests - %span.count= current_user.cared_merge_requests.opened.of_group(@group).count - = nav_link(path: 'groups#people') do - = link_to "People", people_group_path(@group) - - - if can?(current_user, :manage_group, @group) - = nav_link(path: 'groups#edit') do - = link_to edit_group_path(@group), class: "tab " do - Settings - diff --git a/app/views/layouts/nav/_profile.html.haml b/app/views/layouts/nav/_profile.html.haml deleted file mode 100644 index e5e4b27c..00000000 --- a/app/views/layouts/nav/_profile.html.haml +++ /dev/null @@ -1,17 +0,0 @@ -%ul - = nav_link(path: 'profiles#show', html_options: {class: 'home'}) do - = link_to profile_path, title: "Profile" do - %i.icon-home - = nav_link(path: 'profiles#account') do - = link_to "Account", account_profile_path - = nav_link(controller: :notifications) do - = link_to "Notifications", profile_notifications_path - = nav_link(controller: :keys) do - = link_to keys_path do - SSH Keys - %span.count= current_user.keys.count - = nav_link(path: 'profiles#design') do - = link_to "Design", design_profile_path - = nav_link(path: 'profiles#history') do - = link_to "History", history_profile_path - diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml deleted file mode 100644 index ec3da964..00000000 --- a/app/views/layouts/nav/_project.html.haml +++ /dev/null @@ -1,43 +0,0 @@ -%ul - = nav_link(path: 'projects#show', html_options: {class: "home"}) do - = link_to project_path(@project), title: "Project" do - %i.icon-home - - - unless @project.empty_repo? - - if can? current_user, :download_code, @project - = nav_link(controller: %w(tree blob blame)) do - = link_to 'Files', project_tree_path(@project, @ref || @repository.root_ref) - = nav_link(controller: %w(commit commits compare repositories protected_branches)) do - = link_to "Commits", project_commits_path(@project, @ref || @repository.root_ref) - = nav_link(controller: %w(graph)) do - = link_to "Network", project_graph_path(@project, @ref || @repository.root_ref) - - - if @project.issues_enabled - = nav_link(controller: %w(issues milestones labels)) do - = link_to url_for_project_issues do - Issues - - if @project.used_default_issues_tracker? - %span.count.issue_counter= @project.issues.opened.count - - - if @project.repo_exists? && @project.merge_requests_enabled - = nav_link(controller: :merge_requests) do - = link_to project_merge_requests_path(@project) do - Merge Requests - %span.count.merge_counter= @project.merge_requests.opened.count - - - if @project.wiki_enabled - = nav_link(controller: :wikis) do - = link_to 'Wiki', project_wiki_path(@project, :home) - - - if @project.wall_enabled - = nav_link(controller: :walls) do - = link_to 'Wall', project_wall_path(@project) - - - if @project.snippets_enabled - = nav_link(controller: :snippets) do - = link_to 'Snippets', project_snippets_path(@project) - - - if can? current_user, :admin_project, @project - = nav_link(html_options: {class: "#{project_tab_class}"}) do - = link_to edit_project_path(@project), class: "stat-tab tab " do - Settings diff --git a/app/views/layouts/nav/_team.html.haml b/app/views/layouts/nav/_team.html.haml deleted file mode 100644 index 415e4510..00000000 --- a/app/views/layouts/nav/_team.html.haml +++ /dev/null @@ -1,25 +0,0 @@ -%ul - = nav_link(path: 'teams#show', html_options: {class: 'home'}) do - = link_to team_path(@team), title: "Home" do - %i.icon-home - - = nav_link(path: 'teams#issues') do - = link_to issues_team_path(@team) do - Issues - %span.count= Issue.opened.of_user_team(@team).count - - = nav_link(path: 'teams#merge_requests') do - = link_to merge_requests_team_path(@team) do - Merge Requests - %span.count= MergeRequest.opened.of_user_team(@team).count - - = nav_link(controller: [:members]) do - = link_to team_members_path(@team), class: "team-tab tab" do - Members - %span.count= @team.members.count - - - if can? current_user, :admin_user_team, @team - = nav_link(path: 'teams#edit') do - = link_to edit_team_path(@team), class: "stat-tab tab " do - Settings - diff --git a/app/views/layouts/notify.html.haml b/app/views/layouts/notify.html.haml index f88abeca..7b79897b 100644 --- a/app/views/layouts/notify.html.haml +++ b/app/views/layouts/notify.html.haml @@ -3,20 +3,37 @@ %meta{content: "text/html; charset=utf-8", "http-equiv" => "Content-Type"} %title GitLab - - %body - %h1{style: "background: #EEE; border-bottom: 1px solid #DDD; color: #474D57; font: normal 20px Helvetica, Arial, sans-serif; margin: 0; padding: 5px 10px; line-height: 32px; font-size: 16px;"} - GitLab - - if @project - \| - = link_to @project.name_with_namespace, project_url(@project), style: 'color: #29B; text-decoration: none' - %table{align: "left", border: "0", cellpadding: "0", cellspacing: "0", style: "padding: 10px 0;", width: "100%"} + :css + .header h1 {color: #BBBBBB !important; font: bold 32px Helvetica, Arial, sans-serif; margin: 0; padding: 0; line-height: 40px;} + .header p {color: #c6c6c6; font: normal 12px Helvetica, Arial, sans-serif; margin: 0; padding: 0; line-height: 18px;} + .content h2 {color:#646464 !important; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; } + .content p {color:#767676; font-weight: normal; margin: 0; padding: 0; line-height: 20px; font-size: 12px;font-family: Helvetica, Arial, sans-serif;} + .content a {color: #0eb6ce; text-decoration: none;} + .footer p {font-size: 11px; color:#7d7a7a; margin: 0; padding: 0; font-family: Helvetica, Arial, sans-serif;} + .footer a {color: #0eb6ce; text-decoration: none;} + %body{bgcolor: "#EAEAEA", style: "margin: 0; padding: 0; background: #EAEAEA"} + %table{align: "center", border: "0", cellpadding: "0", cellspacing: "0", style: "padding: 35px 0; background: #EAEAEA;", width: "100%"} %tr - %td{align: "left", style: "margin: 0; padding: 10px;"} - = yield - %br - %tr - %td{align: "left", style: "margin: 0; padding: 10px;"} - %p{style: "font-size:small;color:#777"} - - if @project - You're receiving this notification because you are a member of the #{@project.name_with_namespace} project team. + %td{align: "center", style: "margin: 0; padding: 0; background: #EAEAEA;"} + %table.header{align: "center", border: "0", cellpadding: "0", cellspacing: "0", style: "font-family: Helvetica, Arial, sans-serif; background:#333", width: "600"} + %tr + %td{style: "font-size: 0px;", width: "20"} + \  + %td{align: "left", style: "padding: 18px 0 10px;", width: "580"} + %h1{style: "color: #BBBBBB; font: normal 32px Helvetica, Arial, sans-serif; margin: 0; padding: 0; line-height: 40px;"} + GITLAB + - if @project + | #{@project.name} + %table{align: "center", bgcolor: "#fff", border: "0", cellpadding: "0", cellspacing: "0", style: "font-family: Helvetica, Arial, sans-serif; background: #fff;", width: "600"} + %tr= yield + %tr + %td{align: "left", colspan: "2", height: "3", style: "padding: font-size: 0; line-height: 0; height: 3px;", width: "600"} + %table.footer{align: "center", border: "0", cellpadding: "0", cellspacing: "0", style: "font-family: Helvetica, Arial, sans-serif; line-height: 10px;", width: "600"} + %tr + %td{align: "center", style: "padding: 5px 0 10px; font-size: 11px; color:#7d7a7a; margin: 0; line-height: 1.2;font-family: Helvetica, Arial, sans-serif;", valign: "top"} + %br + %p{style: "font-size: 11px; color:#7d7a7a; margin: 0; padding: 0; font-family: Helvetica, Arial, sans-serif;"} + You're receiving this notification because you are a member of the + - if @project + #{@project.name} + project team. diff --git a/app/views/layouts/profile.html.haml b/app/views/layouts/profile.html.haml index 535f94c4..7a54bb7c 100644 --- a/app/views/layouts/profile.html.haml +++ b/app/views/layouts/profile.html.haml @@ -1,11 +1,22 @@ !!! 5 %html{ lang: "en"} - = render "layouts/head", title: "Profile" + = render "layouts/head" %body{class: "#{app_theme} profile"} - = render "layouts/head_panel", title: "Profile" = render "layouts/flash" - %nav.main-nav - .container= render 'layouts/nav/profile' - + = render "layouts/head_panel", title: "Profile" .container + %ul.main_menu + = nav_link(path: 'profile#show', html_options: {class: 'home'}) do + = link_to "Profile", profile_path + = nav_link(path: 'profile#account') do + = link_to "Account", profile_account_path + = nav_link(controller: :keys) do + = link_to keys_path do + SSH Keys + %span.count= current_user.keys.count + = nav_link(path: 'profile#design') do + = link_to "Design", profile_design_path + = nav_link(path: 'profile#history') do + = link_to "History", profile_history_path + .content= yield diff --git a/app/views/layouts/project_resource.html.haml b/app/views/layouts/project_resource.html.haml index 7b0d4789..b1dbe41c 100644 --- a/app/views/layouts/project_resource.html.haml +++ b/app/views/layouts/project_resource.html.haml @@ -1,14 +1,41 @@ !!! 5 %html{ lang: "en"} - = render "layouts/head", title: @project.name_with_namespace + = render "layouts/head" %body{class: "#{app_theme} project"} - = render "layouts/head_panel", title: project_title(@project) = render "layouts/flash" - - if can?(current_user, :download_code, @project) - = render 'shared/no_ssh' - - %nav.main-nav - .container= render 'layouts/nav/project' - + = render "layouts/head_panel", title: @project.name .container + %ul.main_menu + = nav_link(html_options: {class: "home #{project_tab_class}"}) do + = link_to @project.code, project_path(@project), title: "Project" + + - if @project.repo_exists? + - if can? current_user, :download_code, @project + = nav_link(controller: %w(tree blob blame)) do + = link_to 'Files', project_tree_path(@project, @ref || @project.root_ref) + = nav_link(controller: %w(commit commits compare repositories protected_branches)) do + = link_to "Commits", project_commits_path(@project, @ref || @project.root_ref) + = nav_link(path: 'projects#graph') do + = link_to "Network", graph_project_path(@project) + + - if @project.issues_enabled + = nav_link(controller: %w(issues milestones labels)) do + = link_to project_issues_filter_path(@project) do + Issues + %span.count.issue_counter= @project.issues.opened.count + + - if @project.repo_exists? && @project.merge_requests_enabled + = nav_link(controller: :merge_requests) do + = link_to project_merge_requests_path(@project) do + Merge Requests + %span.count.merge_counter= @project.merge_requests.opened.count + + - if @project.wall_enabled + = nav_link(path: 'projects#wall') do + = link_to 'Wall', wall_project_path(@project) + + - if @project.wiki_enabled + = nav_link(controller: :wikis) do + = link_to 'Wiki', project_wiki_path(@project, :index) + .content= yield diff --git a/app/views/layouts/public.html.haml b/app/views/layouts/public.html.haml deleted file mode 100644 index 435250b6..00000000 --- a/app/views/layouts/public.html.haml +++ /dev/null @@ -1,17 +0,0 @@ -!!! 5 -%html{ lang: "en"} - = render "layouts/head", title: "Public Projects" - %body{class: "#{app_theme} application"} - %header.navbar.navbar-static-top.navbar-gitlab - .navbar-inner - .container - %div.app_logo - %span.separator - = link_to root_path, class: "home" do - %h1 GITLAB - %span.separator - %h1.project_name Public Projects - .container - .content - .prepend-top-20 - = yield diff --git a/app/views/layouts/user_team.html.haml b/app/views/layouts/user_team.html.haml deleted file mode 100644 index f2ead9d2..00000000 --- a/app/views/layouts/user_team.html.haml +++ /dev/null @@ -1,11 +0,0 @@ -!!! 5 -%html{ lang: "en"} - = render "layouts/head", title: "#{@team.name}" - %body{class: "#{app_theme} application"} - = render "layouts/head_panel", title: "team: #{@team.name}" - = render "layouts/flash" - %nav.main-nav - .container= render 'layouts/nav/team' - - .container - .content= yield diff --git a/app/views/merge_requests/_filter.html.haml b/app/views/merge_requests/_filter.html.haml deleted file mode 100644 index 4b48306e..00000000 --- a/app/views/merge_requests/_filter.html.haml +++ /dev/null @@ -1,20 +0,0 @@ -= form_tag project_issues_path(@project), method: 'get' do - %fieldset - %ul.nav.nav-pills.nav-stacked - %li{class: ("active" if (params[:f] == 'open' || !params[:f]))} - = link_to project_merge_requests_path(@project, f: 'open', milestone_id: params[:milestone_id]) do - Open - %li{class: ("active" if params[:f] == "closed")} - = link_to project_merge_requests_path(@project, f: "closed", milestone_id: params[:milestone_id]) do - Closed - %li{class: ("active" if params[:f] == 'assigned-to-me')} - = link_to project_merge_requests_path(@project, f: 'assigned-to-me', milestone_id: params[:milestone_id]) do - Assigned To Me - %li{class: ("active" if params[:f] == 'all')} - = link_to project_merge_requests_path(@project, f: 'all', milestone_id: params[:milestone_id]) do - All - - %fieldset - %hr - = link_to "Reset", project_merge_requests_path(@project), class: 'btn pull-right' - diff --git a/app/views/merge_requests/_form.html.haml b/app/views/merge_requests/_form.html.haml index 6d64988c..302e75cf 100644 --- a/app/views/merge_requests/_form.html.haml +++ b/app/views/merge_requests/_form.html.haml @@ -1,72 +1,66 @@ -= form_for [@project, @merge_request], html: { class: "#{controller.action_name}-merge-request form-horizontal" } do |f| += form_for [@project, @merge_request], html: { class: "new_merge_request form-horizontal" } do |f| -if @merge_request.errors.any? - .alert.alert-error + .alert-message.block-message.error %ul - @merge_request.errors.full_messages.each do |msg| %li= msg - %fieldset - %legend 1. Select Branches + %h4.cdark 1. Select Branches + %br - .row - .span5 - .mr_branch_box - %h5.cgray From (Head Branch) - .body - .padded= f.select(:source_branch, @repository.branch_names, { include_blank: "Select branch" }, {class: 'chosen span4'}) - .mr_source_commit + .row + .span5 + .mr_branch_box + %h5 From (Head Branch) + .body + .padded= f.select(:source_branch, @project.heads.map(&:name), { include_blank: "Select branch" }, {class: 'chosen span4'}) + .mr_source_commit - .span2 - %center= image_tag "merge.png", class: 'mr_direction_tip' - .span5 - .mr_branch_box - %h5.cgray To (Base Branch) - .body - .padded= f.select(:target_branch, @repository.branch_names, { include_blank: "Select branch" }, {class: 'chosen span4'}) - .mr_target_commit + .span2 + %center= image_tag "merge.png", class: 'mr_direction_tip' + .span5 + .mr_branch_box + %h5 To (Base Branch) + .body + .padded= f.select(:target_branch, @project.heads.map(&:name), { include_blank: "Select branch" }, {class: 'chosen span4'}) + .mr_target_commit - %fieldset - %legend 2. Fill info + %h4.cdark 2. Fill info - .ui-box.ui-box-show - .ui-box-head - .clearfix - = f.label :title do - %strong= "Title *" - .input= f.text_field :title, class: "input-xxlarge pad js-gfm-input", maxlength: 255, rows: 5, required: true - .ui-box-body - .clearfix - .left - = f.label :assignee_id do - %i.icon-user - Assign to - .input= f.select(:assignee_id, @project.users.all.collect {|p| [ p.name, p.id ] }, { include_blank: "Select user" }, {class: 'chosen span3'}) - .left - = f.label :milestone_id do - %i.icon-time - Milestone - .input= f.select(:milestone_id, @project.milestones.active.all.collect {|p| [ p.title, p.id ] }, { include_blank: "Select milestone" }, {class: 'chosen'}) + .clearfix + .merge_requests_form_box + .top_box_content + = f.label :title do + %strong= "Title *" + .input= f.text_field :title, class: "input-xxlarge pad js-gfm-input", maxlength: 255, rows: 5 + .merge_requests_middle_box + .merge_requests_assignee + = f.label :assignee_id do + %i.icon-user + Assign to + .input= f.select(:assignee_id, @project.users.all.collect {|p| [ p.name, p.id ] }, { include_blank: "Select user" }, {class: 'chosen span3'}) + .merge_requests_milestone + = f.label :milestone_id do + %i.icon-time + Milestone + .input= f.select(:milestone_id, @project.milestones.active.all.collect {|p| [ p.title, p.id ] }, { include_blank: "Select milestone" }, {class: 'chosen'}) .control-group .form-actions + = f.submit 'Save', class: "btn save-btn" - if @merge_request.new_record? - = f.submit 'Submit merge request', class: "btn btn-create" - -else - = f.submit 'Save changes', class: "btn btn-save" - - if @merge_request.new_record? - = link_to project_merge_requests_path(@project), class: "btn btn-cancel" do + = link_to project_merge_requests_path(@project), class: "btn cancel-btn" do Cancel - else - = link_to project_merge_request_path(@project, @merge_request), class: "btn btn-cancel" do + = link_to project_merge_request_path(@project, @merge_request), class: "btn cancel-btn" do Cancel :javascript $(function(){ - disableButtonIfEmptyField("#merge_request_title", ".btn-save"); - - var source_branch = $("#merge_request_source_branch") - , target_branch = $("#merge_request_target_branch"); + disableButtonIfEmptyField("#merge_request_title", ".save-btn"); + var source_branch = $("#merge_request_source_branch"); + var target_branch = $("#merge_request_target_branch"); $.get("#{branch_from_project_merge_requests_path(@project)}", {ref: source_branch.val() }); $.get("#{branch_to_project_merge_requests_path(@project)}", {ref: target_branch.val() }); diff --git a/app/views/merge_requests/_merge_request.html.haml b/app/views/merge_requests/_merge_request.html.haml index 09c55d98..4f68c5f2 100644 --- a/app/views/merge_requests/_merge_request.html.haml +++ b/app/views/merge_requests/_merge_request.html.haml @@ -1,20 +1,20 @@ -%li{ class: mr_css_classes(merge_request) } - .pull-right +%li.wll{ class: mr_css_classes(merge_request) } + .right .left - if merge_request.merged? - %span.btn.btn-small.disabled.grouped + %span.btn.small.disabled.grouped %strong %i.icon-ok = "MERGED" - if merge_request.notes.any? - %span.btn.btn-small.disabled.grouped + %span.btn.small.disabled.grouped %i.icon-comment = merge_request.mr_and_commit_notes.count - if merge_request.milestone_id? - %span.btn.btn-small.disabled.grouped + %span.btn.small.disabled.grouped %i.icon-time = merge_request.milestone.title - %span.btn.btn-small.disabled.grouped + %span.btn.small.disabled.grouped = merge_request.source_branch → = merge_request.target_branch diff --git a/app/views/merge_requests/_show.html.haml b/app/views/merge_requests/_show.html.haml index 08b80172..f1d0c8aa 100644 --- a/app/views/merge_requests/_show.html.haml +++ b/app/views/merge_requests/_show.html.haml @@ -1,39 +1,40 @@ -.merge-request - = render "merge_requests/show/mr_title" - = render "merge_requests/show/how_to_merge" - = render "merge_requests/show/mr_box" - = render "merge_requests/show/mr_accept" - - if @project.gitlab_ci? - = render "merge_requests/show/mr_ci" - = render "merge_requests/show/commits" += render "merge_requests/show/mr_title" += render "merge_requests/show/how_to_merge" += render "merge_requests/show/mr_box" += render "merge_requests/show/mr_accept" += render "merge_requests/show/commits" - - if @commits.present? - %ul.nav.nav-tabs - %li.notes-tab{data: {action: 'notes'}} - = link_to project_merge_request_path(@project, @merge_request) do - %i.icon-comment - Discussion - %li.diffs-tab{data: {action: 'diffs'}} - = link_to diffs_project_merge_request_path(@project, @merge_request) do - %i.icon-list-alt - Diff +- if @commits.present? + %ul.nav.nav-tabs.mr_nav_tabs + %li + = link_to "#notes", "data-url" => project_merge_request_path(@project, @merge_request), class: "merge-notes-tab tab" do + %i.icon-comment + Comments + %li + = link_to "#diffs", "data-url" => diffs_project_merge_request_path(@project, @merge_request), class: "merge-diffs-tab tab" do + %i.icon-list-alt + Diff - .notes.tab-content.voting_notes#notes{ class: (controller.action_name == 'show') ? "" : "hide" } - = render "notes/notes_with_form" - .diffs.tab-content - = render "merge_requests/show/diffs" if @diffs - .status +.merge_request_notes.voting_notes#notes{ class: (controller.action_name == 'show') ? "" : "hide" } + = render("notes/notes_with_form", tid: @merge_request.id, tt: "merge_request") +.merge-request-diffs + = render "merge_requests/show/diffs" if @diffs +.status + += render "notes/per_line_form" :javascript - var merge_request; $(function(){ - merge_request = new MergeRequest({ + MergeRequest.init({ url_to_automerge_check: "#{automerge_check_project_merge_request_path(@project, @merge_request)}", - check_enable: #{@merge_request.unchecked? ? "true" : "false"}, - url_to_ci_check: "#{ci_status_project_merge_request_path(@project, @merge_request)}", - ci_enable: #{@project.gitlab_ci? ? "true" : "false"}, - current_status: "#{@merge_request.merge_status_name}", + check_enable: #{@merge_request.state == MergeRequest::UNCHECKED ? "true" : "false"}, + current_state: "#{@merge_request.human_state}", action: "#{controller.action_name}" }); - }); + + $(".edit_merge_request").live("ajax:beforeSend", function() { + $('.can_be_merged').hide(); + $('.merge_in_progress').show(); + }) + }) diff --git a/app/views/merge_requests/automerge.js.haml b/app/views/merge_requests/automerge.js.haml index e01ff662..93e18445 100644 --- a/app/views/merge_requests/automerge.js.haml +++ b/app/views/merge_requests/automerge.js.haml @@ -3,5 +3,5 @@ location.reload(); -else :plain - merge_request.alreadyOrCannotBeMerged() + MergeRequest.already_cannot_be_merged() diff --git a/app/views/merge_requests/commits.js.haml b/app/views/merge_requests/commits.js.haml index 923b1ea0..76322bdb 100644 --- a/app/views/merge_requests/commits.js.haml +++ b/app/views/merge_requests/commits.js.haml @@ -1,4 +1,4 @@ :plain - merge_request.$(".commits").html("#{escape_javascript(render(partial: "commits"))}"); + $(".merge-request-commits").html("#{escape_javascript(render(partial: "commits"))}"); diff --git a/app/views/merge_requests/diffs.html.haml b/app/views/merge_requests/diffs.html.haml index 2a5b8b14..a755491c 100644 --- a/app/views/merge_requests/diffs.html.haml +++ b/app/views/merge_requests/diffs.html.haml @@ -1 +1,6 @@ = render "show" + +:javascript + $(function(){ + PerLineNotes.init(); + }); diff --git a/app/views/merge_requests/diffs.js.haml b/app/views/merge_requests/diffs.js.haml index 266892c0..98539985 100644 --- a/app/views/merge_requests/diffs.js.haml +++ b/app/views/merge_requests/diffs.js.haml @@ -1,2 +1,7 @@ :plain - merge_request.$(".diffs").html("#{escape_javascript(render(partial: "merge_requests/show/diffs"))}"); + $(".merge-request-diffs").html("#{escape_javascript(render(partial: "merge_requests/show/diffs"))}"); + + $(function(){ + PerLineNotes.init(); + }); + diff --git a/app/views/merge_requests/index.html.haml b/app/views/merge_requests/index.html.haml index 3073c8f6..7bcb7a81 100644 --- a/app/views/merge_requests/index.html.haml +++ b/app/views/merge_requests/index.html.haml @@ -1,35 +1,48 @@ -- if can? current_user, :write_merge_request, @project - = link_to new_project_merge_request_path(@project), class: "pull-right btn btn-primary", title: "New Merge Request" do - %i.icon-plus - New Merge Request %h3.page_title Merge Requests + - if can? current_user, :write_issue, @project + = link_to new_project_merge_request_path(@project), class: "right btn", title: "New Merge Request" do + New Merge Request %br -.row - .span3 - = render 'filter' - .span9 - .ui-box - .title - = form_tag project_merge_requests_path(@project), id: "merge_requests_search_form", method: :get, class: :left do - = select_tag(:assignee_id, options_from_collection_for_select([unassigned_filter] + @project.users.all, "id", "name", params[:assignee_id]), prompt: "Assignee") - = select_tag(:milestone_id, options_from_collection_for_select([unassigned_filter] + @project.milestones.order("id desc").all, "id", "title", params[:milestone_id]), prompt: "Milestone") - = hidden_field_tag :f, params[:f] - .clearfix +.ui-box + .title + .left + %ul.nav.nav-pills + %li{class: ("active" if (params[:f] == 'open' || !params[:f]))} + = link_to project_merge_requests_path(@project, f: 'open', milestone_id: params[:milestone_id]) do + Open + %li{class: ("active" if params[:f] == "closed")} + = link_to project_merge_requests_path(@project, f: "closed", milestone_id: params[:milestone_id]) do + Closed + %li{class: ("active" if params[:f] == 'assigned-to-me')} + = link_to project_merge_requests_path(@project, f: 'assigned-to-me', milestone_id: params[:milestone_id]) do + To Me + %li{class: ("active" if params[:f] == 'all')} + = link_to project_merge_requests_path(@project, f: 'all', milestone_id: params[:milestone_id]) do + All + .right + = form_tag project_merge_requests_path(@project), id: "merge_requests_search_form", method: :get, class: :right do + = select_tag(:assignee_id, options_from_collection_for_select([unassigned_filter] + @project.users.all, "id", "name", params[:assignee_id]), prompt: "Assignee") + = select_tag(:milestone_id, options_from_collection_for_select([unassigned_filter] + @project.milestones.order("id desc").all, "id", "title", params[:milestone_id]), prompt: "Milestone") + = hidden_field_tag :f, params[:f] + .clearfix - %ul.well-list - = render @merge_requests - - if @merge_requests.blank? - %li - %h4.nothing_here_message Nothing to show here - - if @merge_requests.present? - %li.bottom - .left= paginate @merge_requests, theme: "gitlab" - .pull-right - %span.cgray.pull-right #{@merge_requests.total_count} merge requests for this filter + %ul.unstyled + = render @merge_requests + - if @merge_requests.blank? + %li + %h4.nothing_here_message Nothing to show here + - if @merge_requests.present? + %li.bottom + .row + .span7= paginate @merge_requests, theme: "gitlab" + .span4.right + %span.cgray.right #{@merge_requests.total_count} merge requests for this filter :javascript - $(merge_requestsPage); + $(function() { + merge_requestsPage(); + }) diff --git a/app/views/merge_requests/invalid.html.haml b/app/views/merge_requests/invalid.html.haml deleted file mode 100644 index a73bef9e..00000000 --- a/app/views/merge_requests/invalid.html.haml +++ /dev/null @@ -1,17 +0,0 @@ -.merge-request - = render "merge_requests/show/mr_title" - = render "merge_requests/show/mr_box" - - .alert.alert-error - %h5 - %i.icon-exclamation-sign - We cannot find - %span.label-branch= @merge_request.source_branch - or - %span.label-branch= @merge_request.target_branch - branches in the repository. - %p - Maybe it was removed or never pushed. - %p - Please close Merge Request or change branches with existing one - diff --git a/app/views/merge_requests/show.js.haml b/app/views/merge_requests/show.js.haml index 2ce6eb63..f44ccbb5 100644 --- a/app/views/merge_requests/show.js.haml +++ b/app/views/merge_requests/show.js.haml @@ -1,2 +1,2 @@ :plain - merge_request.$(".notes").html("#{escape_javascript(render "notes/notes_with_form")}"); + $(".merge-request-notes").html("#{escape_javascript(render notes/notes_with_form", tid: @merge_request.id, tt: "merge_request")}"); diff --git a/app/views/merge_requests/show/_commits.html.haml b/app/views/merge_requests/show/_commits.html.haml index 5e27b6dc..d25e707c 100644 --- a/app/views/merge_requests/show/_commits.html.haml +++ b/app/views/merge_requests/show/_commits.html.haml @@ -1,23 +1,23 @@ - if @commits.present? .ui-box - %h5.title + %h5 %i.icon-list Commits (#{@commits.count}) - .commits + .merge-request-commits - if @commits.count > 8 - %ul.first-commits.well-list + %ul.first_mr_commits.unstyled - @commits.first(8).each do |commit| = render "commits/commit", commit: commit %li.bottom 8 of #{@commits.count} commits displayed. %strong - %a.show-all-commits Click here to show all - %ul.all-commits.hide.well-list + %a.mr_show_all_commits Click here to show all + %ul.all_mr_commits.hide.unstyled - @commits.each do |commit| = render "commits/commit", commit: commit - else - %ul.well-list + %ul.unstyled - @commits.each do |commit| = render "commits/commit", commit: commit diff --git a/app/views/merge_requests/show/_diffs.html.haml b/app/views/merge_requests/show/_diffs.html.haml index 0807454c..76850903 100644 --- a/app/views/merge_requests/show/_diffs.html.haml +++ b/app/views/merge_requests/show/_diffs.html.haml @@ -1,10 +1,8 @@ - if @merge_request.valid_diffs? = render "commits/diffs", diffs: @diffs - elsif @merge_request.broken_diffs? - %h4.nothing_here_message + %h4.nothing_here_message Can't load diff. - You can - = link_to "download it", project_merge_request_path(@project, @merge_request), format: :diff, class: "vlink" - instead. + You can #{link_to "download MR patch", raw_project_merge_request_path(@project, @merge_request), class: "vlink"} instead. - else %h4.nothing_here_message Nothing to merge diff --git a/app/views/merge_requests/show/_mr_accept.html.haml b/app/views/merge_requests/show/_mr_accept.html.haml index ac97f632..f2422885 100644 --- a/app/views/merge_requests/show/_mr_accept.html.haml +++ b/app/views/merge_requests/show/_mr_accept.html.haml @@ -1,42 +1,37 @@ -- unless @allowed_to_merge - .alert - %strong You don't have enough permissions to merge this MR +- unless can?(current_user, :accept_mr, @project) + .alert-message + %strong Only masters can accept MR -- if @show_merge_controls +- if @merge_request.open? && @commits.any? && can?(current_user, :accept_mr, @project) .automerge_widget.can_be_merged{style: "display:none"} .alert.alert-success %span = form_for [:automerge, @project, @merge_request], remote: true, method: :get do |f| %p - You can accept this request automatically. - If you still want to do it manually - + You can accept this request automatically. + If you still want to do it manually - %strong= link_to "click here", "#", class: "how_to_merge_link vlink", title: "How To Merge" for instructions .accept_group - = f.submit "Accept Merge Request", class: "btn success accept_merge_request" - - unless @project.root_ref? @merge_request.source_branch + = f.submit "Accept Merge Request", class: "btn small success accept_merge_request" + - unless @project.root_ref? @merge_request.source_branch .remove_branch_holder - = label_tag :should_remove_source_branch, class: "checkbox" do + = label_tag :should_remove_source_branch, class: "checkbox" do = check_box_tag :should_remove_source_branch Remove source-branch .clearfix - - - .automerge_widget.no_satellite{style: "display:none"} - .alert.alert-error - %span - %strong This repository does not have satellite. Ask administrator to fix this issue + .automerge_widget.cannot_be_merged{style: "display:none"} - .alert.alert-disabled + .alert.alert-info %span - = link_to "Show how to merge", "#", class: "how_to_merge_link btn btn-small padded", title: "How To Merge" + = link_to "Show how to merge", "#", class: "how_to_merge_link btn small padded", title: "How To Merge"   %strong This request can't be merged with GitLab. You should do it manually .automerge_widget.unchecked - .alert + .alert-message %strong %i.icon-refresh Checking for ability to automatically merge… @@ -45,8 +40,6 @@ .alert.alert-info %strong This merge request already can not be merged. Try to reload page. - .merge-in-progress.hide - %span.cgray - %i.icon-refresh.icon-spin -   - Merge is in progress. Please wait. Page will be automatically reloaded.   + .merge_in_progress.hide + %span.cgray Merge is in progress. Please wait. Page will be automatically reloaded.   + = image_tag "ajax_loader.gif" diff --git a/app/views/merge_requests/show/_mr_box.html.haml b/app/views/merge_requests/show/_mr_box.html.haml index 3b54f613..b4b4be29 100644 --- a/app/views/merge_requests/show/_mr_box.html.haml +++ b/app/views/merge_requests/show/_mr_box.html.haml @@ -1,34 +1,39 @@ -.ui-box.ui-box-show - .ui-box-head - %h4.box-title - - if @merge_request.merged? - .error.status_info - %i.icon-ok - Merged - - elsif @merge_request.closed? - .error.status_info Closed +.main_box + .top_box_content + %h4 + - if @merge_request.closed + .alert-message.error.status_info Closed + - else + .alert-message.success.status_info Open = gfm escape_once(@merge_request.title) + - if @project.gitlab_ci? + .right + = image_tag ci_status_path, class: 'status-badge' - .ui-box-body + .middle_box_content %div - %cite.cgray - Created at #{@merge_request.created_at.stamp("Aug 21, 2011")} by #{link_to_member(@project, @merge_request.author)} - - if @merge_request.assignee - \, currently assigned to #{link_to_member(@project, @merge_request.assignee)} + %cite.cgray Created at #{@merge_request.created_at.stamp("Aug 21, 2011")} by + = image_tag gravatar_icon(@merge_request.author_email), width: 16, class: "lil_av" + %strong.author= link_to_merge_request_author(@merge_request) + + - if @merge_request.assignee + %cite.cgray , currently assigned to + = image_tag gravatar_icon(@merge_request.assignee_email), width: 16, class: "lil_av" + %strong.author= link_to_merge_request_assignee(@merge_request) - if @merge_request.milestone - milestone = @merge_request.milestone %cite.cgray and attached to milestone %strong= link_to_gfm truncate(milestone.title, length: 20), project_milestone_path(milestone.project, milestone) - - if @merge_request.closed? - .ui-box-bottom - %span - Closed by #{link_to_member(@project, @merge_request.closed_event.author)} - %small #{time_ago_in_words(@merge_request.closed_event.created_at)} ago. - - if @merge_request.merged? - .ui-box-bottom - %span - Merged by #{link_to_member(@project, @merge_request.merge_event.author)} - %small #{time_ago_in_words(@merge_request.merge_event.created_at)} ago. + - if @merge_request.closed + .bottom_box_content + - if @merge_request.merged? + %span + Merged by #{@merge_request.merge_event.author_name} + %small #{time_ago_in_words(@merge_request.merge_event.created_at)} ago. + - elsif @merge_request.closed_event + %span + Closed by #{@merge_request.closed_event.author_name} + %small #{time_ago_in_words(@merge_request.closed_event.created_at)} ago. diff --git a/app/views/merge_requests/show/_mr_ci.html.haml b/app/views/merge_requests/show/_mr_ci.html.haml deleted file mode 100644 index a8faa6ba..00000000 --- a/app/views/merge_requests/show/_mr_ci.html.haml +++ /dev/null @@ -1,35 +0,0 @@ -- if @merge_request.opened? && @commits.any? - .ci_widget.ci-success{style: "display:none"} - .alert.alert-success - %i.icon-ok - %strong CI build passed - for #{@merge_request.last_commit_short_sha}. - = link_to "Build page", ci_build_details_path(@merge_request) - - - .ci_widget.ci-failed{style: "display:none"} - .alert.alert-error - %i.icon-remove - %strong CI build failed - for #{@merge_request.last_commit_short_sha}. - = link_to "Build page", ci_build_details_path(@merge_request) - - - [:running, :pending].each do |status| - .ci_widget{class: "ci-#{status}", style: "display:none"} - .alert - %i.icon-time - %strong CI build #{status} - for #{@merge_request.last_commit_short_sha}. - = link_to "Build page", ci_build_details_path(@merge_request) - - .ci_widget - .alert - %strong - %i.icon-refresh - Checking for CI status for #{@merge_request.last_commit_short_sha} - - .ci_widget.ci-error{style: "display:none"} - .alert.alert-error - %i.icon-remove - %strong Cannot connect to CI server. Please check your setting - diff --git a/app/views/merge_requests/show/_mr_title.html.haml b/app/views/merge_requests/show/_mr_title.html.haml index 24285c27..8708469c 100644 --- a/app/views/merge_requests/show/_mr_title.html.haml +++ b/app/views/merge_requests/show/_mr_title.html.haml @@ -1,29 +1,29 @@ %h3.page_title = "Merge Request ##{@merge_request.id}:"   - %span.label-branch= @merge_request.source_branch + %span.label_branch= @merge_request.source_branch → - %span.label-branch= @merge_request.target_branch + %span.label_branch= @merge_request.target_branch - %span.pull-right + %span.right + - if @merge_request.merged? + %span.btn.small.disabled.grouped + %strong + %i.icon-ok + = "MERGED" - if can?(current_user, :modify_merge_request, @merge_request) - - if @merge_request.opened? - .left.btn-group - %a.btn.grouped.dropdown-toggle{ data: {toggle: :dropdown} } - %i.icon-download-alt - Download as - %span.caret - %ul.dropdown-menu - %li= link_to "Email Patches", project_merge_request_path(@project, @merge_request, format: :patch) - %li= link_to "Plain Diff", project_merge_request_path(@project, @merge_request, format: :diff) + - if @merge_request.open? + = link_to raw_project_merge_request_path(@project, @merge_request), class: "btn grouped" do + %i.icon-download-alt + Get Patch - = link_to 'Close', project_merge_request_path(@project, @merge_request, merge_request: {state_event: :close }), method: :put, class: "btn grouped btn-close", title: "Close merge request" + = link_to 'Close', project_merge_request_path(@project, @merge_request, merge_request: {closed: true }, status_only: true), method: :put, class: "btn grouped danger", title: "Close merge request" = link_to edit_project_merge_request_path(@project, @merge_request), class: "btn grouped" do %i.icon-edit Edit -.pull-right +.right .span3#votes= render 'votes/votes_block', votable: @merge_request .back_link diff --git a/app/views/milestones/_form.html.haml b/app/views/milestones/_form.html.haml index fbaf64a3..194eac77 100644 --- a/app/views/milestones/_form.html.haml +++ b/app/views/milestones/_form.html.haml @@ -7,7 +7,7 @@ = form_for [@project, @milestone], html: {class: "new_milestone form-horizontal"} do |f| -if @milestone.errors.any? - .alert.alert-error + .alert-message.block-message.error %ul - @milestone.errors.full_messages.each do |msg| %li= msg @@ -32,18 +32,18 @@ .form-actions - if @milestone.new_record? - = f.submit 'Create milestone', class: "btn-save btn" - = link_to "Cancel", project_milestones_path(@project), class: "btn btn-cancel" + = f.submit 'Create milestone', class: "save-btn btn" + = link_to "Cancel", project_milestones_path(@project), class: "btn cancel-btn" -else - = f.submit 'Save changes', class: "btn-save btn" - = link_to "Cancel", project_milestone_path(@project, @milestone), class: "btn btn-cancel" + = f.submit 'Save changes', class: "save-btn btn" + = link_to "Cancel", project_milestone_path(@project, @milestone), class: "btn cancel-btn" :javascript $(function() { - disableButtonIfEmptyField("#milestone_title", ".btn-save"); + disableButtonIfEmptyField("#milestone_title", ".save-btn"); $( ".datepicker" ).datepicker({ dateFormat: "yy-mm-dd", onSelect: function(dateText, inst) { $("#milestone_due_date").val(dateText) } - }).datepicker("setDate", $.datepicker.parseDate('yy-mm-dd', $('#milestone_due_date').val())); + }); }); diff --git a/app/views/milestones/_milestone.html.haml b/app/views/milestones/_milestone.html.haml index 8a3727c6..7c4c0e67 100644 --- a/app/views/milestones/_milestone.html.haml +++ b/app/views/milestones/_milestone.html.haml @@ -1,27 +1,22 @@ -%li{class: "milestone milestone-#{milestone.closed? ? 'closed' : 'open'}", id: dom_id(milestone) } - .pull-right - - if can?(current_user, :admin_milestone, milestone.project) and milestone.active? - = link_to edit_project_milestone_path(milestone.project, milestone), class: "btn btn-small edit-milestone-link grouped" do +%li{class: "milestone", id: dom_id(milestone) } + .right + - if can? current_user, :admin_milestone, milestone.project + = link_to edit_project_milestone_path(milestone.project, milestone), class: "btn small edit-milestone-link grouped" do %i.icon-edit Edit %h4 = link_to_gfm truncate(milestone.title, length: 100), project_milestone_path(milestone.project, milestone) - - if milestone.expired? and not milestone.closed? - %span.cred (Expired) %small = milestone.expires_at - - if milestone.is_empty? - %span.muted Empty - - else - .row - .span4 - .progress.progress-info - .bar{style: "width: #{milestone.percent_complete}%;"} - .span6 - = link_to project_issues_path(milestone.project, milestone_id: milestone.id) do - = pluralize milestone.issues.count, 'Issue' -   - = link_to project_merge_requests_path(milestone.project, milestone_id: milestone.id) do - = pluralize milestone.merge_requests.count, 'Merge Request' -   - %span.light #{milestone.percent_complete}% complete + .row + .span4 + .progress.progress-info + .bar{style: "width: #{milestone.percent_complete}%;"} + .span6 + = link_to project_issues_path(milestone.project, milestone_id: milestone.id) do + = pluralize milestone.issues.count, 'Issue' +   + = link_to project_merge_requests_path(milestone.project, milestone_id: milestone.id) do + = pluralize milestone.merge_requests.count, 'Merge Request' +   + %span.light #{milestone.percent_complete}% complete diff --git a/app/views/milestones/index.html.haml b/app/views/milestones/index.html.haml index b78f1705..c5333b08 100644 --- a/app/views/milestones/index.html.haml +++ b/app/views/milestones/index.html.haml @@ -3,7 +3,7 @@ %h3.page_title Milestones - if can? current_user, :admin_milestone, @project - = link_to "New Milestone", new_project_milestone_path(@project), class: "pull-right btn btn-small", title: "New Milestone" + = link_to "New Milestone", new_project_milestone_path(@project), class: "right btn small", title: "New Milestone" %br %div.ui-box .title @@ -11,18 +11,15 @@ %li{class: ("active" if (params[:f] == "active" || !params[:f]))} = link_to project_milestones_path(@project, f: "active") do Active - %li{class: ("active" if params[:f] == "closed")} - = link_to project_milestones_path(@project, f: "closed") do - Closed %li{class: ("active" if params[:f] == "all")} = link_to project_milestones_path(@project, f: "all") do All - %ul.well-list + %ul.unstyled = render @milestones - if @milestones.present? - %li.bottom= paginate @milestones, theme: "gitlab" + %li.bottom= paginate @milestones, remote: true, theme: "gitlab" - else %li %h3.nothing_here_message Nothing to show here diff --git a/app/views/milestones/show.html.haml b/app/views/milestones/show.html.haml index e1808a20..b8bc788c 100644 --- a/app/views/milestones/show.html.haml +++ b/app/views/milestones/show.html.haml @@ -1,83 +1,74 @@ -.row - .span6 - %h3.page_title - Milestone ##{@milestone.id} - %small - = @milestone.expires_at - .back_link - = link_to project_milestones_path(@project) do - ← To milestones list - .span6 - .pull-right - - unless @milestone.closed? - = link_to new_project_issue_path(@project, issue: { milestone_id: @milestone.id }), class: "btn btn-small grouped", title: "New Issue" do - %i.icon-plus - New Issue - = link_to 'Browse Issues', project_issues_path(@milestone.project, milestone_id: @milestone.id), class: "btn edit-milestone-link small grouped" - - if can?(current_user, :admin_milestone, @project) - = link_to edit_project_milestone_path(@project, @milestone), class: "btn btn-small grouped" do - %i.icon-edit - Edit +%h3.page_title + Milestone ##{@milestone.id} + %small + = @milestone.expires_at + %span.right + = link_to new_project_issue_path(@project, issue: { milestone_id: @milestone.id }), class: "btn small grouped", title: "New Issue" do + %i.icon-plus + New Issue + = link_to 'Browse Issues', project_issues_path(@milestone.project, milestone_id: @milestone.id), class: "btn edit-milestone-link small grouped" + - if can?(current_user, :admin_milestone, @project) + = link_to edit_project_milestone_path(@project, @milestone), class: "btn small grouped" do + %i.icon-edit + Edit +.back_link + = link_to project_milestones_path(@project) do + ← To milestones list -- if @milestone.can_be_closed? - %hr - %p - %span All issues for this milestone are closed. You may close milestone now. - = link_to 'Close Milestone', project_milestone_path(@project, @milestone, milestone: {state_event: :close }), method: :put, class: "btn btn-small btn-remove" - -.ui-box.ui-box-show - .ui-box-head - %h4.box-title - - if @milestone.closed? - .error.status_info Closed - - elsif @milestone.expired? - .error.status_info Expired - +.main_box + .top_box_content + %h5 + - if @milestone.closed + .alert-message.error.status_info Closed + - else + .alert-message.success.status_info Open = gfm escape_once(@milestone.title) + %small.right= @milestone.expires_at - .ui-box-body - %p + .middle_box_content + %h5 Progress: - #{@milestone.closed_items_count} closed - – - #{@milestone.open_items_count} open - %span.pull-right= @milestone.expires_at + %small + #{@milestone.closed_items_count} closed + – + #{@milestone.open_items_count} open .progress.progress-info .bar{style: "width: #{@milestone.percent_complete}%;"} - if @milestone.description.present? - .ui-box-bottom + .bottom_box_content = preserve do = markdown @milestone.description - .row .span6 - .ui-box.milestone-issue-filter - .title - %ul.nav.nav-pills - %li.active= link_to('Open Issues', '#') - %li=link_to('All Issues', '#') - %ul.well-list - - @issues.each do |issue| - %li{data: {closed: issue.closed?}} + %table.milestone-issue-filter + %thead + %th + %ul.nav.nav-pills + %li.active= link_to('Open Issues', '#') + %li=link_to('All Issues', '#') + - @issues.each do |issue| + %tr{data: {closed: issue.closed}} + %td = link_to [@project, issue] do %span.badge.badge-info ##{issue.id} – = link_to_gfm truncate(issue.title, length: 60), [@project, issue] .span6 - .ui-box.milestone-merge-requests-filter - .title - %ul.nav.nav-pills - %li.active= link_to('Open Merge Requests', '#') - %li=link_to('All Merge Requests', '#') - %ul.well-list - - @merge_requests.each do |merge_request| - %li{data: {closed: merge_request.closed? || merge_request.merged?}} + %table.milestone-merge-requests-filter + %thead + %th + %ul.nav.nav-pills + %li.active= link_to('Open Merge Requests', '#') + %li=link_to('All Merge Requests', '#') + - @merge_requests.each do |merge_request| + %tr{data: {closed: merge_request.closed}} + %td = link_to [@project, merge_request] do %span.badge.badge-info ##{merge_request.id} – @@ -87,7 +78,7 @@ %h6 Participants: %div - @users.each do |user| - = link_to user, class: 'float-link' do + = link_to tm_path(user.tm_of(@project)), class: 'float-link' do = user.avatar_image = user.name diff --git a/app/views/notes/_common_form.html.haml b/app/views/notes/_common_form.html.haml new file mode 100644 index 00000000..0725082d --- /dev/null +++ b/app/views/notes/_common_form.html.haml @@ -0,0 +1,38 @@ +.note-form-holder + = form_for [@project, @note], remote: "true", multipart: true do |f| + %h3.page_title Leave a comment + -if @note.errors.any? + .alert-message.block-message.error + - @note.errors.full_messages.each do |msg| + %div= msg + + = f.hidden_field :noteable_id + = f.hidden_field :noteable_type + = f.text_area :note, size: 255, class: 'note-text js-gfm-input' + #preview-note.preview_note.hide + .hint + .right Comments are parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}. + .clearfix + + .row.note_advanced_opts + .span3 + = f.submit 'Add Comment', class: "btn success submit_note grouped", id: "submit_note" + = link_to 'Preview', preview_project_notes_path(@project), class: 'btn grouped', id: 'preview-link' + .span4.notify_opts + %h6.left Notify via email: + = label_tag :notify do + = check_box_tag :notify, 1, @note.noteable_type != "Commit" + %span Project team + + - if @note.notify_only_author?(current_user) + = label_tag :notify_author do + = check_box_tag :notify_author, 1 , @note.noteable_type == "Commit" + %span Commit author + .span5.attachments + %h6.left Attachment: + %span.file_name File name... + + .input.input_file + %a.file_upload.btn.small Upload File + = f.file_field :attachment, class: "input-file" + %span.hint Any file less than 10 MB diff --git a/app/views/notes/_create_common_note.js.haml b/app/views/notes/_create_common_note.js.haml new file mode 100644 index 00000000..217c9080 --- /dev/null +++ b/app/views/notes/_create_common_note.js.haml @@ -0,0 +1,13 @@ +- if note.valid? + :plain + $(".note-form-holder .error").remove(); + $('.note-form-holder textarea').val(""); + $('.note-form-holder #preview-link').text('Preview'); + $('.note-form-holder #preview-note').hide(); + $('.note-form-holder').show(); + NoteList.appendNewNote(#{note.id}, "#{escape_javascript(render "notes/note", note: note)}"); + +- else + :plain + $(".note-form-holder").replaceWith("#{escape_javascript(render 'notes/common_form')}"); + GitLab.GfmAutoComplete.setup(); diff --git a/app/views/notes/_create_per_line_note.js.haml b/app/views/notes/_create_per_line_note.js.haml new file mode 100644 index 00000000..180960e3 --- /dev/null +++ b/app/views/notes/_create_per_line_note.js.haml @@ -0,0 +1,19 @@ +- if note.valid? + :plain + // hide and reset the form + $(".per_line_form").hide(); + $('.line-note-form-holder textarea').val(""); + + // find the reply button for this line + // (might not be there if this is the first note) + var trRpl = $("a.line_note_reply_link[data-line-code='#{note.line_code}']").closest("tr"); + if (trRpl.size() == 0) { + // find the commented line ... + var trEl = $(".#{note.line_code}").parent(); + // ... and insert the note and the reply button after it + trEl.after("#{escape_javascript(render "notes/per_line_reply_button", line_code: note.line_code)}"); + trEl.after("#{escape_javascript(render "notes/per_line_note", note: note)}"); + } else { + // instert new note before reply button + trRpl.before("#{escape_javascript(render "notes/per_line_note", note: note)}"); + } diff --git a/app/views/notes/_diff_note_link.html.haml b/app/views/notes/_diff_note_link.html.haml deleted file mode 100644 index 377c926a..00000000 --- a/app/views/notes/_diff_note_link.html.haml +++ /dev/null @@ -1,10 +0,0 @@ -- note = @project.notes.new(@comments_target.merge({ line_code: line_code })) -= link_to "", - "javascript:;", - class: "add-diff-note js-add-diff-note-button", - data: { noteable_type: note.noteable_type, - noteable_id: note.noteable_id, - commit_id: note.commit_id, - line_code: note.line_code, - discussion_id: note.discussion_id }, - title: "Add a comment to this line" diff --git a/app/views/notes/_diff_notes_with_reply.html.haml b/app/views/notes/_diff_notes_with_reply.html.haml deleted file mode 100644 index 0808f86b..00000000 --- a/app/views/notes/_diff_notes_with_reply.html.haml +++ /dev/null @@ -1,11 +0,0 @@ -- note = notes.first # example note -%tr.notes_holder - %td.notes_line{ colspan: 2 } - %span.btn.disabled - %i.icon-comment - = notes.count - %td.notes_content - %ul.notes{ rel: note.discussion_id } - = render notes - - = render "notes/discussion_reply_button", note: note diff --git a/app/views/notes/_discussion.html.haml b/app/views/notes/_discussion.html.haml deleted file mode 100644 index 24cb4228..00000000 --- a/app/views/notes/_discussion.html.haml +++ /dev/null @@ -1,63 +0,0 @@ -- note = discussion_notes.first -.discussion.js-details-container.js-toggler-container.open{ class: note.discussion_id } - .discussion-header - .discussion-actions - = link_to "javascript:;", class: "js-details-target turn-on js-toggler-target" do - %i.icon-eye-close - Hide discussion - = link_to "javascript:;", class: "js-details-target turn-off js-toggler-target" do - %i.icon-eye-open - Show discussion - = image_tag gravatar_icon(note.author.email), class: "avatar s32" - %div - = link_to_member(@project, note.author, avatar: false) - - if note.for_merge_request? - - if note.diff - started a discussion on this merge request diff - = link_to_merge_request_diff_line_note(note) - - else - started - %strong - %i.icon-remove - outdated - discussion on this merge request diff - - elsif note.for_commit? - started a discussion on commit - #{link_to note.noteable.short_id, project_commit_path(@project, note.noteable)} - = link_to_commit_diff_line_note(note) if note.for_diff_line? - - else - %cite.cgray started a discussion - %div - - last_note = discussion_notes.last - last updated by - = link_to_member(@project, last_note.author, avatar: false) - %span.discussion-last-update - = time_ago_in_words(last_note.updated_at) - ago - .discussion-body - - if note.for_diff_line? - - if note.diff - .content - .file= render "notes/discussion_diff", discussion_notes: discussion_notes, note: note - - else - = link_to 'show outdated discussion', '#', class: 'js-show-outdated-discussion' - %div.hide.outdated-discussion - .content - .notes{ rel: discussion_notes.first.discussion_id } - = render discussion_notes - - - - else - .content - .notes{ rel: discussion_notes.first.discussion_id } - = render discussion_notes - = render "notes/discussion_reply_button", note: discussion_notes.first - - -# will be shown when the other one is hidden - .discussion-hidden.content.hide - .note - %em Hidden discussion. - = link_to "javascript:;", class: "js-details-target js-toggler-target" do - %i.icon-eye-open - Show - diff --git a/app/views/notes/_discussion_diff.html.haml b/app/views/notes/_discussion_diff.html.haml deleted file mode 100644 index 20bdb3f3..00000000 --- a/app/views/notes/_discussion_diff.html.haml +++ /dev/null @@ -1,24 +0,0 @@ -- diff = note.diff -.header - - if diff.deleted_file - %span= diff.old_path - - else - %span= diff.new_path - - if diff.a_mode && diff.b_mode && diff.a_mode != diff.b_mode - %span.file-mode= "#{diff.a_mode} → #{diff.b_mode}" - %br/ -.content - %table - - each_diff_line_near(diff, note.diff_file_index, note.line_code) do |line, type, line_code, line_new, line_old| - %tr.line_holder{ id: line_code } - - if type == "match" - %td.old_line= "..." - %td.new_line= "..." - %td.line_content.matched= line - - else - %td.old_line= raw(type == "new" ? " " : line_old) - %td.new_line= raw(type == "old" ? " " : line_new) - %td.line_content{class: "noteable_line #{type} #{line_code}", "line_code" => line_code}= raw "#{line}  " - - - if line_code == note.line_code - = render "notes/diff_notes_with_reply", notes: discussion_notes diff --git a/app/views/notes/_discussion_reply_button.html.haml b/app/views/notes/_discussion_reply_button.html.haml deleted file mode 100644 index d1c5ccc2..00000000 --- a/app/views/notes/_discussion_reply_button.html.haml +++ /dev/null @@ -1,10 +0,0 @@ -= link_to "javascript:;", - class: "btn reply-btn js-discussion-reply-button", - data: { noteable_type: note.noteable_type, - noteable_id: note.noteable_id, - commit_id: note.commit_id, - line_code: note.line_code, - discussion_id: note.discussion_id }, - title: "Add a reply" do - %i.icon-comment - Reply diff --git a/app/views/notes/_form.html.haml b/app/views/notes/_form.html.haml deleted file mode 100644 index 7add2921..00000000 --- a/app/views/notes/_form.html.haml +++ /dev/null @@ -1,37 +0,0 @@ -= form_for [@project, @note], remote: true, html: { multipart: true, id: nil, class: "new_note js-new-note-form common-note-form" } do |f| - - = note_target_fields - = f.hidden_field :commit_id - = f.hidden_field :line_code - = f.hidden_field :noteable_id - = f.hidden_field :noteable_type - - .note_text_and_preview.js-toggler-container - %a.js-note-preview-button.js-toggler-target.turn-off{ href: "javascript:;", title: "Preview", data: {url: preview_project_notes_path(@project)} } - %i.icon-eye-open - %a.js-note-edit-button.js-toggler-target.turn-off{ href: "javascript:;", title: "Edit" } - %i.icon-edit - - = f.text_area :note, size: 255, class: 'note_text js-note-text js-gfm-input turn-on' - .note_preview.js-note-preview.turn-off - - .hint - .pull-right Comments are parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}. - .clearfix - - .note-form-actions - .buttons - = f.submit 'Add Comment', class: "btn comment-btn grouped js-comment-button" - = yield(:note_actions) - - %a.btn.grouped.js-close-discussion-note-form Cancel - - .note-form-option - %a.choose-btn.btn.btn-small.js-choose-note-attachment-button - %i.icon-paper-clip - %span Choose File ... -   - %span.file_name.js-attachment-filename File name... - = f.file_field :attachment, class: "js-note-attachment-input hide" - - .clearfix diff --git a/app/views/notes/_form_errors.html.haml b/app/views/notes/_form_errors.html.haml deleted file mode 100644 index 0851536f..00000000 --- a/app/views/notes/_form_errors.html.haml +++ /dev/null @@ -1,3 +0,0 @@ -.error_message.js-errors - - note.errors.full_messages.each do |msg| - %div= msg diff --git a/app/views/notes/_note.html.haml b/app/views/notes/_note.html.haml index b355e2a0..70baa212 100644 --- a/app/views/notes/_note.html.haml +++ b/app/views/notes/_note.html.haml @@ -1,37 +1,40 @@ -%li{ id: dom_id(note), class: dom_class(note), data: { discussion: note.discussion_id } } - .note-header - .note-actions - = link_to "##{dom_id(note)}", name: dom_id(note) do - %i.icon-link - Link here -   - - if(note.author_id == current_user.id) || can?(current_user, :admin_note, @project) - = link_to project_note_path(@project, note), title: "Remove comment", method: :delete, confirm: 'Are you sure you want to remove comment?', remote: true, class: "danger js-note-delete" do - %i.icon-trash.cred - = image_tag gravatar_icon(note.author.email), class: "avatar s32" - = link_to_member(@project, note.author, avatar: false) - %span.note-last-update - = time_ago_in_words(note.updated_at) - ago +%li{id: dom_id(note), class: "note"} + = image_tag gravatar_icon(note.author.email), class: "avatar s32" + %div.note-author + %strong= note.author_name + = link_to "##{dom_id(note)}", name: dom_id(note) do + %cite.cgray + = time_ago_in_words(note.updated_at) + ago - - if note.upvote? - %span.vote.upvote.label.label-success - %i.icon-thumbs-up - \+1 - - if note.downvote? - %span.vote.downvote.label.label-error - %i.icon-thumbs-down - \-1 + - unless note_for_main_target?(note) + - if note.for_commit? + %span.cgray + on #{link_to note.noteable.short_id, project_commit_path(@project, note.noteable)} + = link_to_commit_diff_line_note(note) if note.for_diff_line? + -# only show vote if it's a note for the main target + - if note_for_main_target?(note) + - if note.upvote? + %span.vote.upvote.label.label-success + %i.icon-thumbs-up + \+1 + - if note.downvote? + %span.vote.downvote.label.label-error + %i.icon-thumbs-down + \-1 - .note-body + -# remove button + - if(note.author_id == current_user.id) || can?(current_user, :admin_note, @project) + = link_to [@project, note], confirm: 'Are you sure?', method: :delete, remote: true, class: "cred delete-note btn very_small" do + %i.icon-trash + Remove + + %div.note-title = preserve do = markdown(note.note) - - if note.attachment.url - - if note.attachment.image? - = image_tag note.attachment.url, class: 'note-image-attach' - .attachment.pull-right - = link_to note.attachment.secure_url, target: "_blank" do - %i.icon-paper-clip - = note.attachment_identifier + - if note.attachment.url + .right + %div.file + = link_to note.attachment_identifier, note.attachment.url, target: "_blank" .clear diff --git a/app/views/notes/_notes.html.haml b/app/views/notes/_notes.html.haml index 4904249a..adb5dfcb 100644 --- a/app/views/notes/_notes.html.haml +++ b/app/views/notes/_notes.html.haml @@ -1,11 +1,4 @@ -- if @discussions.present? - - @discussions.each do |discussion_notes| - - note = discussion_notes.first - - if note_for_main_target?(note) - = render discussion_notes - - else - = render 'discussion', discussion_notes: discussion_notes -- else - - @notes.each do |note| - - next unless note.author - = render 'note', note: note +- @notes.each do |note| + - next unless note.author + = render "note", note: note + diff --git a/app/views/notes/_notes_with_form.html.haml b/app/views/notes/_notes_with_form.html.haml index 2566edd8..53716c1d 100644 --- a/app/views/notes/_notes_with_form.html.haml +++ b/app/views/notes/_notes_with_form.html.haml @@ -1,11 +1,11 @@ -%ul#notes-list.notes -.js-notes-busy +%ul#notes-list +%ul#new-notes-list +.notes-status -.js-main-target-form - if can? current_user, :write_note, @project - = render "notes/form" + = render "notes/common_form" :javascript $(function(){ - NoteList.init("#{@target_id}", "#{@target_type}", "#{project_notes_path(@project)}"); + NoteList.init("#{tid}", "#{tt}", "#{project_notes_path(@project)}"); }); diff --git a/app/views/notes/_per_line_form.html.haml b/app/views/notes/_per_line_form.html.haml new file mode 100644 index 00000000..c8d79850 --- /dev/null +++ b/app/views/notes/_per_line_form.html.haml @@ -0,0 +1,39 @@ +%table{style: "display:none;"} + %tr.per_line_form + %td{colspan: 3 } + .line-note-form-holder + = form_for [@project, @note], remote: "true", multipart: true do |f| + %h3.page_title Leave a note + %div.span10 + -if @note.errors.any? + .alert-message.block-message.error + - @note.errors.full_messages.each do |msg| + %div= msg + + = f.hidden_field :noteable_id + = f.hidden_field :noteable_type + = f.hidden_field :line_code + = f.text_area :note, size: 255, class: 'line-note-text js-gfm-input' + .note_actions + .buttons + = f.submit 'Add note', class: "btn save-btn submit_note submit_inline_note", id: "submit_note" + = link_to "Cancel", "#", class: "btn hide-button" + .options + %h6.left Notify via email: + .labels + = label_tag :notify do + = check_box_tag :notify, 1, @note.noteable_type != "Commit" + %span Project team + + - if @note.notify_only_author?(current_user) + = label_tag :notify_author do + = check_box_tag :notify_author, 1 , @note.noteable_type == "Commit" + %span Commit author + +:javascript + $(function(){ + $(".per_line_form .hide-button").bind("click", function(){ + $('.per_line_form').hide(); + return false; + }); + }); diff --git a/app/views/notes/_per_line_note.html.haml b/app/views/notes/_per_line_note.html.haml new file mode 100644 index 00000000..28bcd6e0 --- /dev/null +++ b/app/views/notes/_per_line_note.html.haml @@ -0,0 +1,5 @@ +%tr.line_notes_row + %td{colspan: 3} + %ul + = render "notes/note", note: note + diff --git a/app/views/notes/_per_line_note_link.html.haml b/app/views/notes/_per_line_note_link.html.haml new file mode 100644 index 00000000..72b59a59 --- /dev/null +++ b/app/views/notes/_per_line_note_link.html.haml @@ -0,0 +1 @@ += link_to "", "#", class: "line_note_link", data: { line_code: line_code }, title: "Add note for this line" diff --git a/app/views/notes/_per_line_notes_with_reply.html.haml b/app/views/notes/_per_line_notes_with_reply.html.haml new file mode 100644 index 00000000..1bcfc41f --- /dev/null +++ b/app/views/notes/_per_line_notes_with_reply.html.haml @@ -0,0 +1,3 @@ +- notes.each do |note| + = render "notes/per_line_note", note: note += render "notes/per_line_reply_button", line_code: notes.first.line_code diff --git a/app/views/notes/_per_line_reply_button.html.haml b/app/views/notes/_per_line_reply_button.html.haml new file mode 100644 index 00000000..42c737c7 --- /dev/null +++ b/app/views/notes/_per_line_reply_button.html.haml @@ -0,0 +1,4 @@ +%tr.line_notes_row.reply + %td{colspan: 3} + %i.icon-comment + = link_to "Reply", "#", class: "line_note_reply_link", data: { line_code: line_code }, title: "Add note for this line" diff --git a/app/views/notes/_reversed_notes_with_form.html.haml b/app/views/notes/_reversed_notes_with_form.html.haml new file mode 100644 index 00000000..24d59924 --- /dev/null +++ b/app/views/notes/_reversed_notes_with_form.html.haml @@ -0,0 +1,11 @@ +- if can? current_user, :write_note, @project + = render "notes/common_form" + +%ul.reversed#new-notes-list +%ul.reversed#notes-list +.notes-status + +:javascript + $(function(){ + NoteList.init("#{tid}", "#{tt}", "#{project_notes_path(@project)}"); + }); diff --git a/app/views/notes/create.js.haml b/app/views/notes/create.js.haml index 43e79c69..03866591 100644 --- a/app/views/notes/create.js.haml +++ b/app/views/notes/create.js.haml @@ -1,18 +1,8 @@ -- if @note.valid? - var noteHtml = "#{escape_javascript(render "notes/note", note: @note)}"; - - - if note_for_main_target?(@note) - NoteList.appendNewNote(#{@note.id}, noteHtml); - - else - :plain - var firstDiscussionNoteHtml = "#{escape_javascript(render "notes/diff_notes_with_reply", notes: [@note])}"; - NoteList.appendNewDiscussionNote("#{@note.discussion_id}", - firstDiscussionNoteHtml, - noteHtml); - +- if @note.line_code + = render "create_per_line_note", note: @note - else - var errorsHtml = "#{escape_javascript(render 'notes/form_errors', note: @note)}"; - - if note_for_main_target?(@note) - NoteList.errorsOnForm(errorsHtml); - - else - NoteList.errorsOnForm(errorsHtml, "#{@note.discussion_id}"); + = render "create_common_note", note: @note + +-# Enable submit button +:plain + $("#submit_note").removeAttr("disabled"); diff --git a/app/views/notes/index.js.haml b/app/views/notes/index.js.haml index 826862b1..3814dbd4 100644 --- a/app/views/notes/index.js.haml +++ b/app/views/notes/index.js.haml @@ -1,4 +1,17 @@ - unless @notes.blank? - var notesHtml = "#{escape_javascript(render 'notes/notes')}"; - - new_note_ids = @notes.map(&:id) - NoteList.setContent(#{new_note_ids}, notesHtml); + - if loading_more_notes? + :plain + NoteList.appendMoreNotes(#{@notes.last.id}, "#{escape_javascript(render 'notes/notes')}"); + + - elsif loading_new_notes? + :plain + NoteList.replaceNewNotes("#{escape_javascript(render 'notes/notes')}"); + + - else + :plain + NoteList.setContent(#{@notes.first.id}, #{@notes.last.id}, "#{escape_javascript(render 'notes/notes')}"); + +- else + - if loading_more_notes? + :plain + NoteList.finishedLoadingMore(); diff --git a/app/views/notifications/show.html.haml b/app/views/notifications/show.html.haml deleted file mode 100644 index d8ab93b8..00000000 --- a/app/views/notifications/show.html.haml +++ /dev/null @@ -1,68 +0,0 @@ -%h3.page_title Setup your notification level - -%br - -%p.light - %strong Disabled - – You will not get any notifications via email -%p.light - %strong Participating - – You will receive only notifications from related resources(ex. from assigned issue or your commit) -%p.light - %strong Watch - – You will receive all notifications from projects in which you participate -%hr - -= form_tag profile_notifications_path, method: :put, remote: true, class: 'update-notifications' do - %ul.well-list - %li - .row - .span4 - %h5 Global - .span7 - = label_tag do - = radio_button_tag :notification_level, Notification::N_DISABLED, @notification.disabled? - %span Disabled - - = label_tag do - = radio_button_tag :notification_level, Notification::N_PARTICIPATING, @notification.participating? - %span Participating - - = label_tag do - = radio_button_tag :notification_level, Notification::N_WATCH, @notification.watch? - %span Watch - - - = link_to '#', class: 'js-toggle-visibility-link' do - %h6.btn.btn-tiny - %i.icon-chevron-down - %span Per project notifications settings - %ul.well-list.js-toggle-visibility-container.hide - - @projects.each do |project| - %li - .row - .span4 - %span - = project.name_with_namespace - .span7 - = label_tag do - = radio_button_tag :"notification_level[#{project.id}]", Notification::N_DISABLED, @notification.disabled?, disabled: true - %span Disabled - - = label_tag do - = radio_button_tag :"notification_level[#{project.id}]", Notification::N_PARTICIPATING, @notification.participating?, disabled: true - %span Participating - - = label_tag do - = radio_button_tag :"notification_level[#{project.id}]", Notification::N_WATCH, @notification.watch?, disabled: true - %span Watch - - - .form-actions - = submit_tag 'Save', class: 'btn btn-save' - %span.update-success.cgreen.hide - %i.icon-ok - Saved - %span.update-failed.cred.hide - %i.icon-remove - Failed diff --git a/app/views/notifications/update.js.haml b/app/views/notifications/update.js.haml deleted file mode 100644 index 4468004a..00000000 --- a/app/views/notifications/update.js.haml +++ /dev/null @@ -1,7 +0,0 @@ -- if @saved - :plain - $('.update-notifications .update-success').showAndHide(); -- else - :plain - $('.update-notifications .update-failed').showAndHide(); - diff --git a/app/views/notify/_note_message.html.haml b/app/views/notify/_note_message.html.haml deleted file mode 100644 index 88c4df55..00000000 --- a/app/views/notify/_note_message.html.haml +++ /dev/null @@ -1,6 +0,0 @@ -%p - %strong #{@note.author_name} - left next message: - -%cite{style: 'color: #666'} - = markdown(@note.note) diff --git a/app/views/notify/closed_issue_email.html.haml b/app/views/notify/closed_issue_email.html.haml deleted file mode 100644 index 23ccc453..00000000 --- a/app/views/notify/closed_issue_email.html.haml +++ /dev/null @@ -1,5 +0,0 @@ -%p - = "Issue was closed by #{@updated_by.name}" -%p - = "Issue ##{@issue.id}" - = link_to_gfm truncate(@issue.title, length: 45), project_issue_url(@issue.project, @issue), title: @issue.title diff --git a/app/views/notify/closed_issue_email.text.haml b/app/views/notify/closed_issue_email.text.haml deleted file mode 100644 index 0cca3215..00000000 --- a/app/views/notify/closed_issue_email.text.haml +++ /dev/null @@ -1,3 +0,0 @@ -= "Issue was closed by #{@updated_by.name}" - -Issue ##{@issue.id}: #{project_issue_url(@issue.project, @issue)} diff --git a/app/views/notify/closed_merge_request_email.html.haml b/app/views/notify/closed_merge_request_email.html.haml deleted file mode 100644 index 0c6c79e0..00000000 --- a/app/views/notify/closed_merge_request_email.html.haml +++ /dev/null @@ -1,9 +0,0 @@ -%p - = "Merge Request #{@merge_request.id} was closed by #{@updated_by.name}" -%p - = link_to_gfm truncate(@merge_request.title, length: 40), project_merge_request_url(@merge_request.project, @merge_request) -%p - Branches: #{@merge_request.source_branch} → #{@merge_request.target_branch} -%p - Assignee: #{@merge_request.author_name} → #{@merge_request.assignee_name} - diff --git a/app/views/notify/closed_merge_request_email.text.haml b/app/views/notify/closed_merge_request_email.text.haml deleted file mode 100644 index ee4648e3..00000000 --- a/app/views/notify/closed_merge_request_email.text.haml +++ /dev/null @@ -1,8 +0,0 @@ -= "Merge Request #{@merge_request.id} was closed by #{@updated_by.name}" - -Merge Request url: #{project_merge_request_url(@merge_request.project, @merge_request)} - -Branches: #{@merge_request.source_branch} - #{@merge_request.target_branch} - -Author: #{@merge_request.author_name} -Assignee: #{@merge_request.assignee_name} diff --git a/app/views/notify/issue_status_changed_email.html.haml b/app/views/notify/issue_status_changed_email.html.haml index 4cdb7099..59130f79 100644 --- a/app/views/notify/issue_status_changed_email.html.haml +++ b/app/views/notify/issue_status_changed_email.html.haml @@ -1,5 +1,16 @@ -%p - = "Issue was #{@issue_status} by #{@updated_by.name}" -%p - = "Issue ##{@issue.id}" - = link_to_gfm truncate(@issue.title, length: 45), project_issue_url(@issue.project, @issue), title: @issue.title +%td.content{align: "left", style: "font-family: Helvetica, Arial, sans-serif; padding: 20px 0 0;", valign: "top", width: "600"} + %table{border: "0", cellpadding: "0", cellspacing: "0", style: "color: #717171; font: normal 11px Helvetica, Arial, sans-serif; margin: 0; padding: 0;", width: "600"} + %tr + %td{style: "font-size: 1px; line-height: 1px;", width: "21"} + %td{align: "left", style: "padding: 20px 0 0;"} + %h2{style: "color:#646464; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; "} + = "Issue was #{@issue_status} by #{@updated_by.name}" + %td{style: "font-size: 1px; line-height: 1px;", width: "21"} + %tr + %td{style: "font-size: 1px; line-height: 1px;", width: "21"} + %td{align: "left", style: "padding: 20px 0 0;"} + %h2{style: "color:#646464 !important; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; "} + = "Issue ##{@issue.id}" + = link_to_gfm truncate(@issue.title, length: 45), project_issue_url(@issue.project, @issue), title: @issue.title + %br + diff --git a/app/views/notify/issue_status_changed_email.text.erb b/app/views/notify/issue_status_changed_email.text.erb deleted file mode 100644 index bbca3474..00000000 --- a/app/views/notify/issue_status_changed_email.text.erb +++ /dev/null @@ -1,4 +0,0 @@ -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/merged_merge_request_email.html.haml b/app/views/notify/merged_merge_request_email.html.haml deleted file mode 100644 index 2b8cc030..00000000 --- a/app/views/notify/merged_merge_request_email.html.haml +++ /dev/null @@ -1,9 +0,0 @@ -%p - = "Merge Request #{@merge_request.id} was merged" -%p - = link_to_gfm truncate(@merge_request.title, length: 40), project_merge_request_url(@merge_request.project, @merge_request) -%p - Branches: #{@merge_request.source_branch} → #{@merge_request.target_branch} -%p - Assignee: #{@merge_request.author_name} → #{@merge_request.assignee_name} - diff --git a/app/views/notify/merged_merge_request_email.text.haml b/app/views/notify/merged_merge_request_email.text.haml deleted file mode 100644 index 91c23360..00000000 --- a/app/views/notify/merged_merge_request_email.text.haml +++ /dev/null @@ -1,8 +0,0 @@ -= "Merge Request #{@merge_request.id} was merged" - -Merge Request Url: #{project_merge_request_url(@merge_request.project, @merge_request)} - -Branches: #{@merge_request.source_branch} - #{@merge_request.target_branch} - -Author: #{@merge_request.author_name} -Assignee: #{@merge_request.assignee_name} diff --git a/app/views/notify/new_issue_email.html.haml b/app/views/notify/new_issue_email.html.haml index f11c8506..654d6cd1 100644 --- a/app/views/notify/new_issue_email.html.haml +++ b/app/views/notify/new_issue_email.html.haml @@ -1,9 +1,15 @@ -%p - New Issue was created. -%p - = "Issue ##{@issue.id}" - = link_to_gfm truncate(@issue.title, length: 45), project_issue_url(@issue.project, @issue), title: @issue.title -%p - Author: #{@issue.author_name} -%p - Assignee: #{@issue.assignee_name} +%td.content{align: "left", style: "font-family: Helvetica, Arial, sans-serif; padding: 20px 0 0;", valign: "top", width: "600"} + %table{border: "0", cellpadding: "0", cellspacing: "0", style: "color: #717171; font: normal 11px Helvetica, Arial, sans-serif; margin: 0; padding: 0;", width: "600"} + %tr + %td{style: "font-size: 1px; line-height: 1px;", width: "21"} + %td{align: "left", style: "padding: 20px 0 0;"} + %h2{style: "color:#646464; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; "} + New Issue was created and assigned to you. + %td{style: "font-size: 1px; line-height: 1px;", width: "21"} + %tr + %td{style: "font-size: 1px; line-height: 1px;", width: "21"} + %td{align: "left", style: "padding: 20px 0 0;"} + %h2{style: "color:#646464 !important; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; "} + = "Issue ##{@issue.id}" + = link_to_gfm truncate(@issue.title, length: 45), project_issue_url(@issue.project, @issue), title: @issue.title + %br diff --git a/app/views/notify/new_issue_email.text.erb b/app/views/notify/new_issue_email.text.erb deleted file mode 100644 index 9907ca83..00000000 --- a/app/views/notify/new_issue_email.text.erb +++ /dev/null @@ -1,5 +0,0 @@ -New Issue was created. - -Issue <%= @issue.id %>: <%= url_for(project_issue_url(@issue.project, @issue)) %> -Author: <%= @issue.author_name %> -Asignee: <%= @issue.assignee_name %> diff --git a/app/views/notify/new_merge_request_email.html.haml b/app/views/notify/new_merge_request_email.html.haml index 0f1cfff5..151aac45 100644 --- a/app/views/notify/new_merge_request_email.html.haml +++ b/app/views/notify/new_merge_request_email.html.haml @@ -1,9 +1,18 @@ -%p - = "New Merge Request !#{@merge_request.id}" -%p - = link_to_gfm truncate(@merge_request.title, length: 40), project_merge_request_url(@merge_request.project, @merge_request) -%p - Branches: #{@merge_request.source_branch} → #{@merge_request.target_branch} -%p - Assignee: #{@merge_request.author_name} → #{@merge_request.assignee_name} +%td.content{align: "left", style: "font-family: Helvetica, Arial, sans-serif; padding: 20px 0 0;", valign: "top", width: "600"} + %table{border: "0", cellpadding: "0", cellspacing: "0", style: "color: #717171; font: normal 11px Helvetica, Arial, sans-serif; margin: 0; padding: 0;", width: "600"} + %tr + %td{style: "font-size: 1px; line-height: 1px;", width: "21"} + %td{align: "left", style: "padding: 20px 0 0;"} + %h2{style: "color:#646464; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; "} + = "New Merge Request !#{@merge_request.id}" + = link_to_gfm truncate(@merge_request.title, length: 16), project_merge_request_url(@merge_request.project, @merge_request) + %td{style: "font-size: 1px; line-height: 1px;", width: "21"} + %tr + %td{style: "font-size: 1px; line-height: 1px;", width: "21"} + %td{style: "padding: 15px 0 15px;", valign: "top"} + %p{style: "color:#767676; font-weight: normal; margin: 0; padding: 0; line-height: 20px; font-size: 12px;font-family: Helvetica, Arial, sans-serif; "} + Branches: #{@merge_request.source_branch} → #{@merge_request.target_branch} + %p{style: "color:#767676; font-weight: normal; margin: 0; padding: 0; line-height: 20px; font-size: 12px;font-family: Helvetica, Arial, sans-serif; "} + Asignee: #{@merge_request.author_name} → #{@merge_request.assignee_name} + %td diff --git a/app/views/notify/new_merge_request_email.text.erb b/app/views/notify/new_merge_request_email.text.erb deleted file mode 100644 index 3393d838..00000000 --- a/app/views/notify/new_merge_request_email.text.erb +++ /dev/null @@ -1,9 +0,0 @@ -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_ssh_key_email.html.haml b/app/views/notify/new_ssh_key_email.html.haml deleted file mode 100644 index 57f4297e..00000000 --- a/app/views/notify/new_ssh_key_email.html.haml +++ /dev/null @@ -1,10 +0,0 @@ -%p - Hi #{@user.name}! -%p - A new public key was added to your account: -%p - title: - %code= @key.title -%p - If this key was added in error, you can remove here: - = link_to "SSH Keys", keys_url diff --git a/app/views/notify/new_ssh_key_email.text.erb b/app/views/notify/new_ssh_key_email.text.erb deleted file mode 100644 index 71974eab..00000000 --- a/app/views/notify/new_ssh_key_email.text.erb +++ /dev/null @@ -1,7 +0,0 @@ -Hi <%= @user.name %>! - -A new public key was added to your account: - -title.................. <%= @key.title %> - -If this key was added in error, you can remove here: <%= keys_url %> diff --git a/app/views/notify/new_user_email.html.haml b/app/views/notify/new_user_email.html.haml index 8606dc6a..93bf7c50 100644 --- a/app/views/notify/new_user_email.html.haml +++ b/app/views/notify/new_user_email.html.haml @@ -1,16 +1,23 @@ -%p - Hi #{@user['name']}! -%p - - if Gitlab.config.gitlab.signup_enabled - Account has been created successfully. - - else - Administrator created account for you. Now you are a member of company GitLab application. -%p - login.......................................... - %code= @user['email'] -%p - - unless Gitlab.config.gitlab.signup_enabled - password.................................. - %code= @password -%p - = link_to "Click here to login", root_url +%td.content{align: "left", style: "font-family: Helvetica, Arial, sans-serif; padding: 20px 0 0;", valign: "top", width: "600"} + %table{border: "0", cellpadding: "0", cellspacing: "0", style: "color: #717171; font: normal 11px Helvetica, Arial, sans-serif; margin: 0; padding: 0;", width: "600"} + %tr + %td{style: "font-size: 1px; line-height: 1px;", width: "21"} + %td{align: "left", style: "padding: 20px 0 0;"} + %h2{style: "color:#646464; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; "} + Hi #{@user['name']}! + %p{style: "color:#767676; font-weight: normal; margin: 0; padding: 0; line-height: 20px; font-size: 12px;font-family: Helvetica, Arial, sans-serif; "} + Administrator created account for you. Now you are a member of company GitLab application. + %td{style: "font-size: 1px; line-height: 1px;", width: "21"} + %tr + %td{style: "font-size: 1px; line-height: 1px;", width: "21"} + %td{style: "padding: 15px 0 15px;", valign: "top"} + %p{style: "color:#767676; font-weight: normal; margin: 0; padding: 0; line-height: 28px; font-size: 16px;font-family: Helvetica, Arial, sans-serif; "} + login.......................................... + %code= @user['email'] + %p{style: "color:#767676; font-weight: normal; margin: 0; padding: 0; line-height: 28px; font-size: 16px;font-family: Helvetica, Arial, sans-serif; "} + password.................................. + %code= @password + %p{style: "color:#767676; font-weight: normal; margin: 0; padding: 0; line-height: 28px; font-size: 12px;font-family: Helvetica, Arial, sans-serif; "} + = link_to "Click here to login", root_url + %td{style: "font-size: 1px; line-height: 1px;", width: "21"} + diff --git a/app/views/notify/new_user_email.text.erb b/app/views/notify/new_user_email.text.erb deleted file mode 100644 index 94072d7f..00000000 --- a/app/views/notify/new_user_email.text.erb +++ /dev/null @@ -1,10 +0,0 @@ -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.html.haml b/app/views/notify/note_commit_email.html.haml index 620b258f..e87f9c12 100644 --- a/app/views/notify/note_commit_email.html.haml +++ b/app/views/notify/note_commit_email.html.haml @@ -1,5 +1,23 @@ -%p - = "New comment for Commit #{@commit.short_id}" - = link_to_gfm truncate(@commit.title, length: 16), project_commit_url(@note.project, id: @commit.id, anchor: "note_#{@note.id}") -= render 'note_message' +%td.content{align: "left", style: "font-family: Helvetica, Arial, sans-serif; padding: 20px 0 0;", valign: "top", width: "600"} + %table{border: "0", cellpadding: "0", cellspacing: "0", style: "color: #717171; font: normal 11px Helvetica, Arial, sans-serif; margin: 0; padding: 0;", width: "600"} + %tr + %td{style: "font-size: 1px; line-height: 1px;", width: "21"} + %td{align: "left", style: "padding: 20px 0 0;"} + %h2{style: "color:#646464; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; "} + = "New comment for Commit #{@commit.short_id}" + = link_to_gfm truncate(@commit.title, length: 16), project_commit_url(@note.project, id: @commit.id, anchor: "note_#{@note.id}") + %td{style: "font-size: 1px; line-height: 1px;", width: "21"} + %tr + %td{style: "font-size: 1px; line-height: 1px;", width: "21"} + %td{style: "padding: 15px 0 15px;", valign: "top"} + %p{style: "color:#767676; font-weight: normal; margin: 0; padding: 0; line-height: 20px; font-size: 12px;font-family: Helvetica, Arial, sans-serif; "} + %a{href: "#", style: "color: #0eb6ce; text-decoration: none;"} #{@note.author_name} + left next message: + %br + %table{border: "0", cellpadding: "0", cellspacing: "0", width: "558"} + %tr + %td{valign: "top"} + %div{ style: "background:#f5f5f5; padding:20px;border:1px solid #ddd" } + = markdown(@note.note) + %td{style: "font-size: 1px; line-height: 1px;", width: "21"} diff --git a/app/views/notify/note_commit_email.text.erb b/app/views/notify/note_commit_email.text.erb deleted file mode 100644 index aab8e5cf..00000000 --- a/app/views/notify/note_commit_email.text.erb +++ /dev/null @@ -1,9 +0,0 @@ -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.html.haml b/app/views/notify/note_issue_email.html.haml index ca133126..832f5df4 100644 --- a/app/views/notify/note_issue_email.html.haml +++ b/app/views/notify/note_issue_email.html.haml @@ -1,4 +1,23 @@ -%p - = "New comment for Issue ##{@issue.id}" - = link_to_gfm truncate(@issue.title, length: 35), project_issue_url(@issue.project, @issue, anchor: "note_#{@note.id}") -= render 'note_message' +%td.content{align: "left", style: "font-family: Helvetica, Arial, sans-serif; padding: 20px 0 0;", valign: "top", width: "600"} + %table{border: "0", cellpadding: "0", cellspacing: "0", style: "color: #717171; font: normal 11px Helvetica, Arial, sans-serif; margin: 0; padding: 0;", width: "600"} + %tr + %td{style: "font-size: 1px; line-height: 1px;", width: "21"} + %td{align: "left", style: "padding: 20px 0 0;"} + %h2{style: "color:#646464 !important; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; "} + = "New comment for Issue ##{@issue.id}" + = link_to_gfm truncate(@issue.title, length: 35), project_issue_url(@issue.project, @issue, anchor: "note_#{@note.id}") + %td{style: "font-size: 1px; line-height: 1px;", width: "21"} + %tr + %td{style: "font-size: 1px; line-height: 1px;", width: "21"} + %td{style: "padding: 15px 0 15px;", valign: "top"} + %p{style: "color:#767676; font-weight: normal; margin: 0; padding: 0; line-height: 20px; font-size: 12px;font-family: Helvetica, Arial, sans-serif; "} + %a{href: "#", style: "color: #0eb6ce; text-decoration: none;"} #{@note.author_name} + left next message: + %br + %table{border: "0", cellpadding: "0", cellspacing: "0", width: "558"} + %tr + %td{valign: "top"} + %div{ style: "background:#f5f5f5; padding:20px;border:1px solid #ddd" } + = markdown(@note.note) + %td{style: "font-size: 1px; line-height: 1px;", width: "21"} + diff --git a/app/views/notify/note_issue_email.text.erb b/app/views/notify/note_issue_email.text.erb deleted file mode 100644 index a476b286..00000000 --- a/app/views/notify/note_issue_email.text.erb +++ /dev/null @@ -1,9 +0,0 @@ -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.html.haml b/app/views/notify/note_merge_request_email.html.haml index 4f97867e..764cd094 100644 --- a/app/views/notify/note_merge_request_email.html.haml +++ b/app/views/notify/note_merge_request_email.html.haml @@ -1,8 +1,23 @@ -%p - - if @note.for_diff_line? - = link_to "New comment on diff", diffs_project_merge_request_url(@merge_request.project, @merge_request, anchor: "note_#{@note.id}") - - else - = link_to "New comment", project_merge_request_url(@merge_request.project, @merge_request, anchor: "note_#{@note.id}") - for Merge Request ##{@merge_request.id} - %cite "#{truncate(@merge_request.title, length: 20)}" -= render 'note_message' +%td.content{align: "left", style: "font-family: Helvetica, Arial, sans-serif; padding: 20px 0 0;", valign: "top", width: "600"} + %table{border: "0", cellpadding: "0", cellspacing: "0", style: "color: #717171; font: normal 11px Helvetica, Arial, sans-serif; margin: 0; padding: 0;", width: "600"} + %tr + %td{style: "font-size: 1px; line-height: 1px;", width: "21"} + %td{align: "left", style: "padding: 20px 0 0;"} + %h2{style: "color:#646464; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; "} + = "New comment for Merge Request !#{@merge_request.id}" + = link_to_gfm truncate(@merge_request.title, length: 16), project_merge_request_url(@merge_request.project, @merge_request, anchor: "note_#{@note.id}") + %td{style: "font-size: 1px; line-height: 1px;", width: "21"} + %tr + %td{style: "font-size: 1px; line-height: 1px;", width: "21"} + %td{style: "padding: 15px 0 15px;", valign: "top"} + %p{style: "color:#767676; font-weight: normal; margin: 0; padding: 0; line-height: 20px; font-size: 12px;font-family: Helvetica, Arial, sans-serif; "} + %a{href: "#", style: "color: #0eb6ce; text-decoration: none;"} #{@note.author_name} + left next message: + %br + %table{border: "0", cellpadding: "0", cellspacing: "0", width: "558"} + %tr + %td{valign: "top"} + %div{ style: "background:#f5f5f5; padding:20px;border:1px solid #ddd" } + = markdown(@note.note) + %td{style: "font-size: 1px; line-height: 1px;", width: "21"} + diff --git a/app/views/notify/note_merge_request_email.text.erb b/app/views/notify/note_merge_request_email.text.erb deleted file mode 100644 index 26c73bda..00000000 --- a/app/views/notify/note_merge_request_email.text.erb +++ /dev/null @@ -1,9 +0,0 @@ -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.html.haml b/app/views/notify/note_wall_email.html.haml index 92200e83..0661c580 100644 --- a/app/views/notify/note_wall_email.html.haml +++ b/app/views/notify/note_wall_email.html.haml @@ -1,5 +1,22 @@ -%p - New message on - = link_to "Project Wall", project_wall_url(@note.project, anchor: "note_#{@note.id}") - -= render 'note_message' +%td.content{align: "left", style: "font-family: Helvetica, Arial, sans-serif; padding: 20px 0 0;", valign: "top", width: "600"} + %table{border: "0", cellpadding: "0", cellspacing: "0", style: "color: #717171; font: normal 11px Helvetica, Arial, sans-serif; margin: 0; padding: 0;", width: "600"} + %tr + %td{style: "font-size: 1px; line-height: 1px;", width: "21"} + %td{align: "left", style: "padding: 20px 0 0;"} + %h2{style: "color:#646464; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; "} + New message on + = link_to "Project Wall", wall_project_url(@note.project, anchor: "note_#{@note.id}") + %td{style: "font-size: 1px; line-height: 1px;", width: "21"} + %tr + %td{style: "font-size: 1px; line-height: 1px;", width: "21"} + %td{style: "padding: 15px 0 15px;", valign: "top"} + %p{style: "color:#767676; font-weight: normal; margin: 0; padding: 0; line-height: 20px; font-size: 12px;font-family: Helvetica, Arial, sans-serif; "} + %a{href: "#", style: "color: #0eb6ce; text-decoration: none;"} #{@note.author_name} + left next message: + %br + %table{border: "0", cellpadding: "0", cellspacing: "0", width: "558"} + %tr + %td{valign: "top"} + %div{ style: "background:#f5f5f5; padding:20px;border:1px solid #ddd" } + = markdown(@note.note) + %td{style: "font-size: 1px; line-height: 1px;", width: "21"} diff --git a/app/views/notify/note_wall_email.text.erb b/app/views/notify/note_wall_email.text.erb deleted file mode 100644 index 97910d5e..00000000 --- a/app/views/notify/note_wall_email.text.erb +++ /dev/null @@ -1,9 +0,0 @@ -New message on the project wall <%= @note.project %> - -<%= url_for(project_wall_url(@note.project, anchor: "note_#{@note.id}")) %> - - -<%= @note.author_name %> - -<%= @note.note %> - diff --git a/app/views/notify/note_wiki_email.html.haml b/app/views/notify/note_wiki_email.html.haml new file mode 100644 index 00000000..41a237fc --- /dev/null +++ b/app/views/notify/note_wiki_email.html.haml @@ -0,0 +1,23 @@ +%td.content{align: "left", style: "font-family: Helvetica, Arial, sans-serif; padding: 20px 0 0;", valign: "top", width: "600"} + %table{border: "0", cellpadding: "0", cellspacing: "0", style: "color: #717171; font: normal 11px Helvetica, Arial, sans-serif; margin: 0; padding: 0;", width: "600"} + %tr + %td{style: "font-size: 1px; line-height: 1px;", width: "21"} + %td{align: "left", style: "padding: 20px 0 0;"} + %h2{style: "color:#646464 !important; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; "} + New comment for Wiki page + = link_to_gfm @wiki.title, project_wiki_url(@wiki.project, @wiki, anchor: "note_#{@note.id}") + %td{style: "font-size: 1px; line-height: 1px;", width: "21"} + %tr + %td{style: "font-size: 1px; line-height: 1px;", width: "21"} + %td{style: "padding: 15px 0 15px;", valign: "top"} + %p{style: "color:#767676; font-weight: normal; margin: 0; padding: 0; line-height: 20px; font-size: 12px;font-family: Helvetica, Arial, sans-serif; "} + %a{href: "#", style: "color: #0eb6ce; text-decoration: none;"} #{@note.author_name} + commented on Wiki page: + %br + %table{border: "0", cellpadding: "0", cellspacing: "0", width: "558"} + %tr + %td{valign: "top"} + %div{ style: "background:#f5f5f5; padding:20px;border:1px solid #ddd" } + = markdown(@note.note) + %td{style: "font-size: 1px; line-height: 1px;", width: "21"} + diff --git a/app/views/notify/project_access_granted_email.html.haml b/app/views/notify/project_access_granted_email.html.haml index b4b44eaf..72b3f065 100644 --- a/app/views/notify/project_access_granted_email.html.haml +++ b/app/views/notify/project_access_granted_email.html.haml @@ -1,5 +1,14 @@ -%p - = "You have been granted #{@users_project.project_access_human} access to project" -%p - = link_to project_url(@project) do - = @project.name_with_namespace +%td.content{align: "left", style: "font-family: Helvetica, Arial, sans-serif; padding: 20px 0 0;", valign: "top", width: "600"} + %table{border: "0", cellpadding: "0", cellspacing: "0", style: "color: #717171; font: normal 11px Helvetica, Arial, sans-serif; margin: 0; padding: 0;", width: "600"} + %tr + %td{style: "font-size: 1px; line-height: 1px;", width: "21"} + %td{align: "left", style: "padding: 20px 0 0;"} + %h2{style: "color:#646464; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; "} + = "You have been granted #{@users_project.project_access_human} access to project" + %td{style: "font-size: 1px; line-height: 1px;", width: "21"} + %tr + %td{style: "font-size: 1px; line-height: 1px;", width: "21"} + %td{align: "left", style: "padding: 20px 0 0;"} + %h2{style: "color:#646464 !important; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; "} + = link_to_gfm truncate(@project.name, length: 45), project_url(@project), title: @project.name + %br diff --git a/app/views/notify/project_access_granted_email.text.erb b/app/views/notify/project_access_granted_email.text.erb deleted file mode 100644 index 077c3b8a..00000000 --- a/app/views/notify/project_access_granted_email.text.erb +++ /dev/null @@ -1,4 +0,0 @@ - -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.html.haml b/app/views/notify/project_was_moved_email.html.haml deleted file mode 100644 index 3e761c43..00000000 --- a/app/views/notify/project_was_moved_email.html.haml +++ /dev/null @@ -1,11 +0,0 @@ -%p - = "Project was moved to another location" -%p - The project is now located under - = link_to project_url(@project) do - = @project.name_with_namespace -%p - To update the remote url in your local repository run: -%p{ style: "background:#f5f5f5; padding:10px; border:1px solid #ddd" } - git remote set-url origin #{@project.ssh_url_to_repo} -%br diff --git a/app/views/notify/project_was_moved_email.text.erb b/app/views/notify/project_was_moved_email.text.erb deleted file mode 100644 index 9c41073f..00000000 --- a/app/views/notify/project_was_moved_email.text.erb +++ /dev/null @@ -1,8 +0,0 @@ -Project was moved to another location - -The project is now located under -<%= 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.html.haml b/app/views/notify/reassigned_issue_email.html.haml index 018f20bf..c7896af3 100644 --- a/app/views/notify/reassigned_issue_email.html.haml +++ b/app/views/notify/reassigned_issue_email.html.haml @@ -1,9 +1,16 @@ -%p - = "Reassigned Issue ##{@issue.id}" - = link_to_gfm truncate(@issue.title, length: 30), project_issue_url(@issue.project, @issue) -%p - Assignee changed from - %strong #{@previous_assignee.name} - to - %strong #{@issue.assignee_name} +%td.content{align: "left", style: "font-family: Helvetica, Arial, sans-serif; padding: 20px 0 0;", valign: "top", width: "600"} + %table{border: "0", cellpadding: "0", cellspacing: "0", style: "color: #717171; font: normal 11px Helvetica, Arial, sans-serif; margin: 0; padding: 0;", width: "600"} + %tr + %td{style: "font-size: 1px; line-height: 1px;", width: "21"} + %td{align: "left", style: "padding: 20px 0 0;"} + %h2{style: "color:#646464; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; "} + = "Reassigned Issue ##{@issue.id}" + = link_to_gfm truncate(@issue.title, length: 16), project_issue_url(@issue.project, @issue) + %td{style: "font-size: 1px; line-height: 1px;", width: "21"} + %tr + %td{style: "font-size: 1px; line-height: 1px;", width: "21"} + %td{style: "padding: 15px 0 15px;", valign: "top"} + %p{style: "color:#767676; font-weight: normal; margin: 0; padding: 0; line-height: 20px; font-size: 12px;font-family: Helvetica, Arial, sans-serif; "} + Assignee changed from #{@previous_assignee.name} to #{@issue.assignee_name} + %td diff --git a/app/views/notify/reassigned_issue_email.text.erb b/app/views/notify/reassigned_issue_email.text.erb deleted file mode 100644 index 49704418..00000000 --- a/app/views/notify/reassigned_issue_email.text.erb +++ /dev/null @@ -1,7 +0,0 @@ -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.html.haml b/app/views/notify/reassigned_merge_request_email.html.haml index 9039bc8b..e49b7836 100644 --- a/app/views/notify/reassigned_merge_request_email.html.haml +++ b/app/views/notify/reassigned_merge_request_email.html.haml @@ -1,8 +1,16 @@ -%p - = "Reassigned Merge Request !#{@merge_request.id}" - = link_to_gfm truncate(@merge_request.title, length: 30), project_merge_request_url(@merge_request.project, @merge_request) -%p - Assignee changed from - %strong #{@previous_assignee.name} - to - %strong #{@merge_request.assignee_name} +%td.content{align: "left", style: "font-family: Helvetica, Arial, sans-serif; padding: 20px 0 0;", valign: "top", width: "600"} + %table{border: "0", cellpadding: "0", cellspacing: "0", style: "color: #717171; font: normal 11px Helvetica, Arial, sans-serif; margin: 0; padding: 0;", width: "600"} + %tr + %td{style: "font-size: 1px; line-height: 1px;", width: "21"} + %td{align: "left", style: "padding: 20px 0 0;"} + %h2{style: "color:#646464; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; "} + = "Reassigned Merge Request !#{@merge_request.id}" + = link_to_gfm truncate(@merge_request.title, length: 16), project_merge_request_url(@merge_request.project, @merge_request) + %td{style: "font-size: 1px; line-height: 1px;", width: "21"} + %tr + %td{style: "font-size: 1px; line-height: 1px;", width: "21"} + %td{style: "padding: 15px 0 15px;", valign: "top"} + %p{style: "color:#767676; font-weight: normal; margin: 0; padding: 0; line-height: 20px; font-size: 12px;font-family: Helvetica, Arial, sans-serif; "} + Assignee changed from #{@previous_assignee.name} to #{@merge_request.assignee_name} + %td + diff --git a/app/views/notify/reassigned_merge_request_email.text.erb b/app/views/notify/reassigned_merge_request_email.text.erb deleted file mode 100644 index 1af4ab55..00000000 --- a/app/views/notify/reassigned_merge_request_email.text.erb +++ /dev/null @@ -1,7 +0,0 @@ -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 %> - diff --git a/app/views/profile/account.html.haml b/app/views/profile/account.html.haml new file mode 100644 index 00000000..1e3a8b1a --- /dev/null +++ b/app/views/profile/account.html.haml @@ -0,0 +1,54 @@ +- if Gitlab.config.omniauth_enabled? + %fieldset + %legend Social Accounts + .oauth_select_holder + %p.hint Tip: Click on icon to activate sigin with one of the following services + - User.omniauth_providers.each do |provider| + %span{class: oauth_active_class(provider) } + = link_to authbutton(provider, 32), omniauth_authorize_path(User, provider) + + +%fieldset + %legend + Private token + %span.cred.right + keep it secret! + .padded + = form_for @user, url: profile_reset_private_token_path, method: :put do |f| + .data + %p.slead + Private token used to access application resources without authentication. + %br + It can be used for atom feed or API + %p.cgray + - if current_user.private_token + = text_field_tag "token", current_user.private_token, class: "xxlarge large_text" + = f.submit 'Reset', confirm: "Are you sure?", class: "btn primary btn-build-token" + - else + %span You don`t have one yet. Click generate to fix it. + = f.submit 'Generate', class: "btn success btn-build-token" + +%fieldset + %legend Password + = form_for @user, url: profile_password_path, method: :put do |f| + .padded + %p.slead After successful password update you will be redirected to login page where you should login with new password + -if @user.errors.any? + .alert-message.block-message.error + %ul + - @user.errors.full_messages.each do |msg| + %li= msg + + .clearfix + = f.label :password + .input= f.password_field :password + .clearfix + = f.label :password_confirmation + .input= f.password_field :password_confirmation + .actions + = f.submit 'Save', class: "btn save-btn" + + + + + diff --git a/app/views/profiles/design.html.haml b/app/views/profile/design.html.haml similarity index 73% rename from app/views/profiles/design.html.haml rename to app/views/profile/design.html.haml index 77068dab..502cca42 100644 --- a/app/views/profiles/design.html.haml +++ b/app/views/profile/design.html.haml @@ -1,4 +1,4 @@ -= form_for @user, url: profile_path, remote: true, method: :put do |f| += form_for @user, url: profile_update_path, remote: true, method: :put do |f| %fieldset.application-theme %legend Application theme @@ -43,15 +43,10 @@ = label_tag do .prev = image_tag "white.png" - = f.radio_button :color_scheme_id, 1 - White + = f.radio_button :dark_scheme, false + White code preview = label_tag do .prev = image_tag "dark.png" - = f.radio_button :color_scheme_id, 2 - Dark - = label_tag do - .prev - = image_tag "solarized_dark.png" - = f.radio_button :color_scheme_id, 3 - Solarized Dark + = f.radio_button :dark_scheme, true + Dark code preview diff --git a/app/views/profiles/history.html.haml b/app/views/profile/history.html.haml similarity index 100% rename from app/views/profiles/history.html.haml rename to app/views/profile/history.html.haml diff --git a/app/views/profile/index.html.haml b/app/views/profile/index.html.haml new file mode 100644 index 00000000..84174ac5 --- /dev/null +++ b/app/views/profile/index.html.haml @@ -0,0 +1 @@ +%h1 Profile diff --git a/app/views/profiles/show.html.haml b/app/views/profile/show.html.haml similarity index 63% rename from app/views/profiles/show.html.haml rename to app/views/profile/show.html.haml index 9cab3ba5..7d9e90cf 100644 --- a/app/views/profiles/show.html.haml +++ b/app/views/profile/show.html.haml @@ -6,16 +6,11 @@ %small = @user.email - .pull-right - = link_to destroy_user_session_path, class: "logout", method: :delete do - %small - %i.icon-signout - Logout %hr -= form_for @user, url: profile_path, method: :put, html: { class: "edit_user form-horizontal" } do |f| += form_for @user, url: profile_update_path, method: :put, html: { class: "edit_user form-horizontal" } do |f| -if @user.errors.any? - %div.alert.alert-error + %div.alert-message.block-message.error %ul - @user.errors.full_messages.each do |msg| %li= msg @@ -31,6 +26,25 @@ .controls = f.text_field :email, class: "input-xlarge", required: true %span.help-block We also use email for avatar detection. + + .span5.right + %fieldset.tips + %legend Tips: + %ul + %li + %p You can change your password on Account page + -unless Gitlab.config.disable_gravatar? + %li + %p You can change your avatar at #{link_to "gravatar.com", "http://gravatar.com"} + + - if Gitlab.config.omniauth_enabled? && @user.provider? + %li + %p.hint + You can login through #{@user.provider.titleize}! + = link_to "click here to change", profile_account_path + + .row + .span7 .control-group = f.label :skype, class: "control-label" .controls= f.text_field :skype, class: "input-xlarge" @@ -45,39 +59,12 @@ .controls = f.text_area :bio, rows: 6, class: "input-xlarge", maxlength: 250 %span.help-block Tell us about yourself in fewer than 250 characters. - - .span5.pull-right - %fieldset.tips - %legend Tips: - %ul - %li - %p You can change your password on Account page - - if Gitlab.config.gravatar.enabled - %li - %p You can change your avatar at #{link_to "gravatar.com", "http://gravatar.com"} - - - if Gitlab.config.omniauth.enabled && @user.provider? - %li - %p - You can login through #{@user.provider.titleize}! - = link_to "click here to change", account_profile_path - - if current_user.can_create_group? - %li - %p - Need a group for several dependent projects? - = link_to new_group_path, class: "btn btn-tiny" do - Create a group - - if current_user.can_create_team? - %li - %p - Want to share a team between projects? - = link_to new_team_path, class: "btn btn-tiny" do - Create a team + .span5.right %fieldset %legend Personal projects: - %small.pull-right - %span= current_user.owned_projects.count + %small.right + %span= current_user.my_own_projects.count of %span= current_user.projects_limit .padded @@ -87,10 +74,9 @@ %fieldset %legend SSH public keys: - %span.pull-right - = link_to pluralize(current_user.keys.count, 'key'), keys_path + %strong.right= link_to current_user.keys.count, keys_path .padded - = link_to "Add Public Key", new_key_path, class: "btn btn-small" + = link_to "Add Public Key", new_key_path, class: "btn small" .form-actions - = f.submit 'Save', class: "btn btn-save" + = f.submit 'Save', class: "btn save-btn" diff --git a/app/views/profiles/update.js.erb b/app/views/profile/update.js.erb similarity index 100% rename from app/views/profiles/update.js.erb rename to app/views/profile/update.js.erb diff --git a/app/views/profiles/account.html.haml b/app/views/profiles/account.html.haml deleted file mode 100644 index b20d5c7a..00000000 --- a/app/views/profiles/account.html.haml +++ /dev/null @@ -1,87 +0,0 @@ -- if Gitlab.config.omniauth.enabled - %fieldset - %legend Social Accounts - .oauth_select_holder - %p.hint Tip: Click on icon to activate sigin with one of the following services - - User.omniauth_providers.each do |provider| - %span{class: oauth_active_class(provider) } - = link_to authbutton(provider, 32), omniauth_authorize_path(User, provider) - - - -%fieldset.update-token - %legend - Private token - %span.cred.pull-right - keep it secret! - .padded - = form_for @user, url: reset_private_token_profile_path, method: :put do |f| - .data - %p.slead - Private token used to access application resources without authentication. - %br - It can be used for atom feed or API - %p.cgray - - if current_user.private_token - = text_field_tag "token", current_user.private_token, class: "xxlarge large_text" - = f.submit 'Reset', confirm: "Are you sure?", class: "btn btn-primary btn-build-token" - - else - %span You don`t have one yet. Click generate to fix it. - = f.submit 'Generate', class: "btn success btn-build-token" - -%fieldset.update-password - %legend Password - = form_for @user, url: update_password_profile_path, method: :put do |f| - .padded - %p.slead After successful password update you will be redirected to login page where you should login with new password - -if @user.errors.any? - .alert.alert-error - %ul - - @user.errors.full_messages.each do |msg| - %li= msg - - .clearfix - = f.label :password - .input= f.password_field :password, required: true - .clearfix - = f.label :password_confirmation - .input - = f.password_field :password_confirmation, required: true - .clearfix - .input - = f.submit 'Save password', class: "btn btn-save" - - - -- if current_user.can_change_username? - %fieldset.update-username - %legend - Username - %small.cred.pull-right - Changing your username can have unintended side effects! - = form_for @user, url: update_username_profile_path, method: :put, remote: true do |f| - .padded - = f.label :username - .input - = f.text_field :username, required: true -   - %span.loading-gif.hide= image_tag "ajax_loader.gif" - %span.update-success.cgreen.hide - %i.icon-ok - Saved - %span.update-failed.cred.hide - %i.icon-remove - Failed - %ul.cred - %li It will change web url for personal projects. - %li It will change the git path to repositories for personal projects. - .input - = f.submit 'Save username', class: "btn btn-save" - -- if Gitlab.config.gitlab.signup_enabled - %fieldset.remove-account - %legend - Remove account - %small.cred.pull-right - Before removing the account you must remove all projects! - = link_to 'Delete account', user_registration_path, confirm: "REMOVE #{current_user.name}? Are you sure?", method: :delete, class: "btn btn-remove delete-key btn-small pull-right" diff --git a/app/views/profiles/update_username.js.haml b/app/views/profiles/update_username.js.haml deleted file mode 100644 index abd90269..00000000 --- a/app/views/profiles/update_username.js.haml +++ /dev/null @@ -1,6 +0,0 @@ -- if @user.valid? - :plain - $('.update-username .update-success').show(); -- else - :plain - $('.update-username .update-failed').show(); diff --git a/app/views/projects/_clone_panel.html.haml b/app/views/projects/_clone_panel.html.haml index 9a2be429..461f4fea 100644 --- a/app/views/projects/_clone_panel.html.haml +++ b/app/views/projects/_clone_panel.html.haml @@ -2,16 +2,16 @@ .row .span7 .form-horizontal= render "shared/clone_panel" - .span4.pull-right - .pull-right + .span4.right + .right - unless @project.empty_repo? - if can? current_user, :download_code, @project - = link_to archive_project_repository_path(@project), class: "btn-small btn grouped" do + = link_to archive_project_repository_path(@project), class: "btn grouped" do %i.icon-download-alt Download - if @project.merge_requests_enabled && can?(current_user, :write_merge_request, @project) - = link_to new_project_merge_request_path(@project), title: "New Merge Request", class: "btn-small btn grouped" do + = link_to new_project_merge_request_path(@project), title: "New Merge Request", class: "btn grouped" do Merge Request - if @project.issues_enabled && can?(current_user, :write_issue, @project) - = link_to url_for_new_issue, title: "New Issue", class: "btn-small btn grouped" do + = link_to new_project_issue_path(@project), title: "New Issue", class: "btn grouped" do Issue diff --git a/app/views/projects/_form.html.haml b/app/views/projects/_form.html.haml index 4d51e10d..9ee65942 100644 --- a/app/views/projects/_form.html.haml +++ b/app/views/projects/_form.html.haml @@ -1,140 +1,62 @@ -.row - .span3 - %ul.nav.nav-pills.nav-stacked - %li.active - = link_to 'Settings', '#tab-settings', 'data-toggle' => 'tab' - %li - = link_to 'Transfer', '#tab-transfer', 'data-toggle' => 'tab' - %li - = link_to 'Remove', '#tab-remove', 'data-toggle' => 'tab' += form_for(@project, remote: true) do |f| + - if @project.errors.any? + .alert-message.block-message.error + %ul + - @project.errors.full_messages.each do |msg| + %li= msg + .clearfix.project_name_holder + = f.label :name do + Project name is + .input + = f.text_field :name, placeholder: "Example Project", class: "xxlarge" - .span9 - .tab-content - .tab-pane.active#tab-settings - .ui-box.white - %h5.title Settings: - .form-holder - = form_for(@project, remote: true) do |f| - - if @project.errors.any? - .alert.alert-error - %ul - - @project.errors.full_messages.each do |msg| - %li= msg + %fieldset + %legend Advanced settings: + .clearfix + = f.label :path do + Path + .input + .input-prepend + %strong + = text_field_tag :ppath, @project.path_to_repo, class: "xlarge", disabled: true + .clearfix + = f.label :code do + URL + .input + .input-prepend + %span.add-on= web_app_url + = f.text_field :code, placeholder: "example" - %fieldset - .clearfix.project_name_holder - = f.label :name do - Project name is - .input - = f.text_field :name, placeholder: "Example Project", class: "span5" + - unless @project.new_record? || @project.heads.empty? + .clearfix + = f.label :default_branch, "Default Branch" + .input= f.select(:default_branch, @project.heads.map(&:name), {}, style: "width:210px;") + - unless @project.new_record? + %fieldset + %legend Features: - .clearfix - = f.label :description do - Project description - %span.light (optional) - .input - = f.text_area :description, placeholder: "awesome project", class: "span5", rows: 3, maxlength: 250 + .clearfix + = f.label :issues_enabled, "Issues" + .input= f.check_box :issues_enabled - - unless @project.empty_repo? - .clearfix - = f.label :default_branch, "Default Branch" - .input= f.select(:default_branch, @repository.branch_names, {}) + .clearfix + = f.label :merge_requests_enabled, "Merge Requests" + .input= f.check_box :merge_requests_enabled + .clearfix + = f.label :wall_enabled, "Wall" + .input= f.check_box :wall_enabled - - if can?(current_user, :change_public_mode, @project) - %fieldset.public-mode - %legend - Public mode: - .control-group - = f.label :public, class: 'control-label' do - %span Public clone access - .controls - = f.check_box :public - %span.descr - If checked, this project can be cloned - %em without any - authentication. - It will also be listed on the #{link_to "public access directory", public_root_path}. + .clearfix + = f.label :wiki_enabled, "Wiki" + .input= f.check_box :wiki_enabled - %fieldset.features - %legend - Features: - .control-group - = f.label :issues_enabled, "Issues", class: 'control-label' - .controls - = f.check_box :issues_enabled - %span.descr Lightweight issue tracking system for this project + %br - - 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, disabled: !@project.can_have_issues_tracker_id? - - .control-group - = f.label :merge_requests_enabled, "Merge Requests", class: 'control-label' - .controls - = f.check_box :merge_requests_enabled - %span.descr Submit changes to be merged upstream. - - .control-group - = f.label :wiki_enabled, "Wiki", class: 'control-label' - .controls - = f.check_box :wiki_enabled - %span.descr Pages for project documentation - - .control-group - = f.label :wall_enabled, "Wall", class: 'control-label' - .controls - = f.check_box :wall_enabled - %span.descr Simple chat system for broadcasting inside project - - .control-group - = f.label :snippets_enabled, "Snippets", class: 'control-label' - .controls - = f.check_box :snippets_enabled - %span.descr Share code pastes with others out of git repository - - - .form-actions - = f.submit 'Save', class: "btn btn-save" - - .tab-pane#tab-transfer - - if can?(current_user, :change_namespace, @project) - .ui-box.ui-box-danger - %h5.title Transfer project - .errors-holder - .form-holder - = form_for(@project, url: transfer_project_path(@project), remote: true, html: { class: 'transfer-project' }) do |f| - .control-group - = f.label :namespace_id do - %span Namespace - .controls - .clearfix - = f.select :namespace_id, namespaces_options(@project.namespace_id || Namespace::global_id), {prompt: 'Choose a project namespace'}, {class: 'chosen'} - %ul - %li Be careful. Changing project namespace can have unintended side effects - %li You can transfer project only to namespaces you can manage - %li You will need to update your local repositories to point to the new location. - .form-actions - = f.submit 'Transfer', class: "btn btn-remove" - - else - %p.nothing_here_message Only project owner can transfer a project - - .tab-pane#tab-remove - - if can?(current_user, :remove_project, @project) - .ui-box.ui-box-danger - %h5.title Remove project - .ui-box-body - %p - Remove of project will cause removing repository and all related resources like issues, merge requests etc. - %p - %strong Removed project can not be restored! - - = link_to 'Remove project', @project, confirm: 'Removed project can not be restored! Are you sure?', method: :delete, class: "btn btn-remove btn-small" - - else - %p.nothing_here_message Only project owner can remove a project + .actions + = f.submit 'Save', class: "btn save-btn" + = link_to 'Cancel', @project, class: "btn" + - unless @project.new_record? + .right + = link_to 'Remove', @project, confirm: 'Are you sure?', method: :delete, class: "btn danger" diff --git a/app/views/projects/_new_form.html.haml b/app/views/projects/_new_form.html.haml index b6503636..e6d5e93f 100644 --- a/app/views/projects/_new_form.html.haml +++ b/app/views/projects/_new_form.html.haml @@ -1,48 +1,29 @@ = form_for(@project, remote: true) do |f| - if @project.errors.any? - .alert.alert-error + .alert-message.block-message.error %span= @project.errors.full_messages.first .clearfix.project_name_holder = f.label :name do Project name is .input = f.text_field :name, placeholder: "Example Project", class: "xxlarge" - = f.submit 'Create project', class: "btn btn-create project-submit" + = f.submit 'Create project', class: "btn primary project-submit" - - if current_user.can_select_namespace? - .clearfix - = f.label :namespace_id do - %span Namespace - .input - = f.select :namespace_id, namespaces_options(params[:namespace_id] || :current_user), {}, {class: 'chosen'} - - - .clearfix - .input - = link_to "#", class: 'appear-link' do - %i.icon-upload-alt - %span Import existing repository? - .clearfix.appear-data - = f.label :import_url do - %span Import existing repo - .input - = f.text_field :import_url, class: 'xlarge', placeholder: 'https://github.com/randx/six.git' - .light - URL must be clonable - - %p.padded - New projects are private by default. You choose who can see the project and commit to repository. %hr - - - if current_user.can_create_group? + %div.adv_settings + %h6 Advanced settings: .clearfix - .input.light - Need a group for several dependent projects? - = link_to new_group_path, class: "btn btn-tiny" do - Create a group - - if current_user.can_create_team? + = f.label :path do + Git Clone + .input + .input-prepend + %span.add-on= Gitlab.config.ssh_path + = f.text_field :path, placeholder: "example_project", disabled: !@project.new_record? + %span.add-on= ".git" .clearfix - .input.light - Want to share a project between team? - = link_to new_team_path, class: "btn btn-tiny" do - Create a team + = f.label :code do + URL + .input + .input-prepend + %span.add-on= web_app_url + = f.text_field :code, placeholder: "example" diff --git a/app/views/projects/_project_head.html.haml b/app/views/projects/_project_head.html.haml new file mode 100644 index 00000000..94052650 --- /dev/null +++ b/app/views/projects/_project_head.html.haml @@ -0,0 +1,31 @@ +%ul.nav.nav-tabs + = nav_link(path: 'projects#show') do + = link_to project_path(@project), class: "activities-tab tab" do + %i.icon-home + Show + = nav_link(controller: :team_members) do + = link_to project_team_index_path(@project), class: "team-tab tab" do + %i.icon-user + Team + = nav_link(path: 'projects#files') do + = link_to 'Attachments', files_project_path(@project), class: "files-tab tab" + = nav_link(controller: :snippets) do + = link_to 'Snippets', project_snippets_path(@project), class: "snippets-tab tab" + + - if can? current_user, :admin_project, @project + = nav_link(controller: :deploy_keys, html_options: {class: 'right'}) do + = link_to project_deploy_keys_path(@project) do + %span + Deploy Keys + = nav_link(controller: :hooks, html_options: {class: 'right'}) do + = link_to project_hooks_path(@project) do + %span + Hooks + = nav_link(controller: :services, html_options: {class: 'right'}) do + = link_to project_services_path(@project) do + %span + Services + = nav_link(path: 'projects#edit', html_options: {class: 'right'}) do + = link_to edit_project_path(@project), class: "stat-tab tab " do + %i.icon-edit + Edit diff --git a/app/views/projects/_settings_nav.html.haml b/app/views/projects/_settings_nav.html.haml deleted file mode 100644 index acaa03f9..00000000 --- a/app/views/projects/_settings_nav.html.haml +++ /dev/null @@ -1,21 +0,0 @@ -%ul.nav.nav-tabs - = nav_link(path: 'projects#edit') do - = link_to edit_project_path(@project), class: "stat-tab tab " do - %i.icon-edit - Edit - = nav_link(controller: [:team_members, :teams]) do - = link_to project_team_index_path(@project), class: "team-tab tab" do - %i.icon-user - Team - = nav_link(controller: :deploy_keys) do - = link_to project_deploy_keys_path(@project) do - %span - Deploy Keys - = nav_link(controller: :hooks) do - = link_to project_hooks_path(@project) do - %span - Hooks - = nav_link(controller: :services) do - = link_to project_services_path(@project) do - %span - Services diff --git a/app/views/projects/create.js.haml b/app/views/projects/create.js.haml index 296c8688..ce73fe0c 100644 --- a/app/views/projects/create.js.haml +++ b/app/views/projects/create.js.haml @@ -2,9 +2,10 @@ :plain location.href = "#{project_path(@project)}"; - else + - if @project.git_error? + location.href = "#{errors_githost_path}"; + -else :plain $('.project_new_holder').show(); $("#new_project").replaceWith("#{escape_javascript(render('new_form'))}"); $('.save-project-loader').hide(); - new Projects(); - $('select.chosen').chosen() diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index 394522bf..fdd537da 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -1,5 +1,4 @@ -= render "projects/settings_nav" - += render "project_head" .project_edit_holder %h3.page_title Edit Project %hr diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml index b1795b30..d9a151fc 100644 --- a/app/views/projects/empty.html.haml +++ b/app/views/projects/empty.html.haml @@ -1,34 +1,32 @@ += render 'shared/no_ssh' = render 'clone_panel' %div.git-empty - %fieldset - %legend Git global setup: - %pre.dark - :preserve - git config --global user.name "#{current_user.name}" - git config --global user.email "#{current_user.email}" + %h4 Git global setup: + %pre.dark + = preserve do + git config --global user.name "#{current_user.name}" + git config --global user.email "#{current_user.email}" - %fieldset - %legend Create Repository - %pre.dark - :preserve - mkdir #{@project.path} - cd #{@project.path} - git init - touch README - git add README - git commit -m 'first commit' - git remote add origin #{@project.url_to_repo} - git push -u origin master + %h4.prepend-top-20 Create Repository + %pre.dark + = preserve do + mkdir #{@project.path} + cd #{@project.path} + git init + touch README + git add README + git commit -m 'first commit' + git remote add origin #{@project.url_to_repo} + git push -u origin master - %fieldset - %legend Existing Git Repo? - %pre.dark - :preserve - cd existing_git_repo - git remote add origin #{@project.url_to_repo} - git push -u origin master + %h4.prepend-top-20 Existing Git Repo? + %pre.dark + = preserve do + cd existing_git_repo + git remote add origin #{@project.url_to_repo} + git push -u origin master - - if can? current_user, :remove_project, @project + - if can? current_user, :admin_project, @project .prepend-top-20 - = link_to 'Remove project', @project, confirm: 'Are you sure?', method: :delete, class: "btn btn-remove pull-right" + = link_to 'Remove project', @project, confirm: 'Are you sure?', method: :delete, class: "btn danger right" diff --git a/app/views/projects/files.html.haml b/app/views/projects/files.html.haml new file mode 100644 index 00000000..9f7efcdc --- /dev/null +++ b/app/views/projects/files.html.haml @@ -0,0 +1,23 @@ += render "project_head" +- unless @notes.empty? + %table + %thead + %tr + %th File name + %th + + - @notes.each do |note| + %tr + %td + %a{href: note.attachment.url} + = image_tag gravatar_icon(note.author_email), class: "avatar s24" + = note.attachment_identifier + %td + Added + = time_ago_in_words(note.created_at) + ago +- else + .alert-message.block-message + %span All files attached to project wall, issues etc will be displayed here + + diff --git a/app/views/projects/graph.html.haml b/app/views/projects/graph.html.haml new file mode 100644 index 00000000..07f038d2 --- /dev/null +++ b/app/views/projects/graph.html.haml @@ -0,0 +1,14 @@ +%h3.page_title Project Network Graph +%br +.graph_holder + %h4 + %small You can move around the graph by using arrow keys. + #holder.graph +:javascript + var chunk1={commits:#{@commits_json}}; + var days=#{@days_json}; + initGraph(); + $(function(){ + branchGraph($("#holder")[0]); + GraphNav.init(); + }); diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index 824d4daf..2c4f55eb 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -1,32 +1,8 @@ += render "project_head" = render 'clone_panel' = render "events/event_last_push", event: @last_push +.content_list= render @events +.loading.hide -.row - .span9 - .content_list= render @events - .loading.hide - .span3 - .ui-box.white - .padded - %h3.page_title - = @project.name - - if @project.description.present? - %p.light= @project.description - - %hr - %p - Access level: - - if @project.public - %span.cblue - %i.icon-share - Public - - else - %span.cgreen - %i.icon-lock - Private - - %p Repo Size: #{@project.repository.size} MB - %p Created at: #{@project.created_at.stamp('Aug 22, 2013')} - %p Owner: #{link_to @project.owner_name, @project.owner} :javascript $(function(){ Pager.init(20); }); diff --git a/app/views/projects/teams/available.html.haml b/app/views/projects/teams/available.html.haml deleted file mode 100644 index 29fe8ed2..00000000 --- a/app/views/projects/teams/available.html.haml +++ /dev/null @@ -1,22 +0,0 @@ -= render "projects/settings_nav" - -%h3.page_title - = "Assign project to team of users" -%hr -%p.slead - Read more about assign to team of users #{link_to "here", '#', class: 'vlink'}. -= form_tag assign_project_teams_path(@project), method: 'post' do - %p.slead Choose Team of users you want to assign: - .padded - = label_tag :team_id, "Team" - .input= select_tag(:team_id, options_from_collection_for_select(@teams, :id, :name), prompt: "Select team", class: "chosen xxlarge", required: true) - %p.slead Choose greatest user acces in team you want to assign: - .padded - = label_tag :team_ids, "Permission" - .input= select_tag :greatest_project_access, options_for_select(UserTeam.access_roles), {class: "project-access-select chosen span3" } - - - .actions - = submit_tag 'Assign', class: "btn btn-create" - = link_to "Cancel", project_team_index_path(@project), class: "btn btn-cancel" - diff --git a/app/views/projects/transfer.js.haml b/app/views/projects/transfer.js.haml deleted file mode 100644 index 10b0de98..00000000 --- a/app/views/projects/transfer.js.haml +++ /dev/null @@ -1,7 +0,0 @@ -- if @project.errors[:namespace_id].present? - :plain - $("#tab-transfer .errors-holder").replaceWith(errorMessage('#{escape_javascript(@project.errors[:namespace_id].first)}')); - $("#tab-transfer .form-actions input").removeAttr('disabled').removeClass('disabled'); -- else - :plain - location.href = "#{edit_project_path(@project)}"; diff --git a/app/views/projects/update.js.haml b/app/views/projects/update.js.haml index f44ed529..8aaa0e49 100644 --- a/app/views/projects/update.js.haml +++ b/app/views/projects/update.js.haml @@ -1,6 +1,6 @@ - if @project.valid? :plain - location.href = "#{edit_project_path(@project)}"; + location.href = "#{edit_project_path(@project, notice: 'Project was successfully updated.')}"; - else :plain $('.project_edit_holder').show(); diff --git a/app/views/projects/wall.html.haml b/app/views/projects/wall.html.haml new file mode 100644 index 00000000..591a8cd0 --- /dev/null +++ b/app/views/projects/wall.html.haml @@ -0,0 +1,2 @@ +%div.wall_page + = render "notes/reversed_notes_with_form", tid: nil, tt: "wall" diff --git a/app/views/protected_branches/index.html.haml b/app/views/protected_branches/index.html.haml index 15644de5..50a81712 100644 --- a/app/views/protected_branches/index.html.haml +++ b/app/views/protected_branches/index.html.haml @@ -1,54 +1,48 @@ -= render "commits/head" -.row - .span3 - = render "repositories/filter" - .span9 - .alert - %p Protected branches designed to prevent push for all except #{link_to "masters", help_permissions_path, class: "vlink"}. - %p This ability allows: - %ul - %li keep stable branches secured - %li forced code review before merge to protected branches - %p Read more about project permissions #{link_to "here", help_permissions_path, class: "vlink"} += render "repositories/branches_head" - - if can? current_user, :admin_project, @project - = form_for [@project, @protected_branch] do |f| - -if @protected_branch.errors.any? - .alert.alert-error - %ul - - @protected_branch.errors.full_messages.each do |msg| - %li= msg +.alert + %p Protected branches designed to prevent push for all except #{link_to "masters", help_permissions_path, class: "vlink"}. + %p This ability allows: + %ul + %li keep stable branches secured + %li forced code review before merge to protected branches + %p Read more about project permissions #{link_to "here", help_permissions_path, class: "vlink"} - .entry.clearfix - = f.label :name, "Branch" - .span3 - = f.select(:name, @project.open_branches.map { |br| [br.name, br.name] } , {include_blank: "Select branch"}, {class: "chosen span3"}) -   - = f.submit 'Protect', class: "btn-primary btn" +- if can? current_user, :admin_project, @project + = form_for [@project, @protected_branch] do |f| + -if @protected_branch.errors.any? + .alert-message.block-message.error + %ul + - @protected_branch.errors.full_messages.each do |msg| + %li= msg - - unless @branches.empty? - %table - %thead - %tr - %th Name - %th Last commit - %th - %tbody - - @branches.each do |branch| - %tr - %td - = link_to project_commits_path(@project, branch.name) do - %strong= branch.name - - if @project.root_ref?(branch.name) - %span.label default - %td - - if branch.commit - = link_to project_commit_path(@project, branch.commit.id) do - = truncate branch.commit.id.to_s, length: 10 - = time_ago_in_words(branch.commit.committed_date) - ago - - else - (branch was removed from repository) - %td - - if can? current_user, :admin_project, @project - = link_to 'Unprotect', [@project, branch], confirm: 'Are you sure?', method: :delete, class: "btn btn-remove btn-small" + .entry.clearfix + = f.label :name, "Branch" + .span3 + = f.select(:name, @project.open_branches.map { |br| [br.name, br.name] } , {include_blank: "Select branch"}, {class: "chosen span3"}) +   + = f.submit 'Protect', class: "primary btn" + +- unless @branches.empty? + %table + %thead + %tr + %th Name + %th Last commit + %th + %tbody + - @branches.each do |branch| + %tr + %td + = link_to project_commits_path(@project, branch.name) do + %strong= branch.name + - if branch.name == @project.root_ref + %span.label default + %td + = link_to project_commit_path(@project, branch.commit.id) do + = truncate branch.commit.id.to_s, length: 10 + = time_ago_in_words(branch.commit.committed_date) + ago + %td + - if can? current_user, :admin_project, @project + = link_to 'Unprotect', [@project, branch], confirm: 'Are you sure?', method: :delete, class: "danger btn small" diff --git a/app/views/public/projects/index.html.haml b/app/views/public/projects/index.html.haml deleted file mode 100644 index b50484f6..00000000 --- a/app/views/public/projects/index.html.haml +++ /dev/null @@ -1,20 +0,0 @@ -%h3.page_title - Projects (#{@projects.total_count}) - %small with read-only access -%hr - -.public-projects - %ul.unstyled - - @projects.each do |project| - %li.clearfix - %h5 - %i.icon-share - = project.name_with_namespace - .pull-right - %pre.dark.tiny git clone #{project.http_url_to_repo} - %p.description - = project.description - - unless @projects.present? - %h3.nothing_here_message No public projects - - = paginate @projects, theme: "admin" diff --git a/app/views/repositories/_branch.html.haml b/app/views/repositories/_branch.html.haml index dd91e14b..2728b100 100644 --- a/app/views/repositories/_branch.html.haml +++ b/app/views/repositories/_branch.html.haml @@ -1,4 +1,5 @@ -- commit = Commit.new(Gitlab::Git::Commit.new(branch.commit)) +- commit = Commit.new(branch.commit) +- commit = CommitDecorator.decorate(commit) %tr %td = link_to project_commits_path(@project, branch.name) do @@ -7,7 +8,7 @@ - else %i.icon-unlock %strong= truncate(branch.name, length: 60) - - if branch.name == @repository.root_ref + - if branch.name == @project.root_ref %span.label default %td = link_to project_commit_path(@project, commit.id), class: 'commit_short_id' do @@ -21,6 +22,6 @@ %td - if can? current_user, :download_code, @project = link_to archive_project_repository_path(@project, ref: branch.name) do - %i.icon-download-alt + %i.icon-download Download diff --git a/app/views/repositories/_filter.html.haml b/app/views/repositories/_branches_head.html.haml similarity index 73% rename from app/views/repositories/_filter.html.haml rename to app/views/repositories/_branches_head.html.haml index e718d481..8f3e1ba3 100644 --- a/app/views/repositories/_filter.html.haml +++ b/app/views/repositories/_branches_head.html.haml @@ -1,9 +1,10 @@ -%ul.nav.nav-pills.nav-stacked += render "commits/head" +%ul.nav.nav-pills = nav_link(path: 'repositories#show') do = link_to 'Recent', project_repository_path(@project) = nav_link(path: 'protected_branches#index') do = link_to project_protected_branches_path(@project) do - Protected %i.icon-lock + Protected = nav_link(path: 'repositories#branches') do - = link_to 'All branches', branches_project_repository_path(@project) + = link_to 'All', branches_project_repository_path(@project) diff --git a/app/views/repositories/_feed.html.haml b/app/views/repositories/_feed.html.haml index 6bb75265..496328ba 100644 --- a/app/views/repositories/_feed.html.haml +++ b/app/views/repositories/_feed.html.haml @@ -1,10 +1,11 @@ - commit = update +- commit = CommitDecorator.new(commit) %tr %td = link_to project_commits_path(@project, commit.head.name) do %strong = commit.head.name - - if @project.root_ref?(commit.head.name) + - if commit.head.name == @project.root_ref %span.label default %td @@ -14,6 +15,6 @@ = image_tag gravatar_icon(commit.author_email), class: "", width: 16 = gfm escape_once(truncate(commit.title, length: 40)) %td - %span.pull-right.cgray + %span.right.cgray = time_ago_in_words(commit.committed_date) ago diff --git a/app/views/repositories/_head.html.haml b/app/views/repositories/_head.html.haml new file mode 100644 index 00000000..bc96f306 --- /dev/null +++ b/app/views/repositories/_head.html.haml @@ -0,0 +1 @@ += render "projects/project_head" diff --git a/app/views/repositories/branches.html.haml b/app/views/repositories/branches.html.haml index 14b5082e..4c246c69 100644 --- a/app/views/repositories/branches.html.haml +++ b/app/views/repositories/branches.html.haml @@ -1,15 +1,12 @@ -= render "commits/head" -.row - .span3 - = render "filter" - .span9 - - unless @branches.empty? - %table - %thead - %tr - %th Name - %th Last commit - %th - %tbody - - @branches.each do |branch| - = render "repositories/branch", branch: branch += render "repositories/branches_head" +- unless @branches.empty? + %table + %thead + %tr + %th Name + %th Last commit + %th + + %tbody + - @branches.each do |branch| + = render "repositories/branch", branch: branch diff --git a/app/views/repositories/show.html.haml b/app/views/repositories/show.html.haml index e58e16f8..fd0abac8 100644 --- a/app/views/repositories/show.html.haml +++ b/app/views/repositories/show.html.haml @@ -1,14 +1,11 @@ -= render "commits/head" -.row - .span3 - = render "filter" - .span9 - %table - %thead - %tr - %th Name - %th Last commit - %th - - @activities.each do |update| - = render "repositories/branch", branch: update.head += render "branches_head" + +%table + %thead + %tr + %th Name + %th Last commit + %th + - @activities.each do |update| + = render "repositories/branch", branch: update.head diff --git a/app/views/repositories/stats.html.haml b/app/views/repositories/stats.html.haml index dde35ea3..a93814a4 100644 --- a/app/views/repositories/stats.html.haml +++ b/app/views/repositories/stats.html.haml @@ -7,7 +7,7 @@ %b Total commits: %span= @stats.commits_count %p - %b Total files in #{@repository.root_ref}: + %b Total files in #{@project.root_ref}: %span= @stats.files_count %p %b Authors: @@ -23,7 +23,7 @@ = image_tag gravatar_icon(author.email, 16), class: 'avatar s16' = author.name %small.light= author.email - .pull-right + .right = author.commits diff --git a/app/views/repositories/tags.html.haml b/app/views/repositories/tags.html.haml index 2d311124..193cb2e3 100644 --- a/app/views/repositories/tags.html.haml +++ b/app/views/repositories/tags.html.haml @@ -7,7 +7,8 @@ %th Last commit %th - @tags.each do |tag| - - commit = Commit.new(Gitlab::Git::Commit.new(tag.commit)) + - commit = Commit.new(tag.commit) + - commit = CommitDecorator.decorate(commit) %tr %td %strong @@ -25,14 +26,8 @@ %td - if can? current_user, :download_code, @project = link_to archive_project_repository_path(@project, ref: tag.name) do - %i.icon-download-alt + %i.icon-download Download - else - %h3.nothing_here_message - Repository has no tags yet. - %br - %small - Use git tag command to add a new one: - %br - %span.monospace git tag -a v1.4 -m 'version 1.4' + %h3 No tags diff --git a/app/views/search/_filter.html.haml b/app/views/search/_filter.html.haml deleted file mode 100644 index a523fa25..00000000 --- a/app/views/search/_filter.html.haml +++ /dev/null @@ -1,16 +0,0 @@ -%fieldset - %legend Groups: - .clearfix - = select_tag 'group_id', options_from_collection_for_select(current_user.authorized_groups, :id, :name, params[:group_id]), prompt: 'All', include_blank: true, class: 'trigger-submit chosen' - - -%fieldset - %legend Teams: - .clearfix - = select_tag 'team_id', options_from_collection_for_select(current_user.authorized_teams, :id, :name, params[:team_id]), prompt: 'All', include_blank: true, class: 'trigger-submit chosen' - -%fieldset - %legend Projects: - .clearfix - = select_tag 'project_id', options_from_collection_for_select(current_user.authorized_projects, :id, :name_with_namespace, params[:project_id]), prompt: 'All', include_blank: true, class: 'trigger-submit chosen' - diff --git a/app/views/search/_result.html.haml b/app/views/search/_result.html.haml index bfa46075..8b137891 100644 --- a/app/views/search/_result.html.haml +++ b/app/views/search/_result.html.haml @@ -1,40 +1 @@ -%fieldset - %legend - Search results - %span.cgray (#{@projects.count + @merge_requests.count + @issues.count + @wiki_pages.count}) -.search_results - %ul.well-list - - @projects.each do |project| - %li - project: - = link_to project do - %strong.term= project.name_with_namespace - - @merge_requests.each do |merge_request| - %li - merge request: - = link_to [merge_request.project, merge_request] do - %span ##{merge_request.id} - %strong.term - = truncate merge_request.title, length: 50 - %span.light (#{merge_request.project.name_with_namespace}) - - @issues.each do |issue| - %li - issue: - = link_to [issue.project, issue] do - %span ##{issue.id} - %strong.term - = truncate issue.title, length: 50 - %span.light (#{issue.project.name_with_namespace}) - - @wiki_pages.each do |wiki_page| - %li - wiki: - = link_to project_wiki_path(wiki_page.project, wiki_page) do - %strong.term - = truncate wiki_page.title, length: 50 - %span.light (#{wiki_page.project.name_with_namespace}) - -:javascript - $(function() { - $(".search_results .term").highlight("#{escape_javascript(params[:search])}"); - }) diff --git a/app/views/search/show.html.haml b/app/views/search/show.html.haml index 5914c22d..0d5f5458 100644 --- a/app/views/search/show.html.haml +++ b/app/views/search/show.html.haml @@ -1,15 +1,92 @@ = form_tag search_path, method: :get, class: 'form-inline' do |f| - .search-holder + .padded = label_tag :search do - %span Looking for + %strong Looking for .input = search_field_tag :search, params[:search], placeholder: "issue 143", class: "input-xxlarge search-text-input", id: "dashboard_search" - = submit_tag 'Search', class: "btn btn-primary wide" - .clearfix - .row - .span3 - = render 'filter', f: f - .span9 - .results - - if params[:search].present? - = render 'search/result' + = submit_tag 'Search', class: "btn primary wide" +- if params[:search].present? + %br + %h3 + Search results + %small (#{@projects.count + @merge_requests.count + @issues.count + @wiki_pages.count}) + %hr + .search_results + .row + .span6 + %table + %thead + %tr + %th Projects + %tbody + - @projects.each do |project| + %tr + %td + = link_to project do + %strong.term= project.name + %small.cgray + last activity at + = project.last_activity_date.stamp("Aug 25, 2011") + - if @projects.blank? + %tr + %td + %h4.nothing_here_message No Projects + %br + %table + %thead + %tr + %th Merge Requests + %tbody + - @merge_requests.each do |merge_request| + %tr + %td + = link_to [merge_request.project, merge_request] do + %span.badge.badge-info ##{merge_request.id} + – + %strong.term= truncate merge_request.title, length: 50 + %strong.right + %span.label= merge_request.project.name + - if @merge_requests.blank? + %tr + %td + %h4.nothing_here_message No Merge Requests + .span6 + %table + %thead + %tr + %th Issues + %tbody + - @issues.each do |issue| + %tr + %td + = link_to [issue.project, issue] do + %span.badge.badge-info ##{issue.id} + – + %strong.term= truncate issue.title, length: 40 + %strong.right + %span.label= issue.project.name + - if @issues.blank? + %tr + %td + %h4.nothing_here_message No Issues + .span6 + %table + %thead + %tr + %th Wiki + %tbody + - @wiki_pages.each do |wiki_page| + %tr + %td + = link_to project_wiki_path(wiki_page.project, wiki_page) do + %strong.term= truncate wiki_page.title, length: 40 + %strong.right + %span.label= wiki_page.project.name + - if @wiki_pages.blank? + %tr + %td + %h4.nothing_here_message No wiki pages + :javascript + $(function() { + $(".search_results .term").highlight("#{params[:search]}"); + }) diff --git a/app/views/services/_gitlab_ci.html.haml b/app/views/services/_gitlab_ci.html.haml index dfde6438..3c9820b3 100644 --- a/app/views/services/_gitlab_ci.html.haml +++ b/app/views/services/_gitlab_ci.html.haml @@ -1,22 +1,19 @@ %h3.page_title - GitLab CI - %small Continuous integration server from GitLab - .pull-right - - if @service.active - %small.cgreen Enabled - - else - %small.cgray Disabled + Services → GitLab CI Integration - - -.back_link - = link_to project_services_path(@project) do - ← to services + .right + .thumbnail + - if @service.active + = image_tag 'service-gitlab-ci.png', class: 'small' + - else + = image_tag 'service-disabled-gitlab-ci.png', class: 'small' %hr + + = form_for(@service, :as => :service, :url => project_service_path(@project, :gitlab_ci), :method => :put) do |f| - if @service.errors.any? - .alert.alert-error + .alert-message.block-message.error %ul - @service.errors.full_messages.each do |msg| %li= msg @@ -28,7 +25,7 @@ = f.check_box :active .control-group - = f.label :project_url, "Project URL", class: "control-label" + = f.label :active, "Project URL", class: "control-label" .controls = f.text_field :project_url, class: "input-xlarge", placeholder: "http://ci.gitlabhq.com/projects/3" @@ -40,7 +37,7 @@ .form-actions - = f.submit 'Save', class: 'btn btn-save' + = f.submit 'Save', class: 'btn save-btn'   - if @service.valid? && @service.active = link_to 'Test settings', test_project_service_path(@project), class: 'btn btn-small' diff --git a/app/views/services/edit.html.haml b/app/views/services/edit.html.haml index 0c63a7ed..d893847f 100644 --- a/app/views/services/edit.html.haml +++ b/app/views/services/edit.html.haml @@ -1,3 +1,2 @@ -= render "projects/settings_nav" - += render "projects/project_head" = render 'gitlab_ci' diff --git a/app/views/services/index.html.haml b/app/views/services/index.html.haml index eb2f8d0c..3894fcee 100644 --- a/app/views/services/index.html.haml +++ b/app/views/services/index.html.haml @@ -1,32 +1,15 @@ -= render "projects/settings_nav" - += render "projects/project_head" %h3.page_title Services -%br +%hr + +.row + .span6 + .padded + %p.slead Continuous integration server from GitLab + .thumbnail.left + = link_to edit_project_service_path(@project, :gitlab_ci) do + - if @gitlab_ci_service.try :active + = image_tag 'service-gitlab-ci.png' + - else + = image_tag 'service-disabled-gitlab-ci.png' -%ul.ui-box.well-list - %li - %h4.cgreen - = link_to edit_project_service_path(@project, :gitlab_ci) do - GitLab CI - %small Continuous integration server from GitLab - .pull-right - - if @gitlab_ci_service.try(:active) - %small.cgreen - %i.icon-ok - Enabled - - else - %small.cgray - %i.icon-off - Disabled - %li.disabled - %h4 - Jenkins CI - %small An extendable open source continuous integration server - .pull-right - %small Not implemented yet - %li.disabled - %h4 - Campfire - %small Web-based group chat tool - .pull-right - %small Not implemented yet diff --git a/app/views/shared/_clone_panel.html.haml b/app/views/shared/_clone_panel.html.haml index bd9ca729..924eb3fc 100644 --- a/app/views/shared/_clone_panel.html.haml +++ b/app/views/shared/_clone_panel.html.haml @@ -1,4 +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", readonly: true + %button{class: "btn", :"data-clone" => @project.http_url_to_repo}= Gitlab.config.web_protocol.upcase + = text_field_tag :project_clone, @project.url_to_repo, class: "one_click_select span5" diff --git a/app/views/shared/_merge_requests.html.haml b/app/views/shared/_merge_requests.html.haml deleted file mode 100644 index 85391a34..00000000 --- a/app/views/shared/_merge_requests.html.haml +++ /dev/null @@ -1,14 +0,0 @@ -- if @merge_requests.any? - - @merge_requests.group_by(&:project).each do |group| - .ui-box - - project = group[0] - %h5.title - = link_to_project project - %ul.well-list - - group[1].each do |merge_request| - = render(partial: 'merge_requests/merge_request', locals: {merge_request: merge_request}) - %hr - = paginate @merge_requests, theme: "gitlab" - -- else - %h3.nothing_here_message Nothing to show here diff --git a/app/views/shared/_no_ssh.html.haml b/app/views/shared/_no_ssh.html.haml index 5fdcea85..c75a1d93 100644 --- a/app/views/shared/_no_ssh.html.haml +++ b/app/views/shared/_no_ssh.html.haml @@ -1,3 +1,3 @@ - if current_user.require_ssh_key? - %p.error_message.centered - You won't be able to pull or push project code via SSH until you #{link_to 'add an SSH key', new_key_path} to your profile + %p.error_message + You won't be able to pull or push project code until you #{link_to 'add an SSH key', new_key_path} to your profile diff --git a/app/views/snippets/_blob.html.haml b/app/views/snippets/_blob.html.haml deleted file mode 100644 index 017a33b3..00000000 --- a/app/views/snippets/_blob.html.haml +++ /dev/null @@ -1,12 +0,0 @@ -.file_holder - .file_title - %i.icon-file - %strong= @snippet.file_name - %span.options - = link_to "raw", raw_project_snippet_path(@project, @snippet), class: "btn btn-tiny", target: "_blank" - .file_content.code - - unless @snippet.content.empty? - %div{class: user_color_scheme_class} - = raw @snippet.colorize(formatter: :gitlab) - - else - %p.nothing_here_message Empty file diff --git a/app/views/snippets/_form.html.haml b/app/views/snippets/_form.html.haml index 77162cdc..e61e61a7 100644 --- a/app/views/snippets/_form.html.haml +++ b/app/views/snippets/_form.html.haml @@ -1,41 +1,28 @@ -%h3.page_title - = @snippet.new_record? ? "New Snippet" : "Edit Snippet ##{@snippet.id}" +%h3= @snippet.new_record? ? "New Snippet" : "Edit Snippet ##{@snippet.id}" %hr -.snippet-form-holder - = form_for [@project, @snippet] do |f| += form_for [@project, @snippet] do |f| + %table.no-borders -if @snippet.errors.any? - .alert.alert-error + .alert-message.block-message.error %ul - @snippet.errors.full_messages.each do |msg| %li= msg .clearfix = f.label :title - .input= f.text_field :title, placeholder: "Example Snippet", class: 'input-xlarge', required: true + .input= f.text_field :title, placeholder: "Example Snippet" + .clearfix + = f.label :file_name + .input= f.text_field :file_name, placeholder: "example.rb" .clearfix = f.label "Lifetime" .input= f.select :expires_at, lifetime_select_options, {}, {class: 'chosen span2'} .clearfix - .file-editor - = f.label :file_name, "File" - .input - .file_holder.snippet - .file_title - = f.text_field :file_name, placeholder: "example.rb", class: 'snippet-file-name', required: true - .file_content.code - %pre#editor= @snippet.content - = f.hidden_field :content, class: 'snippet-file-content' - - .form-actions - = f.submit 'Save', class: "btn-save btn" - = link_to "Cancel", project_snippets_path(@project), class: " btn" - - unless @snippet.new_record? - .pull-right= link_to 'Destroy', [@project, @snippet], confirm: 'Are you sure?', method: :delete, class: "btn pull-right danger delete-snippet", id: "destroy_snippet_#{@snippet.id}" - - -:javascript - var editor = ace.edit("editor"); - $(".snippet-form-holder form").submit(function(){ - $(".snippet-file-content").val(editor.getValue()); - }); + = f.label :content, "Code" + .input= f.text_area :content, class: "span8" + .actions + = f.submit 'Save', class: "primary btn" + = link_to "Cancel", project_snippets_path(@project), class: " btn" + - unless @snippet.new_record? + .right= link_to 'Destroy', [@project, @snippet], confirm: 'Are you sure?', method: :delete, class: "btn right danger delete-snippet", id: "destroy_snippet_#{@snippet.id}" diff --git a/app/views/snippets/_snippet.html.haml b/app/views/snippets/_snippet.html.haml index a576500c..a2d3a65e 100644 --- a/app/views/snippets/_snippet.html.haml +++ b/app/views/snippets/_snippet.html.haml @@ -1,13 +1,12 @@ %tr %td - = image_tag gravatar_icon(snippet.author_email), class: "avatar s24" %a{href: project_snippet_path(snippet.project, snippet)} %strong= truncate(snippet.title, length: 60) %td = snippet.file_name %td %span.cgray - - if snippet.expires_at + - if snippet.expires_at = snippet.expires_at.to_date.to_s(:short) - else Never diff --git a/app/views/snippets/edit.html.haml b/app/views/snippets/edit.html.haml index f81c0b8b..8afaf46e 100644 --- a/app/views/snippets/edit.html.haml +++ b/app/views/snippets/edit.html.haml @@ -1 +1,2 @@ += render "projects/project_head" = render "snippets/form" diff --git a/app/views/snippets/index.html.haml b/app/views/snippets/index.html.haml index bacf23d8..515daec6 100644 --- a/app/views/snippets/index.html.haml +++ b/app/views/snippets/index.html.haml @@ -1,19 +1,21 @@ -%h3.page_title - Snippets - %small share code pastes with others out of git repository += render "projects/project_head" - - if can? current_user, :write_snippet, @project - = link_to new_project_snippet_path(@project), class: "btn btn-small add_new pull-right", title: "New Snippet" do +- if can? current_user, :write_snippet, @project + .alert-message.block-message + = link_to new_project_snippet_path(@project), class: "btn small add_new right", title: "New Snippet" do Add new snippet -%br + Share code pastes with others if it can't be in a git repository + %br + To add new snippet - click on button. + %table %thead %tr %th Title %th File Name %th Expires At - = render @snippets - - if @snippets.empty? + = render @snippets.fresh + - if @snippets.fresh.empty? %tr %td{colspan: 3} %h3.nothing_here_message Nothing here. diff --git a/app/views/snippets/new.html.haml b/app/views/snippets/new.html.haml index f81c0b8b..8afaf46e 100644 --- a/app/views/snippets/new.html.haml +++ b/app/views/snippets/new.html.haml @@ -1 +1,2 @@ += render "projects/project_head" = render "snippets/form" diff --git a/app/views/snippets/show.html.haml b/app/views/snippets/show.html.haml index 12534edf..1b8701e9 100644 --- a/app/views/snippets/show.html.haml +++ b/app/views/snippets/show.html.haml @@ -1,9 +1,23 @@ -%h3.page_title += render "projects/project_head" + +%h3 = @snippet.title %small= @snippet.file_name - if can?(current_user, :admin_snippet, @project) || @snippet.author == current_user - = link_to "Edit", edit_project_snippet_path(@project, @snippet), class: "btn btn-small pull-right", title: 'Edit Snippet' + = link_to "Edit", edit_project_snippet_path(@project, @snippet), class: "btn small right" %br -%div= render 'blob' -%div#notes= render "notes/notes_with_form" +%div + .file_holder + .file_title + %i.icon-file + %strong= @snippet.file_name + %span.options + = link_to "raw", raw_project_snippet_path(@project, @snippet), class: "btn very_small", target: "_blank" + .file_content.code + %div{class: current_user.dark_scheme ? "black" : ""} + = raw @snippet.colorize(options: { linenos: 'True'}) + + +%div + = render "notes/notes_with_form", tid: @snippet.id, tt: "snippet" diff --git a/app/views/team_members/_assigned_team.html.haml b/app/views/team_members/_assigned_team.html.haml deleted file mode 100644 index 51a31a64..00000000 --- a/app/views/team_members/_assigned_team.html.haml +++ /dev/null @@ -1,10 +0,0 @@ -%li{id: dom_id(team), class: "user_team_row team_#{team.id}"} - .pull-right - - if can?(current_user, :admin_team_member, @project) - = link_to resign_project_team_path(@project, team), method: :delete, confirm: "Are you sure?", class: "btn btn-remove btn-tiny" do - %i.icon-minus.icon-white - - %strong= link_to team.name, team_path(team), title: team.name, class: "dark" - %br - %small.cgray Members: #{team.members.count} - %small.cgray Max access: #{team_relation.human_max_access} diff --git a/app/views/team_members/_assigned_teams.html.haml b/app/views/team_members/_assigned_teams.html.haml deleted file mode 100644 index 91c6d8f7..00000000 --- a/app/views/team_members/_assigned_teams.html.haml +++ /dev/null @@ -1,4 +0,0 @@ -.ui-box - %ul.well-list - - assigned_teams.sort_by(&:team_name).each do |team_relation| - = render "team_members/assigned_team", team_relation: team_relation, team: team_relation.user_team diff --git a/app/views/team_members/_form.html.haml b/app/views/team_members/_form.html.haml index 4e8f6770..92167138 100644 --- a/app/views/team_members/_form.html.haml +++ b/app/views/team_members/_form.html.haml @@ -1,24 +1,23 @@ %h3.page_title = "New Team member(s)" %hr -= form_for @user_project_relation, as: :team_member, url: project_team_members_path(@project) do |f| - -if @user_project_relation.errors.any? - .alert.alert-error += form_for @team_member, as: :team_member, url: project_team_members_path(@project, @team_member) do |f| + -if @team_member.errors.any? + .alert-message.block-message.error %ul - - @user_project_relation.errors.full_messages.each do |msg| + - @team_member.errors.full_messages.each do |msg| %li= msg %h6 1. Choose people you want in the team .clearfix = f.label :user_ids, "People" - .input - = users_select_tag(:user_ids, multiple: true) + .input= select_tag(:user_ids, options_from_collection_for_select(User.not_in_project(@project).all, :id, :name), {data: {placeholder: "Select users"}, class: "chosen xxlarge", multiple: true}) %h6 2. Set access level for them .clearfix = f.label :project_access, "Project Access" - .input= select_tag :project_access, options_for_select(Project.access_options, @user_project_relation.project_access), class: "project-access-select chosen" + .input= select_tag :project_access, options_for_select(Project.access_options, @team_member.project_access), class: "project-access-select chosen" .actions - = f.submit 'Add users', class: "btn btn-create" - = link_to "Cancel", project_team_index_path(@project), class: "btn btn-cancel" + = f.submit 'Save', class: "btn save-btn" + = link_to "Cancel", project_team_index_path(@project), class: "btn cancel-btn" diff --git a/app/views/team_members/_show.html.haml b/app/views/team_members/_show.html.haml new file mode 100644 index 00000000..f68f8eb4 --- /dev/null +++ b/app/views/team_members/_show.html.haml @@ -0,0 +1,26 @@ +- user = member.user +- allow_admin = can? current_user, :admin_project, @project +%tr{id: dom_id(member), class: "team_member_row user_#{user.id}"} + %td.span6 + = link_to project_team_member_path(@project, member), title: user.name, class: "dark" do + = image_tag gravatar_icon(user.email, 40), class: "avatar s32" + = link_to project_team_member_path(@project, member), title: user.name, class: "dark" do + %strong= truncate(user.name, lenght: 40) + %br + %small.cgray= user.email + + %td.span5 + .right + - if current_user == user + %span.btn.disabled This is you! + - if @project.owner == user + %span.btn.disabled.success Owner + - elsif user.blocked + %span.btn.disabled.blocked Blocked + - elsif allow_admin + = link_to project_team_member_path(project_id: @project, id: member.id), confirm: remove_from_team_message(@project, member), method: :delete, class: "very_small btn danger" do + %i.icon-minus.icon-white + + - if allow_admin + = form_for(member, as: :team_member, url: project_team_member_path(@project, member)) do |f| + = f.select :project_access, options_for_select(UsersProject.access_roles, member.project_access), {}, class: "medium project-access-select span2" diff --git a/app/views/team_members/_team.html.haml b/app/views/team_members/_team.html.haml index 2ec8c1a8..26d13533 100644 --- a/app/views/team_members/_team.html.haml +++ b/app/views/team_members/_team.html.haml @@ -1,8 +1,18 @@ -- team.each do |access, members| - .ui-box - %h5.title - = Project.access_options.key(access).pluralize - %small= members.size - %ul.well-list - - members.sort_by(&:user_name).each do |team_member| - = render 'team_members/team_member', member: team_member +- grouper_project_members(@project).each do |access, members| + %table.low + %thead + %tr + %th.span7 + = Project.access_options.key(access).pluralize + %th + %tbody + - members.each do |up| + = render(partial: 'team_members/show', locals: {member: up}) + + +:javascript + $(function(){ + $('.repo-access-select, .project-access-select').live("change", function() { + $(this.form).submit(); + }); + }) diff --git a/app/views/team_members/_team_member.html.haml b/app/views/team_members/_team_member.html.haml deleted file mode 100644 index 2b0709be..00000000 --- a/app/views/team_members/_team_member.html.haml +++ /dev/null @@ -1,27 +0,0 @@ -- user = member.user -- allow_admin = can? current_user, :admin_project, @project -%li{id: dom_id(user), class: "team_member_row user_#{user.id}"} - .row - .span4 - = link_to user, title: user.name, class: "dark" do - = image_tag gravatar_icon(user.email, 32), class: "avatar s32" - %strong= truncate(user.name, lenght: 40) - %br - %small.cgray= user.username - - .span4.pull-right - - if allow_admin - .left - = form_for(member, as: :team_member, url: project_team_member_path(@project, member.user)) do |f| - = f.select :project_access, options_for_select(UsersProject.access_roles, member.project_access), {}, class: "medium project-access-select span2 trigger-submit" - .pull-right - - if current_user == user - %span.label This is you! - - if @project.namespace_owner == user - %span.label Owner - - 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", title: 'Remove user from team' do - %i.icon-minus.icon-white - diff --git a/app/views/team_members/create.js.haml b/app/views/team_members/create.js.haml index b7dff35a..d5ae5d0c 100644 --- a/app/views/team_members/create.js.haml +++ b/app/views/team_members/create.js.haml @@ -1,4 +1,4 @@ -- if @user_project_relation.valid? +- if @team_member.valid? :plain $("#new_team_member").hide("slide", { direction: "right" }, 150, function(){ $("#team-table").show("slide", { direction: "left" }, 150, function() { diff --git a/app/views/team_members/import.html.haml b/app/views/team_members/import.html.haml index 0f7175b9..34f7fb03 100644 --- a/app/views/team_members/import.html.haml +++ b/app/views/team_members/import.html.haml @@ -1,17 +1,17 @@ -= render "projects/settings_nav" += render "projects/project_head" %h3.page_title = "Import team from another project" %hr %p.slead - Read more about project team import #{link_to "here", '#', class: 'vlink'}. + Read more about team import #{link_to "here", '#', class: 'vlink'}. = form_tag apply_import_project_team_members_path(@project), method: 'post' do %p.slead Choose project you want to use as team source: .padded = label_tag :source_project_id, "Project" - .input= select_tag(:source_project_id, options_from_collection_for_select(current_user.authorized_projects, :id, :name_with_namespace), prompt: "Select project", class: "chosen xxlarge", required: true) + .input= select_tag(:source_project_id, options_from_collection_for_select(current_user.projects, :id, :name), prompt: "Select project", class: "chosen xxlarge", required: true) .actions - = submit_tag 'Import', class: "btn btn-save" - = link_to "Cancel", project_team_index_path(@project), class: "btn btn-cancel" + = submit_tag 'Import', class: "btn save-btn" + = link_to "Cancel", project_team_index_path(@project), class: "btn cancel-btn" diff --git a/app/views/team_members/index.html.haml b/app/views/team_members/index.html.haml index 50d44bcd..ca3edcf7 100644 --- a/app/views/team_members/index.html.haml +++ b/app/views/team_members/index.html.haml @@ -1,56 +1,18 @@ -= render "projects/settings_nav" += render "projects/project_head" %h3.page_title Team Members - (#{@project.users.count}) - %small + %small (#{@project.users_projects.count}) + +- if can? current_user, :admin_team_member, @project + %p.slead Read more about project permissions %strong= link_to "here", help_permissions_path, class: "vlink" - - if can? current_user, :admin_team_member, @project - %span.pull-right - = link_to import_project_team_members_path(@project), class: "btn btn-small grouped", title: "Import team from another project" do + %span.right + = link_to import_project_team_members_path(@project), class: "btn small grouped", title: "Import team from another project" do Import team from another project - = link_to available_project_teams_path(@project), class: "btn btn-small grouped", title: "Assign project to team of users" do - Assign project to Team of users - = link_to new_project_team_member_path(@project), class: "btn btn-primary small grouped", title: "New Team Member" do + = link_to new_project_team_member_path(@project), class: "btn success small grouped", title: "New Team Member" do New Team Member -%hr - -.clearfix -.row - .span3 - %ul.nav.nav-pills.nav-stacked - %li{class: ("active" if !params[:type])} - = link_to project_team_members_path(type: nil) do - All - %li{class: ("active" if params[:type] == 'masters')} - = link_to project_team_members_path(type: 'masters') do - Masters - %span.pull-right= @project.users_projects.masters.count - %li{class: ("active" if params[:type] == 'developers')} - = link_to project_team_members_path(type: 'developers') do - Developers - %span.pull-right= @project.users_projects.developers.count - %li{class: ("active" if params[:type] == 'reporters')} - = link_to project_team_members_path(type: 'reporters') do - Reporters - %span.pull-right= @project.users_projects.reporters.count - %li{class: ("active" if params[:type] == 'guests')} - = link_to project_team_members_path(type: 'guests') do - Guests - %span.pull-right= @project.users_projects.guests.count - - - if @assigned_teams.present? - %h5 - Assigned teams - (#{@project.user_teams.count}) - %div - = render "team_members/assigned_teams", assigned_teams: @assigned_teams - - .span9 - %div.team-table - = render "team_members/team", team: @team - - - + .clearfix += render partial: "team_members/team", locals: {project: @project} diff --git a/app/views/team_members/new.html.haml b/app/views/team_members/new.html.haml index 7e20f50d..40eb4ceb 100644 --- a/app/views/team_members/new.html.haml +++ b/app/views/team_members/new.html.haml @@ -1,2 +1,2 @@ -= render "projects/settings_nav" += render "projects/project_head" = render "team_members/form" diff --git a/app/views/team_members/show.html.haml b/app/views/team_members/show.html.haml new file mode 100644 index 00000000..9d03cd2c --- /dev/null +++ b/app/views/team_members/show.html.haml @@ -0,0 +1,61 @@ +- allow_admin = can? current_user, :admin_project, @project +- user = @team_member.user + +.team_member_show + - if can? current_user, :admin_project, @project + = link_to 'Remove from team', project_team_member_path(project_id: @project, id: @team_member.id), confirm: 'Are you sure?', method: :delete, class: "right btn danger" + .profile_avatar_holder + = image_tag gravatar_icon(user.email, 60), class: "borders" + %h3 + = user.name + %small + = user.email + + %hr + .back_link + %br + = link_to project_team_index_path(@project), class: "" do + ← To team list + %br + .row + .span6 + %table.lite + %tr + %td Email + %td= mail_to user.email + %tr + %td Skype + %td= user.skype + - unless user.linkedin.blank? + %tr + %td LinkedIn + %td= user.linkedin + - unless user.twitter.blank? + %tr + %td Twitter + %td= user.twitter + - unless user.bio.blank? + %tr + %td Bio + %td= user.bio + .span6 + %table.lite + %tr + %td Member since + %td= @team_member.created_at.stamp("Aug 21, 2011") + %tr + %td + Project Access: + %small (#{link_to "read more", help_permissions_path, class: "vlink"}) + %td + = form_for(@team_member, as: :team_member, url: project_team_member_path(@project, @team_member)) do |f| + = f.select :project_access, options_for_select(Project.access_options, @team_member.project_access), {}, class: "project-access-select", disabled: !allow_admin + %hr + = render @events +:javascript + $(function(){ + $('.repo-access-select, .project-access-select').live("change", function() { + $(this.form).submit(); + }); + }) + diff --git a/app/views/team_members/update.js.haml b/app/views/team_members/update.js.haml index c68fe957..6d7f8816 100644 --- a/app/views/team_members/update.js.haml +++ b/app/views/team_members/update.js.haml @@ -1,6 +1,6 @@ -- if @user_project_relation.valid? +- if @team_member.valid? :plain - $("##{dom_id(@user_project_relation)}").effect("highlight", {color: "#529214"}, 1000);; + $("##{dom_id(@team_member)}").effect("highlight", {color: "#529214"}, 1000);; - else :plain - $("##{dom_id(@user_project_relation)}").effect("highlight", {color: "#D12F19"}, 1000);; + $("##{dom_id(@team_member)}").effect("highlight", {color: "#D12F19"}, 1000);; diff --git a/app/views/teams/_filter.html.haml b/app/views/teams/_filter.html.haml deleted file mode 100644 index f461fcad..00000000 --- a/app/views/teams/_filter.html.haml +++ /dev/null @@ -1,33 +0,0 @@ -= form_tag team_filter_path(entity), method: 'get' do - %fieldset.dashboard-search-filter - = search_field_tag "search", params[:search], { placeholder: 'Search', class: 'search-text-input' } - = button_tag type: 'submit', class: 'btn' do - %i.icon-search - - %fieldset - %legend Status: - %ul.nav.nav-pills.nav-stacked - %li{class: ("active" if !params[:status])} - = link_to team_filter_path(entity, status: nil) do - Open - %li{class: ("active" if params[:status] == 'closed')} - = link_to team_filter_path(entity, status: 'closed') do - Closed - %li{class: ("active" if params[:status] == 'all')} - = link_to team_filter_path(entity, status: 'all') do - All - - %fieldset - %legend Projects: - %ul.nav.nav-pills.nav-stacked - - @projects.each do |project| - - unless entities_per_project(project, entity).zero? - %li{class: ("active" if params[:project_id] == project.id.to_s)} - = link_to team_filter_path(entity, project_id: project.id) do - = project.name_with_namespace - %small.pull-right= entities_per_project(project, entity) - - %fieldset - %hr - = link_to "Reset", team_filter_path(entity), class: 'btn pull-right' - diff --git a/app/views/teams/_projects.html.haml b/app/views/teams/_projects.html.haml deleted file mode 100644 index d0061aa2..00000000 --- a/app/views/teams/_projects.html.haml +++ /dev/null @@ -1,22 +0,0 @@ -.ui-box - %h5.title - Projects - %small - (#{projects.count}) - - if can? current_user, :manage_user_team, @team - %span.pull-right - = link_to edit_team_path(@team), class: "btn btn-tiny info" do - %i.icon-plus - Assign Project - %ul.well-list - - if projects.blank? - %p.nothing_here_message This team has no projects yet - - projects.each do |project| - %li - = link_to project_path(project), class: dom_class(project) do - %strong.well-title= truncate(project.name_with_namespace, length: 40) - %span.arrow - → - %span.last_activity - %strong Last activity: - %span= project_last_activity(project) diff --git a/app/views/teams/edit.html.haml b/app/views/teams/edit.html.haml deleted file mode 100644 index c9d573ea..00000000 --- a/app/views/teams/edit.html.haml +++ /dev/null @@ -1,73 +0,0 @@ -.row - .span3 - %ul.nav.nav-pills.nav-stacked - %li.active - = link_to 'Projects', '#tab-projects', 'data-toggle' => 'tab' - %li - = link_to 'Edit Team', '#tab-edit', 'data-toggle' => 'tab' - %li - = link_to 'Remove', '#tab-remove', 'data-toggle' => 'tab' - - .span9 - .tab-content - .tab-pane.active#tab-projects - .ui-box.projects-table - %h5.title Projects - %ul.well-list - - @projects.each do |project| - %li - - if project.public - %i.icon-share - - else - %i.icon-lock.cgreen - = link_to project.name_with_namespace, project - .pull-right - = link_to 'Edit max access', edit_team_project_path(@team, project), class: "btn btn-small" - = link_to 'Relegate', team_project_path(@team, project), confirm: 'Remove project from team and move to global namespace. Are you sure?', method: :delete, class: "btn btn-remove small" - .form-holder - = form_tag team_projects_path(@team), id: "assign_projects", class: "bulk_import", method: :post do - %table.headless - %tr - %td= select_tag :project_ids, options_from_collection_for_select(@avaliable_projects , :id, :name_with_namespace), multiple: true, data: {placeholder: 'Select projects'}, class: 'chosen span4' - %td= select_tag :greatest_project_access, options_for_select(UserTeam.access_roles), {class: "project-access-select chosen" } - %td= submit_tag 'Add Project', class: "btn btn-create", id: :assign_projects_to_team - - - .tab-pane#tab-edit - .ui-box - %h5.title Edit Team - %div.form-holder - = form_for @team, url: team_path(@team) do |f| - - if @team.errors.any? - .alert.alert-error - %span= @team.errors.full_messages.first - .clearfix - = f.label :name do - Team name is - .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-primary" - - .tab-pane#tab-remove - .ui-box.ui-box-danger - %h5.title Remove team - .ui-box-body - %p - Remove of team will cause removing members access to projects. - %p - %strong 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" diff --git a/app/views/teams/issues.html.haml b/app/views/teams/issues.html.haml deleted file mode 100644 index 5b17c5d4..00000000 --- a/app/views/teams/issues.html.haml +++ /dev/null @@ -1,23 +0,0 @@ -%h3.page_title - Issues - %small (in Team projects assigned to Team members) - %small.pull-right #{@issues.total_count} issues - -%hr -.row - .span3 - = render 'filter', entity: 'issue' - .span9 - - if @issues.any? - - @issues.group_by(&:project).each do |group| - %div.ui-box - - @project = group[0] - %h5.title - = link_to_project @project - %ul.well-list.issues_table - - group[1].each do |issue| - = render issue - %hr - = paginate @issues, theme: "gitlab" - - else - %p.nothing_here_message Nothing to show here diff --git a/app/views/teams/members/_form.html.haml b/app/views/teams/members/_form.html.haml deleted file mode 100644 index c22ee783..00000000 --- a/app/views/teams/members/_form.html.haml +++ /dev/null @@ -1,20 +0,0 @@ -= form_tag admin_team_member_path(@team, @member), method: :put do - -if @member.errors.any? - .alert.alert-error - %ul - - @member.errors.full_messages.each do |msg| - %li= msg - - .clearfix - %label Default access for Team projects: - .input - = select_tag :default_project_access, options_for_select(UserTeam.access_roles, @team.default_projects_access(@member)), class: "project-access-select chosen span3" - .clearfix - %label Team admin? - .input - = check_box_tag :group_admin, true, @team.admin?(@member) - - %br - .actions - = submit_tag 'Save', class: "btn btn-save" - = link_to 'Cancel', :back, class: "btn" diff --git a/app/views/teams/members/_show.html.haml b/app/views/teams/members/_show.html.haml deleted file mode 100644 index 1a323043..00000000 --- a/app/views/teams/members/_show.html.haml +++ /dev/null @@ -1,30 +0,0 @@ -- user = member.user -- allow_admin = can? current_user, :manage_user_team, @team -%li{id: dom_id(member), class: "team_member_row user_#{user.id}"} - .row - .span5 - = link_to user_path(user.username), title: user.name, class: "dark" do - = image_tag gravatar_icon(user.email, 40), class: "avatar s32" - = link_to user_path(user.username), title: user.name, class: "dark" do - %strong= truncate(user.name, lenght: 40) - %br - %small.cgray= user.username - - .span4 - - if allow_admin - = form_for(member, as: :team_member, url: team_member_path(@team, user)) do |f| - = f.select :permission, options_for_select(UsersProject.access_roles, @team.default_projects_access(user)), {}, class: "medium trigger-submit" - %br - = label_tag do - = f.check_box :group_admin, class: 'trigger-submit' - %span Admin access - .pull-right - - if current_user == user - %span.btn.disabled This is you! - - if @team.owner == user - %span.btn.disabled Owner - - 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 - %i.icon-minus.icon-white diff --git a/app/views/teams/members/_team.html.haml b/app/views/teams/members/_team.html.haml deleted file mode 100644 index d8afc1fa..00000000 --- a/app/views/teams/members/_team.html.haml +++ /dev/null @@ -1,16 +0,0 @@ -- grouped_user_team_members(@team).each do |access, members| - .ui-box - %h5.title - = Project.access_options.key(access).pluralize - %small= members.size - %ul.well-list - - members.sort_by(&:user_name).each do |up| - = render(partial: 'teams/members/show', locals: {member: up}) - - -:javascript - $(function(){ - $('.repo-access-select, .project-access-select').live("change", function() { - $(this.form).submit(); - }); - }) diff --git a/app/views/teams/members/edit.html.haml b/app/views/teams/members/edit.html.haml deleted file mode 100644 index 37588049..00000000 --- a/app/views/teams/members/edit.html.haml +++ /dev/null @@ -1,16 +0,0 @@ -%h3.page_title - Edit access #{@member.name} in #{@team.name} team - -%hr -%table.zebra-striped - %tr - %td User: - %td= @member.name - %tr - %td Team: - %td= @team.name - %tr - %td Since: - %td= member_since(@team, @member).stamp("Nov 11, 2010") - -= render 'form' diff --git a/app/views/teams/members/index.html.haml b/app/views/teams/members/index.html.haml deleted file mode 100644 index 87438266..00000000 --- a/app/views/teams/members/index.html.haml +++ /dev/null @@ -1,17 +0,0 @@ -%h3.page_title - Team Members - (#{@members.count}) - %small - Read more about project permissions - %strong= link_to "here", help_permissions_path, class: "vlink" - - - if can? current_user, :manage_user_team, @team - %span.pull-right - = link_to new_team_member_path(@team), class: "btn btn-primary small grouped", title: "New Team Member" do - New Team Member -%hr - - -.clearfix -%div.team-table - = render partial: "teams/members/team", locals: {project: @team} diff --git a/app/views/teams/members/new.html.haml b/app/views/teams/members/new.html.haml deleted file mode 100644 index 9b9b3cef..00000000 --- a/app/views/teams/members/new.html.haml +++ /dev/null @@ -1,29 +0,0 @@ -%h3.page_title - Team: #{@team.name} - -%fieldset - %legend Members (#{@team.members.count}) - = form_tag team_members_path(@team), id: "team_members", class: "bulk_import", method: :post do - %table#members_list - %thead - %tr - %th User name - %th Default project access - %th Team access - %th - - @team.members.each do |member| - %tr.member - %td - = member.name - %small= "(#{member.username})" - %td= @team.human_default_projects_access(member) - %td= @team.admin?(member) ? "Admin" : "Member" - %td - %tr - %td - = users_select_tag(:user_ids, multiple: true) - %td= select_tag :default_project_access, options_for_select(Project.access_options), {class: "project-access-select chosen span3" } - %td - %span= check_box_tag :group_admin - %span Admin? - %td= submit_tag 'Add User', class: "btn btn-create", id: :add_members_to_team diff --git a/app/views/teams/merge_requests.html.haml b/app/views/teams/merge_requests.html.haml deleted file mode 100644 index 417d1aa6..00000000 --- a/app/views/teams/merge_requests.html.haml +++ /dev/null @@ -1,24 +0,0 @@ -%h3.page_title - Merge Requests - %small (authored by or assigned to Team members) - %small.pull-right #{@merge_requests.total_count} merge requests - -%hr -.row - .span3 - = render 'filter', entity: 'merge_request' - .span9 - - if @merge_requests.any? - - @merge_requests.group_by(&:project).each do |group| - .ui-box - - @project = group[0] - %h5.title - = link_to_project @project - %ul.well-list - - group[1].each do |merge_request| - = render(partial: 'merge_requests/merge_request', locals: {merge_request: merge_request}) - %hr - = paginate @merge_requests, theme: "gitlab" - - - else - %h3.nothing_here_message Nothing to show here diff --git a/app/views/teams/new.html.haml b/app/views/teams/new.html.haml deleted file mode 100644 index 99d30821..00000000 --- a/app/views/teams/new.html.haml +++ /dev/null @@ -1,39 +0,0 @@ -%h3.page_title New Team -%hr -= form_for @team, url: teams_path do |f| - - if @team.errors.any? - .alert.alert-error - %span= @team.errors.full_messages.first - .clearfix - = f.label :name do - Team name is - .input - = f.text_field :name, placeholder: "Ex. Ruby Developers", class: "xxlarge left" - - .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) - %li People within a team see only projects they have access to - %li You will be able to assign existing projects for team - %hr - - - if current_user.can_create_group? - .clearfix - .input.light - Need a group for several dependent projects? - = link_to new_group_path, class: "btn btn-tiny" do - Create a group - - if current_user.can_create_project? - .clearfix - .input.light - Want to create a project? - = link_to new_project_path, class: "btn btn-tiny" do - Create a project diff --git a/app/views/teams/projects/_form.html.haml b/app/views/teams/projects/_form.html.haml deleted file mode 100644 index d2c89b0c..00000000 --- a/app/views/teams/projects/_form.html.haml +++ /dev/null @@ -1,16 +0,0 @@ -= form_tag team_project_path(@team, @project), method: :put do - -if @project.errors.any? - .alert.alert-error - %ul - - @project.errors.full_messages.each do |msg| - %li= msg - - .clearfix - %label Max access for Team members: - .input - = select_tag :greatest_project_access, options_for_select(UserTeam.access_roles, @team.max_project_access(@project)), class: "project-access-select chosen span3" - - %br - .actions - = submit_tag 'Save', class: "btn btn-save" - = link_to 'Cancel', :back, class: "btn btn-cancel" diff --git a/app/views/teams/projects/edit.html.haml b/app/views/teams/projects/edit.html.haml deleted file mode 100644 index 82c7d734..00000000 --- a/app/views/teams/projects/edit.html.haml +++ /dev/null @@ -1,6 +0,0 @@ -%h3.page_title - Edit max access in #{link_to @project.name_with_namespace, @project} for #{link_to(@team.name, team_path(@team))} team - -%hr - -= render 'form' diff --git a/app/views/teams/show.atom.builder b/app/views/teams/show.atom.builder deleted file mode 100644 index bb0f666e..00000000 --- a/app/views/teams/show.atom.builder +++ /dev/null @@ -1,29 +0,0 @@ -xml.instruct! -xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do - xml.title "Team feed - #{@team.name}" - xml.link :href => team_url(@team, :atom), :rel => "self", :type => "application/atom+xml" - xml.link :href => team_url(@team), :rel => "alternate", :type => "text/html" - xml.id projects_url - xml.updated @events.maximum(:updated_at).strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any? - - @events.each do |event| - if event.proper? - event = EventDecorator.decorate(event) - xml.entry do - event_link = event.feed_url - event_title = event.feed_title - - xml.id "tag:#{request.host},#{event.created_at.strftime("%Y-%m-%d")}:#{event.id}" - xml.link :href => event_link - xml.title truncate(event_title, :length => 80) - xml.updated event.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") - xml.media :thumbnail, :width => "40", :height => "40", :url => gravatar_icon(event.author_email) - xml.author do |author| - xml.name event.author_name - xml.email event.author_email - end - xml.summary event_title - end - end - end -end diff --git a/app/views/teams/show.html.haml b/app/views/teams/show.html.haml deleted file mode 100644 index 2eb0283e..00000000 --- a/app/views/teams/show.html.haml +++ /dev/null @@ -1,31 +0,0 @@ -.projects - .activities.span8 - = link_to dashboard_path, class: 'btn btn-tiny' do - ← To dashboard -   - %span.cgray Events and projects are filtered in scope of team - %hr - - if @events.any? - .content_list - - else - %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 - .prepend-top-20 - = link_to team_path(@team, { format: :atom, private_token: current_user.private_token }), title: "Feed" do - %strong - %i.icon-rss - News Feed - - %hr - .gitlab-promo - = link_to "Homepage", "http://gitlabhq.com" - = link_to "Blog", "http://blog.gitlabhq.com" - = link_to "@gitlabhq", "https://twitter.com/gitlabhq" - -:javascript - $(function(){ Pager.init(20, true); }); diff --git a/app/views/teams/show.js.haml b/app/views/teams/show.js.haml deleted file mode 100644 index 7e5a148e..00000000 --- a/app/views/teams/show.js.haml +++ /dev/null @@ -1,2 +0,0 @@ -:plain - Pager.append(#{@events.count}, "#{escape_javascript(render(@events))}"); diff --git a/app/views/tree/_blob_actions.html.haml b/app/views/tree/_blob_actions.html.haml index 1d55a4ff..21334ea1 100644 --- a/app/views/tree/_blob_actions.html.haml +++ b/app/views/tree/_blob_actions.html.haml @@ -1,12 +1,12 @@ .btn-group.tree-btn-group -# only show edit link for text files - if @tree.text? - = link_to "edit", project_edit_tree_path(@project, @id), class: "btn btn-tiny", disabled: !allowed_tree_edit? - = link_to "raw", project_blob_path(@project, @id), class: "btn btn-tiny", target: "_blank" + = link_to "edit", edit_project_tree_path(@project, @id), class: "btn very_small", disabled: !allowed_tree_edit? + = link_to "raw", project_blob_path(@project, @id), class: "btn very_small", target: "_blank" -# only show normal/blame view links for text files - if @tree.text? - if current_page? project_blame_path(@project, @id) - = link_to "normal view", project_tree_path(@project, @id), class: "btn btn-tiny" + = link_to "normal view", project_tree_path(@project, @id), class: "btn very_small" - else - = link_to "blame", project_blame_path(@project, @id), class: "btn btn-tiny" - = link_to "history", project_commits_path(@project, @id), class: "btn btn-tiny" + = link_to "blame", project_blame_path(@project, @id), class: "btn very_small" + = link_to "history", project_commits_path(@project, @id), class: "btn very_small" diff --git a/app/views/tree/_head.html.haml b/app/views/tree/_head.html.haml new file mode 100644 index 00000000..f8e5c99f --- /dev/null +++ b/app/views/tree/_head.html.haml @@ -0,0 +1,7 @@ +%ul.nav.nav-tabs + %li + = render partial: 'shared/ref_switcher', locals: {destination: 'tree', path: @path} + = nav_link(controller: :tree) do + = link_to 'Source', project_tree_path(@project, @ref) + %li.right + = render "shared/clone_panel" \ No newline at end of file diff --git a/app/views/tree/_submodule_item.html.haml b/app/views/tree/_submodule_item.html.haml index 092a024a..b0562fe1 100644 --- a/app/views/tree/_submodule_item.html.haml +++ b/app/views/tree/_submodule_item.html.haml @@ -1,6 +1,6 @@ - url = submodule_item.url(@ref) rescue '' - name = submodule_item.basename -- return '' unless url +- return unless url %tr{ class: "tree-item", url: url } %td.tree-item-file-name = image_tag "submodule.png" diff --git a/app/views/tree/_tree.html.haml b/app/views/tree/_tree.html.haml index caab6e45..02ae3d90 100644 --- a/app/views/tree/_tree.html.haml +++ b/app/views/tree/_tree.html.haml @@ -1,15 +1,14 @@ %ul.breadcrumb %li - %i.icon-angle-right + %span.arrow = link_to project_tree_path(@project, @ref) do - = @project.path - - tree_breadcrumbs(tree, 6) do |title, path| + = @project.name + - tree.breadcrumbs(6) do |link| \/ - %li - - if path - = link_to truncate(title, length: 40), project_tree_path(@project, path) - - else - = link_to title, '#' + %li= link + +.clear +%div.tree_progress %div#tree-content-holder.tree-content-holder - if tree.is_blob? @@ -17,17 +16,16 @@ - else %table#tree-slider{class: "table_#{@hex_path} tree-table" } %thead - %tr - %th Name - %th Last Update - %th Last Commit - %th= link_to "history", project_commits_path(@project, @id), class: "btn btn-tiny pull-right" + %th Name + %th Last Update + %th Last Commit + %th= link_to "history", project_commits_path(@project, @id), class: "btn very_small right" - if tree.up_dir? %tr.tree-item %td.tree-item-file-name = image_tag "file_empty.png", size: '16x16' - = link_to "..", project_tree_path(@project, up_dir_path(tree)) + = link_to "..", tree.up_dir_path %td %td %td @@ -37,8 +35,6 @@ - if tree.readme = render "tree/readme", readme: tree.readme -%div.tree_progress - - unless tree.is_blob? :javascript // Load last commit log for each file in tree diff --git a/app/views/tree/_tree_commit_column.html.haml b/app/views/tree/_tree_commit_column.html.haml index 7ae2582c..9d02132b 100644 --- a/app/views/tree/_tree_commit_column.html.haml +++ b/app/views/tree/_tree_commit_column.html.haml @@ -1,2 +1,2 @@ -%span.tree_author= commit_author_link(commit, avatar: true) +%span.tree_author= commit.author_link avatar: true = link_to_gfm truncate(commit.title, length: 80), project_commit_path(@project, commit.id), class: "tree-commit-link" diff --git a/app/views/tree/blob/_download.html.haml b/app/views/tree/blob/_download.html.haml index 864c209d..c3076229 100644 --- a/app/views/tree/blob/_download.html.haml +++ b/app/views/tree/blob/_download.html.haml @@ -2,7 +2,7 @@ %center = link_to project_blob_path(@project, @id) do %div.padded - %h4 - %i.icon-download-alt - %br + %br + = image_tag "download.png", width: 64 + %h3 Download (#{number_to_human_size blob.size}) diff --git a/app/views/tree/blob/_text.html.haml b/app/views/tree/blob/_text.html.haml index 122e2752..9e0f4bc4 100644 --- a/app/views/tree/blob/_text.html.haml +++ b/app/views/tree/blob/_text.html.haml @@ -8,7 +8,8 @@ - else .file_content.code - unless blob.empty? - %div{class: user_color_scheme_class} - = raw blob.colorize(formatter: :gitlab) + %div{class: current_user.dark_scheme ? "black" : "white"} + = preserve do + = raw blob.colorize(formatter: :gitlab) - else - %p.nothing_here_message Empty file + %h4.nothing_here_message Empty file diff --git a/app/views/edit_tree/show.html.haml b/app/views/tree/edit.html.haml similarity index 86% rename from app/views/edit_tree/show.html.haml rename to app/views/tree/edit.html.haml index 211396ba..adee68a0 100644 --- a/app/views/edit_tree/show.html.haml +++ b/app/views/tree/edit.html.haml @@ -1,5 +1,5 @@ .file-editor - = form_tag(project_edit_tree_path(@project, @id), method: :put, class: "form-horizontal") do + = form_tag(project_tree_path(@project, @id), method: :put, class: "form-horizontal") do .file_holder .file_title %i.icon-file @@ -10,7 +10,7 @@ %strong= @ref %span.options .btn-group.tree-btn-group - = link_to "Cancel", project_tree_path(@project, @id), class: "btn btn-tiny btn-cancel", confirm: "Are you sure?" + = link_to "Cancel", project_tree_path(@project, @id), class: "btn very_small cancel-btn", confirm: "Are you sure?" .file_content.code %pre#editor= @tree.data @@ -27,7 +27,7 @@ .message to branch %strong= @ref - = link_to "Cancel", project_tree_path(@project, @id), class: "btn btn-cancel", confirm: "Are you sure?" + = link_to "Cancel", project_tree_path(@project, @id), class: "btn cancel-btn", confirm: "Are you sure?" :javascript var ace_mode = "#{@tree.language.try(:ace_mode)}"; diff --git a/app/views/tree/show.html.haml b/app/views/tree/show.html.haml index 0f7692ab..a4034f22 100644 --- a/app/views/tree/show.html.haml +++ b/app/views/tree/show.html.haml @@ -1,4 +1,3 @@ -%div.tree-ref-holder - = render 'shared/ref_switcher', destination: 'tree', path: @path += render "head" %div#tree-holder.tree-holder = render "tree", tree: @tree diff --git a/app/views/tree/show.js.haml b/app/views/tree/show.js.haml index a01d4917..fadd5e22 100644 --- a/app/views/tree/show.js.haml +++ b/app/views/tree/show.js.haml @@ -1,7 +1,7 @@ :plain // Load Files list $("#tree-holder").html("#{escape_javascript(render(partial: "tree", locals: {tree: @tree}))}"); - $("#tree-content-holder").show("slide", { direction: "right" }, 400); + $("#tree-content-holder").show("slide", { direction: "right" }, 150); $('.project-refs-form #path').val("#{@path}"); // Load last commit log for each file in tree diff --git a/app/views/users/_profile.html.haml b/app/views/users/_profile.html.haml deleted file mode 100644 index fba3660b..00000000 --- a/app/views/users/_profile.html.haml +++ /dev/null @@ -1,23 +0,0 @@ -.ui-box - %h5.title - Profile - %ul.well-list - %li - %span.light Member since - %strong= user.created_at.stamp("Aug 21, 2011") - - unless user.skype.blank? - %li - %span.light Skype: - %strong= user.skype - - unless user.linkedin.blank? - %li - %span.light LinkedIn: - %strong= user.linkedin - - unless user.twitter.blank? - %li - %span.light Twitter: - %strong= user.twitter - - unless user.bio.blank? - %li - %span.light Bio: - %span= user.bio diff --git a/app/views/users/_projects.html.haml b/app/views/users/_projects.html.haml deleted file mode 100644 index 4bee2f0c..00000000 --- a/app/views/users/_projects.html.haml +++ /dev/null @@ -1,20 +0,0 @@ -.ui-box - %h5.title Projects - %ul.well-list - - @projects.each do |project| - %li - = link_to project_path(project), class: dom_class(project) do - - if project.namespace - = project.namespace.human_name - \/ - %strong.well-title - = truncate(project.name, length: 45) - %span.pull-right.light - - if project.owner == user - %i.icon-wrench - - tm = project.team.get_tm(user.id) - - if tm - = tm.project_access_human -%p.light - %i.icon-wrench - – user is a project owner diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml deleted file mode 100644 index 10bd90b1..00000000 --- a/app/views/users/show.html.haml +++ /dev/null @@ -1,21 +0,0 @@ -.row - .span8 - %h3.page_title - = image_tag gravatar_icon(@user.email, 90), class: "avatar s90" - = @user.name - - if @user == current_user - .pull-right - = link_to profile_path, class: 'btn btn-small' do - %i.icon-edit - Edit Profile - %br - %small #{@user.username} - %br - %small member since #{@user.created_at.stamp("Nov 12, 2031")} - .clearfix - %hr - %h5 Recent events - = render @events - .span4 - = render 'profile', user: @user - = render 'projects', user: @user diff --git a/app/views/walls/show.html.haml b/app/views/walls/show.html.haml deleted file mode 100644 index 139e66f5..00000000 --- a/app/views/walls/show.html.haml +++ /dev/null @@ -1,28 +0,0 @@ -%div.wall-page - %ul.notes - - - if can? current_user, :write_note, @project - .note-form-holder - = form_for [@project, @note], remote: true, html: { multipart: true, id: nil, class: "new_note wall-note-form" } do |f| - = note_target_fields - .note_text_and_preview - = f.text_area :note, size: 255, class: 'note_text js-note-text js-gfm-input turn-on' - .note-form-actions - .buttons - = f.submit 'Add Comment', class: "btn comment-btn grouped js-comment-button" - - .note-form-option - %a.choose-btn.btn.btn-small.js-choose-note-attachment-button - %i.icon-paper-clip - %span Choose File ... -   - %span.file_name.js-attachment-filename File name... - = f.file_field :attachment, class: "js-note-attachment-input hide" - - .hint.pull-right CTRL + Enter to send message - .clearfix - -:javascript - $(function(){ - Wall.init(#{@project.id}); - }); diff --git a/app/views/wikis/_form.html.haml b/app/views/wikis/_form.html.haml index 7cf08815..83b16b13 100644 --- a/app/views/wikis/_form.html.haml +++ b/app/views/wikis/_form.html.haml @@ -6,15 +6,12 @@ - @wiki.errors.full_messages.each do |msg| %li= msg - .ui-box.ui-box-show - .ui-box-head - %h3.page_title - .edit-wiki-header - = @wiki.title.titleize - = f.hidden_field :title, value: @wiki.title - = f.select :format, options_for_select(GollumWiki::MARKUPS, {selected: @wiki.format}), {}, class: "pull-right input-medium" - = f.label :format, class: "pull-right", style: "padding-right: 20px;" - .ui-box-body + .main_box + .top_box_content + = f.label :title + .input= f.text_field :title, class: 'span8' + = f.hidden_field :slug + .middle_box_content .input %span.cgray Wiki content is parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}. @@ -22,15 +19,9 @@ %code [Link Title](page-slug) \. - .ui-box-bottom + .bottom_box_content = f.label :content .input= f.text_area :content, class: 'span8 js-gfm-input' - .ui-box-bottom - = f.label :commit_message - .input= f.text_field :message, class: 'span8' .actions - = f.submit 'Save', class: "btn-save btn" - - if @wiki && @wiki.persisted? - = link_to "Cancel", project_wiki_path(@project, @wiki), class: "btn btn-cancel" - - else - = link_to "Cancel", project_wiki_path(@project, :home), class: "btn btn-cancel" + = f.submit 'Save', class: "save-btn btn" + = link_to "Cancel", project_wiki_path(@project, :index), class: "btn cancel-btn" diff --git a/app/views/wikis/_main_links.html.haml b/app/views/wikis/_main_links.html.haml deleted file mode 100644 index cb8ccf81..00000000 --- a/app/views/wikis/_main_links.html.haml +++ /dev/null @@ -1,8 +0,0 @@ -%span.pull-right - - if (@wiki && @wiki.persisted?) - = link_to history_project_wiki_path(@project, @wiki), class: "btn btn-small grouped" do - Page History - - if can?(current_user, :write_wiki, @project) - = link_to edit_project_wiki_path(@project, @wiki), class: "btn btn-small grouped" do - %i.icon-edit - Edit diff --git a/app/views/wikis/_nav.html.haml b/app/views/wikis/_nav.html.haml deleted file mode 100644 index 0dffdd8f..00000000 --- a/app/views/wikis/_nav.html.haml +++ /dev/null @@ -1,19 +0,0 @@ -%ul.nav.nav-tabs - = nav_link(html_options: {class: params[:id] == 'home' ? 'active' : '' }) do - = link_to 'Home', project_wiki_path(@project, :home) - - = nav_link(path: 'wikis#pages') do - = link_to 'Pages', pages_project_wikis_path(@project) - - = nav_link(path: 'wikis#git_access') do - = link_to git_access_project_wikis_path(@project) do - %i.icon-download-alt - Git Access - - - if can?(current_user, :write_wiki, @project) - %li.pull-right - = link_to '#', class: "add-new-wiki" do - %i.icon-plus - New Page - -= render 'wikis/new' diff --git a/app/views/wikis/_new.html.haml b/app/views/wikis/_new.html.haml deleted file mode 100644 index 50b40bff..00000000 --- a/app/views/wikis/_new.html.haml +++ /dev/null @@ -1,25 +0,0 @@ -%div#modal-new-wiki.modal.hide - .modal-header - %a.close{href: "#"} × - %h3 New Wiki Page - .modal-body - = label_tag :new_wiki_path do - %span Page slug - = text_field_tag :new_wiki_path, nil, placeholder: 'how-to-setup', class: 'input-xlarge' - .modal-footer - = link_to 'Build', '#', class: 'build-new-wiki btn btn-create' - -:javascript - $(function(){ - var modal = $('#modal-new-wiki').modal({modal: true, show:false}); - $('.add-new-wiki').bind("click", function(){ - modal.show(); - }); - $('.build-new-wiki').bind("click", function(){ - location.href = "#{project_wikis_path(@project)}/" + $('#new_wiki_path').val(); - }); - $('.modal-header .close').bind("click", function(){ - modal.hide(); - }) - }) - diff --git a/app/views/wikis/edit.html.haml b/app/views/wikis/edit.html.haml index 7441ceff..27d2a8f9 100644 --- a/app/views/wikis/edit.html.haml +++ b/app/views/wikis/edit.html.haml @@ -1,10 +1,3 @@ -= render 'wikis/nav' -%h3.page_title - Editing page - = render partial: 'main_links' +%h3.page_title Editing page +%hr = render 'form' - -.pull-right - - if @wiki.persisted? && can?(current_user, :admin_wiki, @project) - = link_to project_wiki_path(@project, @wiki), confirm: "Are you sure you want to delete this page?", method: :delete, class: "btn btn-small btn-remove" do - Delete this page diff --git a/app/views/wikis/empty.html.haml b/app/views/wikis/empty.html.haml index 08b59f03..32b1c925 100644 --- a/app/views/wikis/empty.html.haml +++ b/app/views/wikis/empty.html.haml @@ -1,4 +1,4 @@ %h3.page_title Empty page %hr -.error_message - You are not allowed to create wiki pages +.alert-message.block-message.warning + %span You are not allowed to create wiki pages diff --git a/app/views/wikis/git_access.html.haml b/app/views/wikis/git_access.html.haml deleted file mode 100644 index 58c8aa06..00000000 --- a/app/views/wikis/git_access.html.haml +++ /dev/null @@ -1,37 +0,0 @@ -= render 'wikis/nav' -%h3.page_title - Git Access - %strong= @gollum_wiki.path_with_namespace - = render partial: 'main_links' - -%br -.content - .project_clone_panel - .row - .span7 - .form-horizontal - .input-prepend.project_clone_holder - %button{class: "btn active", :"data-clone" => @gollum_wiki.ssh_url_to_repo} SSH - %button{class: "btn", :"data-clone" => @gollum_wiki.http_url_to_repo}= Gitlab.config.gitlab.protocol.upcase - = text_field_tag :project_clone, @gollum_wiki.url_to_repo, class: "one_click_select input-xxlarge", readonly: true - .git-empty - %fieldset - %legend Install Gollum: - %pre.dark - :preserve - gem install gollum - - %legend Clone Your Wiki: - %pre.dark - :preserve - git clone #{@gollum_wiki.path_with_namespace}.git - cd #{@gollum_wiki.path_with_namespace} - - %legend Start Gollum And Edit Locally: - %pre.dark - :preserve - gollum - == Sinatra/1.3.5 has taken the stage on 4567 for development with backup from Thin - >> Thin web server (v1.5.0 codename Knife) - >> Maximum connections set to 1024 - >> Listening on 0.0.0.0:4567, CTRL+C to stop diff --git a/app/views/wikis/history.html.haml b/app/views/wikis/history.html.haml index f4946ed0..0a81817c 100644 --- a/app/views/wikis/history.html.haml +++ b/app/views/wikis/history.html.haml @@ -1,31 +1,20 @@ -= render 'wikis/nav' %h3.page_title - %span.light History for - = @wiki.title.titleize - = render partial: 'main_links' + %span.cgray History for + = @wikis.last.title %br %table %thead %tr - %th Page version - %th Author - %th Commit Message - %th Last updated - %th Format + %th # + %th last edit + %th created by %tbody - - @wiki.versions.each do |version| - - commit = version + - @wikis.each_with_index do |wiki_page, i| %tr + %td= i + 1 %td - = link_to project_wiki_path(@project, @wiki, version_id: commit.id) do - = commit.short_id - %td - = commit_author_link(commit, avatar: true, size: 24) - %td - = commit.title - %td - = time_ago_in_words(version.date) - ago - %td - %strong - = @wiki.page.wiki.page(@wiki.page.name, commit.id).try(:format) + = link_to wiki_page.created_at.to_s(:short), project_wiki_path(@project, wiki_page, old_page_id: wiki_page.id) + (#{time_ago_in_words(wiki_page.created_at)} + ago) + %td= wiki_page.user.name + diff --git a/app/views/wikis/pages.html.haml b/app/views/wikis/pages.html.haml index 95d5eef1..7421d8f9 100644 --- a/app/views/wikis/pages.html.haml +++ b/app/views/wikis/pages.html.haml @@ -1,25 +1,18 @@ -= render 'wikis/nav' -%h3.page_title - All Pages - = render partial: 'main_links' +%h3.page_title All Pages %br %table %thead %tr %th Title - %th Format - %th Last updated - %th Updated by + %th slug + %th created by %tbody - - @wiki_pages.each do |wiki_page| + - @wikis.each_with_index do |wiki_page, i| %tr %td - %strong= link_to wiki_page.title.titleize, project_wiki_path(@project, wiki_page) - %td - %strong= wiki_page.format - %td - = wiki_page.created_at.to_s(:short) do - (#{time_ago_in_words(wiki_page.created_at)} - ago) - %td - = commit_author_link(wiki_page.version, avatar: true, size: 24) + = link_to wiki_page.title, project_wiki_path(@project, wiki_page, old_page_id: wiki_page.id) + (#{time_ago_in_words(wiki_page.created_at)} + ago) + %td= wiki_page.slug + %td= wiki_page.user.name + diff --git a/app/views/wikis/show.html.haml b/app/views/wikis/show.html.haml index b237bc52..579ea1b3 100644 --- a/app/views/wikis/show.html.haml +++ b/app/views/wikis/show.html.haml @@ -1,16 +1,24 @@ -= render 'wikis/nav' %h3.page_title - = @wiki.title.titleize - = render partial: 'main_links' + = @wiki.title + %span.right + = link_to pages_project_wikis_path(@project), class: "btn small grouped" do + Pages + - if can? current_user, :write_wiki, @project + = link_to history_project_wiki_path(@project, @wiki), class: "btn small grouped" do + History + = link_to edit_project_wiki_path(@project, @wiki), class: "btn small grouped" do + %i.icon-edit + Edit %br -- if @wiki.historical? - .warning_message - This is an old version of this page. - You can view the #{link_to "most recent version", project_wiki_path(@project, @wiki)} or browse the #{link_to "history", history_project_wiki_path(@project, @wiki)}. - .file_holder .file_content.wiki = preserve do - = render_wiki_content(@wiki) + = markdown @wiki.content -%p.time Last edited by #{commit_author_link(@wiki.version, avatar: true, size: 16)} #{time_ago_in_words @wiki.created_at} ago +%p.time Last edited by #{@wiki.user.name}, #{time_ago_in_words @wiki.created_at} ago +- if can? current_user, :admin_wiki, @project + = link_to project_wiki_path(@project, @wiki), confirm: "Are you sure you want to delete this page?", method: :delete do + Delete this page + +%hr +.wiki_notes#notes= render "notes/notes_with_form", tid: @wiki.id, tt: "wiki" diff --git a/app/workers/gitlab_shell_worker.rb b/app/workers/gitlab_shell_worker.rb deleted file mode 100644 index cfeda88b..00000000 --- a/app/workers/gitlab_shell_worker.rb +++ /dev/null @@ -1,10 +0,0 @@ -class GitlabShellWorker - include Sidekiq::Worker - include Gitlab::ShellAdapter - - sidekiq_options queue: :gitlab_shell - - def perform(action, *arg) - gitlab_shell.send(action, *arg) - end -end diff --git a/app/workers/post_receive.rb b/app/workers/post_receive.rb index 72cef0fd..10128660 100644 --- a/app/workers/post_receive.rb +++ b/app/workers/post_receive.rb @@ -1,47 +1,21 @@ class PostReceive - include Sidekiq::Worker + @queue = :post_receive - sidekiq_options queue: :post_receive + def self.perform(reponame, oldrev, newrev, ref, identifier) + project = Project.find_by_path(reponame) + return false if project.nil? - def perform(repo_path, oldrev, newrev, ref, identifier) - - if repo_path.start_with?(Gitlab.config.gitlab_shell.repos_path.to_s) - repo_path.gsub!(Gitlab.config.gitlab_shell.repos_path.to_s, "") + # Ignore push from non-gitlab users + user = if identifier.eql? Gitlab.config.gitolite_admin_key + email = project.commit(newrev).author.email + User.find_by_email(email) + elsif /^[A-Z0-9._%a-z\-]+@(?:[A-Z0-9a-z\-]+\.)+[A-Za-z]{2,4}$/.match(identifier) + User.find_by_email(identifier) else - Gitlab::GitLogger.error("POST-RECEIVE: Check gitlab.yml config for correct gitlab_shell.repos_path variable. \"#{Gitlab.config.gitlab_shell.repos_path}\" does not match \"#{repo_path}\"") + Key.find_by_identifier(identifier).try(:user) end + return false unless user - repo_path.gsub!(/.git$/, "") - repo_path.gsub!(/^\//, "") - - project = Project.find_with_namespace(repo_path) - - if project.nil? - Gitlab::GitLogger.error("POST-RECEIVE: Triggered hook for non-existing project with full path \"#{repo_path} \"") - return false - end - - user = if identifier.blank? - # Local push from gitlab - email = project.repository.commit(newrev).author.email rescue nil - User.find_by_email(email) if email - - elsif identifier =~ /\Auser-\d+\Z/ - # git push over http - user_id = identifier.gsub("user-", "") - User.find_by_id(user_id) - - elsif identifier =~ /\Akey-\d+\Z/ - # git push over ssh - key_id = identifier.gsub("key-", "") - Key.find_by_id(key_id).try(:user) - end - - unless user - Gitlab::GitLogger.error("POST-RECEIVE: Triggered hook for non-existing user \"#{identifier} \"") - return false - end - - GitPushService.new.execute(project, user, oldrev, newrev, ref) + project.trigger_post_receive(oldrev, newrev, ref, user) end end diff --git a/app/workers/project_web_hook_worker.rb b/app/workers/project_web_hook_worker.rb deleted file mode 100644 index 9f9b9b1d..00000000 --- a/app/workers/project_web_hook_worker.rb +++ /dev/null @@ -1,9 +0,0 @@ -class ProjectWebHookWorker - include Sidekiq::Worker - - sidekiq_options queue: :project_web_hook - - def perform(hook_id, data) - WebHook.find(hook_id).execute data - end -end diff --git a/app/workers/system_hook_worker.rb b/app/workers/system_hook_worker.rb index 3ebc62b7..ca154136 100644 --- a/app/workers/system_hook_worker.rb +++ b/app/workers/system_hook_worker.rb @@ -1,9 +1,7 @@ class SystemHookWorker - include Sidekiq::Worker + @queue = :system_hook - sidekiq_options queue: :system_hook - - def perform(hook_id, data) + def self.perform(hook_id, data) SystemHook.find(hook_id).execute data end end diff --git a/config.ru b/config.ru index dfd2d862..5ef2a028 100644 --- a/config.ru +++ b/config.ru @@ -1,7 +1,4 @@ # This file is used by Rack-based servers to start the application. require ::File.expand_path('../config/environment', __FILE__) - -map ENV['RAILS_RELATIVE_URL_ROOT'] || "/" do - run Gitlab::Application -end +run Gitlab::Application diff --git a/config/application.rb b/config/application.rb index d71de88e..d6bb90a3 100644 --- a/config/application.rb +++ b/config/application.rb @@ -16,7 +16,7 @@ module Gitlab # -- all .rb files in that directory are automatically loaded. # Custom directories with classes and modules you want to be autoloadable. - config.autoload_paths += %W(#{config.root}/lib #{config.root}/app/models/concerns) + config.autoload_paths += %W(#{config.root}/lib) # Only load the plugins named here, in the order given (default is alphabetical). # :all can be used as a placeholder for all plugins not explicitly named. diff --git a/config/database.yml.example b/config/database.yml.example new file mode 100644 index 00000000..c5a2b8d6 --- /dev/null +++ b/config/database.yml.example @@ -0,0 +1,39 @@ +# +# PRODUCTION +# +production: + adapter: mysql2 + encoding: utf8 + reconnect: false + database: gitlabhq_production + pool: 5 + username: root + password: "secure password" + # host: localhost + # socket: /tmp/mysql.sock + +# +# Development specific +# +development: + adapter: mysql2 + encoding: utf8 + reconnect: false + database: gitlabhq_development + pool: 5 + username: root + password: "secure password" + # socket: /tmp/mysql.sock + +# Warning: The database defined as "test" will be erased and +# re-generated from your development database when you run "rake". +# Do not set this db to the same as development or production. +test: &test + adapter: mysql2 + encoding: utf8 + reconnect: false + database: gitlabhq_test + pool: 5 + username: root + password: "secure password" + # socket: /tmp/mysql.sock diff --git a/config/database.yml.postgresql b/config/database.yml.postgresql index 4a4aa346..17b38f3d 100644 --- a/config/database.yml.postgresql +++ b/config/database.yml.postgresql @@ -6,10 +6,9 @@ production: encoding: unicode database: gitlabhq_production pool: 5 - username: git + username: postgres password: # host: localhost - # port: 5432 # socket: /tmp/postgresql.sock # diff --git a/config/database.yml.sqlite b/config/database.yml.sqlite new file mode 100644 index 00000000..591448f6 --- /dev/null +++ b/config/database.yml.sqlite @@ -0,0 +1,31 @@ +# +# PRODUCTION +# +# SQLite version 3.x +# gem install sqlite3 +# +# Ensure the SQLite 3 gem is defined in your Gemfile +# gem 'sqlite3' +production: + adapter: sqlite3 + database: db/production.sqlite3 + pool: 5 + timeout: 5000 + +# +# Development specific +# +development: + adapter: sqlite3 + database: db/development.sqlite3 + pool: 5 + timeout: 5000 + +# Warning: The database defined as "test" will be erased and +# re-generated from your development database when you run "rake". +# Do not set this db to the same as development or production. +test: &test + adapter: sqlite3 + database: db/test.sqlite3 + pool: 5 + timeout: 5000 diff --git a/config/deploy.rb.example b/config/deploy.rb.example deleted file mode 100644 index ddce4671..00000000 --- a/config/deploy.rb.example +++ /dev/null @@ -1,72 +0,0 @@ -set :domain, 'set application domain here' -set :db_adapter, 'mysql' # or postgres -set :mount_point, '/' -set :application, 'gitlabhq' -set :user, 'git' -set :rails_env, 'production' -set :deploy_to, "/home/#{user}/apps/#{application}" -set :bundle_without, %w[development test] + (%w[mysql postgres] - [db_adapter]) -set :asset_env, "RAILS_GROUPS=assets RAILS_RELATIVE_URL_ROOT=#{mount_point.sub /\/+\Z/, ''}" - -set :use_sudo, false -default_run_options[:pty] = true - -# Or: `accurev`, `bzr`, `cvs`, `darcs`, `git`, `mercurial`, `perforce`, `subversion` or `none` -set :scm, :git -set :repository, "git@#{domain}:#{application}.git" -set :deploy_via, :remote_cache - -# Alternatively, you can deploy via copy, if you don't have gitlab in git -#set :scm, :none -#set :repository, '.' -#set :deploy_via, :copy - -server domain, :app, :web, :db, primary: true - -namespace :foreman do - desc 'Export the Procfile to Ubuntu upstart scripts' - task :export, roles: :app do - foreman_export = "foreman export upstart /etc/init -f Procfile -a #{application} -u #{user} -l #{shared_path}/log/foreman" - run "cd #{release_path} && #{sudo} #{fetch :bundle_cmd, 'bundle'} exec #{foreman_export}" - end - - desc 'Start the application services' - task :start, roles: :app do - run "#{sudo} service #{application} start" - end - - desc 'Stop the application services' - task :stop, roles: :app do - run "#{sudo} service #{application} stop" - end - - desc 'Restart the application services' - task :restart, roles: :app do - run "#{sudo} service #{application} restart" - end -end - -namespace :deploy do - desc 'Start the application services' - task :start, roles: :app do - foreman.start - end - - desc 'Stop the application services' - task :stop, roles: :app do - foreman.stop - end - - desc 'Restart the application services' - task :restart, roles: :app do - foreman.restart - end -end - -after 'deploy:cold' do - run "cd #{release_path} && #{rake} gitlab:setup force=yes RAILS_ENV=#{rails_env}" - deploy.restart -end - -after 'deploy:update', 'foreman:export' # Export foreman scripts -#after 'deploy:update', 'foreman:restart' # Restart application scripts diff --git a/config/environments/production.rb b/config/environments/production.rb index 6ae0324f..52fb8877 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -40,7 +40,7 @@ Gitlab::Application.configure do # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new) # Use a different cache store in production - config.cache_store = :redis_store + config.cache_store = :memory_store # Enable serving of images, stylesheets, and JavaScripts from an asset server # config.action_controller.asset_host = "http://assets.example.com" diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index 90d04b39..35683489 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -1,165 +1,88 @@ -# # # # # # # # # # # # # # # # # # -# GitLab application config file # # # # # # # # # # # # # # # # # # # +# Gitlab application config file # +# # # # # # # # # # # # # # # # # # + # -# How to use: -# 1. copy file as gitlab.yml -# 2. Replace gitlab -> host with your domain -# 3. Replace gitlab -> email_from +# 1. Common settings +# ========================== -production: &base - # - # 1. GitLab app settings - # ========================== +# Web application specific settings +web: + host: localhost + port: 80 + https: false - ## GitLab settings - gitlab: - ## Web server settings - host: localhost - port: 80 - https: false - # Uncomment and customize to run in non-root path - # Note that ENV['RAILS_RELATIVE_URL_ROOT'] in config/unicorn.rb may need to be changed - # relative_url_root: /gitlab +# Email used for notification +# about new issues, comments +email: + from: notify@localhost - # Uncomment and customize if you can't use the default user to run GitLab (default: 'git') - # user: git - - ## Email settings - # Email address used in the "From" field in mails sent by GitLab - email_from: gitlab@localhost - - # Email address of your support contact (default: same as email_from) - support_email: support@localhost - - ## Project settings - default_projects_limit: 10 - # 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 +# Application specific settings +# Like default project limit for user etc +app: + default_projects_limit: 10 + # backup_path: "/vol/backups" # default: Rails.root + backups/ + # backup_keep_time: 604800 # default: 0 (forever) (in seconds) + # disable_gravatar: true # default: false - Disable user avatars from Gravatar.com - ## 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" - # - # ## If not nil, linkis to creating new issues will be replaced with this - # ## Use placeholders: - # ## :project_id - GitLab project identifier - # ## :issues_tracker_id - Project Name or Id in external issue tracker - # new_issue_url: "http://redmine.sample/projects/:issues_tracker_id/issues/new" +# +# 2. Auth settings +# ========================== +ldap: + enabled: false + host: '_your_ldap_server' + base: '_the_base_where_you_search_for_users' + port: 636 + uid: 'sAMAccountName' + method: 'ssl' # plain + bind_dn: '_the_full_dn_of_the_user_you_will_bind_with' + password: '_the_password_of_the_bind_user' - ## Gravatar - gravatar: - 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 +omniauth: + # Enable ability for users + # to login via twitter, google .. + enabled: false + + # IMPORTANT! + # It allows user to login without having user account + allow_single_sign_on: false + block_auto_created_users: true + + # Auth providers + providers: + # - { name: 'google_oauth2', app_id: 'YOUR APP ID', + # app_secret: 'YOUR APP SECRET', + # args: { access_type: 'offline', approval_prompt: '' } } + # - { name: 'twitter', app_id: 'YOUR APP ID', + # app_secret: 'YOUR APP SECRET'} + # - { name: 'github', app_id: 'YOUR APP ID', + # app_secret: 'YOUR APP SECRET' } +# +# 3. Advanced settings: +# ========================== - # - # 2. Auth settings - # ========================== +# Git Hosting configuration +git_host: + admin_uri: git@localhost:gitolite-admin + base_path: /home/git/repositories/ + hooks_path: /home/git/.gitolite/hooks/ + gitolite_admin_key: gitlab + git_user: git + upload_pack: true + receive_pack: true + # host: localhost + # config_file: gitolite.conf + # port: 22 - ## LDAP settings - ldap: - enabled: false - host: '_your_ldap_server' - base: '_the_base_where_you_search_for_users' - port: 636 - uid: 'sAMAccountName' - method: 'ssl' # "ssl" or "plain" - bind_dn: '_the_full_dn_of_the_user_you_will_bind_with' - password: '_the_password_of_the_bind_user' - - ## OmniAuth settings - omniauth: - # 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). - # 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). - block_auto_created_users: true - - ## Auth providers - # 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. - providers: - # - { name: 'google_oauth2', app_id: 'YOUR APP ID', - # app_secret: 'YOUR APP SECRET', - # args: { access_type: 'offline', approval_prompt: '' } } - # - { name: 'twitter', app_id: 'YOUR APP ID', - # app_secret: 'YOUR APP SECRET'} - # - { name: 'github', app_id: 'YOUR APP ID', - # app_secret: 'YOUR APP SECRET' } - - - - # - # 3. Advanced settings - # ========================== - - # GitLab Satellites - satellites: - # Relative paths are relative to Rails.root (default: tmp/repo_satellites/) - path: /home/git/gitlab-satellites/ - - ## Backup settings - backup: - path: "tmp/backups" # Relative paths are relative to Rails.root (default: tmp/backups/) - # keep_time: 604800 # default: 0 (forever) (in seconds) - - ## GitLab Shell settings - gitlab_shell: - # REPOS_PATH MUST NOT BE A SYMLINK!!! - repos_path: /home/git/repositories/ - hooks_path: /home/git/gitlab-shell/hooks/ - - # Git over HTTP - upload_pack: true - receive_pack: true - - # If you use non-standard ssh port you need to specify it - # ssh_port: 22 - - ## Git settings - # CAUTION! - # Use the default values unless you really know what you are doing - git: - bin_path: /usr/bin/git - # 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 a commit, in seconds - timeout: 10 - -development: - <<: *base - -test: - <<: *base - issues_tracker: - redmine: - project_url: "http://redmine/projects/:issues_tracker_id" - issues_url: "http://redmine/:project_id/:issues_tracker_id/:id" - new_issue_url: "http://redmine/projects/:issues_tracker_id/insues/new" - -staging: - <<: *base +# Git settings +# Use default values unless you understand it +git: + path: /usr/bin/git + # Max size of git object like commit, in bytes + # This value can be increased if you have a very large commits + git_max_size: 5242880 # 5.megabytes + # Git timeout to read commit, in seconds + git_timeout: 10 diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index ac35eef4..32af3d07 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -1,102 +1,149 @@ class Settings < Settingslogic source "#{Rails.root}/config/gitlab.yml" - namespace Rails.env class << self - def gitlab_on_non_standard_port? - ![443, 80].include?(gitlab.port.to_i) + def web_protocol + self.web['protocol'] ||= web.https ? "https" : "http" end - private + def web_host + self.web['host'] ||= 'localhost' + end - def build_gitlab_shell_ssh_path_prefix - if gitlab_shell.ssh_port != 22 - "ssh://#{gitlab_shell.ssh_user}@#{gitlab_shell.ssh_host}:#{gitlab_shell.ssh_port}/" + def email_from + self.email['from'] ||= ("notify@" + web_host) + end + + def url + self['url'] ||= build_url + end + + def web_port + if web.https + web['port'] = 443 else - "#{gitlab_shell.ssh_user}@#{gitlab_shell.ssh_host}:" - end + web['port'] ||= 80 + end.to_i end - def build_gitlab_url - if gitlab_on_non_standard_port? - custom_port = ":#{gitlab.port}" + def web_custom_port? + ![443, 80].include?(web_port) + end + + def build_url + if web_custom_port? + custom_port = ":#{web_port}" else custom_port = nil end - [ gitlab.protocol, + [ + web_protocol, "://", - gitlab.host, - custom_port, - gitlab.relative_url_root + web_host, + custom_port ].join('') end + + def ssh_port + git_host['port'] || 22 + end + + def ssh_user + git_host['git_user'] || 'git' + end + + def ssh_host + git_host['host'] || web_host || 'localhost' + end + + def ssh_path + if ssh_port != 22 + "ssh://#{ssh_user}@#{ssh_host}:#{ssh_port}/" + else + "#{ssh_user}@#{ssh_host}:" + end + end + + def git_base_path + git_host['base_path'] || '/home/git/repositories/' + end + + def git_hooks_path + git_host['hooks_path'] || '/home/git/share/gitolite/hooks/' + end + + def git_upload_pack + if git_host['upload_pack'] != false + true + else + false + end + end + + def git_receive_pack + if git_host['receive_pack'] != false + true + else + false + end + end + + def git_bin_path + git['path'] || '/usr/bin/git' + end + + def git_max_size + git['git_max_size'] || 5242880 # 5.megabytes + end + + def git_timeout + git['git_timeout'] || 10 + end + + def gitolite_admin_uri + git_host['admin_uri'] || 'git@localhost:gitolite-admin' + end + + def gitolite_config_file + git_host['config_file'] || 'gitolite.conf' + end + + def gitolite_admin_key + git_host['gitolite_admin_key'] || 'gitlab' + end + + def default_projects_limit + app['default_projects_limit'] || 10 + end + + def backup_path + t = app['backup_path'] || "backups/" + t = /^\//.match(t) ? t : Rails.root .join(t) + t + end + + def backup_keep_time + app['backup_keep_time'] || 0 + end + + def ldap_enabled? + ldap && ldap['enabled'] + rescue Settingslogic::MissingSetting + false + end + + def omniauth_enabled? + omniauth && omniauth['enabled'] + rescue Settingslogic::MissingSetting + false + end + + def omniauth_providers + (omniauth_enabled? && omniauth['providers']) || [] + end + + def disable_gravatar? + app['disable_gravatar'] || false + end end end - - -# Default settings -Settings['ldap'] ||= Settingslogic.new({}) -Settings.ldap['enabled'] = false if Settings.ldap['enabled'].nil? - -Settings['omniauth'] ||= Settingslogic.new({}) -Settings.omniauth['enabled'] = false if Settings.omniauth['enabled'].nil? -Settings.omniauth['providers'] ||= [] - -Settings['issues_tracker'] ||= {} - -# -# GitLab -# -Settings['gitlab'] ||= Settingslogic.new({}) -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'] ||= 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 -Settings.gitlab['url'] ||= Settings.send(:build_gitlab_url) -Settings.gitlab['user'] ||= 'git' -Settings.gitlab['signup_enabled'] ||= false -Settings.gitlab['username_changing_enabled'] = true if Settings.gitlab['username_changing_enabled'].nil? - -# -# Gravatar -# -Settings['gravatar'] ||= Settingslogic.new({}) -Settings.gravatar['enabled'] = true if Settings.gravatar['enabled'].nil? -Settings.gravatar['plain_url'] ||= 'http://www.gravatar.com/avatar/%{hash}?s=%{size}&d=mm' -Settings.gravatar['ssl_url'] ||= 'https://secure.gravatar.com/avatar/%{hash}?s=%{size}&d=mm' - -# -# GitLab Shell -# -Settings['gitlab_shell'] ||= Settingslogic.new({}) -Settings.gitlab_shell['hooks_path'] ||= '/home/git/gitlab-shell/hooks/' -Settings.gitlab_shell['receive_pack'] = true if Settings.gitlab_shell['receive_pack'].nil? -Settings.gitlab_shell['upload_pack'] = true if Settings.gitlab_shell['upload_pack'].nil? -Settings.gitlab_shell['repos_path'] ||= '/home/git/repositories/' -Settings.gitlab_shell['ssh_host'] ||= (Settings.gitlab.host || 'localhost') -Settings.gitlab_shell['ssh_port'] ||= 22 -Settings.gitlab_shell['ssh_user'] ||= Settings.gitlab.user -Settings.gitlab_shell['owner_group'] ||= Settings.gitlab.user -Settings.gitlab_shell['ssh_path_prefix'] ||= Settings.send(:build_gitlab_shell_ssh_path_prefix) - -# -# Backup -# -Settings['backup'] ||= Settingslogic.new({}) -Settings.backup['keep_time'] ||= 0 -Settings.backup['path'] = File.expand_path(Settings.backup['path'] || "tmp/backups/", Rails.root) - -# -# Git -# -Settings['git'] ||= Settingslogic.new({}) -Settings.git['max_size'] ||= 5242880 # 5.megabytes -Settings.git['bin_path'] ||= '/usr/bin/git' -Settings.git['timeout'] ||= 10 - -Settings['satellites'] ||= Settingslogic.new({}) -Settings.satellites['path'] = File.expand_path(Settings.satellites['path'] || "tmp/repo_satellites/", Rails.root) diff --git a/config/initializers/2_app.rb b/config/initializers/2_app.rb index 27a0c0ff..fc97960b 100644 --- a/config/initializers/2_app.rb +++ b/config/initializers/2_app.rb @@ -1,8 +1,8 @@ module Gitlab - VERSION = File.read(Rails.root.join("VERSION")).strip - REVISION = `git log --pretty=format:'%h' -n 1` + Version = File.read(Rails.root.join("VERSION")) + Revision = `git log --pretty=format:'%h' -n 1` def self.config - Settings + Settings end end diff --git a/config/initializers/3_grit_ext.rb b/config/initializers/3_grit_ext.rb index 097c301a..d114ea6c 100644 --- a/config/initializers/3_grit_ext.rb +++ b/config/initializers/3_grit_ext.rb @@ -1,8 +1,8 @@ require 'grit' require 'pygments' -Grit::Git.git_timeout = Gitlab.config.git.timeout -Grit::Git.git_max_size = Gitlab.config.git.max_size +Grit::Git.git_timeout = Gitlab.config.git_timeout +Grit::Git.git_max_size = Gitlab.config.git_max_size Grit::Blob.class_eval do include Linguist::BlobHelper diff --git a/config/initializers/4_resque.rb b/config/initializers/4_resque.rb new file mode 100644 index 00000000..419dbe06 --- /dev/null +++ b/config/initializers/4_resque.rb @@ -0,0 +1,29 @@ +# Custom Redis configuration +config_file = Rails.root.join('config', 'resque.yml') + +if File.exists?(config_file) + resque_config = YAML.load_file(config_file) + Resque.redis = resque_config[Rails.env] +end +Resque.redis.namespace = 'resque:gitlab' +# Queues +Resque.watch_queue(PostReceive.instance_variable_get("@queue")) + +# Authentication +require 'resque/server' +class Authentication + def initialize(app) + @app = app + end + + def call(env) + account = env['warden'].authenticate!(:database_authenticatable, :rememberable, scope: :user) + raise "Access denied" if !account.admin? + @app.call(env) + end +end + +Resque::Server.use Authentication + +# Mailer +Resque::Mailer.excluded_environments = [] diff --git a/config/initializers/4_sidekiq.rb b/config/initializers/4_sidekiq.rb deleted file mode 100644 index c90d3762..00000000 --- a/config/initializers/4_sidekiq.rb +++ /dev/null @@ -1,22 +0,0 @@ -# Custom Redis configuration -config_file = Rails.root.join('config', 'resque.yml') - -resque_url = if File.exists?(config_file) - YAML.load_file(config_file)[Rails.env] - else - "redis://localhost:6379" - end - -Sidekiq.configure_server do |config| - config.redis = { - url: resque_url, - namespace: 'resque:gitlab' - } -end - -Sidekiq.configure_client do |config| - config.redis = { - url: resque_url, - namespace: 'resque:gitlab' - } -end diff --git a/config/initializers/5_backend.rb b/config/initializers/5_backend.rb index 7c2e7f39..85f747ac 100644 --- a/config/initializers/5_backend.rb +++ b/config/initializers/5_backend.rb @@ -1,8 +1,5 @@ # GIT over HTTP require Rails.root.join("lib", "gitlab", "backend", "grack_auth") -# GIT over SSH -require Rails.root.join("lib", "gitlab", "backend", "shell") - -# GitLab shell adapter -require Rails.root.join("lib", "gitlab", "backend", "shell_adapter") +# GITOLITE backend +require Rails.root.join("lib", "gitlab", "backend", "gitolite") diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index 5714407f..8f3cef5a 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -4,7 +4,7 @@ Devise.setup do |config| # ==> Mailer Configuration # Configure the e-mail address which will be shown in Devise::Mailer, # note that it will be overwritten if you use your own mailer class with default "from" parameter. - config.mailer_sender = Gitlab.config.gitlab.email_from + config.mailer_sender = Gitlab.config.email_from # Configure the class responsible to send e-mails. # config.mailer = "Devise::Mailer" @@ -23,7 +23,7 @@ Devise.setup do |config| # session. If you need permissions, you should implement that in a before filter. # You can also supply a hash where the value is a boolean determining whether # or not authentication should be aborted when the value is not present. - config.authentication_keys = [ :login ] + # config.authentication_keys = [ :email ] # Configure parameters from the request object used for authentication. Each entry # given should be a request method and it will automatically be passed to the @@ -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 @@ -205,27 +205,20 @@ Devise.setup do |config| # manager.default_strategies(:scope => :user).unshift :some_external_strategy # end - if Gitlab.config.ldap.enabled + gl = Gitlab.config + + if gl.ldap_enabled? config.omniauth :ldap, - :host => Gitlab.config.ldap['host'], - :base => Gitlab.config.ldap['base'], - :uid => Gitlab.config.ldap['uid'], - :port => Gitlab.config.ldap['port'], - :method => Gitlab.config.ldap['method'], - :bind_dn => Gitlab.config.ldap['bind_dn'], - :password => Gitlab.config.ldap['password'] + :host => gl.ldap['host'], + :base => gl.ldap['base'], + :uid => gl.ldap['uid'], + :port => gl.ldap['port'], + :method => gl.ldap['method'], + :bind_dn => gl.ldap['bind_dn'], + :password => gl.ldap['password'] end - Gitlab.config.omniauth.providers.each do |provider| - case provider['args'] - when Array - # An Array from the configuration will be expanded. - config.omniauth provider['name'].to_sym, provider['app_id'], provider['app_secret'], *provider['args'] - when Hash - # A Hash from the configuration will be passed as is. - config.omniauth provider['name'].to_sym, provider['app_id'], provider['app_secret'], provider['args'] - else - config.omniauth provider['name'].to_sym, provider['app_id'], provider['app_secret'] - end + gl.omniauth_providers.each do |gl_provider| + config.omniauth gl_provider['name'].to_sym, gl_provider['app_id'], gl_provider['app_secret'] end end diff --git a/config/initializers/mime_types.rb b/config/initializers/mime_types.rb index 8f8bef42..3549b836 100644 --- a/config/initializers/mime_types.rb +++ b/config/initializers/mime_types.rb @@ -4,5 +4,4 @@ # Mime::Type.register "text/richtext", :rtf # Mime::Type.register_alias "text/html", :iphone -Mime::Type.register_alias "text/plain", :diff -Mime::Type.register_alias "text/plain", :patch +Mime::Type.register_alias 'text/plain', :patch diff --git a/config/locales/devise.en.yml b/config/locales/devise.en.yml index 275273a0..a78cb6b6 100644 --- a/config/locales/devise.en.yml +++ b/config/locales/devise.en.yml @@ -14,10 +14,9 @@ en: devise: failure: already_authenticated: 'You are already signed in.' - unauthenticated: 'You need to sign in before continuing.' + unauthenticated: 'You need to sign in or sign up 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.' diff --git a/config/resque.yml.example b/config/resque.yml.example index 3c7ad0e5..cd3d4874 100644 --- a/config/resque.yml.example +++ b/config/resque.yml.example @@ -1,3 +1,3 @@ -development: redis://localhost:6379 -test: redis://localhost:6379 -production: redis://redis.example.com:6379 +development: localhost:6379 +test: localhost:6379 +production: redis.example.com:6379 diff --git a/config/routes.rb b/config/routes.rb index 61a604b9..98cf7e81 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,5 +1,3 @@ -require 'sidekiq/web' - Gitlab::Application.routes.draw do # # Search @@ -8,89 +6,61 @@ Gitlab::Application.routes.draw do # API require 'api' - Gitlab::API.logger Rails.logger mount Gitlab::API => '/api' - constraint = lambda { |request| request.env["warden"].authenticate? and request.env['warden'].user.admin? } - constraints constraint do - mount Sidekiq::Web, at: "/admin/sidekiq", as: :sidekiq - end + # Optionally, enable Resque here + require 'resque/server' + mount Resque::Server => '/info/resque', as: 'resque' # Enable Grack support mount Grack::Bundle.new({ - git_path: Gitlab.config.git.bin_path, - project_root: Gitlab.config.gitlab_shell.repos_path, - upload_pack: Gitlab.config.gitlab_shell.upload_pack, - receive_pack: Gitlab.config.gitlab_shell.receive_pack - }), at: '/', constraints: lambda { |request| /[-\/\w\.]+\.git\//.match(request.path_info) } + git_path: Gitlab.config.git_bin_path, + project_root: Gitlab.config.git_base_path, + upload_pack: Gitlab.config.git_upload_pack, + receive_pack: Gitlab.config.git_receive_pack + }), at: '/:path', constraints: { path: /[\w\.-]+\.git/ } # # Help # - get 'help' => 'help#index' - get 'help/api' => 'help#api' - get 'help/markdown' => 'help#markdown' - get 'help/permissions' => 'help#permissions' - get 'help/public_access' => 'help#public_access' - get 'help/raketasks' => 'help#raketasks' - get 'help/ssh' => 'help#ssh' - get 'help/system_hooks' => 'help#system_hooks' - get 'help/web_hooks' => 'help#web_hooks' - get 'help/workflow' => 'help#workflow' - - # - # Public namespace - # - namespace :public do - resources :projects, only: [:index] - root to: "projects#index" - end - - # - # Attachments serving - # - get 'files/:type/:id/:filename' => 'files#download', constraints: { id: /\d+/, type: /[a-z]+/, filename: /.+/ } + get 'help' => 'help#index' + get 'help/permissions' => 'help#permissions' + get 'help/workflow' => 'help#workflow' + get 'help/api' => 'help#api' + get 'help/web_hooks' => 'help#web_hooks' + get 'help/system_hooks' => 'help#system_hooks' + get 'help/markdown' => 'help#markdown' + get 'help/ssh' => 'help#ssh' # # Admin Area # namespace :admin do - resources :users, constraints: { id: /[a-zA-Z.\/0-9_\-]+/ } do + resources :users do member do put :team_update put :block put :unblock end end - resources :groups, constraints: { id: /[^\/]+/ } do member do put :project_update - put :project_teams_update delete :remove_project end end - - resources :teams, constraints: { id: /[^\/]+/ } do - scope module: :teams do - resources :members, only: [:edit, :update, :destroy, :new, :create] - resources :projects, only: [:edit, :update, :destroy, :new, :create], constraints: { id: /[a-zA-Z.\/0-9_\-]+/ } + resources :projects, constraints: { id: /[^\/]+/ } do + member do + get :team + put :team_update end end - + resources :team_members, only: [:edit, :update, :destroy] resources :hooks, only: [:index, :create, :destroy] do get :test end - resource :logs, only: [:show] resource :resque, controller: 'resque', only: [:show] - - resources :projects, constraints: { id: /[a-zA-Z.\/0-9_\-]+/ }, only: [:index, :show] do - scope module: :projects, constraints: { id: /[a-zA-Z.\/0-9_\-]+/ } do - resources :members, only: [:edit, :update, :destroy] - end - end - root to: "dashboard#index" end @@ -99,92 +69,54 @@ Gitlab::Application.routes.draw do # # Profile Area # - resource :profile, only: [:show, :update] do - member do - get :account - get :history - get :token - get :design - - put :update_password - put :reset_private_token - put :update_username - end - - resource :notifications - end + get "profile/account" => "profile#account" + get "profile/history" => "profile#history" + put "profile/password" => "profile#password_update" + get "profile/token" => "profile#token" + put "profile/reset_private_token" => "profile#reset_private_token" + get "profile" => "profile#show" + get "profile/design" => "profile#design" + put "profile/update" => "profile#update" resources :keys - match "/u/:username" => "users#show", as: :user, constraints: { username: /.*/ } - - # # Dashboard Area # - resource :dashboard, controller: "dashboard" do - member do - get :projects - get :issues - get :merge_requests - end - end + get "dashboard" => "dashboard#index" + get "dashboard/issues" => "dashboard#issues" + get "dashboard/merge_requests" => "dashboard#merge_requests" + # # Groups Area # - resources :groups, constraints: {id: /(?:[^.]|\.(?!atom$))+/, format: /atom/} do + resources :groups, constraints: { id: /[^\/]+/ }, only: [:show] do member do get :issues get :merge_requests get :search get :people - post :team_members - end - end - - # - # Teams Area - # - resources :teams, constraints: {id: /(?:[^.]|\.(?!atom$))+/, format: /atom/} do - member do - get :issues - get :merge_requests - end - scope module: :teams do - resources :members, only: [:index, :new, :create, :edit, :update, :destroy] - resources :projects, only: [:index, :new, :create, :edit, :update, :destroy], constraints: { id: /[a-zA-Z.0-9_\-\/]+/ } end end resources :projects, constraints: { id: /[^\/]+/ }, only: [:new, :create] - devise_for :users, controllers: { omniauth_callbacks: :omniauth_callbacks, registrations: :registrations } + devise_for :users, controllers: { omniauth_callbacks: :omniauth_callbacks } # # Project Area # - resources :projects, constraints: { id: /(?:[a-zA-Z.0-9_\-]+\/)?[a-zA-Z.0-9_\-]+/ }, except: [:new, :create, :index], path: "/" do + resources :projects, constraints: { id: /[^\/]+/ }, except: [:new, :create, :index], path: "/" do member do - put :transfer + get "wall" + get "graph" + get "files" end - resources :blob, only: [:show], constraints: {id: /.+/} - resources :tree, only: [:show], constraints: {id: /.+/, format: /(html|js)/ } - resources :edit_tree, only: [:show, :update], constraints: {id: /.+/}, path: 'edit' - resources :commit, only: [:show], constraints: {id: /[[:alnum:]]{6,40}/} - resources :commits, only: [:show], constraints: {id: /(?:[^.]|\.(?!atom$))+/, format: /atom/} - resources :compare, only: [:index, :create] - resources :blame, only: [:show], constraints: {id: /.+/} - resources :graph, only: [:show], constraints: {id: /(?:[^.]|\.(?!json$))+/, format: /json/} - match "/compare/:from...:to" => "compare#show", as: "compare", - :via => [:get, :post], constraints: {from: /.+/, to: /.+/} - resources :wikis, only: [:show, :edit, :destroy, :create] do collection do get :pages - put ':id' => 'wikis#update' - get :git_access end member do @@ -192,12 +124,6 @@ Gitlab::Application.routes.draw do end end - resource :wall, only: [:show] do - member do - get 'notes' - end - end - resource :repository do member do get "branches" @@ -216,7 +142,7 @@ Gitlab::Application.routes.draw do resources :deploy_keys resources :protected_branches, only: [:index, :create, :destroy] - resources :refs, only: [] do + resources :refs, only: [], path: "/" do collection do get "switch" end @@ -233,12 +159,12 @@ Gitlab::Application.routes.draw do end end - resources :merge_requests, constraints: {id: /\d+/}, except: [:destroy] do + resources :merge_requests do member do get :diffs get :automerge get :automerge_check - get :ci_status + get :raw end collection do @@ -259,11 +185,19 @@ Gitlab::Application.routes.draw do end end + resources :commit, only: [:show], constraints: {id: /[[:alnum:]]{6,40}/} + resources :commits, only: [:show], constraints: {id: /.+/} + resources :compare, only: [:index, :create] + resources :blame, only: [:show], constraints: {id: /.+/} + resources :blob, only: [:show], constraints: {id: /.+/} + resources :tree, only: [:show, :edit, :update], constraints: {id: /.+/} + match "/compare/:from...:to" => "compare#show", as: "compare", + :via => [:get, :post], constraints: {from: /.+/, to: /.+/} resources :team, controller: 'team_members', only: [:index] - resources :milestones, except: [:destroy] + resources :milestones resources :labels, only: [:index] - resources :issues, except: [:destroy] do + resources :issues do collection do post :sort post :bulk_update @@ -281,18 +215,6 @@ Gitlab::Application.routes.draw do end end - scope module: :projects do - resources :teams, only: [] do - collection do - get :available - post :assign - end - member do - delete :resign - end - end - end - resources :notes, only: [:index, :create, :destroy] do collection do post :preview @@ -300,5 +222,5 @@ Gitlab::Application.routes.draw do end end - root to: "dashboard#show" + root to: "dashboard#index" end diff --git a/config/unicorn.rb.example b/config/unicorn.rb.example index 75945182..425dbf33 100644 --- a/config/unicorn.rb.example +++ b/config/unicorn.rb.example @@ -1,8 +1,4 @@ -# uncomment and customize to run in non-root path -# note that config/gitlab.yml web path should also be changed -# ENV['RAILS_RELATIVE_URL_ROOT'] = "/gitlab" - -app_dir = File.expand_path '../../', __FILE__ +app_dir = "/home/gitlab/gitlab/" worker_processes 2 working_directory app_dir @@ -30,7 +26,7 @@ end before_fork do |server, worker| - # the following is highly recommended for Rails + "preload_app true" + # the following is highly recomended for Rails + "preload_app true" # as there's no need for the master process to hold a connection defined?(ActiveRecord::Base) and ActiveRecord::Base.connection.disconnect! diff --git a/db/fixtures/development/001_admin.rb b/db/fixtures/development/001_admin.rb new file mode 100644 index 00000000..c857f6bc --- /dev/null +++ b/db/fixtures/development/001_admin.rb @@ -0,0 +1,21 @@ +unless User.count > 0 + admin = User.create( + :email => "admin@local.host", + :name => "Administrator", + :password => "5iveL!fe", + :password_confirmation => "5iveL!fe" + ) + + admin.projects_limit = 10000 + admin.admin = true + admin.save! + + if admin.valid? + puts %q[ + Administrator account created: + + login.........admin@local.host + password......5iveL!fe + ] + end +end diff --git a/db/fixtures/development/002_project.rb b/db/fixtures/development/002_project.rb new file mode 100644 index 00000000..eb68b5fe --- /dev/null +++ b/db/fixtures/development/002_project.rb @@ -0,0 +1,5 @@ +Project.seed(:id, [ + { id: 1, name: "Underscore.js", path: "underscore", code: "underscore", owner_id: 1 }, + { id: 2, name: "Diaspora", path: "diaspora", code: "diaspora", owner_id: 1 }, + { id: 3, name: "Ruby on Rails", path: "rails", code: "rails", owner_id: 1 } +]) diff --git a/db/fixtures/development/003_users.rb b/db/fixtures/development/003_users.rb new file mode 100644 index 00000000..309eb90b --- /dev/null +++ b/db/fixtures/development/003_users.rb @@ -0,0 +1,11 @@ +User.seed(:id, [ + { :id => 2, :name => Faker::Internet.user_name, :email => Faker::Internet.email}, + { :id => 3, :name => Faker::Internet.user_name, :email => Faker::Internet.email}, + { :id => 4, :name => Faker::Internet.user_name, :email => Faker::Internet.email}, + { :id => 5, :name => Faker::Internet.user_name, :email => Faker::Internet.email}, + { :id => 6, :name => Faker::Internet.user_name, :email => Faker::Internet.email}, + { :id => 7, :name => Faker::Internet.user_name, :email => Faker::Internet.email}, + { :id => 8, :name => Faker::Internet.user_name, :email => Faker::Internet.email}, + { :id => 9, :name => Faker::Internet.user_name, :email => Faker::Internet.email} +]) + diff --git a/db/fixtures/development/004_teams.rb b/db/fixtures/development/004_teams.rb new file mode 100644 index 00000000..2752cb44 --- /dev/null +++ b/db/fixtures/development/004_teams.rb @@ -0,0 +1,27 @@ +UsersProject.skip_callback(:save, :after, :update_repository) + +UsersProject.seed(:id, [ + { :id => 1, :project_id => 1, :user_id => 1, :project_access => UsersProject::MASTER }, + { :id => 2, :project_id => 1, :user_id => 2, :project_access => UsersProject::REPORTER}, + { :id => 3, :project_id => 1, :user_id => 3, :project_access => UsersProject::REPORTER}, + { :id => 4, :project_id => 1, :user_id => 4, :project_access => UsersProject::REPORTER}, + { :id => 5, :project_id => 1, :user_id => 5, :project_access => UsersProject::REPORTER}, + + { :id => 6, :project_id => 2, :user_id => 1, :project_access => UsersProject::MASTER }, + { :id => 7, :project_id => 2, :user_id => 2, :project_access => UsersProject::REPORTER}, + { :id => 8, :project_id => 2, :user_id => 3, :project_access => UsersProject::REPORTER}, + { :id => 9, :project_id => 2, :user_id => 4, :project_access => UsersProject::MASTER}, + { :id => 11, :project_id => 2, :user_id => 5, :project_access => UsersProject::MASTER}, + + { :id => 12, :project_id => 3, :user_id => 1, :project_access => UsersProject::MASTER }, + { :id => 13, :project_id => 3, :user_id => 2, :project_access => UsersProject::REPORTER}, + { :id => 14, :project_id => 3, :user_id => 3, :project_access => UsersProject::MASTER}, + { :id => 15, :project_id => 3, :user_id => 4, :project_access => UsersProject::REPORTER}, + { :id => 16, :project_id => 3, :user_id => 5, :project_access => UsersProject::MASTER} +]) + +UsersProject.set_callback(:save, :after, :update_repository) + +puts "\nRebuild gitolite\n".yellow +Project.all.each(&:update_repository) +puts "OK".green diff --git a/db/fixtures/development/07_milestones.rb b/db/fixtures/development/005_milestones.rb similarity index 100% rename from db/fixtures/development/07_milestones.rb rename to db/fixtures/development/005_milestones.rb diff --git a/db/fixtures/development/08_wall.rb b/db/fixtures/development/006_wall.rb similarity index 73% rename from db/fixtures/development/08_wall.rb rename to db/fixtures/development/006_wall.rb index c4e304cc..a9fb66dd 100644 --- a/db/fixtures/development/08_wall.rb +++ b/db/fixtures/development/006_wall.rb @@ -1,18 +1,16 @@ Gitlab::Seeder.quiet do (1..300).each do |i| # Random Project - project = Project.all.sample + project_id = rand(2) + 1 + project = Project.find(project_id) # Random user user = project.users.sample - - next unless user - user_id = user.id Note.seed(:id, [{ id: i, - project_id: project.id, + project_id: project_id, author_id: user_id, note: Faker::Lorem.sentence(6) }]) diff --git a/db/fixtures/development/09_issues.rb b/db/fixtures/development/007_issues.rb similarity index 74% rename from db/fixtures/development/09_issues.rb rename to db/fixtures/development/007_issues.rb index cd9b2b3e..98e32fcc 100644 --- a/db/fixtures/development/09_issues.rb +++ b/db/fixtures/development/007_issues.rb @@ -1,22 +1,20 @@ Gitlab::Seeder.quiet do (1..300).each do |i| # Random Project - project = Project.all.sample + project_id = rand(2) + 1 + project = Project.find(project_id) # Random user user = project.users.sample - - next unless user - user_id = user.id IssueObserver.current_user = user Issue.seed(:id, [{ id: i, - project_id: project.id, + project_id: project_id, author_id: user_id, assignee_id: user_id, - state: ['opened', 'closed'].sample, + closed: [true, false].sample, milestone: project.milestones.sample, title: Faker::Lorem.sentence(6) }]) diff --git a/db/fixtures/development/10_merge_requests.rb b/db/fixtures/development/008_merge_requests.rb similarity index 77% rename from db/fixtures/development/10_merge_requests.rb rename to db/fixtures/development/008_merge_requests.rb index 6d111b26..698c55ad 100644 --- a/db/fixtures/development/10_merge_requests.rb +++ b/db/fixtures/development/008_merge_requests.rb @@ -1,23 +1,22 @@ Gitlab::Seeder.quiet do (1..300).each do |i| # Random Project - project = Project.all.sample + project_id = rand(2) + 1 + project = Project.find(project_id) # Random user user = project.users.sample - - next unless user - user_id = user.id MergeRequestObserver.current_user = user + MergeRequest.seed(:id, [{ id: i, source_branch: 'master', target_branch: 'feature', - project_id: project.id, + project_id: project_id, author_id: user_id, assignee_id: user_id, - state: ['opened', 'closed'].sample, + closed: [true, false].sample, milestone: project.milestones.sample, title: Faker::Lorem.sentence(6) }]) diff --git a/db/fixtures/development/009_source_code.rb b/db/fixtures/development/009_source_code.rb new file mode 100644 index 00000000..489bd02e --- /dev/null +++ b/db/fixtures/development/009_source_code.rb @@ -0,0 +1,27 @@ +root = Gitlab.config.git_base_path + +projects = [ + { path: 'underscore.git', git: 'https://github.com/documentcloud/underscore.git' }, + { path: 'diaspora.git', git: 'https://github.com/diaspora/diaspora.git' }, + { path: 'rails.git', git: 'https://github.com/rails/rails.git' }, +] + +projects.each do |project| + project_path = File.join(root, project[:path]) + + + next if File.exists?(project_path) + + cmds = [ + "cd #{root} && sudo -u git -H git clone --bare #{project[:git]}", + "sudo cp ./lib/hooks/post-receive #{project_path}/hooks/post-receive", + "sudo chown git:git #{project_path}/hooks/post-receive" + ] + + cmds.each do |cmd| + puts cmd.yellow + `#{cmd}` + end +end + +puts "OK".green diff --git a/db/fixtures/development/01_admin.rb b/db/fixtures/development/01_admin.rb deleted file mode 100644 index fbe41e4d..00000000 --- a/db/fixtures/development/01_admin.rb +++ /dev/null @@ -1,11 +0,0 @@ -User.seed(:id, [ - { - id: 1, - name: "Administrator", - email: "admin@local.host", - username: 'root', - password: "5iveL!fe", - password_confirmation: "5iveL!fe", - admin: true, - } -]) diff --git a/db/fixtures/development/02_source_code.rb b/db/fixtures/development/02_source_code.rb deleted file mode 100644 index a0a46c9e..00000000 --- a/db/fixtures/development/02_source_code.rb +++ /dev/null @@ -1,29 +0,0 @@ -root = Gitlab.config.gitlab_shell.repos_path - -projects = [ - { path: 'underscore.git', git: 'https://github.com/documentcloud/underscore.git' }, - { path: 'diaspora.git', git: 'https://github.com/diaspora/diaspora.git' }, - { path: 'brightbox/brightbox-cli.git', git: 'https://github.com/brightbox/brightbox-cli.git' }, - { path: 'brightbox/puppet.git', git: 'https://github.com/brightbox/puppet.git' }, - { path: 'gitlab/gitlabhq.git', git: 'https://github.com/gitlabhq/gitlabhq.git' }, - { path: 'gitlab/gitlab-ci.git', git: 'https://github.com/gitlabhq/gitlab-ci.git' }, - { path: 'gitlab/gitlab-recipes.git', git: 'https://github.com/gitlabhq/gitlab-recipes.git' }, -] - -projects.each do |project| - project_path = File.join(root, project[:path]) - - if File.exists?(project_path) - print '-' - next - end - - if system("/home/git/gitlab-shell/bin/gitlab-projects import-project #{project[:path]} #{project[:git]}") - print '.' - else - print 'F' - end -end - -puts "OK".green - diff --git a/db/fixtures/development/03_group.rb b/db/fixtures/development/03_group.rb deleted file mode 100644 index 01174a4b..00000000 --- a/db/fixtures/development/03_group.rb +++ /dev/null @@ -1,5 +0,0 @@ -Group.seed(:id, [ - { id: 99, name: "GitLab", path: 'gitlab', owner_id: 1 }, - { id: 100, name: "Brightbox", path: 'brightbox', owner_id: 1 }, - { id: 101, name: "KDE", path: 'kde', owner_id: 1 }, -]) diff --git a/db/fixtures/development/04_project.rb b/db/fixtures/development/04_project.rb deleted file mode 100644 index 9904c48e..00000000 --- a/db/fixtures/development/04_project.rb +++ /dev/null @@ -1,20 +0,0 @@ -Project.seed(:id, [ - - # Global - { id: 1, name: "Underscore.js", path: "underscore", creator_id: 1 }, - { id: 2, name: "Diaspora", path: "diaspora", creator_id: 1 }, - - # Brightbox - { id: 3, namespace_id: 100, name: "Brightbox CLI", path: "brightbox-cli", creator_id: 1 }, - { id: 4, namespace_id: 100, name: "Puppet", path: "puppet", creator_id: 1 }, - - # KDE - { id: 5, namespace_id: 101, name: "kdebase", path: "kdebase", creator_id: 1}, - { id: 6, namespace_id: 101, name: "kdelibs", path: "kdelibs", creator_id: 1}, - { id: 7, namespace_id: 101, name: "amarok", path: "amarok", creator_id: 1}, - - # GitLab - { id: 8, namespace_id: 99, name: "gitlabhq", path: "gitlabhq", creator_id: 1}, - { id: 9, namespace_id: 99, name: "gitlab-ci", path: "gitlab-ci", creator_id: 1}, - { id: 10, namespace_id: 99, name: "gitlab-recipes", path: "gitlab-recipes", creator_id: 1}, -]) diff --git a/db/fixtures/development/05_users.rb b/db/fixtures/development/05_users.rb deleted file mode 100644 index abcb0259..00000000 --- a/db/fixtures/development/05_users.rb +++ /dev/null @@ -1,15 +0,0 @@ -Gitlab::Seeder.quiet do - (2..300).each do |i| - begin - User.seed(:id, [{ - id: i, - username: Faker::Internet.user_name, - name: Faker::Name.name, - email: Faker::Internet.email, - }]) - print '.' - rescue ActiveRecord::RecordNotSaved - print 'F' - end - end -end diff --git a/db/fixtures/development/06_teams.rb b/db/fixtures/development/06_teams.rb deleted file mode 100644 index 9fbf21a0..00000000 --- a/db/fixtures/development/06_teams.rb +++ /dev/null @@ -1,22 +0,0 @@ -Gitlab::Seeder.quiet do - - (1..300).each do |i| - # Random Project - project = Project.scoped.sample - - # Random user - user = User.not_in_project(project).sample - - next unless user - - UsersProject.seed(:id, [{ - id: i, - project_id: project.id, - user_id: user.id, - project_access: UsersProject.access_roles.values.sample - }]) - - print('.') - end -end -puts "OK".green diff --git a/db/fixtures/development/11_keys.rb b/db/fixtures/development/11_keys.rb deleted file mode 100644 index 8e4724c2..00000000 --- a/db/fixtures/development/11_keys.rb +++ /dev/null @@ -1,14 +0,0 @@ - -Gitlab::Seeder.quiet do - User.first(30).each_with_index do |user, i| - Key.seed(:id, [ - { - id: i, - title: "Sample key #{i}", - key: "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt#{i + 100}6k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=", - user_id: user.id, - } - ]) - puts "SSH KEY ##{i} added.".green - end -end diff --git a/db/fixtures/production/001_admin.rb b/db/fixtures/production/001_admin.rb index f119694d..cfff6bf8 100644 --- a/db/fixtures/production/001_admin.rb +++ b/db/fixtures/production/001_admin.rb @@ -1,9 +1,8 @@ admin = User.create( - email: "admin@local.host", - name: "Administrator", - username: 'root', - password: "5iveL!fe", - password_confirmation: "5iveL!fe" + :email => "admin@local.host", + :name => "Administrator", + :password => "5iveL!fe", + :password_confirmation => "5iveL!fe" ) admin.projects_limit = 10000 diff --git a/db/migrate/20121122145155_convert_group_to_namespace.rb b/db/migrate/20121122145155_convert_group_to_namespace.rb deleted file mode 100644 index fc8b023d..00000000 --- a/db/migrate/20121122145155_convert_group_to_namespace.rb +++ /dev/null @@ -1,13 +0,0 @@ -class ConvertGroupToNamespace < ActiveRecord::Migration - def up - rename_table 'groups', 'namespaces' - add_column :namespaces, :type, :string, null: true - - # Migrate old groups - Namespace.update_all(type: 'Group') - end - - def down - raise 'Rollback is not allowed' - end -end diff --git a/db/migrate/20121122150932_add_namespace_id_to_project.rb b/db/migrate/20121122150932_add_namespace_id_to_project.rb deleted file mode 100644 index 904f3aa3..00000000 --- a/db/migrate/20121122150932_add_namespace_id_to_project.rb +++ /dev/null @@ -1,5 +0,0 @@ -class AddNamespaceIdToProject < ActiveRecord::Migration - def change - rename_column :projects, :group_id, :namespace_id - end -end diff --git a/db/migrate/20121123104937_add_username_to_user.rb b/db/migrate/20121123104937_add_username_to_user.rb deleted file mode 100644 index 04232a11..00000000 --- a/db/migrate/20121123104937_add_username_to_user.rb +++ /dev/null @@ -1,5 +0,0 @@ -class AddUsernameToUser < ActiveRecord::Migration - def change - add_column :users, :username, :string, null: true - end -end diff --git a/db/migrate/20121123164910_rename_code_to_path.rb b/db/migrate/20121123164910_rename_code_to_path.rb deleted file mode 100644 index fb10baf5..00000000 --- a/db/migrate/20121123164910_rename_code_to_path.rb +++ /dev/null @@ -1,11 +0,0 @@ -class RenameCodeToPath < ActiveRecord::Migration - def up - remove_column :projects, :code - rename_column :namespaces, :code, :path - end - - def down - add_column :projects, :code, :string - rename_column :namespaces, :path, :code - end -end diff --git a/db/migrate/20121203154450_add_events_indices.rb b/db/migrate/20121203154450_add_events_indices.rb deleted file mode 100644 index 502a2ccb..00000000 --- a/db/migrate/20121203154450_add_events_indices.rb +++ /dev/null @@ -1,8 +0,0 @@ -class AddEventsIndices < ActiveRecord::Migration - def change - add_index :events, :project_id - add_index :events, :author_id - add_index :events, :action - add_index :events, :target_type - end -end diff --git a/db/migrate/20121203160507_more_indices.rb b/db/migrate/20121203160507_more_indices.rb deleted file mode 100644 index 52170a7c..00000000 --- a/db/migrate/20121203160507_more_indices.rb +++ /dev/null @@ -1,26 +0,0 @@ -class MoreIndices < ActiveRecord::Migration - def change - add_index :notes, :project_id - add_index :namespaces, :owner_id - add_index :keys, :user_id - - add_index :projects, :namespace_id - add_index :projects, :owner_id - - add_index :services, :project_id - add_index :snippets, :project_id - - add_index :users_projects, :project_id - - # Issues - add_index :issues, :assignee_id - add_index :issues, :milestone_id - add_index :issues, :author_id - - # Merge Requests - add_index :merge_requests, :assignee_id - add_index :merge_requests, :milestone_id - add_index :merge_requests, :author_id - - end -end diff --git a/db/migrate/20121205201726_add_more_indexes.rb b/db/migrate/20121205201726_add_more_indexes.rb deleted file mode 100644 index a2b36f7f..00000000 --- a/db/migrate/20121205201726_add_more_indexes.rb +++ /dev/null @@ -1,44 +0,0 @@ -class AddMoreIndexes < ActiveRecord::Migration - def change - add_index :events, :created_at - add_index :events, :target_id - - add_index :issues, :closed - add_index :issues, :created_at - add_index :issues, :title - - add_index :keys, :identifier - # FIXME: MySQL can't index text columns - #add_index :keys, :key - add_index :keys, :project_id - - add_index :merge_requests, :closed - add_index :merge_requests, :created_at - add_index :merge_requests, :source_branch - add_index :merge_requests, :target_branch - add_index :merge_requests, :title - - add_index :milestones, :due_date - add_index :milestones, :project_id - - add_index :namespaces, :name - add_index :namespaces, :path - add_index :namespaces, :type - - add_index :notes, :created_at - - add_index :snippets, :created_at - add_index :snippets, :expires_at - - add_index :users, :admin - add_index :users, :blocked - add_index :users, :name - add_index :users, :username - - add_index :users_projects, :project_access - add_index :users_projects, :user_id - - add_index :wikis, :project_id - add_index :wikis, :slug - end -end diff --git a/db/migrate/20121218164840_move_noteable_commit_to_own_field.rb b/db/migrate/20121218164840_move_noteable_commit_to_own_field.rb deleted file mode 100644 index 6f2da413..00000000 --- a/db/migrate/20121218164840_move_noteable_commit_to_own_field.rb +++ /dev/null @@ -1,20 +0,0 @@ -class MoveNoteableCommitToOwnField < ActiveRecord::Migration - def up - add_column :notes, :commit_id, :string, null: true - add_column :notes, :new_noteable_id, :integer, null: true - Note.where(noteable_type: 'Commit').update_all('commit_id = noteable_id') - - if ActiveRecord::Base.connection.adapter_name == 'PostgreSQL' - Note.where("noteable_type != 'Commit'").update_all('new_noteable_id = CAST (noteable_id AS INTEGER)') - else - Note.where("noteable_type != 'Commit'").update_all('new_noteable_id = noteable_id') - end - - remove_column :notes, :noteable_id - rename_column :notes, :new_noteable_id, :noteable_id - end - - def down - raise ActiveRecord::IrreversibleMigration - end -end diff --git a/db/migrate/20121219095402_indices_for_notes.rb b/db/migrate/20121219095402_indices_for_notes.rb deleted file mode 100644 index 4c5d041c..00000000 --- a/db/migrate/20121219095402_indices_for_notes.rb +++ /dev/null @@ -1,6 +0,0 @@ -class IndicesForNotes < ActiveRecord::Migration - def change - add_index :notes, :commit_id - add_index :notes, [:project_id, :noteable_type] - end -end diff --git a/db/migrate/20121219183753_create_user_teams.rb b/db/migrate/20121219183753_create_user_teams.rb deleted file mode 100644 index 65c4d053..00000000 --- a/db/migrate/20121219183753_create_user_teams.rb +++ /dev/null @@ -1,11 +0,0 @@ -class CreateUserTeams < ActiveRecord::Migration - def change - create_table :user_teams do |t| - t.string :name - t.string :path - t.integer :owner_id - - t.timestamps - end - end -end diff --git a/db/migrate/20121220064104_create_user_team_project_relationships.rb b/db/migrate/20121220064104_create_user_team_project_relationships.rb deleted file mode 100644 index 8eb654c8..00000000 --- a/db/migrate/20121220064104_create_user_team_project_relationships.rb +++ /dev/null @@ -1,11 +0,0 @@ -class CreateUserTeamProjectRelationships < ActiveRecord::Migration - def change - create_table :user_team_project_relationships do |t| - t.integer :project_id - t.integer :user_team_id - t.integer :greatest_access - - t.timestamps - end - end -end diff --git a/db/migrate/20121220064453_create_user_team_user_relationships.rb b/db/migrate/20121220064453_create_user_team_user_relationships.rb deleted file mode 100644 index 7783b0ae..00000000 --- a/db/migrate/20121220064453_create_user_team_user_relationships.rb +++ /dev/null @@ -1,12 +0,0 @@ -class CreateUserTeamUserRelationships < ActiveRecord::Migration - def change - create_table :user_team_user_relationships do |t| - t.integer :user_id - t.integer :user_team_id - t.boolean :group_admin - t.integer :permission - - t.timestamps - end - end -end diff --git a/db/migrate/20130102143055_rename_owner_to_creator_for_project.rb b/db/migrate/20130102143055_rename_owner_to_creator_for_project.rb deleted file mode 100644 index d0fca269..00000000 --- a/db/migrate/20130102143055_rename_owner_to_creator_for_project.rb +++ /dev/null @@ -1,5 +0,0 @@ -class RenameOwnerToCreatorForProject < ActiveRecord::Migration - def change - rename_column :projects, :owner_id, :creator_id - end -end diff --git a/db/migrate/20130110172407_add_public_to_project.rb b/db/migrate/20130110172407_add_public_to_project.rb deleted file mode 100644 index 45edba48..00000000 --- a/db/migrate/20130110172407_add_public_to_project.rb +++ /dev/null @@ -1,5 +0,0 @@ -class AddPublicToProject < ActiveRecord::Migration - def change - add_column :projects, :public, :boolean, default: false, null: false - end -end diff --git a/db/migrate/20130123114545_add_issues_tracker_to_project.rb b/db/migrate/20130123114545_add_issues_tracker_to_project.rb deleted file mode 100644 index 288d0f07..00000000 --- a/db/migrate/20130123114545_add_issues_tracker_to_project.rb +++ /dev/null @@ -1,5 +0,0 @@ -class AddIssuesTrackerToProject < ActiveRecord::Migration - def change - add_column :projects, :issues_tracker, :string, default: :gitlab, null: false - end -end diff --git a/db/migrate/20130125090214_add_user_permissions.rb b/db/migrate/20130125090214_add_user_permissions.rb deleted file mode 100644 index 38b5f439..00000000 --- a/db/migrate/20130125090214_add_user_permissions.rb +++ /dev/null @@ -1,11 +0,0 @@ -class AddUserPermissions < ActiveRecord::Migration - def up - add_column :users, :can_create_group, :boolean, default: true, null: false - add_column :users, :can_create_team, :boolean, default: true, null: false - end - - def down - remove_column :users, :can_create_group - remove_column :users, :can_create_team - end -end diff --git a/db/migrate/20130131070232_remove_private_flag_from_project.rb b/db/migrate/20130131070232_remove_private_flag_from_project.rb deleted file mode 100644 index 5754db11..00000000 --- a/db/migrate/20130131070232_remove_private_flag_from_project.rb +++ /dev/null @@ -1,9 +0,0 @@ -class RemovePrivateFlagFromProject < ActiveRecord::Migration - def up - remove_column :projects, :private_flag - end - - def down - add_column :projects, :private_flag, :boolean, default: true, null: false - end -end diff --git a/db/migrate/20130206084024_add_description_to_namsespace.rb b/db/migrate/20130206084024_add_description_to_namsespace.rb deleted file mode 100644 index ef02e489..00000000 --- a/db/migrate/20130206084024_add_description_to_namsespace.rb +++ /dev/null @@ -1,5 +0,0 @@ -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 deleted file mode 100644 index 6d037779..00000000 --- a/db/migrate/20130207104426_add_description_to_teams.rb +++ /dev/null @@ -1,5 +0,0 @@ -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 deleted file mode 100644 index 71763d18..00000000 --- a/db/migrate/20130211085435_add_issues_tracker_id_to_project.rb +++ /dev/null @@ -1,5 +0,0 @@ -class AddIssuesTrackerIdToProject < ActiveRecord::Migration - def change - add_column :projects, :issues_tracker_id, :string - end -end diff --git a/db/migrate/20130214154045_rename_state_to_merge_status_in_milestone.rb b/db/migrate/20130214154045_rename_state_to_merge_status_in_milestone.rb deleted file mode 100644 index 23797fe1..00000000 --- a/db/migrate/20130214154045_rename_state_to_merge_status_in_milestone.rb +++ /dev/null @@ -1,5 +0,0 @@ -class RenameStateToMergeStatusInMilestone < ActiveRecord::Migration - def change - rename_column :merge_requests, :state, :merge_status - end -end diff --git a/db/migrate/20130218140952_add_state_to_issue.rb b/db/migrate/20130218140952_add_state_to_issue.rb deleted file mode 100644 index 062103d0..00000000 --- a/db/migrate/20130218140952_add_state_to_issue.rb +++ /dev/null @@ -1,5 +0,0 @@ -class AddStateToIssue < ActiveRecord::Migration - def change - add_column :issues, :state, :string - end -end diff --git a/db/migrate/20130218141038_add_state_to_merge_request.rb b/db/migrate/20130218141038_add_state_to_merge_request.rb deleted file mode 100644 index ac4108ee..00000000 --- a/db/migrate/20130218141038_add_state_to_merge_request.rb +++ /dev/null @@ -1,5 +0,0 @@ -class AddStateToMergeRequest < ActiveRecord::Migration - def change - add_column :merge_requests, :state, :string - end -end diff --git a/db/migrate/20130218141117_add_state_to_milestone.rb b/db/migrate/20130218141117_add_state_to_milestone.rb deleted file mode 100644 index c8403910..00000000 --- a/db/migrate/20130218141117_add_state_to_milestone.rb +++ /dev/null @@ -1,5 +0,0 @@ -class AddStateToMilestone < ActiveRecord::Migration - def change - add_column :milestones, :state, :string - end -end diff --git a/db/migrate/20130218141258_convert_closed_to_state_in_issue.rb b/db/migrate/20130218141258_convert_closed_to_state_in_issue.rb deleted file mode 100644 index 9fa96203..00000000 --- a/db/migrate/20130218141258_convert_closed_to_state_in_issue.rb +++ /dev/null @@ -1,14 +0,0 @@ -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) - end - end - - def down - Issue.transaction do - Issue.where(state: :closed).update_all(closed: true) - end - end -end diff --git a/db/migrate/20130218141327_convert_closed_to_state_in_merge_request.rb b/db/migrate/20130218141327_convert_closed_to_state_in_merge_request.rb deleted file mode 100644 index ebb7ae58..00000000 --- a/db/migrate/20130218141327_convert_closed_to_state_in_merge_request.rb +++ /dev/null @@ -1,16 +0,0 @@ -class ConvertClosedToStateInMergeRequest < ActiveRecord::Migration - def up - MergeRequest.transaction do - 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 - - def down - MergeRequest.transaction do - MergeRequest.where(state: :closed).update_all(closed: true) - MergeRequest.where(state: :merged).update_all(closed: true, merged: true) - end - end -end diff --git a/db/migrate/20130218141344_convert_closed_to_state_in_milestone.rb b/db/migrate/20130218141344_convert_closed_to_state_in_milestone.rb deleted file mode 100644 index 1978ea89..00000000 --- a/db/migrate/20130218141344_convert_closed_to_state_in_milestone.rb +++ /dev/null @@ -1,14 +0,0 @@ -class ConvertClosedToStateInMilestone < ActiveRecord::Migration - def up - Milestone.transaction do - 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: true) - end - end -end diff --git a/db/migrate/20130218141444_remove_merged_from_merge_request.rb b/db/migrate/20130218141444_remove_merged_from_merge_request.rb deleted file mode 100644 index a7bd82f5..00000000 --- a/db/migrate/20130218141444_remove_merged_from_merge_request.rb +++ /dev/null @@ -1,9 +0,0 @@ -class RemoveMergedFromMergeRequest < ActiveRecord::Migration - def up - remove_column :merge_requests, :merged - end - - def down - add_column :merge_requests, :merged, :boolean, default: true, null: false - end -end diff --git a/db/migrate/20130218141507_remove_closed_from_issue.rb b/db/migrate/20130218141507_remove_closed_from_issue.rb deleted file mode 100644 index 95cc0642..00000000 --- a/db/migrate/20130218141507_remove_closed_from_issue.rb +++ /dev/null @@ -1,9 +0,0 @@ -class RemoveClosedFromIssue < ActiveRecord::Migration - def up - remove_column :issues, :closed - end - - def down - add_column :issues, :closed, :boolean - end -end diff --git a/db/migrate/20130218141536_remove_closed_from_merge_request.rb b/db/migrate/20130218141536_remove_closed_from_merge_request.rb deleted file mode 100644 index 37183593..00000000 --- a/db/migrate/20130218141536_remove_closed_from_merge_request.rb +++ /dev/null @@ -1,9 +0,0 @@ -class RemoveClosedFromMergeRequest < ActiveRecord::Migration - def up - remove_column :merge_requests, :closed - end - - def down - add_column :merge_requests, :closed, :boolean - end -end diff --git a/db/migrate/20130218141554_remove_closed_from_milestone.rb b/db/migrate/20130218141554_remove_closed_from_milestone.rb deleted file mode 100644 index e8dae4a1..00000000 --- a/db/migrate/20130218141554_remove_closed_from_milestone.rb +++ /dev/null @@ -1,9 +0,0 @@ -class RemoveClosedFromMilestone < ActiveRecord::Migration - def up - remove_column :milestones, :closed - end - - def down - add_column :milestones, :closed, :boolean - end -end diff --git a/db/migrate/20130220124204_add_new_merge_status_to_merge_request.rb b/db/migrate/20130220124204_add_new_merge_status_to_merge_request.rb deleted file mode 100644 index d78bd0ae..00000000 --- a/db/migrate/20130220124204_add_new_merge_status_to_merge_request.rb +++ /dev/null @@ -1,5 +0,0 @@ -class AddNewMergeStatusToMergeRequest < ActiveRecord::Migration - def change - add_column :merge_requests, :new_merge_status, :string - end -end diff --git a/db/migrate/20130220125544_convert_merge_status_in_merge_request.rb b/db/migrate/20130220125544_convert_merge_status_in_merge_request.rb deleted file mode 100644 index b310b35e..00000000 --- a/db/migrate/20130220125544_convert_merge_status_in_merge_request.rb +++ /dev/null @@ -1,17 +0,0 @@ -class ConvertMergeStatusInMergeRequest < ActiveRecord::Migration - def up - MergeRequest.transaction do - MergeRequest.where(merge_status: 1).update_all("new_merge_status = 'unchecked'") - MergeRequest.where(merge_status: 2).update_all("new_merge_status = 'can_be_merged'") - MergeRequest.where(merge_status: 3).update_all("new_merge_status = 'cannot_be_merged'") - end - end - - def down - MergeRequest.transaction do - MergeRequest.where(new_merge_status: :unchecked).update_all("merge_status = 1") - MergeRequest.where(new_merge_status: :can_be_merged).update_all("merge_status = 2") - MergeRequest.where(new_merge_status: :cannot_be_merged).update_all("merge_status = 3") - end - end -end diff --git a/db/migrate/20130220125545_remove_merge_status_from_merge_request.rb b/db/migrate/20130220125545_remove_merge_status_from_merge_request.rb deleted file mode 100644 index 9083183b..00000000 --- a/db/migrate/20130220125545_remove_merge_status_from_merge_request.rb +++ /dev/null @@ -1,9 +0,0 @@ -class RemoveMergeStatusFromMergeRequest < ActiveRecord::Migration - def up - remove_column :merge_requests, :merge_status - end - - def down - add_column :merge_requests, :merge_status, :integer - end -end diff --git a/db/migrate/20130220133245_rename_new_merge_status_to_merge_status_in_milestone.rb b/db/migrate/20130220133245_rename_new_merge_status_to_merge_status_in_milestone.rb deleted file mode 100644 index 3f8f38dc..00000000 --- a/db/migrate/20130220133245_rename_new_merge_status_to_merge_status_in_milestone.rb +++ /dev/null @@ -1,5 +0,0 @@ -class RenameNewMergeStatusToMergeStatusInMilestone < ActiveRecord::Migration - def change - rename_column :merge_requests, :new_merge_status, :merge_status - end -end diff --git a/db/migrate/20130304104623_add_state_to_user.rb b/db/migrate/20130304104623_add_state_to_user.rb deleted file mode 100644 index 8154c210..00000000 --- a/db/migrate/20130304104623_add_state_to_user.rb +++ /dev/null @@ -1,5 +0,0 @@ -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 deleted file mode 100644 index e8d5257a..00000000 --- a/db/migrate/20130304104740_convert_blocked_to_state.rb +++ /dev/null @@ -1,14 +0,0 @@ -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 deleted file mode 100644 index e0104745..00000000 --- a/db/migrate/20130304105317_remove_blocked_from_user.rb +++ /dev/null @@ -1,9 +0,0 @@ -class RemoveBlockedFromUser < ActiveRecord::Migration - def up - remove_column :users, :blocked - end - - def down - add_column :users, :blocked, :boolean - end -end diff --git a/db/migrate/20130315124931_user_color_scheme.rb b/db/migrate/20130315124931_user_color_scheme.rb deleted file mode 100644 index fe139e32..00000000 --- a/db/migrate/20130315124931_user_color_scheme.rb +++ /dev/null @@ -1,12 +0,0 @@ -class UserColorScheme < ActiveRecord::Migration - def up - add_column :users, :color_scheme_id, :integer, null: false, default: 1 - User.where(dark_scheme: true).update_all(color_scheme_id: 2) - remove_column :users, :dark_scheme - end - - def down - add_column :users, :dark_scheme, :boolean, null: false, default: false - remove_column :users, :color_scheme_id - end -end diff --git a/db/migrate/20130318212250_add_snippets_to_features.rb b/db/migrate/20130318212250_add_snippets_to_features.rb deleted file mode 100644 index ad0b4434..00000000 --- a/db/migrate/20130318212250_add_snippets_to_features.rb +++ /dev/null @@ -1,5 +0,0 @@ -class AddSnippetsToFeatures < ActiveRecord::Migration - def change - add_column :projects, :snippets_enabled, :boolean, null: false, default: true - end -end diff --git a/db/migrate/20130325173941_add_notification_level_to_user.rb b/db/migrate/20130325173941_add_notification_level_to_user.rb deleted file mode 100644 index 9f466e38..00000000 --- a/db/migrate/20130325173941_add_notification_level_to_user.rb +++ /dev/null @@ -1,5 +0,0 @@ -class AddNotificationLevelToUser < ActiveRecord::Migration - def change - add_column :users, :notification_level, :integer, null: false, default: 1 - end -end diff --git a/db/schema.rb b/db/schema.rb index 3c8b9eae..27b1f4aa 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20130325173941) do +ActiveRecord::Schema.define(:version => 20121120113838) do create_table "events", :force => true do |t| t.string "target_type" @@ -25,33 +25,29 @@ ActiveRecord::Schema.define(:version => 20130325173941) do t.integer "author_id" end - add_index "events", ["action"], :name => "index_events_on_action" - add_index "events", ["author_id"], :name => "index_events_on_author_id" - add_index "events", ["created_at"], :name => "index_events_on_created_at" - add_index "events", ["project_id"], :name => "index_events_on_project_id" - add_index "events", ["target_id"], :name => "index_events_on_target_id" - add_index "events", ["target_type"], :name => "index_events_on_target_type" + create_table "groups", :force => true do |t| + t.string "name", :null => false + t.string "code", :null => false + t.integer "owner_id", :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end create_table "issues", :force => true do |t| t.string "title" t.integer "assignee_id" t.integer "author_id" t.integer "project_id" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.boolean "closed", :default => false, :null => false t.integer "position", :default => 0 t.string "branch_name" t.text "description" t.integer "milestone_id" - t.string "state" end - add_index "issues", ["assignee_id"], :name => "index_issues_on_assignee_id" - add_index "issues", ["author_id"], :name => "index_issues_on_author_id" - add_index "issues", ["created_at"], :name => "index_issues_on_created_at" - add_index "issues", ["milestone_id"], :name => "index_issues_on_milestone_id" add_index "issues", ["project_id"], :name => "index_issues_on_project_id" - add_index "issues", ["title"], :name => "index_issues_on_title" create_table "keys", :force => true do |t| t.integer "user_id" @@ -63,65 +59,38 @@ ActiveRecord::Schema.define(:version => 20130325173941) do t.integer "project_id" end - add_index "keys", ["identifier"], :name => "index_keys_on_identifier" - add_index "keys", ["project_id"], :name => "index_keys_on_project_id" - add_index "keys", ["user_id"], :name => "index_keys_on_user_id" - create_table "merge_requests", :force => true do |t| - t.string "target_branch", :null => false - t.string "source_branch", :null => false - t.integer "project_id", :null => false + t.string "target_branch", :null => false + t.string "source_branch", :null => false + t.integer "project_id", :null => false t.integer "author_id" t.integer "assignee_id" t.string "title" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false + t.boolean "closed", :default => false, :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false t.text "st_commits", :limit => 2147483647 t.text "st_diffs", :limit => 2147483647 + t.boolean "merged", :default => false, :null => false + t.integer "state", :default => 1, :null => false t.integer "milestone_id" - t.string "state" - t.string "merge_status" end - add_index "merge_requests", ["assignee_id"], :name => "index_merge_requests_on_assignee_id" - add_index "merge_requests", ["author_id"], :name => "index_merge_requests_on_author_id" - add_index "merge_requests", ["created_at"], :name => "index_merge_requests_on_created_at" - add_index "merge_requests", ["milestone_id"], :name => "index_merge_requests_on_milestone_id" add_index "merge_requests", ["project_id"], :name => "index_merge_requests_on_project_id" - add_index "merge_requests", ["source_branch"], :name => "index_merge_requests_on_source_branch" - add_index "merge_requests", ["target_branch"], :name => "index_merge_requests_on_target_branch" - add_index "merge_requests", ["title"], :name => "index_merge_requests_on_title" create_table "milestones", :force => true do |t| - t.string "title", :null => false - t.integer "project_id", :null => false + t.string "title", :null => false + t.integer "project_id", :null => false t.text "description" t.date "due_date" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - t.string "state" + t.boolean "closed", :default => false, :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false end - add_index "milestones", ["due_date"], :name => "index_milestones_on_due_date" - add_index "milestones", ["project_id"], :name => "index_milestones_on_project_id" - - create_table "namespaces", :force => true do |t| - t.string "name", :null => false - t.string "path", :null => false - t.integer "owner_id", :null => false - 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" - add_index "namespaces", ["owner_id"], :name => "index_namespaces_on_owner_id" - add_index "namespaces", ["path"], :name => "index_namespaces_on_path" - add_index "namespaces", ["type"], :name => "index_namespaces_on_type" - create_table "notes", :force => true do |t| t.text "note" + t.string "noteable_id" t.string "noteable_type" t.integer "author_id" t.datetime "created_at", :null => false @@ -129,38 +98,28 @@ ActiveRecord::Schema.define(:version => 20130325173941) do t.integer "project_id" t.string "attachment" t.string "line_code" - t.string "commit_id" - t.integer "noteable_id" end - add_index "notes", ["commit_id"], :name => "index_notes_on_commit_id" - add_index "notes", ["created_at"], :name => "index_notes_on_created_at" + add_index "notes", ["noteable_id"], :name => "index_notes_on_noteable_id" add_index "notes", ["noteable_type"], :name => "index_notes_on_noteable_type" - add_index "notes", ["project_id", "noteable_type"], :name => "index_notes_on_project_id_and_noteable_type" - add_index "notes", ["project_id"], :name => "index_notes_on_project_id" create_table "projects", :force => true do |t| t.string "name" t.string "path" t.text "description" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - t.integer "creator_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.boolean "private_flag", :default => true, :null => false + t.string "code" + t.integer "owner_id" t.string "default_branch" - t.boolean "issues_enabled", :default => true, :null => false - t.boolean "wall_enabled", :default => true, :null => false - t.boolean "merge_requests_enabled", :default => true, :null => false - 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" - t.boolean "snippets_enabled", :default => true, :null => false + t.boolean "issues_enabled", :default => true, :null => false + t.boolean "wall_enabled", :default => true, :null => false + t.boolean "merge_requests_enabled", :default => true, :null => false + t.boolean "wiki_enabled", :default => true, :null => false + t.integer "group_id" end - add_index "projects", ["creator_id"], :name => "index_projects_on_owner_id" - add_index "projects", ["namespace_id"], :name => "index_projects_on_namespace_id" - create_table "protected_branches", :force => true do |t| t.integer "project_id", :null => false t.string "name", :null => false @@ -179,8 +138,6 @@ ActiveRecord::Schema.define(:version => 20130325173941) do t.string "project_url" end - add_index "services", ["project_id"], :name => "index_services_on_project_id" - create_table "snippets", :force => true do |t| t.string "title" t.text "content" @@ -192,10 +149,6 @@ ActiveRecord::Schema.define(:version => 20130325173941) do t.datetime "expires_at" end - add_index "snippets", ["created_at"], :name => "index_snippets_on_created_at" - add_index "snippets", ["expires_at"], :name => "index_snippets_on_expires_at" - add_index "snippets", ["project_id"], :name => "index_snippets_on_project_id" - create_table "taggings", :force => true do |t| t.integer "tag_id" t.integer "taggable_id" @@ -213,32 +166,6 @@ ActiveRecord::Schema.define(:version => 20130325173941) do t.string "name" end - create_table "user_team_project_relationships", :force => true do |t| - t.integer "project_id" - t.integer "user_team_id" - t.integer "greatest_access" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - end - - create_table "user_team_user_relationships", :force => true do |t| - t.integer "user_id" - t.integer "user_team_id" - t.boolean "group_admin" - t.integer "permission" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - end - - create_table "user_teams", :force => true do |t| - t.string "name" - t.string "path" - 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| t.string "email", :default => "", :null => false t.string "encrypted_password", :default => "", :null => false @@ -259,26 +186,19 @@ ActiveRecord::Schema.define(:version => 20130325173941) do t.string "linkedin", :default => "", :null => false t.string "twitter", :default => "", :null => false t.string "authentication_token" + 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" t.string "provider" - t.string "username" - t.boolean "can_create_group", :default => true, :null => false - t.boolean "can_create_team", :default => true, :null => false - t.string "state" - t.integer "color_scheme_id", :default => 1, :null => false - t.integer "notification_level", :default => 1, :null => false end - add_index "users", ["admin"], :name => "index_users_on_admin" 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" add_index "users", ["reset_password_token"], :name => "index_users_on_reset_password_token", :unique => true - add_index "users", ["username"], :name => "index_users_on_username" create_table "users_projects", :force => true do |t| t.integer "user_id", :null => false @@ -288,10 +208,6 @@ ActiveRecord::Schema.define(:version => 20130325173941) do t.integer "project_access", :default => 0, :null => false end - add_index "users_projects", ["project_access"], :name => "index_users_projects_on_project_access" - add_index "users_projects", ["project_id"], :name => "index_users_projects_on_project_id" - add_index "users_projects", ["user_id"], :name => "index_users_projects_on_user_id" - create_table "web_hooks", :force => true do |t| t.string "url" t.integer "project_id" @@ -311,7 +227,4 @@ ActiveRecord::Schema.define(:version => 20130325173941) do t.integer "user_id" end - add_index "wikis", ["project_id"], :name => "index_wikis_on_project_id" - add_index "wikis", ["slug"], :name => "index_wikis_on_slug" - end diff --git a/doc/api/README.md b/doc/api/README.md index c4d44c52..19b7ff20 100644 --- a/doc/api/README.md +++ b/doc/api/README.md @@ -1,6 +1,6 @@ # GitLab API -All API requests require authentication. You need to pass a `private_token` parameter by url or header. If passed as header, the header name must be "PRIVATE-TOKEN" (capital and with dash instead of underscore). You can find or reset your private token in your profile. +All API requests require authentication. You need to pass a `private_token` parameter by url or header. You can find or reset your private token in your profile. If no, or an invalid, `private_token` is provided then an error message will be returned with status code 401: @@ -10,72 +10,29 @@ If no, or an invalid, `private_token` is provided then an error message will be } ``` -API requests should be prefixed with `api` and the API version. The API version is defined in `lib/api.rb`. +API requests should be prefixed with `api` and the API version. The API version is equal to the GitLab major version number, which is defined in `lib/api.rb`. Example of a valid API request: ``` -GET http://example.com/api/v3/projects?private_token=QVy1PB7sTxfy4pqfZM1U +GET http://example.com/api/v2/projects?private_token=QVy1PB7sTxfy4pqfZM1U ``` -Example for a valid API request using curl and authentication via header: - -``` -curl --header "PRIVATE-TOKEN: QVy1PB7sTxfy4pqfZM1U" "http://example.com/api/v3/projects" -``` - - The API uses JSON to serialize data. You don't need to specify `.json` at the end of API URL. - - -## Status codes - -The API is designed to return different status codes according to context and action. In this way -if a request results in an error the caller is able to get insight into what went wrong, e.g. -status code `400 Bad Request` is returned if a required attribute is missing from the request. -The following list gives an overview of how the API functions generally behave. - -API request types: - -* `GET` requests access one or more resources and return the result as JSON -* `POST` requests return `201 Created` if the resource is successfully created and return the newly created resource as JSON -* `GET`, `PUT` and `DELETE` return `200 Ok` if the resource is accessed, modified or deleted successfully, the (modified) result is returned as JSON -* `DELETE` requests are designed to be idempotent, meaning a request a resource still returns `200 Ok` even it was deleted before or is not available. The reasoning behind it is the user is not really interested if the resource existed before or not. - - -The following list shows the possible return codes for API requests. - -Return values: - -* `200 Ok` - The `GET`, `PUT` or `DELETE` request was successful, the resource(s) itself is returned as JSON -* `201 Created` - The `POST` request was successful and the resource is returned as JSON -* `400 Bad Request` - A required attribute of the API request is missing, e.g. the title of an issue is not given -* `401 Unauthorized` - The user is not authenticated, a valid user token is necessary, see above -* `403 Forbidden` - The request is not allowed, e.g. the user is not allowed to delete a project -* `404 Not Found` - A resource could not be accessed, e.g. an ID for a resource could not be found -* `405 Method Not Allowed` - The request is not supported -* `409 Conflict` - A conflicting resource already exists, e.g. creating a project with a name that already exists -* `500 Server Error` - While handling the request something went wrong on the server side - - - #### Pagination When listing resources you can pass the following parameters: + `page` (default: `1`) - page number -+ `per_page` (default: `20`, max: `100`) - number of items to list per page ++ `per_page` (default: `20`, max: `100`) - how many items to list per page ## Contents + [Users](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/users.md) + [Session](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/session.md) + [Projects](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/projects.md) -+ [Groups](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/groups.md) + [Snippets](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/snippets.md) + [Repositories](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/repositories.md) + [Issues](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/issues.md) + [Milestones](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/milestones.md) -+ [Notes](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/notes.md) -+ [System Hooks](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/system_hooks.md) diff --git a/doc/api/groups.md b/doc/api/groups.md deleted file mode 100644 index e9702ea2..00000000 --- a/doc/api/groups.md +++ /dev/null @@ -1,57 +0,0 @@ -## List project groups - -Get a list of groups. (As user: my groups, as admin: all groups) - -``` -GET /groups -``` - -```json -[ - { - "id": 1, - "name": "Foobar Group", - "path": "foo-bar", - "owner_id": 18 - } -] -``` - - -## Details of a group - -Get all details of a group. - -``` -GET /groups/:id -``` - -Parameters: - -+ `id` (required) - The ID of a group - - -## New group - -Creates a new project group. Available only for admin. - -``` -POST /groups -``` - -Parameters: - -+ `name` (required) - The name of the group -+ `path` (required) - The path of the group - -## Transfer project to group - -Transfer a project to the Group namespace. Available only for admin - -``` -POST /groups/:id/projects/:project_id -``` - -Parameters: -+ `id` (required) - The ID of a group -+ `project_id (required) - The ID of a project diff --git a/doc/api/issues.md b/doc/api/issues.md index a8ae7401..aaad3305 100644 --- a/doc/api/issues.md +++ b/doc/api/issues.md @@ -1,7 +1,6 @@ ## List issues -Get all issues created by authenticed user. This function takes pagination parameters -`page` and `per_page` to restrict the list of issues. +Get all issues created by authenticed user. ``` GET /issues @@ -19,7 +18,6 @@ GET /issues "assignee": null, "author": { "id": 1, - "username": "john_smith", "email": "john@example.com", "name": "John Smith", "blocked": false, @@ -48,7 +46,6 @@ GET /issues }, "assignee": { "id": 2, - "username": "jack_smith", "email": "jack@example.com", "name": "Jack Smith", "blocked": false, @@ -56,7 +53,6 @@ GET /issues }, "author": { "id": 1, - "username": "john_smith", "email": "john@example.com", "name": "John Smith", "blocked": false, @@ -69,11 +65,9 @@ GET /issues ] ``` - ## List project issues -Get a list of project issues. This function accepts pagination parameters `page` and `per_page` -to return the list of project issues. +Get a list of project issues. ``` GET /projects/:id/issues @@ -81,12 +75,11 @@ GET /projects/:id/issues Parameters: -+ `id` (required) - The ID of a project - ++ `id` (required) - The ID or code name of a project ## Single issue -Gets a single project issue. +Get a project issue. ``` GET /projects/:id/issues/:issue_id @@ -94,7 +87,7 @@ GET /projects/:id/issues/:issue_id Parameters: -+ `id` (required) - The ID of a project ++ `id` (required) - The ID or code name of a project + `issue_id` (required) - The ID of a project issue ```json @@ -117,7 +110,6 @@ Parameters: }, "assignee": { "id": 2, - "username": "jack_smith", "email": "jack@example.com", "name": "Jack Smith", "blocked": false, @@ -125,7 +117,6 @@ Parameters: }, "author": { "id": 1, - "username": "john_smith", "email": "john@example.com", "name": "John Smith", "blocked": false, @@ -137,10 +128,9 @@ Parameters: } ``` - ## New issue -Creates a new project issue. +Create a new project issue. ``` POST /projects/:id/issues @@ -148,17 +138,18 @@ POST /projects/:id/issues Parameters: -+ `id` (required) - The ID of a project ++ `id` (required) - The ID or code name of a project + `title` (required) - The title of an issue + `description` (optional) - The description of an issue + `assignee_id` (optional) - The ID of a user to assign issue + `milestone_id` (optional) - The ID of a milestone to assign issue + `labels` (optional) - Comma-separated label names for an issue +Will return created issue with status `201 Created` on success, or `404 Not found` on fail. ## Edit issue -Updates an existing project issue. This function is also used to mark an issue as closed. +Update an existing project issue. ``` PUT /projects/:id/issues/:issue_id @@ -166,7 +157,7 @@ PUT /projects/:id/issues/:issue_id Parameters: -+ `id` (required) - The ID of a project ++ `id` (required) - The ID or code name of a project + `issue_id` (required) - The ID of a project's issue + `title` (optional) - The title of an issue + `description` (optional) - The description of an issue @@ -175,12 +166,11 @@ Parameters: + `labels` (optional) - Comma-separated label names for an issue + `closed` (optional) - The state of an issue (0 = false, 1 = true) +Will return updated issue with status `200 OK` on success, or `404 Not found` on fail. -## Delete existing issue (**Deprecated**) +## Delete issue -The function is deprecated and returns a `405 Method Not Allowed` -error if called. An issue gets now closed and is done by calling `PUT /projects/:id/issues/:issue_id` with -parameter `closed` set to 1. +Delete existing project issue. ``` DELETE /projects/:id/issues/:issue_id @@ -188,6 +178,7 @@ DELETE /projects/:id/issues/:issue_id Parameters: -+ `id` (required) - The project ID -+ `issue_id` (required) - The ID of the issue ++ `id` (required) - The ID or code name of a project ++ `issue_id` (required) - The ID of a project's issue +Status code `200` will be returned on success. diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md index 111c5211..e5b067a6 100644 --- a/doc/api/merge_requests.md +++ b/doc/api/merge_requests.md @@ -1,7 +1,6 @@ ## List merge requests -Get all merge requests for this project. This function takes pagination parameters -`page` and `per_page` to restrict the list of merge requests. +Get all MR for this project. ``` GET /projects/:id/merge_requests @@ -9,7 +8,7 @@ GET /projects/:id/merge_requests Parameters: -+ `id` (required) - The ID of a project ++ `id` (required) - The ID or code name of a project ```json [ @@ -23,7 +22,6 @@ Parameters: "merged":false, "author":{ "id":1, - "username": "admin", "email":"admin@local.host", "name":"Administrator", "blocked":false, @@ -31,7 +29,6 @@ Parameters: }, "assignee":{ "id":1, - "username": "admin", "email":"admin@local.host", "name":"Administrator", "blocked":false, @@ -41,10 +38,9 @@ Parameters: ] ``` +## Show MR -## Get single MR - -Shows information about a single merge request. +Show information about MR. ``` GET /projects/:id/merge_request/:merge_request_id @@ -52,7 +48,7 @@ GET /projects/:id/merge_request/:merge_request_id Parameters: -+ `id` (required) - The ID of a project ++ `id` (required) - The ID or code name of a project + `merge_request_id` (required) - The ID of MR ```json @@ -66,7 +62,6 @@ Parameters: "merged":false, "author":{ "id":1, - "username": "admin", "email":"admin@local.host", "name":"Administrator", "blocked":false, @@ -74,7 +69,6 @@ Parameters: }, "assignee":{ "id":1, - "username": "admin", "email":"admin@local.host", "name":"Administrator", "blocked":false, @@ -86,7 +80,7 @@ Parameters: ## Create MR -Creates a new merge request. +Create MR. ``` POST /projects/:id/merge_requests @@ -94,7 +88,7 @@ POST /projects/:id/merge_requests Parameters: -+ `id` (required) - The ID of a project ++ `id` (required) - The ID or code name of a project + `source_branch` (required) - The source branch + `target_branch` (required) - The target branch + `assignee_id` - Assignee user ID @@ -111,7 +105,6 @@ Parameters: "merged":false, "author":{ "id":1, - "username": "admin", "email":"admin@local.host", "name":"Administrator", "blocked":false, @@ -119,7 +112,6 @@ Parameters: }, "assignee":{ "id":1, - "username": "admin", "email":"admin@local.host", "name":"Administrator", "blocked":false, @@ -128,10 +120,9 @@ Parameters: } ``` - ## Update MR -Updates an existing merge request. You can change branches, title, or even close the MR. +Update MR. You can change branches, title, or even close the MR. ``` PUT /projects/:id/merge_request/:merge_request_id @@ -139,7 +130,7 @@ PUT /projects/:id/merge_request/:merge_request_id Parameters: -+ `id` (required) - The ID of a project ++ `id` (required) - The ID or code name of a project + `merge_request_id` (required) - ID of MR + `source_branch` - The source branch + `target_branch` - The target branch @@ -159,7 +150,6 @@ Parameters: "merged":false, "author":{ "id":1, - "username": "admin", "email":"admin@local.host", "name":"Administrator", "blocked":false, @@ -167,7 +157,6 @@ Parameters: }, "assignee":{ "id":1, - "username": "admin", "email":"admin@local.host", "name":"Administrator", "blocked":false, @@ -175,11 +164,9 @@ Parameters: } } ``` - - ## Post comment to MR -Adds a comment to a merge request. +Post comment to MR ``` POST /projects/:id/merge_request/:merge_request_id/comments @@ -187,16 +174,16 @@ POST /projects/:id/merge_request/:merge_request_id/comments Parameters: -+ `id` (required) - The ID of a project -+ `merge_request_id` (required) - ID of merge request ++ `id` (required) - The ID or code name of a project ++ `merge_request_id` (required) - ID of MR + `note` (required) - Text of comment +Will return created note with status `201 Created` on success, or `404 Not found` on fail. ```json { "author":{ "id":1, - "username": "admin", "email":"admin@local.host", "name":"Administrator", "blocked":false, diff --git a/doc/api/milestones.md b/doc/api/milestones.md index 92a29cee..f68d8eb7 100644 --- a/doc/api/milestones.md +++ b/doc/api/milestones.md @@ -1,6 +1,6 @@ ## List project milestones -Returns a list of project milestones. +Get a list of project milestones. ``` GET /projects/:id/milestones @@ -8,12 +8,11 @@ GET /projects/:id/milestones Parameters: -+ `id` (required) - The ID of a project ++ `id` (required) - The ID or code name of a project +## Single milestone -## Get single milestone - -Gets a single project milestone. +Get a single project milestone. ``` GET /projects/:id/milestones/:milestone_id @@ -21,13 +20,12 @@ GET /projects/:id/milestones/:milestone_id Parameters: -+ `id` (required) - The ID of a project ++ `id` (required) - The ID or code name of a project + `milestone_id` (required) - The ID of a project milestone +## New milestone -## Create new milestone - -Creates a new project milestone. +Create a new project milestone. ``` POST /projects/:id/milestones @@ -35,15 +33,15 @@ POST /projects/:id/milestones Parameters: -+ `id` (required) - The ID of a project ++ `id` (required) - The ID or code name of a project ++ `milestone_id` (required) - The ID of a project milestone + `title` (required) - The title of an milestone + `description` (optional) - The description of the milestone + `due_date` (optional) - The due date of the milestone - ## Edit milestone -Updates an existing project milestone. +Update an existing project milestone. ``` PUT /projects/:id/milestones/:milestone_id @@ -51,10 +49,9 @@ PUT /projects/:id/milestones/:milestone_id Parameters: -+ `id` (required) - The ID of a project ++ `id` (required) - The ID or code name of a project + `milestone_id` (required) - The ID of a project milestone + `title` (optional) - The title of a milestone + `description` (optional) - The description of a milestone + `due_date` (optional) - The due date of the milestone + `closed` (optional) - The status of the milestone - diff --git a/doc/api/notes.md b/doc/api/notes.md deleted file mode 100644 index 4b57f636..00000000 --- a/doc/api/notes.md +++ /dev/null @@ -1,198 +0,0 @@ -## Wall - -### List project wall notes - -Get a list of project wall notes. - -``` -GET /projects/:id/notes -``` - -```json -[ - { - "id": 522, - "body": "The solution is rather tricky", - "author": { - "id": 1, - "username": "john_smith", - "email": "john@example.com", - "name": "John Smith", - "blocked": false, - "created_at": "2012-05-23T08:00:58Z" - }, - "created_at": "2012-11-27T19:16:44Z" - } -] -``` - -Parameters: - -+ `id` (required) - The ID of a project - - -### Get single wall note - -Returns a single wall note. - -``` -GET /projects/:id/notes/:note_id -``` - -Parameters: - -+ `id` (required) - The ID of a project -+ `note_id` (required) - The ID of a wall note - - -### Create new wall note - -Creates a new wall note. - -``` -POST /projects/:id/notes -``` - -Parameters: - -+ `id` (required) - The ID of a project -+ `body` (required) - The content of a note - - -## Issues - -### List project issue notes - -Gets a list of all notes for a single issue. - -``` -GET /projects/:id/issues/:issue_id/notes -``` - -Parameters: - -+ `id` (required) - The ID of a project -+ `issue_id` (required) - The ID of an issue - - -### Get single issue note - -Returns a single note for a specific project issue - -``` -GET /projects/:id/issues/:issue_id/notes/:note_id -``` - -Parameters: - -+ `id` (required) - The ID of a project -+ `issue_id` (required) - The ID of a project issue -+ `note_id` (required) - The ID of an issue note - - -### Create new issue note - -Creates a new note to a single project issue. - -``` -POST /projects/:id/issues/:issue_id/notes -``` - -Parameters: - -+ `id` (required) - The ID of a project -+ `issue_id` (required) - The ID of an issue -+ `body` (required) - The content of a note - - -## Snippets - -### List all snippet notes - -Gets a list of all notes for a single snippet. Snippet notes are comments users can post to a snippet. - -``` -GET /projects/:id/snippets/:snippet_id/notes -``` - -Parameters: - -+ `id` (required) - The ID of a project -+ `snippet_id` (required) - The ID of a project snippet - - -### Get single snippet note - -Returns a single note for a given snippet. - -``` -GET /projects/:id/snippets/:snippet_id/notes/:note_id -``` - -Parameters: - -+ `id` (required) - The ID of a project -+ `snippet_id` (required) - The ID of a project snippet -+ `note_id` (required) - The ID of an snippet note - - -### Create new snippet note - -Creates a new note for a single snippet. Snippet notes are comments users can post to a snippet. - -``` -POST /projects/:id/snippets/:snippet_id/notes -``` - -Parameters: - -+ `id` (required) - The ID of a project -+ `snippet_id` (required) - The ID of an snippet -+ `body` (required) - The content of a note - - -## Merge Requests - -### List all merge request notes - -Gets a list of all notes for a single merge request. - -``` -GET /projects/:id/merge_requests/:merge_request_id/notes -``` - -Parameters: - -+ `id` (required) - The ID of a project -+ `merge_request_id` (required) - The ID of a project merge request - - -### Get single merge request note - -Returns a single note for a given merge request. - -``` -GET /projects/:id/merge_requests/:merge_request_id/notes/:note_id -``` - -Parameters: - -+ `id` (required) - The ID of a project -+ `merge_request_id` (required) - The ID of a project merge request -+ `note_id` (required) - The ID of a merge request note - - -### Create new merge request note - -Creates a new note for a single merge request. - -``` -POST /projects/:id/merge_requests/:merge_request_id/notes -``` - -Parameters: - -+ `id` (required) - The ID of a project -+ `merge_request_id` (required) - The ID of a merge request -+ `body` (required) - The content of a note - diff --git a/doc/api/projects.md b/doc/api/projects.md index d6a9a885..fdedf904 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -1,6 +1,4 @@ -## Projects - -### List projects +## List projects Get a list of projects owned by the authenticated user. @@ -12,20 +10,19 @@ GET /projects [ { "id": 3, + "code": "rails", "name": "rails", "description": null, + "path": "rails", "default_branch": "master", "owner": { "id": 1, - "username": "john_smith", "email": "john@example.com", "name": "John Smith", "blocked": false, "created_at": "2012-05-23T08:00:58Z" }, - "public": true, - "path": "rails", - "path_with_namespace": "rails/rails", + "private": true, "issues_enabled": false, "merge_requests_enabled": false, "wall_enabled": true, @@ -34,20 +31,19 @@ GET /projects }, { "id": 5, + "code": "gitlab", "name": "gitlab", "description": null, + "path": "gitlab", "default_branch": "api", "owner": { "id": 1, - "username": "john_smith", "email": "john@example.com", "name": "John Smith", "blocked": false, "created_at": "2012-05-23T08:00:58Z" }, - "public": true, - "path": "gitlab", - "path_with_namespace": "randx/gitlab", + "private": true, "issues_enabled": true, "merge_requests_enabled": true, "wall_enabled": true, @@ -57,11 +53,9 @@ GET /projects ] ``` +## Single project -### Get single project - -Get a specific project, identified by project ID or NAME, which is owned by the authentication user. -Currently namespaced projects cannot retrieved by name. +Get a specific project, identified by project ID, which is owned by the authentication user. ``` GET /projects/:id @@ -69,25 +63,24 @@ GET /projects/:id Parameters: -+ `id` (required) - The ID or NAME of a project ++ `id` (required) - The ID or code name of a project ```json { "id": 5, + "code": "gitlab", "name": "gitlab", "description": null, + "path": "gitlab", "default_branch": "api", "owner": { "id": 1, - "username": "john_smith", "email": "john@example.com", "name": "John Smith", "blocked": false, "created_at": "2012-05-23T08:00:58Z" }, - "public": true, - "path": "gitlab", - "path_with_namespace": "randx/gitlab", + "private": true, "issues_enabled": true, "merge_requests_enabled": true, "wall_enabled": true, @@ -96,10 +89,9 @@ Parameters: } ``` +## Create project -### Create project - -Creates new project owned by user. +Create new project owned by user ``` POST /projects @@ -108,6 +100,8 @@ POST /projects Parameters: + `name` (required) - new project name ++ `code` (optional) - new project code, uses project name if not set ++ `path` (optional) - new project path, uses project name if not set + `description` (optional) - short project description + `default_branch` (optional) - 'master' by default + `issues_enabled` (optional) - enabled by default @@ -115,42 +109,10 @@ Parameters: + `merge_requests_enabled` (optional) - enabled by default + `wiki_enabled` (optional) - enabled by default -**Project access levels** +Will return created project with status `201 Created` on success, or `404 Not +found` on fail. -The project access levels are defined in the `user_project.rb` class. Currently, these levels are recoginized: - -``` - GUEST = 10 - REPORTER = 20 - DEVELOPER = 30 - MASTER = 40 -``` - - -### Create project for user - -Creates a new project owned by user. Available only for admins. - -``` -POST /projects/user/:user_id -``` - -Parameters: - -+ `user_id` (required) - user_id of owner -+ `name` (required) - new project name -+ `description` (optional) - short project description -+ `default_branch` (optional) - 'master' by default -+ `issues_enabled` (optional) - enabled by default -+ `wall_enabled` (optional) - enabled by default -+ `merge_requests_enabled` (optional) - enabled by default -+ `wiki_enabled` (optional) - enabled by default - - - -## Team members - -### List project team members +## List project team members Get a list of project team members. @@ -160,13 +122,11 @@ GET /projects/:id/members Parameters: -+ `id` (required) - The ID or NAME of a project -+ `query` (optional) - Query string to search for members ++ `id` (required) - The ID or code name of a project +## Get project team member -### Get project team member - -Gets a project team member. +Get a project team member. ``` GET /projects/:id/members/:user_id @@ -174,13 +134,13 @@ GET /projects/:id/members/:user_id Parameters: -+ `id` (required) - The ID or NAME of a project ++ `id` (required) - The ID or code name of a project + `user_id` (required) - The ID of a user ```json { + "id": 1, - "username": "john_smith", "email": "john@example.com", "name": "John Smith", "blocked": false, @@ -189,12 +149,9 @@ Parameters: } ``` +## Add project team member -### Add project team member - -Adds a user to a project team. This is an idempotent method and can be called multiple times -with the same parameters. Adding team membership to a user that is already a member does not -affect the existing membership. +Add a user to a project team. ``` POST /projects/:id/members @@ -202,14 +159,15 @@ POST /projects/:id/members Parameters: -+ `id` (required) - The ID or NAME of a project ++ `id` (required) - The ID or code name of a project + `user_id` (required) - The ID of a user to add + `access_level` (required) - Project access level +Will return status `201 Created` on success, or `404 Not found` on fail. -### Edit project team member +## Edit project team member -Updates project team member to a specified access level. +Update project team member to specified access level. ``` PUT /projects/:id/members/:user_id @@ -217,12 +175,13 @@ PUT /projects/:id/members/:user_id Parameters: -+ `id` (required) - The ID or NAME of a project ++ `id` (required) - The ID or code name of a project + `user_id` (required) - The ID of a team member + `access_level` (required) - Project access level +Will return status `200 OK` on success, or `404 Not found` on fail. -### Remove project team member +## Remove project team member Removes user from project team. @@ -232,20 +191,14 @@ DELETE /projects/:id/members/:user_id Parameters: -+ `id` (required) - The ID or NAME of a project ++ `id` (required) - The ID or code name of a project + `user_id` (required) - The ID of a team member -This method is idempotent and can be called multiple times with the same parameters. -Revoking team membership for a user who is not currently a team member is considered success. -Please note that the returned JSON currently differs slightly. Thus you should not -rely on the returned JSON structure. +Status code `200` will be returned on success. +## List project hooks -## Hooks - -### List project hooks - -Get list of project hooks. +Get list for project hooks ``` GET /projects/:id/hooks @@ -253,12 +206,13 @@ GET /projects/:id/hooks Parameters: -+ `id` (required) - The ID or NAME of a project ++ `id` (required) - The ID or code name of a project +Will return hooks with status `200 OK` on success, or `404 Not found` on fail. -### Get project hook +## Get project hook -Get a specific hook for project. +Get hook for project ``` GET /projects/:id/hooks/:hook_id @@ -266,21 +220,14 @@ GET /projects/:id/hooks/:hook_id Parameters: -+ `id` (required) - The ID or NAME of a project ++ `id` (required) - The ID or code name of a project + `hook_id` (required) - The ID of a project hook -```json -{ - "id": 1, - "url": "http://example.com/hook", - "created_at": "2012-10-12T17:04:47Z" -} -``` +Will return hook with status `200 OK` on success, or `404 Not found` on fail. +## Add project hook -### Add project hook - -Adds a hook to project. +Add hook to project ``` POST /projects/:id/hooks @@ -288,13 +235,14 @@ POST /projects/:id/hooks Parameters: -+ `id` (required) - The ID or NAME of a project ++ `id` (required) - The ID or code name of a project + `url` (required) - The hook URL +Will return status `201 Created` on success, or `404 Not found` on fail. -### Edit project hook +## Edit project hook -Edits a hook for project. +Edit hook for project ``` PUT /projects/:id/hooks/:hook_id @@ -302,205 +250,24 @@ PUT /projects/:id/hooks/:hook_id Parameters: -+ `id` (required) - The ID or NAME of a project ++ `id` (required) - The ID or code name of a project + `hook_id` (required) - The ID of a project hook + `url` (required) - The hook URL +Will return status `201 Created` on success, or `404 Not found` on fail. -### Delete project hook -Removes a hook from project. This is an idempotent method and can be called multiple times. -Either the hook is available or not. +## Delete project hook + +Delete hook from project ``` -DELETE /projects/:id/hooks/ +DELETE /projects/:id/hooks ``` Parameters: -+ `id` (required) - The ID or NAME of a project ++ `id` (required) - The ID or code name of a project + `hook_id` (required) - The ID of hook to delete -Note the JSON response differs if the hook is available or not. If the project hook -is available before it is returned in the JSON response or an empty response is returned. - - -## Branches - -### List branches - -Lists all branches of a project. - -``` -GET /projects/:id/repository/branches -``` - -Parameters: - -+ `id` (required) - The ID of the project - - -### List single branch - -Lists a specific branch of a project. - -``` -GET /projects/:id/repository/branches/:branch -``` - -Parameters: - -+ `id` (required) - The ID of the project. -+ `branch` (required) - The name of the branch. - - -### Protect single branch - -Protects a single branch of a project. - -``` -PUT /projects/:id/repository/branches/:branch/protect -``` - -Parameters: - -+ `id` (required) - The ID of the project. -+ `branch` (required) - The name of the branch. - - -### Unprotect single branch - -Unprotects a single branch of a project. - -``` -PUT /projects/:id/repository/branches/:branch/unprotect -``` - -Parameters: - -+ `id` (required) - The ID of the project. -+ `branch` (required) - The name of the branch. - - -### List tags - -Lists all tags of a project. - -``` -GET /projects/:id/repository/tags -``` - -Parameters: - -+ `id` (required) - The ID of the project - - -### List commits - -Lists all commits with pagination. If the optional `ref_name` name is not given the commits of -the default branch (usually master) are returned. - -``` -GET /projects/:id/repository/commits -``` - -Parameters: - -+ `id` (required) - The Id of the project -+ `ref_name` (optional) - The name of a repository branch or tag -+ `page` (optional) - The page of commits to return (`0` default) -+ `per_page` (optional) - The number of commits per page (`20` default) - -Returns values: - -+ `200 Ok` on success and a list with commits -+ `404 Not Found` if project with id or the branch with `ref_name` not found - - - -## Deploy Keys - -### List deploy keys - -Get a list of a project's deploy keys. - -``` -GET /projects/:id/keys -``` - -Parameters: - -+ `id` (required) - The ID of the project - -```json -[ - { - "id": 1, - "title" : "Public key" - "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4 - 596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4 - soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=", - }, - { - "id": 3, - "title" : "Another Public key" - "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4 - 596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4 - soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=" - } -] -``` - - -### Single deploy key - -Get a single key. - -``` -GET /projects/:id/keys/:key_id -``` - -Parameters: - -+ `id` (required) - The ID of the project -+ `key_id` (required) - The ID of the deploy key - -```json -{ - "id": 1, - "title" : "Public key" - "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4 - 596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4 - soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=" -} -``` - - -### Add deploy key - -Creates a new deploy key for a project. - -``` -POST /projects/:id/keys -``` - -Parameters: - -+ `id` (required) - The ID of the project -+ `title` (required) - New deploy key's title -+ `key` (required) - New deploy key - - -### Delete deploy key - -Delete a deploy key from a project - -``` -DELETE /projects/:id/keys/:key_id -``` - -Parameters: - -+ `id` (required) - The ID of the project -+ `key_id` (required) - The ID of the deploy key - +Will return status `200 OK` on success, or `404 Not found` on fail. diff --git a/doc/api/repositories.md b/doc/api/repositories.md index 90fda387..487ad9b2 100644 --- a/doc/api/repositories.md +++ b/doc/api/repositories.md @@ -1,4 +1,4 @@ -## List repository branches +## Project repository branches Get a list of repository branches from a project, sorted by name alphabetically. @@ -8,7 +8,7 @@ GET /projects/:id/repository/branches Parameters: -+ `id` (required) - The ID of a project ++ `id` (required) - The ID or code name of a project ```json [ @@ -33,14 +33,12 @@ Parameters: }, "authored_date": "2012-06-27T05:51:39-07:00", "committed_date": "2012-06-28T03:44:20-07:00" - }, - "protected": true + } } ] ``` - -## Get single repository branch +## Project repository branch Get a single project repository branch. @@ -50,7 +48,7 @@ GET /projects/:id/repository/branches/:branch Parameters: -+ `id` (required) - The ID of a project ++ `id` (required) - The ID or code name of a project + `branch` (required) - The name of the branch ```json @@ -75,97 +73,11 @@ Parameters: }, "authored_date": "2012-06-27T05:51:39-07:00", "committed_date": "2012-06-28T03:44:20-07:00" - }, - "protected": true + } } ``` - -## Protect repository branch - -Protects a single project repository branch. This is an idempotent function, protecting an already -protected repository branch still returns a `200 Ok` status code. - -``` -PUT /projects/:id/repository/branches/:branch/protect -``` - -Parameters: - -+ `id` (required) - The ID of a project -+ `branch` (required) - The name of the branch - -```json -{ - "name": "master", - "commit": { - "id": "7b5c3cc8be40ee161ae89a06bba6229da1032a0c", - "parents": [ - { - "id": "4ad91d3c1144c406e50c7b33bae684bd6837faf8" - } - ], - "tree": "46e82de44b1061621357f24c05515327f2795a95", - "message": "add projects API", - "author": { - "name": "John Smith", - "email": "john@example.com" - }, - "committer": { - "name": "John Smith", - "email": "john@example.com" - }, - "authored_date": "2012-06-27T05:51:39-07:00", - "committed_date": "2012-06-28T03:44:20-07:00" - }, - "protected": true -} -``` - - -## Unprotect repository branch - -Unprotects a single project repository branch. This is an idempotent function, unprotecting an already -unprotected repository branch still returns a `200 Ok` status code. - -``` -PUT /projects/:id/repository/branches/:branch/unprotect -``` - -Parameters: - -+ `id` (required) - The ID of a project -+ `branch` (required) - The name of the branch - -```json -{ - "name": "master", - "commit": { - "id": "7b5c3cc8be40ee161ae89a06bba6229da1032a0c", - "parents": [ - { - "id": "4ad91d3c1144c406e50c7b33bae684bd6837faf8" - } - ], - "tree": "46e82de44b1061621357f24c05515327f2795a95", - "message": "add projects API", - "author": { - "name": "John Smith", - "email": "john@example.com" - }, - "committer": { - "name": "John Smith", - "email": "john@example.com" - }, - "authored_date": "2012-06-27T05:51:39-07:00", - "committed_date": "2012-06-28T03:44:20-07:00" - }, - "protected": false -} -``` - - -## List project repository tags +## Project repository tags Get a list of repository tags from a project, sorted by name in reverse alphabetical order. @@ -175,7 +87,7 @@ GET /projects/:id/repository/tags Parameters: -+ `id` (required) - The ID of a project ++ `id` (required) - The ID or code name of a project ```json [ @@ -198,14 +110,12 @@ Parameters: }, "authored_date": "2012-05-28T04:42:42-07:00", "committed_date": "2012-05-28T04:42:42-07:00" - }, - "protected": null + } } ] ``` - -## List repository commits +## Project repository commits Get a list of repository commits in a project. @@ -215,8 +125,8 @@ GET /projects/:id/repository/commits Parameters: -+ `id` (required) - The ID of a project -+ `ref_name` (optional) - The name of a repository branch or tag or if not given the default branch ++ `id` (required) - The ID or code name of a project ++ `ref_name` (optional) - The name of a repository branch or tag ```json [ @@ -239,7 +149,6 @@ Parameters: ] ``` - ## Raw blob content Get the raw file contents for a file. @@ -250,6 +159,8 @@ GET /projects/:id/repository/commits/:sha/blob Parameters: -+ `id` (required) - The ID of a project ++ `id` (required) - The ID or code name of a project + `sha` (required) - The commit or branch name + `filepath` (required) - The path the file + +Will return the raw file contents. diff --git a/doc/api/session.md b/doc/api/session.md index d68788d5..9fdbeb43 100644 --- a/doc/api/session.md +++ b/doc/api/session.md @@ -13,21 +13,10 @@ Parameters: ```json { "id": 1, - "username": "john_smith", "email": "john@example.com", "name": "John Smith", "private_token": "dd34asd13as", - "blocked": false, "created_at": "2012-05-23T08:00:58Z", - "bio": null, - "skype": "", - "linkedin": "", - "twitter": "", - "dark_scheme": false, - "theme_id": 1 - "is_admin": false, - "can_create_group" : true, - "can_create_team" : true, - "can_create_project" : true + "blocked": true } ``` diff --git a/doc/api/snippets.md b/doc/api/snippets.md index 04ea367d..288fd529 100644 --- a/doc/api/snippets.md +++ b/doc/api/snippets.md @@ -8,12 +8,11 @@ GET /projects/:id/snippets Parameters: -+ `id` (required) - The ID of a project - ++ `id` (required) - The ID or code name of a project ## Single snippet -Get a single project snippet. +Get a project snippet. ``` GET /projects/:id/snippets/:snippet_id @@ -21,7 +20,7 @@ GET /projects/:id/snippets/:snippet_id Parameters: -+ `id` (required) - The ID of a project ++ `id` (required) - The ID or code name of a project + `snippet_id` (required) - The ID of a project's snippet ```json @@ -31,7 +30,6 @@ Parameters: "file_name": "add.rb", "author": { "id": 1, - "username": "john_smith", "email": "john@example.com", "name": "John Smith", "blocked": false, @@ -43,60 +41,9 @@ Parameters: } ``` - -## Create new snippet - -Creates a new project snippet. The user must have permission to create new snippets. - -``` -POST /projects/:id/snippets -``` - -Parameters: - -+ `id` (required) - The ID of a project -+ `title` (required) - The title of a snippet -+ `file_name` (required) - The name of a snippet file -+ `lifetime` (optional) - The expiration date of a snippet -+ `code` (required) - The content of a snippet - - -## Update snippet - -Updates an existing project snippet. The user must have permission to change an existing snippet. - -``` -PUT /projects/:id/snippets/:snippet_id -``` - -Parameters: - -+ `id` (required) - The ID of a project -+ `snippet_id` (required) - The ID of a project's snippet -+ `title` (optional) - The title of a snippet -+ `file_name` (optional) - The name of a snippet file -+ `lifetime` (optional) - The expiration date of a snippet -+ `code` (optional) - The content of a snippet - - -## Delete snippet - -Deletes an existing project snippet. This is an idempotent function and deleting a non-existent -snippet still returns a `200 Ok` status code. - -``` -DELETE /projects/:id/snippets/:snippet_id -``` - -Parameters: - -+ `id` (required) - The ID of a project -+ `snippet_id` (required) - The ID of a project's snippet - - ## Snippet content -Returns the raw project snippet as plain text. +Get a raw project snippet. ``` GET /projects/:id/snippets/:snippet_id/raw @@ -104,5 +51,58 @@ GET /projects/:id/snippets/:snippet_id/raw Parameters: -+ `id` (required) - The ID of a project ++ `id` (required) - The ID or code name of a project + `snippet_id` (required) - The ID of a project's snippet + +## New snippet + +Create a new project snippet. + +``` +POST /projects/:id/snippets +``` + +Parameters: + ++ `id` (required) - The ID or code name of a project ++ `title` (required) - The title of a snippet ++ `file_name` (required) - The name of a snippet file ++ `lifetime` (optional) - The expiration date of a snippet ++ `code` (required) - The content of a snippet + +Will return created snippet with status `201 Created` on success, or `404 Not found` on fail. + +## Edit snippet + +Update an existing project snippet. + +``` +PUT /projects/:id/snippets/:snippet_id +``` + +Parameters: + ++ `id` (required) - The ID or code name of a project ++ `snippet_id` (required) - The ID of a project's snippet ++ `title` (optional) - The title of a snippet ++ `file_name` (optional) - The name of a snippet file ++ `lifetime` (optional) - The expiration date of a snippet ++ `code` (optional) - The content of a snippet + +Will return updated snippet with status `200 OK` on success, or `404 Not found` on fail. + +## Delete snippet + +Delete existing project snippet. + +``` +DELETE /projects/:id/snippets/:snippet_id +``` + +Parameters: + ++ `id` (required) - The ID or code name of a project ++ `snippet_id` (required) - The ID of a project's snippet + +Status code `200` will be returned on success. + diff --git a/doc/api/system_hooks.md b/doc/api/system_hooks.md deleted file mode 100644 index dca22c43..00000000 --- a/doc/api/system_hooks.md +++ /dev/null @@ -1,49 +0,0 @@ -All methods require admin authorization. - -## List system hooks - -Get list of system hooks - -``` -GET /hooks -``` - -Parameters: - -+ **none** - - -## Add new system hook hook - -``` -POST /hooks -``` - -Parameters: - -+ `url` (required) - The hook URL - - -## Test system hook - -``` -GET /hooks/:id -``` - -Parameters: - -+ `id` (required) - The ID of hook - - -## Delete system hook - -Deletes a system hook. This is an idempotent API function and returns `200 Ok` even if the hook -is not available. If the hook is deleted it is also returned as JSON. - -``` -DELETE /hooks/:id -``` - -Parameters: - -+ `id` (required) - The ID of hook diff --git a/doc/api/users.md b/doc/api/users.md index c05bcb3e..c116144d 100644 --- a/doc/api/users.md +++ b/doc/api/users.md @@ -10,7 +10,6 @@ GET /users [ { "id": 1, - "username": "john_smith", "email": "john@example.com", "name": "John Smith", "blocked": false, @@ -20,13 +19,10 @@ GET /users "linkedin": "", "twitter": "", "dark_scheme": false, - "extern_uid": "john.smith", - "provider": "provider_name", "theme_id": 1 }, { "id": 2, - "username": "jack_smith", "email": "jack@example.com", "name": "Jack Smith", "blocked": false, @@ -36,14 +32,11 @@ GET /users "linkedin": "", "twitter": "", "dark_scheme": true, - "extern_uid": "jack.smith", - "provider": "provider_name", "theme_id": 1 } ] ``` - ## Single user Get a single user. @@ -59,7 +52,6 @@ Parameters: ```json { "id": 1, - "username": "john_smith", "email": "john@example.com", "name": "John Smith", "blocked": false, @@ -69,80 +61,32 @@ Parameters: "linkedin": "", "twitter": "", "dark_scheme": false, - "extern_uid": "john.smith", - "provider": "provider_name", "theme_id": 1 } ``` - ## User creation - -Creates a new user. Note only administrators can create new users. +Create user. Available only for admin ``` POST /users ``` Parameters: - -+ `email` (required) - Email -+ `password` (required) - Password -+ `username` (required) - Username -+ `name` (required) - Name -+ `skype` (optional) - Skype ID -+ `linkedin` (optional) - Linkedin -+ `twitter` (optional) - Twitter account -+ `projects_limit` (optional) - Number of projects user can create -+ `extern_uid` (optional) - External UID -+ `provider` (optional) - External provider name -+ `bio` (optional) - User's bio - - -## User modification - -Modifies an existing user. Only administrators can change attributes of a user. - -``` -PUT /users/:id -``` - -Parameters: - -+ `email` - Email -+ `username` - Username ++ `email` (required) - Email ++ `password` (required) - Password + `name` - Name -+ `password` - Password + `skype` - Skype ID + `linkedin` - Linkedin + `twitter` - Twitter account -+ `projects_limit` - Limit projects each user can create -+ `extern_uid` - External UID -+ `provider` - External provider name -+ `bio` - User's bio - -Note, at the moment this method does only return a 404 error, even in cases where a 409 (Conflict) would -be more appropriate, e.g. when renaming the email address to some exsisting one. - - -## User deletion - -Deletes a user. Available only for administrators. This is an idempotent function, calling this function -for a non-existent user id still returns a status code `200 Ok`. The JSON response differs if the user -was actually deleted or not. In the former the user is returned and in the latter not. - -``` -DELETE /users/:id -``` - -Parameters: - -+ `id` (required) - The ID of the user ++ `projects_limit` - Number of projects user can create +Will return created user with status `201 Created` on success, or `404 Not +found` on fail. ## Current user -Gets currently authenticated user. +Get currently authenticated user. ``` GET /user @@ -151,10 +95,8 @@ GET /user ```json { "id": 1, - "username": "john_smith", "email": "john@example.com", "name": "John Smith", - "private_token": "dd34asd13as", "blocked": false, "created_at": "2012-05-23T08:00:58Z", "bio": null, @@ -163,14 +105,9 @@ GET /user "twitter": "", "dark_scheme": false, "theme_id": 1 - "is_admin": false, - "can_create_group" : true, - "can_create_team" : true, - "can_create_project" : true } ``` - ## List SSH keys Get a list of currently authenticated user's SSH keys. @@ -198,11 +135,6 @@ GET /user/keys ] ``` -Parameters: - -+ **none** - - ## Single SSH key Get a single key. @@ -224,11 +156,9 @@ Parameters: soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=" } ``` - - ## Add SSH key -Creates a new key owned by the currently authenticated user. +Create new key owned by currently authenticated user ``` POST /user/keys @@ -239,28 +169,12 @@ Parameters: + `title` (required) - new SSH Key's title + `key` (required) - new SSH key - -## Add SSH key for user - -Create new key owned by specified user. Available only for admin - -``` -POST /users/:id/keys -``` - -Parameters: - -+ `id` (required) - id of specified user -+ `title` (required) - new SSH Key's title -+ `key` (required) - new SSH key - Will return created key with status `201 Created` on success, or `404 Not found` on fail. ## Delete SSH key -Deletes key owned by currently authenticated user. This is an idempotent function and calling it on a key that is already -deleted or not available results in `200 Ok`. +Delete key owned by currently authenticated user ``` DELETE /user/keys/:id @@ -270,3 +184,4 @@ Parameters: + `id` (required) - SSH key ID +Will return `200 OK` on success, or `404 Not Found` on fail. diff --git a/doc/app/Ability.html b/doc/app/Ability.html new file mode 100644 index 00000000..f780d067 --- /dev/null +++ b/doc/app/Ability.html @@ -0,0 +1,570 @@ + + + + + + +class Ability - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Ability

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Class Methods

    + + +
    + +
    + allowed(object, subject) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/ability.rb, line 3
    +def allowed(object, subject)
    +  case subject.class.name
    +  when "Project" then project_abilities(object, subject)
    +  when "Issue" then issue_abilities(object, subject)
    +  when "Note" then note_abilities(object, subject)
    +  when "Snippet" then snippet_abilities(object, subject)
    +  when "MergeRequest" then merge_request_abilities(object, subject)
    +  else []
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + project_abilities(user, project) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/ability.rb, line 14
    +def project_abilities(user, project)
    +  rules = []
    +
    +  rules << [
    +    :read_project,
    +    :read_wiki,
    +    :read_issue,
    +    :read_milestone,
    +    :read_snippet,
    +    :read_team_member,
    +    :read_merge_request,
    +    :read_note,
    +    :write_project,
    +    :write_issue,
    +    :write_note
    +  ] if project.guest_access_for?(user)
    +
    +  rules << [
    +    :download_code,
    +    :write_merge_request,
    +    :write_snippet
    +  ] if project.report_access_for?(user)
    +
    +  rules << [
    +    :write_wiki,
    +    :push_code
    +  ] if project.dev_access_for?(user)
    +
    +  rules << [
    +    :push_code_to_protected_branches
    +  ] if project.master_access_for?(user)
    +
    +  rules << [
    +    :modify_issue,
    +    :modify_snippet,
    +    :modify_merge_request,
    +    :admin_project,
    +    :admin_issue,
    +    :admin_milestone,
    +    :admin_snippet,
    +    :admin_team_member,
    +    :admin_merge_request,
    +    :admin_note,
    +    :accept_mr,
    +    :admin_wiki
    +  ] if project.master_access_for?(user) || project.owner == user
    +
    +  rules.flatten
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/Account.html b/doc/app/Account.html new file mode 100644 index 00000000..58f65758 --- /dev/null +++ b/doc/app/Account.html @@ -0,0 +1,881 @@ + + + + + + +module Account - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    module Account

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + block() + click to toggle source +
    + + +
    + +

    Remove user from all projects and set blocked attribute to true

    + + + +
    +
    # File app/roles/account.rb, line 47
    +def block
    +  users_projects.find_each do |membership|
    +    return false unless membership.destroy
    +  end
    +
    +  self.blocked = true
    +  save
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + can_create_group?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/account.rb, line 25
    +def can_create_group?
    +  is_admin?
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + can_create_project?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/account.rb, line 21
    +def can_create_project?
    +  projects_limit > my_own_projects.count
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + cared_merge_requests() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/account.rb, line 37
    +def cared_merge_requests
    +  MergeRequest.where("author_id = :id or assignee_id = :id", id: self.id).opened
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + first_name() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/account.rb, line 33
    +def first_name
    +  name.split.first unless name.blank?
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + identifier() + click to toggle source +
    + + +
    + +

    Returns a string for use as a Gitolite user identifier

    + +

    Note that Gitolite 2.x requires the following +pattern for users:

    + +
    ^@?[0-9a-zA-Z][0-9a-zA-Z._\@+-]*$
    + + + +
    +
    # File app/roles/account.rb, line 7
    +def identifier
    +  # Replace non-word chars with underscores, then make sure it starts with
    +  # valid chars
    +  email.gsub(%r\W/, '_').gsub(%r\A([\W\_])+/, '')
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + is_admin?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/account.rb, line 13
    +def is_admin?
    +  admin
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + last_activity_project() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/account.rb, line 29
    +def last_activity_project
    +  projects.first
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + project_ids() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/account.rb, line 41
    +def project_ids
    +  projects.map(&:id)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + projects_limit_percent() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/account.rb, line 56
    +def projects_limit_percent
    +  return 100 if projects_limit.zero?
    +  (my_own_projects.count.to_f / projects_limit) * 100
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + projects_with_events() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/account.rb, line 70
    +def projects_with_events
    +  projects.includes(:events).order("events.created_at DESC")
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + recent_push(project_id = nil) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/account.rb, line 61
    +def recent_push project_id = nil
    +  # Get push events not earlier than 2 hours ago
    +  events = recent_events.code_push.where("created_at > ?", Time.now - 2.hours)
    +  events = events.where(project_id: project_id) if project_id
    +
    +  # Take only latest one
    +  events = events.recent.limit(1).first
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + require_ssh_key?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/account.rb, line 17
    +def require_ssh_key?
    +  keys.count == 0
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/ActivityObserver.html b/doc/app/ActivityObserver.html new file mode 100644 index 00000000..3b31271d --- /dev/null +++ b/doc/app/ActivityObserver.html @@ -0,0 +1,531 @@ + + + + + + +class ActivityObserver - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class ActivityObserver

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + after_create(record) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/observers/activity_observer.rb, line 4
    +def after_create(record)
    +  Event.create(
    +    project: record.project,
    +    target_id: record.id,
    +    target_type: record.class.name,
    +    action: Event.determine_action(record),
    +    author_id: record.author_id
    +  )
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + after_save(record) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/observers/activity_observer.rb, line 14
    +def after_save(record)
    +  if record.changed.include?("closed") 
    +    Event.create(
    +      project: record.project,
    +      target_id: record.id,
    +      target_type: record.class.name,
    +      action: (record.closed ? Event::Closed : Event::Reopened),
    +      author_id: record.author_id_of_changes
    +    )
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/Admin.html b/doc/app/Admin.html new file mode 100644 index 00000000..94fb27ef --- /dev/null +++ b/doc/app/Admin.html @@ -0,0 +1,432 @@ + + + + + + +module Admin - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    module Admin

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    + +
    + + + + diff --git a/doc/app/Admin/DashboardController.html b/doc/app/Admin/DashboardController.html new file mode 100644 index 00000000..00154b3d --- /dev/null +++ b/doc/app/Admin/DashboardController.html @@ -0,0 +1,488 @@ + + + + + + +class Admin::DashboardController - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Admin::DashboardController

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + index() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/admin/dashboard_controller.rb, line 2
    +def index
    +  @workers = Resque.workers
    +  @pending_jobs = Resque.size(:post_receive)
    +  @projects = Project.order("created_at DESC").limit(10)
    +  @users = User.order("created_at DESC").limit(10)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/Admin/GroupsController.html b/doc/app/Admin/GroupsController.html new file mode 100644 index 00000000..ef4dae9e --- /dev/null +++ b/doc/app/Admin/GroupsController.html @@ -0,0 +1,771 @@ + + + + + + +class Admin::GroupsController - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Admin::GroupsController

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + create() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/admin/groups_controller.rb, line 23
    +def create
    +  @group = Group.new(params[:group])
    +  @group.owner = current_user
    +
    +  if @group.save
    +    redirect_to [:admin, @group], notice: 'Group was successfully created.'
    +  else
    +    render action: "new"
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + destroy() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/admin/groups_controller.rb, line 64
    +def destroy
    +  @group.destroy
    +
    +  redirect_to admin_groups_path, notice: 'Group was successfully deleted.'
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + edit() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/admin/groups_controller.rb, line 20
    +def edit
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + index() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/admin/groups_controller.rb, line 4
    +def index
    +  @groups = Group.scoped
    +  @groups = @groups.search(params[:name]) if params[:name].present?
    +  @groups = @groups.page(params[:page]).per(20)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + new() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/admin/groups_controller.rb, line 16
    +def new
    +  @group = Group.new
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + project_update() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/admin/groups_controller.rb, line 49
    +def project_update
    +  project_ids = params[:project_ids]
    +  Project.where(id: project_ids).update_all(group_id: @group.id)
    +
    +  redirect_to :back, notice: 'Group was successfully updated.'
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + remove_project() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/admin/groups_controller.rb, line 56
    +def remove_project
    +  @project = Project.find(params[:project_id])
    +  @project.group_id = nil
    +  @project.save
    +
    +  redirect_to :back, notice: 'Group was successfully updated.'
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + show() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/admin/groups_controller.rb, line 10
    +def show
    +  @projects = Project.scoped
    +  @projects = @projects.not_in_group(@group) if @group.projects.present?
    +  @projects = @projects.all
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + update() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/admin/groups_controller.rb, line 34
    +def update
    +  group_params = params[:group].dup
    +  owner_id =group_params.delete(:owner_id)
    +
    +  if owner_id
    +    @group.owner = User.find(owner_id)
    +  end
    +
    +  if @group.update_attributes(group_params)
    +    redirect_to [:admin, @group], notice: 'Group was successfully updated.'
    +  else
    +    render action: "edit"
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/Admin/HooksController.html b/doc/app/Admin/HooksController.html new file mode 100644 index 00000000..48cd5d00 --- /dev/null +++ b/doc/app/Admin/HooksController.html @@ -0,0 +1,603 @@ + + + + + + +class Admin::HooksController - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Admin::HooksController

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + create() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/admin/hooks_controller.rb, line 7
    +def create
    +  @hook = SystemHook.new(params[:hook])
    +
    +  if @hook.save
    +    redirect_to admin_hooks_path, notice: 'Hook was successfully created.'
    +  else
    +    @hooks = SystemHook.all
    +    render :index
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + destroy() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/admin/hooks_controller.rb, line 18
    +def destroy
    +  @hook = SystemHook.find(params[:id])
    +  @hook.destroy
    +
    +  redirect_to admin_hooks_path
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + index() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/admin/hooks_controller.rb, line 2
    +def index
    +  @hooks = SystemHook.all
    +  @hook = SystemHook.new
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + test() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/admin/hooks_controller.rb, line 26
    +def test
    +  @hook = SystemHook.find(params[:hook_id])
    +  data = {
    +    event_name: "project_create",
    +    name: "Ruby",
    +    path: "ruby",
    +    project_id: 1,
    +    owner_name: "Someone",
    +    owner_email: "example@gitlabhq.com"
    +  }
    +  @hook.execute(data)
    +
    +  redirect_to :back
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/Admin/LogsController.html b/doc/app/Admin/LogsController.html new file mode 100644 index 00000000..8f70b1ad --- /dev/null +++ b/doc/app/Admin/LogsController.html @@ -0,0 +1,439 @@ + + + + + + +class Admin::LogsController - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Admin::LogsController

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    + +
    + + + + diff --git a/doc/app/Admin/ProjectsController.html b/doc/app/Admin/ProjectsController.html new file mode 100644 index 00000000..5744746b --- /dev/null +++ b/doc/app/Admin/ProjectsController.html @@ -0,0 +1,733 @@ + + + + + + +class Admin::ProjectsController - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Admin::ProjectsController

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + create() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/admin/projects_controller.rb, line 29
    +def create
    +  @admin_project = Project.new(params[:project])
    +  @admin_project.owner = current_user
    +
    +  if @admin_project.save
    +    redirect_to [:admin, @admin_project], notice: 'Project was successfully created.'
    +  else
    +    render action: "new"
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + destroy() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/admin/projects_controller.rb, line 54
    +def destroy
    +  @admin_project.destroy
    +
    +  redirect_to admin_projects_url, notice: 'Project was successfully deleted.'
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + edit() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/admin/projects_controller.rb, line 20
    +def edit
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + index() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/admin/projects_controller.rb, line 4
    +def index
    +  @admin_projects = Project.scoped
    +  @admin_projects = @admin_projects.search(params[:name]) if params[:name].present?
    +  @admin_projects = @admin_projects.page(params[:page]).per(20)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + new() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/admin/projects_controller.rb, line 16
    +def new
    +  @admin_project = Project.new
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + show() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/admin/projects_controller.rb, line 10
    +def show
    +  @users = User.scoped
    +  @users = @users.not_in_project(@admin_project) if @admin_project.users.present?
    +  @users = @users.all
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + team_update() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/admin/projects_controller.rb, line 23
    +def team_update
    +  @admin_project.add_users_ids_to_team(params[:user_ids], params[:project_access])
    +
    +  redirect_to [:admin, @admin_project], notice: 'Project was successfully updated.'
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + update() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/admin/projects_controller.rb, line 40
    +def update
    +  owner_id = params[:project].delete(:owner_id)
    +
    +  if owner_id
    +    @admin_project.owner = User.find(owner_id)
    +  end
    +
    +  if @admin_project.update_attributes(params[:project])
    +    redirect_to [:admin, @admin_project], notice: 'Project was successfully updated.'
    +  else
    +    render action: "edit"
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/Admin/ResqueController.html b/doc/app/Admin/ResqueController.html new file mode 100644 index 00000000..db73599b --- /dev/null +++ b/doc/app/Admin/ResqueController.html @@ -0,0 +1,484 @@ + + + + + + +class Admin::ResqueController - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Admin::ResqueController

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + show() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/admin/resque_controller.rb, line 2
    +def show
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/Admin/TeamMembersController.html b/doc/app/Admin/TeamMembersController.html new file mode 100644 index 00000000..907bf279 --- /dev/null +++ b/doc/app/Admin/TeamMembersController.html @@ -0,0 +1,558 @@ + + + + + + +class Admin::TeamMembersController - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Admin::TeamMembersController

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + destroy() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/admin/team_members_controller.rb, line 16
    +def destroy
    +  @admin_team_member = UsersProject.find(params[:id])
    +  @admin_team_member.destroy
    +
    +  redirect_to :back
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + edit() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/admin/team_members_controller.rb, line 2
    +def edit
    +  @admin_team_member = UsersProject.find(params[:id])
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + update() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/admin/team_members_controller.rb, line 6
    +def update
    +  @admin_team_member = UsersProject.find(params[:id])
    +
    +  if @admin_team_member.update_attributes(params[:team_member])
    +    redirect_to [:admin, @admin_team_member.project],  notice: 'Project Access was successfully updated.'
    +  else
    +    render action: "edit"
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/Admin/UsersController.html b/doc/app/Admin/UsersController.html new file mode 100644 index 00000000..b300e4f7 --- /dev/null +++ b/doc/app/Admin/UsersController.html @@ -0,0 +1,839 @@ + + + + + + +class Admin::UsersController - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Admin::UsersController

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + block() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/admin/users_controller.rb, line 40
    +def block
    +  @admin_user = User.find(params[:id])
    +
    +  if @admin_user.block
    +    redirect_to :back, alert: "Successfully blocked"
    +  else
    +    redirect_to :back, alert: "Error occured. User was not blocked"
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + create() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/admin/users_controller.rb, line 60
    +def create
    +  admin = params[:user].delete("admin")
    +
    +  @admin_user = User.new(params[:user], as: :admin)
    +  @admin_user.admin = (admin && admin.to_i > 0)
    +
    +  respond_to do |format|
    +    if @admin_user.save
    +      format.html { redirect_to [:admin, @admin_user], notice: 'User was successfully created.' }
    +      format.json { render json: @admin_user, status: :created, location: @admin_user }
    +    else
    +      format.html { render action: "new" }
    +      format.json { render json: @admin_user.errors, status: :unprocessable_entity }
    +    end
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + destroy() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/admin/users_controller.rb, line 99
    +def destroy
    +  @admin_user = User.find(params[:id])
    +  @admin_user.destroy
    +
    +  respond_to do |format|
    +    format.html { redirect_to admin_users_url }
    +    format.json { head :ok }
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + edit() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/admin/users_controller.rb, line 36
    +def edit
    +  @admin_user = User.find(params[:id])
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + index() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/admin/users_controller.rb, line 2
    +def index
    +  @admin_users = User.scoped
    +  @admin_users = @admin_users.filter(params[:filter])
    +  @admin_users = @admin_users.search(params[:name]) if params[:name].present?
    +  @admin_users = @admin_users.order("updated_at DESC").page(params[:page])
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + new() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/admin/users_controller.rb, line 32
    +def new
    +  @admin_user = User.new({ projects_limit: Gitlab.config.default_projects_limit }, as: :admin)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + show() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/admin/users_controller.rb, line 9
    +def show
    +  @admin_user = User.find(params[:id])
    +
    +  @projects = if @admin_user.projects.empty?
    +             Project
    +           else
    +             Project.without_user(@admin_user)
    +           end.all
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + team_update() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/admin/users_controller.rb, line 19
    +def team_update
    +  @admin_user = User.find(params[:id])
    +
    +  UsersProject.user_bulk_import(
    +    @admin_user,
    +    params[:project_ids],
    +    params[:project_access]
    +  )
    +
    +  redirect_to [:admin, @admin_user], notice: 'Teams were successfully updated.'
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + unblock() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/admin/users_controller.rb, line 50
    +def unblock
    +  @admin_user = User.find(params[:id])
    +
    +  if @admin_user.update_attribute(:blocked, false)
    +    redirect_to :back, alert: "Successfully unblocked"
    +  else
    +    redirect_to :back, alert: "Error occured. User was not unblocked"
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + update() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/admin/users_controller.rb, line 77
    +def update
    +  admin = params[:user].delete("admin")
    +
    +  if params[:user][:password].blank?
    +    params[:user].delete(:password)
    +    params[:user].delete(:password_confirmation)
    +  end
    +
    +  @admin_user = User.find(params[:id])
    +  @admin_user.admin = (admin && admin.to_i > 0)
    +
    +  respond_to do |format|
    +    if @admin_user.update_attributes(params[:user], as: :admin)
    +      format.html { redirect_to [:admin, @admin_user], notice: 'User was successfully updated.' }
    +      format.json { head :ok }
    +    else
    +      format.html { render action: "edit" }
    +      format.json { render json: @admin_user.errors, status: :unprocessable_entity }
    +    end
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/AdminController.html b/doc/app/AdminController.html new file mode 100644 index 00000000..bdda7958 --- /dev/null +++ b/doc/app/AdminController.html @@ -0,0 +1,490 @@ + + + + + + +class AdminController - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class AdminController

    + +
    + +

    Provides a base class for Admin controllers to +subclass

    + +

    Automatically sets the layout and ensures an administrator is logged in

    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + authenticate_admin!() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/admin_controller.rb, line 8
    +def authenticate_admin!
    +  return render_404 unless current_user.is_admin?
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/ApplicationController.html b/doc/app/ApplicationController.html new file mode 100644 index 00000000..dafa3413 --- /dev/null +++ b/doc/app/ApplicationController.html @@ -0,0 +1,1015 @@ + + + + + + +class ApplicationController - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class ApplicationController

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Protected Instance Methods

    + + +
    + +
    + abilities() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/application_controller.rb, line 48
    +def abilities
    +  @abilities ||= Six.new
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + access_denied!() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/application_controller.rb, line 73
    +def access_denied!
    +  render "errors/access_denied", layout: "errors", status: 404
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + add_project_abilities() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/application_controller.rb, line 61
    +def add_project_abilities
    +  abilities << Ability
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + after_sign_in_path_for(resource) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/application_controller.rb, line 33
    +def after_sign_in_path_for resource
    +  if resource.is_a?(User) && resource.respond_to?(:blocked) && resource.blocked
    +    sign_out resource
    +    flash[:alert] = "Your account was blocked"
    +    new_user_session_path
    +  else
    +    super
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + authorize_code_access!() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/application_controller.rb, line 69
    +def authorize_code_access!
    +  return access_denied! unless can?(current_user, :download_code, project)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + authorize_project!(action) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/application_controller.rb, line 65
    +def authorize_project!(action)
    +  return access_denied! unless can?(current_user, action, project)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + can?(object, action, subject) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/application_controller.rb, line 52
    +def can?(object, action, subject)
    +  abilities.allowed?(object, action, subject)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + dev_tools() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/application_controller.rb, line 107
    +def dev_tools
    +  Rack::MiniProfiler.authorize_request
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + git_not_found!() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/application_controller.rb, line 81
    +def git_not_found!
    +  render "errors/git_not_found", layout: "errors", status: 404
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + method_missing(method_sym, *arguments, &block) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/application_controller.rb, line 85
    +def method_missing(method_sym, *arguments, &block)
    +  if method_sym.to_s =~ %r^authorize_(.*)!$/
    +    authorize_project!($1.to_sym)
    +  else
    +    super
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + no_cache_headers() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/application_controller.rb, line 101
    +def no_cache_headers
    +  response.headers["Cache-Control"] = "no-cache, no-store, max-age=0, must-revalidate"
    +  response.headers["Pragma"] = "no-cache"
    +  response.headers["Expires"] = "Fri, 01 Jan 1990 00:00:00 GMT"
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + not_found!() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/application_controller.rb, line 77
    +def not_found!
    +  render "errors/not_found", layout: "errors", status: 404
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + project() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/application_controller.rb, line 56
    +def project
    +  @project ||= current_user.projects.find_by_code(params[:project_id] || params[:id])
    +  @project || render_404
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + reject_blocked!() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/application_controller.rb, line 25
    +def reject_blocked!
    +  if current_user && current_user.blocked
    +    sign_out current_user
    +    flash[:alert] = "Your account was blocked"
    +    redirect_to new_user_session_path
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + render_404() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/application_controller.rb, line 93
    +def render_404
    +  render file: Rails.root.join("public", "404"), layout: false, status: "404"
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + require_non_empty_project() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/application_controller.rb, line 97
    +def require_non_empty_project
    +  redirect_to @project if @project.empty_repo?
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + set_current_user_for_observers() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/application_controller.rb, line 43
    +def set_current_user_for_observers
    +  MergeRequestObserver.current_user = current_user
    +  IssueObserver.current_user = current_user
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/ApplicationDecorator.html b/doc/app/ApplicationDecorator.html new file mode 100644 index 00000000..72a459bb --- /dev/null +++ b/doc/app/ApplicationDecorator.html @@ -0,0 +1,439 @@ + + + + + + +class ApplicationDecorator - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class ApplicationDecorator

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    + +
    + + + + diff --git a/doc/app/ApplicationHelper.html b/doc/app/ApplicationHelper.html new file mode 100644 index 00000000..9bc4243e --- /dev/null +++ b/doc/app/ApplicationHelper.html @@ -0,0 +1,1018 @@ + + + + + + +module ApplicationHelper - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    module ApplicationHelper

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + app_theme() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/helpers/application_helper.rb, line 125
    +def app_theme
    +  Gitlab::Theme.css_class_by_id(current_user.try(:theme_id))
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + authbutton(provider, size = 64) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/helpers/application_helper.rb, line 150
    +def authbutton(provider, size = 64)
    +  file_name = "#{provider.to_s.split('_').first}_#{size}.png"
    +  image_tag("authbuttons/#{file_name}",
    +            alt: "Sign in with #{provider.to_s.titleize}")
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + current_action?(*args) + click to toggle source +
    + + +
    + +

    Check if a partcular action is the current one

    + +

    args - One or more action names to check

    + +

    Examples

    + +
    # On Projects#new
    +current_action?(:new)           # => true
    +current_action?(:create)        # => false
    +current_action?(:new, :create)  # => true
    +
    + + + +
    +
    # File app/helpers/application_helper.rb, line 29
    +def current_action?(*args)
    +  args.any? { |v| v.to_s.downcase == action_name }
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + current_controller?(*args) + click to toggle source +
    + + +
    + +

    Check if a particular controller is the current one

    + +

    args - One or more controller names to check

    + +

    Examples

    + +
    # On TreeController
    +current_controller?(:tree)           # => true
    +current_controller?(:commits)        # => false
    +current_controller?(:commits, :tree) # => true
    +
    + + + +
    +
    # File app/helpers/application_helper.rb, line 15
    +def current_controller?(*args)
    +  args.any? { |v| v.to_s.downcase == controller.controller_name }
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + emoji_autocomplete_source() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/helpers/application_helper.rb, line 115
    +def emoji_autocomplete_source
    +  # should be an array of strings
    +  # so to_s can be called, because it is sufficient and to_json is too slow
    +  Emoji.names.to_s
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + gravatar_icon(user_email = '', size = 40) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/helpers/application_helper.rb, line 33
    +def gravatar_icon(user_email = '', size = 40)
    +  if Gitlab.config.disable_gravatar? || user_email.blank?
    +    'no_avatar.png'
    +  else
    +    gravatar_prefix = request.ssl? ? "https://secure" : "http://www"
    +    user_email.strip!
    +    "#{gravatar_prefix}.gravatar.com/avatar/#{Digest::MD5.hexdigest(user_email.downcase)}?s=#{size}&d=identicon"
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + grouped_options_refs(destination = :tree) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/helpers/application_helper.rb, line 61
    +def grouped_options_refs(destination = :tree)
    +  options = [
    +    ["Branch", @project.branch_names ],
    +    [ "Tag", @project.tag_names ]
    +  ]
    +
    +  # If reference is commit id -
    +  # we should add it to branch/tag selectbox
    +  if(@ref && !options.flatten.include?(@ref) &&
    +     @ref =~ %r^[0-9a-zA-Z]{6,52}$/)
    +    options << ["Commit", [@ref]]
    +  end
    +
    +  grouped_options_for_select(options, @ref || @project.default_branch)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + hexdigest(string) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/helpers/application_helper.rb, line 137
    +def hexdigest(string)
    +  Digest::SHA1.hexdigest string
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + last_commit(project) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/helpers/application_helper.rb, line 51
    +def last_commit(project)
    +  if project.repo_exists?
    +    time_ago_in_words(project.commit.committed_date) + " ago"
    +  else
    +    "Never"
    +  end
    +rescue
    +  "Never"
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + ldap_enable?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/helpers/application_helper.rb, line 121
    +def ldap_enable?
    +  Devise.omniauth_providers.include?(:ldap)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + project_last_activity(project) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/helpers/application_helper.rb, line 141
    +def project_last_activity project
    +  activity = project.last_activity
    +  if activity && activity.created_at
    +    time_ago_in_words(activity.created_at) + " ago"
    +  else
    +    "Never"
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + request_protocol() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/helpers/application_helper.rb, line 43
    +def request_protocol
    +  request.ssl? ? "https" : "http"
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + search_autocomplete_source() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/helpers/application_helper.rb, line 77
    +def search_autocomplete_source
    +  projects = current_user.projects.map{ |p| { label: p.name, url: project_path(p) } }
    +
    +  default_nav = [
    +    { label: "My Profile", url: profile_path },
    +    { label: "My SSH Keys", url: keys_path },
    +    { label: "My Dashboard", url: root_path },
    +    { label: "Admin Section", url: admin_root_path },
    +  ]
    +
    +  help_nav = [
    +    { label: "Workflow Help", url: help_workflow_path },
    +    { label: "Permissions Help", url: help_permissions_path },
    +    { label: "Web Hooks Help", url: help_web_hooks_path },
    +    { label: "System Hooks Help", url: help_system_hooks_path },
    +    { label: "API Help", url: help_api_path },
    +    { label: "Markdown Help", url: help_markdown_path },
    +    { label: "SSH Keys Help", url: help_ssh_path },
    +  ]
    +
    +  project_nav = []
    +  if @project && !@project.new_record?
    +    project_nav = [
    +      { label: "#{@project.name} Issues",   url: project_issues_path(@project) },
    +      { label: "#{@project.name} Commits",  url: project_commits_path(@project, @ref || @project.root_ref) },
    +      { label: "#{@project.name} Merge Requests", url: project_merge_requests_path(@project) },
    +      { label: "#{@project.name} Milestones", url: project_milestones_path(@project) },
    +      { label: "#{@project.name} Snippets", url: project_snippets_path(@project) },
    +      { label: "#{@project.name} Team",     url: project_team_index_path(@project) },
    +      { label: "#{@project.name} Tree",     url: project_tree_path(@project, @ref || @project.root_ref) },
    +      { label: "#{@project.name} Wall",     url: wall_project_path(@project) },
    +      { label: "#{@project.name} Wiki",     url: project_wikis_path(@project) },
    +    ]
    +  end
    +
    +  [projects, default_nav, project_nav, help_nav].flatten.to_json
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + show_last_push_widget?(event) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/helpers/application_helper.rb, line 129
    +def show_last_push_widget?(event)
    +  event &&
    +    event.last_push_to_non_root? &&
    +    !event.rm_ref? &&
    +    event.project &&
    +    event.project.merge_requests_enabled
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + web_app_url() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/helpers/application_helper.rb, line 47
    +def web_app_url
    +  "#{request_protocol}://#{Gitlab.config.web_host}/"
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/AttachmentUploader.html b/doc/app/AttachmentUploader.html new file mode 100644 index 00000000..b6e9d1ba --- /dev/null +++ b/doc/app/AttachmentUploader.html @@ -0,0 +1,486 @@ + + + + + + +class AttachmentUploader - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class AttachmentUploader

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + store_dir() + click to toggle source +
    + + +
    + +

    Override the directory where uploaded files will be stored. This is a +sensible default for uploaders that are meant to be mounted:

    + + + +
    +
    # File app/uploaders/attachment_uploader.rb, line 15
    +def store_dir
    +  "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/Authority.html b/doc/app/Authority.html new file mode 100644 index 00000000..92f46c55 --- /dev/null +++ b/doc/app/Authority.html @@ -0,0 +1,782 @@ + + + + + + +module Authority - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    module Authority

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + add_access(user, *access) + click to toggle source +
    + + +
    + +

    Compatible with all access rights Should be rewrited for new access rights

    + + + +
    +
    # File app/roles/authority.rb, line 4
    +def add_access(user, *access)
    +  access = if access.include?(:admin)
    +             { project_access: UsersProject::MASTER }
    +           elsif access.include?(:write)
    +             { project_access: UsersProject::DEVELOPER }
    +           else
    +             { project_access: UsersProject::REPORTER }
    +           end
    +  opts = { user: user }
    +  opts.merge!(access)
    +  users_projects.create(opts)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + allow_read_for?(user) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/authority.rb, line 39
    +def allow_read_for?(user)
    +  !users_projects.where(user_id: user.id).empty?
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + dev_access_for?(user) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/authority.rb, line 51
    +def dev_access_for?(user)
    +  !users_projects.where(user_id: user.id, project_access: [UsersProject::DEVELOPER, UsersProject::MASTER]).empty?
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + guest_access_for?(user) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/authority.rb, line 43
    +def guest_access_for?(user)
    +  !users_projects.where(user_id: user.id).empty?
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + master_access_for?(user) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/authority.rb, line 55
    +def master_access_for?(user)
    +  !users_projects.where(user_id: user.id, project_access: [UsersProject::MASTER]).empty?
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + report_access_for?(user) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/authority.rb, line 47
    +def report_access_for?(user)
    +  !users_projects.where(user_id: user.id, project_access: [UsersProject::REPORTER, UsersProject::DEVELOPER, UsersProject::MASTER]).empty?
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + repository_masters() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/authority.rb, line 33
    +def repository_masters
    +  keys = Key.joins({user: :users_projects}).
    +    where("users_projects.project_id = ? AND users_projects.project_access = ?", id, UsersProject::MASTER)
    +  keys.map(&:identifier)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + repository_readers() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/authority.rb, line 21
    +def repository_readers
    +  keys = Key.joins({user: :users_projects}).
    +    where("users_projects.project_id = ? AND users_projects.project_access = ?", id, UsersProject::REPORTER)
    +  keys.map(&:identifier) + deploy_keys.map(&:identifier)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + repository_writers() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/authority.rb, line 27
    +def repository_writers
    +  keys = Key.joins({user: :users_projects}).
    +    where("users_projects.project_id = ? AND users_projects.project_access = ?", id, UsersProject::DEVELOPER)
    +  keys.map(&:identifier)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + reset_access(user) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/authority.rb, line 17
    +def reset_access(user)
    +  users_projects.where(project_id: self.id, user_id: user.id).destroy if self.id
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/BaseContext.html b/doc/app/BaseContext.html new file mode 100644 index 00000000..b9c46738 --- /dev/null +++ b/doc/app/BaseContext.html @@ -0,0 +1,605 @@ + + + + + + +class BaseContext - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class BaseContext

    + +
    + +
    + + + + +
    + + + + + + + + +
    +

    Attributes

    + + +
    +
    + current_user[RW] +
    + +
    + + + +
    +
    + +
    +
    + params[RW] +
    + +
    + + + +
    +
    + +
    +
    + project[RW] +
    + +
    + + + +
    +
    + +
    + + + + +
    +

    Public Class Methods

    + + +
    + +
    + new(project, user, params) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/contexts/base_context.rb, line 4
    +def initialize(project, user, params)
    +  @project, @current_user, @params = project, user, params.dup
    +end
    +
    + +
    + + + + +
    + + +
    + +
    +

    Public Instance Methods

    + + +
    + +
    + abilities() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/contexts/base_context.rb, line 8
    +def abilities
    +  @abilities ||= begin
    +                   abilities = Six.new
    +                   abilities << Ability
    +                   abilities
    +                 end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + can?(object, action, subject) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/contexts/base_context.rb, line 16
    +def can?(object, action, subject)
    +  abilities.allowed?(object, action, subject)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/BlameController.html b/doc/app/BlameController.html new file mode 100644 index 00000000..4b23b227 --- /dev/null +++ b/doc/app/BlameController.html @@ -0,0 +1,500 @@ + + + + + + +class BlameController - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class BlameController

    + +
    + +

    Controller for viewing a file’s blame

    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + show() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/blame_controller.rb, line 12
    +def show
    +  @repo = @project.repo
    +  @blame = Grit::Blob.blame(@repo, @commit.id, @path)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/BlobController.html b/doc/app/BlobController.html new file mode 100644 index 00000000..e8f069c2 --- /dev/null +++ b/doc/app/BlobController.html @@ -0,0 +1,519 @@ + + + + + + +class BlobController - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class BlobController

    + +
    + +

    Controller for viewing a file’s blame

    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + show() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/blob_controller.rb, line 13
    +def show
    +  if @tree.is_blob?
    +    if @tree.text?
    +      encoding = detect_encoding(@tree.data)
    +      mime_type = encoding ? "text/plain; charset=#{encoding}" : "text/plain"
    +    else
    +      mime_type = @tree.mime_type
    +    end
    +
    +    send_data(
    +      @tree.data,
    +      type: mime_type,
    +      disposition: 'inline',
    +      filename: @tree.name
    +    )
    +  else
    +    not_found!
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/Commit.html b/doc/app/Commit.html new file mode 100644 index 00000000..05a3f3ce --- /dev/null +++ b/doc/app/Commit.html @@ -0,0 +1,1191 @@ + + + + + + +class Commit - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Commit

    + +
    + +
    + + + + +
    + + + + + + + + +
    +

    Attributes

    + + +
    +
    + commit[RW] +
    + +
    + + + +
    +
    + +
    +
    + head[RW] +
    + +
    + + + +
    +
    + +
    +
    + refs[RW] +
    + +
    + + + +
    +
    + +
    + + + + +
    +

    Public Class Methods

    + + +
    + +
    + commits(repo, ref, path = nil, limit = nil, offset = nil) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/commit.rb, line 58
    +def commits(repo, ref, path = nil, limit = nil, offset = nil)
    +  if path
    +    repo.log(ref, path, max_count: limit, skip: offset)
    +  elsif limit && offset
    +    repo.commits(ref, limit, offset)
    +  else
    +    repo.commits(ref)
    +  end.map{ |c| Commit.new(c) }
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + commits_between(repo, from, to) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/commit.rb, line 68
    +def commits_between(repo, from, to)
    +  repo.commits_between(from, to).map { |c| Commit.new(c) }
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + commits_since(repo, date) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/commit.rb, line 46
    +def commits_since(repo, date)
    +  commits = repo.heads.map do |h|
    +    repo.log(h.name, nil, since: date).each { |c| Commit.new(c, h) }
    +  end.flatten.uniq { |c| c.id }
    +
    +  commits.sort! do |x, y|
    +    y.committed_date <=> x.committed_date
    +  end
    +
    +  commits
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + commits_with_refs(repo, n = 20) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/commit.rb, line 36
    +def commits_with_refs(repo, n = 20)
    +  commits = repo.branches.map { |ref| Commit.new(ref.commit, ref) }
    +
    +  commits.sort! do |x, y|
    +    y.committed_date <=> x.committed_date
    +  end
    +
    +  commits[0..n]
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + compare(project, from, to) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/commit.rb, line 72
    +def compare(project, from, to)
    +  result = {
    +    commits: [],
    +    diffs: [],
    +    commit: nil,
    +    same: false
    +  }
    +
    +  return result unless from && to
    +
    +  first = project.commit(to.try(:strip))
    +  last = project.commit(from.try(:strip))
    +
    +  if first && last
    +    commits = [first, last].sort_by(&:created_at)
    +    younger = commits.first
    +    older = commits.last
    +
    +    result[:same] = (younger.id == older.id)
    +    result[:commits] = project.repo.commits_between(younger.id, older.id).map {|c| Commit.new(c)}
    +    result[:diffs] = project.repo.diff(younger.id, older.id) rescue []
    +    result[:commit] = Commit.new(older)
    +  end
    +
    +  result
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + find_or_first(repo, commit_id = nil, root_ref) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/commit.rb, line 14
    +def find_or_first(repo, commit_id = nil, root_ref)
    +  commit = if commit_id
    +             repo.commit(commit_id)
    +           else
    +             repo.commits(root_ref).first
    +           end
    +
    +  Commit.new(commit) if commit
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + fresh_commits(repo, n = 10) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/commit.rb, line 24
    +def fresh_commits(repo, n = 10)
    +  commits = repo.heads.map do |h|
    +    repo.commits(h.name, n).map { |c| Commit.new(c, h) }
    +  end.flatten.uniq { |c| c.id }
    +
    +  commits.sort! do |x, y|
    +    y.committed_date <=> x.committed_date
    +  end
    +
    +  commits[0...n]
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + new(raw_commit, head = nil) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/commit.rb, line 100
    +def initialize(raw_commit, head = nil)
    +  @commit = raw_commit
    +  @head = head
    +end
    +
    + +
    + + + + +
    + + +
    + +
    +

    Public Instance Methods

    + + +
    + +
    + author_email() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/commit.rb, line 117
    +def author_email
    +  author.email
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + author_name() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/commit.rb, line 121
    +def author_name
    +  utf8 author.name
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + committer_email() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/commit.rb, line 134
    +def committer_email
    +  committer.email
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + committer_name() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/commit.rb, line 130
    +def committer_name
    +  utf8 committer.name
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + created_at() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/commit.rb, line 113
    +def created_at
    +  committed_date
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + different_committer?() + click to toggle source +
    + + +
    + +

    Was this commit committed by a different person than the original author?

    + + + +
    +
    # File app/models/commit.rb, line 126
    +def different_committer?
    +  author_name != committer_name || author_email != committer_email
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + parents_count() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/commit.rb, line 146
    +def parents_count
    +  parents && parents.count || 0
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + prev_commit() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/commit.rb, line 138
    +def prev_commit
    +  parents.try :first
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + prev_commit_id() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/commit.rb, line 142
    +def prev_commit_id
    +  prev_commit.try :id
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + safe_message() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/commit.rb, line 109
    +def safe_message
    +  @safe_message ||= utf8 message
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + short_id(length = 10) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/commit.rb, line 105
    +def short_id(length = 10)
    +  id.to_s[0..length]
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/CommitController.html b/doc/app/CommitController.html new file mode 100644 index 00000000..564161e9 --- /dev/null +++ b/doc/app/CommitController.html @@ -0,0 +1,509 @@ + + + + + + +class CommitController - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class CommitController

    + +
    + +

    Controller for a specific Commit

    + +

    Not to be confused with CommitsController, plural.

    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + show() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/commit_controller.rb, line 10
    +def show
    +  result = CommitLoadContext.new(project, current_user, params).execute
    +
    +  @commit = result[:commit]
    +  git_not_found! unless @commit
    +
    +  @suppress_diff    = result[:suppress_diff]
    +  @note             = result[:note]
    +  @line_notes       = result[:line_notes]
    +  @notes_count      = result[:notes_count]
    +  @comments_allowed = true
    +
    +  respond_to do |format|
    +    format.html do
    +      if result[:status] == :huge_commit
    +        render "huge_commit" and return
    +      end
    +    end
    +
    +    format.patch
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/CommitDecorator.html b/doc/app/CommitDecorator.html new file mode 100644 index 00000000..3cca57a4 --- /dev/null +++ b/doc/app/CommitDecorator.html @@ -0,0 +1,663 @@ + + + + + + +class CommitDecorator - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class CommitDecorator

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + + + + +
    + +
    + description() + click to toggle source +
    + + +
    + +

    Returns the commits description

    + +

    cut off, ellipses (`&hellp;`) are prepended to the commit message.

    + + + +
    +
    # File app/decorators/commit_decorator.rb, line 34
    +def description
    +  description = safe_message
    +
    +  title_end = description.index(%r\n/)
    +  if (!title_end && description.length > 80) || (title_end && title_end > 80)
    +    "&hellip;".html_safe << description[70..-1]
    +  else
    +    description.split(%r\n/, 2)[1].try(:chomp)
    +  end
    +end
    +
    + +
    + + + + +
    + + + + + +
    + +
    + title() + click to toggle source +
    + + +
    + +

    Returns the commits title.

    + +

    Usually, the commit title is the first line of the commit message. In case +this first line is longer than 80 characters, it is cut off after 70 +characters and ellipses (`&hellp;`) are appended.

    + + + +
    +
    # File app/decorators/commit_decorator.rb, line 18
    +def title
    +  title = safe_message
    +
    +  return no_commit_message if title.blank?
    +
    +  title_end = title.index(%r\n/)
    +  if (!title_end && title.length > 80) || (title_end && title_end > 80)
    +    title[0..69] << "&hellip;".html_safe
    +  else
    +    title.split(%r\n/, 2).first
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    +

    Protected Instance Methods

    + + +
    + +
    + no_commit_message() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/decorators/commit_decorator.rb, line 69
    +def no_commit_message
    +  "--no commit message"
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/CommitLoadContext.html b/doc/app/CommitLoadContext.html new file mode 100644 index 00000000..feb6dbf4 --- /dev/null +++ b/doc/app/CommitLoadContext.html @@ -0,0 +1,513 @@ + + + + + + +class CommitLoadContext - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class CommitLoadContext

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + execute() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/contexts/commit_load_context.rb, line 2
    +def execute
    +  result = {
    +    commit: nil,
    +    suppress_diff: false,
    +    line_notes: [],
    +    notes_count: 0,
    +    note: nil,
    +    status: :ok
    +  }
    +
    +  commit = project.commit(params[:id])
    +
    +  if commit
    +    commit = CommitDecorator.decorate(commit)
    +    line_notes = project.commit_line_notes(commit)
    +
    +    result[:commit] = commit
    +    result[:note] = project.build_commit_note(commit)
    +    result[:line_notes] = line_notes
    +    result[:notes_count] = line_notes.count + project.commit_notes(commit).count
    +
    +    begin
    +      result[:suppress_diff] = true if commit.diffs.size > 200 && !params[:force_show_diff]
    +    rescue Grit::Git::GitTimeout
    +      result[:suppress_diff] = true
    +      result[:status] = :huge_commit
    +    end
    +  end
    +
    +  result
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/CommitsController.html b/doc/app/CommitsController.html new file mode 100644 index 00000000..52eaad04 --- /dev/null +++ b/doc/app/CommitsController.html @@ -0,0 +1,507 @@ + + + + + + +class CommitsController - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class CommitsController

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + show() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/commits_controller.rb, line 11
    +def show
    +  @repo = @project.repo
    +  @limit, @offset = (params[:limit] || 40), (params[:offset] || 0)
    +
    +  @commits = @project.commits(@ref, @path, @limit, @offset)
    +  @commits = CommitDecorator.decorate(@commits)
    +
    +  respond_to do |format|
    +    format.html # index.html.erb
    +    format.js
    +    format.atom { render layout: false }
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/CommitsHelper.html b/doc/app/CommitsHelper.html new file mode 100644 index 00000000..58844bec --- /dev/null +++ b/doc/app/CommitsHelper.html @@ -0,0 +1,625 @@ + + + + + + +module CommitsHelper - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    module CommitsHelper

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + build_line_anchor(index, line_new, line_old) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/helpers/commits_helper.rb, line 12
    +def build_line_anchor(index, line_new, line_old)
    +  "#{index}_#{line_old}_#{line_new}"
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + each_diff_line(diff_arr, index) { |full_line, type, nil, nil, nil| ... } + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/helpers/commits_helper.rb, line 16
    +def each_diff_line(diff_arr, index)
    +  line_old = 1
    +  line_new = 1
    +  type = nil
    +
    +  lines_arr = ::Gitlab::InlineDiff.processing diff_arr
    +  lines_arr.each do |line|
    +    next if line.match(%r^\-\-\- \/dev\/null/)
    +    next if line.match(%r^\+\+\+ \/dev\/null/)
    +    next if line.match(%r^\-\-\- a/)
    +    next if line.match(%r^\+\+\+ b/)
    +
    +    full_line = html_escape(line.gsub(%r\n/, ''))
    +    full_line = ::Gitlab::InlineDiff.replace_markers full_line
    +
    +    if line.match(%r^@@ -/)
    +      type = "match"
    +
    +      line_old = line.match(%r\-[0-9]*/)[0].to_i.abs rescue 0
    +      line_new = line.match(%r\+[0-9]*/)[0].to_i.abs rescue 0
    +
    +      next if line_old == 1 && line_new == 1 #top of file
    +      yield(full_line, type, nil, nil, nil)
    +      next
    +    else
    +      type = identification_type(line)
    +      line_code = build_line_anchor(index, line_new, line_old)
    +      yield(full_line, type, line_code, line_new, line_old)
    +    end
    +
    +
    +    if line[0] == "+"
    +      line_new += 1
    +    elsif line[0] == "-"
    +      line_old += 1
    +    else
    +      line_new += 1
    +      line_old += 1
    +    end
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + identification_type(line) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/helpers/commits_helper.rb, line 2
    +def identification_type(line)
    +  if line[0] == "+"
    +    "new"
    +  elsif line[0] == "-"
    +    "old"
    +  else
    +    nil
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + image_diff_class(diff) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/helpers/commits_helper.rb, line 58
    +def image_diff_class(diff)
    +  if diff.deleted_file
    +    "diff_image_removed"
    +  elsif diff.new_file
    +    "diff_image_added"
    +  else
    +    nil
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/CompareController.html b/doc/app/CompareController.html new file mode 100644 index 00000000..177e22d2 --- /dev/null +++ b/doc/app/CompareController.html @@ -0,0 +1,556 @@ + + + + + + +class CompareController - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class CompareController

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + create() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/compare_controller.rb, line 22
    +def create
    +  redirect_to project_compare_path(@project, params[:from], params[:to])
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + index() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/compare_controller.rb, line 7
    +def index
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + show() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/compare_controller.rb, line 10
    +def show
    +  result = Commit.compare(project, params[:from], params[:to])
    +
    +  @commits       = result[:commits]
    +  @commit        = result[:commit]
    +  @diffs         = result[:diffs]
    +  @refs_are_same = result[:same]
    +  @line_notes    = []
    +
    +  @commits = CommitDecorator.decorate(@commits)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/DashboardController.html b/doc/app/DashboardController.html new file mode 100644 index 00000000..e3f4d1b4 --- /dev/null +++ b/doc/app/DashboardController.html @@ -0,0 +1,569 @@ + + + + + + +class DashboardController - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class DashboardController

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + index() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/dashboard_controller.rb, line 4
    +def index
    +  @groups = Group.where(id: current_user.projects.pluck(:group_id))
    +  @projects = current_user.projects_with_events
    +  @projects = @projects.page(params[:page]).per(30)
    +
    +  @events = Event.in_projects(current_user.project_ids).limit(20).offset(params[:offset] || 0)
    +  @last_push = current_user.recent_push
    +
    +  respond_to do |format|
    +    format.html
    +    format.js
    +    format.atom { render layout: false }
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + issues() + click to toggle source +
    + + +
    + +

    Get only assigned issues

    + + + +
    +
    # File app/controllers/dashboard_controller.rb, line 26
    +def issues
    +  @projects = current_user.projects.all
    +  @user   = current_user
    +  @issues = current_user.assigned_issues.opened.recent.page(params[:page]).per(20)
    +  @issues = @issues.includes(:author, :project)
    +
    +  respond_to do |format|
    +    format.html
    +    format.atom { render layout: false }
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + merge_requests() + click to toggle source +
    + + +
    + +

    Get authored or assigned open merge requests

    + + + +
    +
    # File app/controllers/dashboard_controller.rb, line 20
    +def merge_requests
    +  @projects = current_user.projects.all
    +  @merge_requests = current_user.cared_merge_requests.recent.page(params[:page]).per(20)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/DeployKeysController.html b/doc/app/DeployKeysController.html new file mode 100644 index 00000000..318a1441 --- /dev/null +++ b/doc/app/DeployKeysController.html @@ -0,0 +1,626 @@ + + + + + + +class DeployKeysController - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class DeployKeysController

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + create() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/deploy_keys_controller.rb, line 21
    +def create
    +  @key = @project.deploy_keys.new(params[:key])
    +  if @key.save
    +    redirect_to project_deploy_keys_path(@project)
    +  else
    +    render "new"
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + destroy() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/deploy_keys_controller.rb, line 30
    +def destroy
    +  @key = @project.deploy_keys.find(params[:id])
    +  @key.destroy
    +
    +  respond_to do |format|
    +    format.html { redirect_to project_deploy_keys_url }
    +    format.js { render nothing: true }
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + index() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/deploy_keys_controller.rb, line 7
    +def index
    +  @keys = @project.deploy_keys.all
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + new() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/deploy_keys_controller.rb, line 15
    +def new
    +  @key = @project.deploy_keys.new
    +
    +  respond_with(@key)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + show() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/deploy_keys_controller.rb, line 11
    +def show
    +  @key = @project.deploy_keys.find(params[:id])
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/ErrorsController.html b/doc/app/ErrorsController.html new file mode 100644 index 00000000..e77a67d7 --- /dev/null +++ b/doc/app/ErrorsController.html @@ -0,0 +1,485 @@ + + + + + + +class ErrorsController - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class ErrorsController

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + githost() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/errors_controller.rb, line 2
    +def githost
    +  render "errors/gitolite"
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/Event.html b/doc/app/Event.html new file mode 100644 index 00000000..1347cda3 --- /dev/null +++ b/doc/app/Event.html @@ -0,0 +1,1222 @@ + + + + + + +class Event - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Event

    + +
    + +
    + + + + +
    + + + + + + +
    +

    Constants

    +
    + +
    Closed + +
    + + +
    Commented + +
    + + +
    Created + +
    + + +
    Joined + +
    + + +
    Left + +
    + + +
    Merged + +
    + + +
    Pushed + +
    + + +
    Reopened + +
    + + +
    Updated + +
    + + +
    +
    + + + + + + +
    +

    Public Class Methods

    + + +
    + +
    + determine_action(record) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/event.rb, line 36
    +def determine_action(record)
    +  if [Issue, MergeRequest].include? record.class
    +    Event::Created
    +  elsif record.kind_of? Note
    +    Event::Commented
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    +

    Public Instance Methods

    + + +
    + +
    + action_name() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/event.rb, line 133
    +def action_name
    +  if closed?
    +    "closed"
    +  elsif merged?
    +    "merged"
    +  elsif joined?
    +    'joined'
    +  elsif left?
    +    'left'
    +  else
    +    "opened"
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + allowed?() + click to toggle source +
    + + +
    + +

    Next events currently enabled for system

    + +
    - push
    +- new issue
    +- merge request
    + + + +
    +
    # File app/models/event.rb, line 49
    +def allowed?
    +  push? || issue? || merge_request? || membership_changed?
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + author() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/event.rb, line 129
    +def author
    +  @author ||= User.find(author_id)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + changed_issue?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/event.rb, line 104
    +def changed_issue?
    +  target_type == "Issue" &&
    +    [Closed, Reopened].include?(action)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + changed_merge_request?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/event.rb, line 99
    +def changed_merge_request?
    +  target_type == "MergeRequest" &&
    +    [Closed, Reopened].include?(action)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + closed?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/event.rb, line 73
    +def closed?
    +  action == self.class::Closed
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + issue() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/event.rb, line 121
    +def issue
    +  target if target_type == "Issue"
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + issue?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/event.rb, line 81
    +def issue?
    +  target_type == "Issue"
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + joined?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/event.rb, line 109
    +def joined?
    +  action == Joined
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + left?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/event.rb, line 113
    +def left?
    +  action == Left
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + membership_changed?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/event.rb, line 117
    +def membership_changed?
    +  joined? || left?
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + merge_request() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/event.rb, line 125
    +def merge_request
    +  target if target_type == "MergeRequest"
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + merge_request?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/event.rb, line 85
    +def merge_request?
    +  target_type == "MergeRequest"
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + merged?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/event.rb, line 69
    +def merged?
    +  action == self.class::Merged
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + new_issue?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/event.rb, line 89
    +def new_issue?
    +  target_type == "Issue" &&
    +    action == Created
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + new_merge_request?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/event.rb, line 94
    +def new_merge_request?
    +  target_type == "MergeRequest" &&
    +    action == Created
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + project_name() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/event.rb, line 53
    +def project_name
    +  if project
    +    project.name
    +  else
    +    "(deleted project)"
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + push?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/event.rb, line 65
    +def push?
    +  action == self.class::Pushed && valid_push?
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + reopened?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/event.rb, line 77
    +def reopened?
    +  action == self.class::Reopened
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + target_title() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/event.rb, line 61
    +def target_title
    +  target.try :title
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/EventDecorator.html b/doc/app/EventDecorator.html new file mode 100644 index 00000000..dddb98a6 --- /dev/null +++ b/doc/app/EventDecorator.html @@ -0,0 +1,578 @@ + + + + + + +class EventDecorator - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class EventDecorator

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + feed_summary() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/decorators/event_decorator.rb, line 37
    +def feed_summary
    +  if self.issue?
    +    h.render "events/event_issue", issue: self.issue
    +  elsif self.push?
    +    h.render "events/event_push", event: self
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + feed_title() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/decorators/event_decorator.rb, line 4
    +def feed_title
    +  if self.issue?
    +    "#{self.author_name} #{self.action_name} issue ##{self.target_id}: #{self.issue_title} at #{self.project.name}"
    +  elsif self.merge_request?
    +    "#{self.author_name} #{self.action_name} MR ##{self.target_id}: #{self.merge_request_title} at #{self.project.name}"
    +  elsif self.push?
    +    "#{self.author_name} #{self.push_action_name} #{self.ref_type} #{self.ref_name} at #{self.project.name}"
    +  elsif self.membership_changed?
    +    "#{self.author_name} #{self.action_name} #{self.project.name}"
    +  else
    +    ""
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + feed_url() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/decorators/event_decorator.rb, line 18
    +def feed_url
    +  if self.issue?
    +    h.project_issue_url(self.project, self.issue)
    +  elsif self.merge_request?
    +    h.project_merge_request_url(self.project, self.merge_request)
    +
    +  elsif self.push?
    +    if self.push_with_commits?
    +      if self.commits_count > 1
    +        h.project_compare_url(self.project, :from => self.parent_commit.id, :to => self.last_commit.id)
    +      else
    +        h.project_commit_url(self.project, :id => self.last_commit.id)
    +      end
    +    else
    +      h.project_commits_url(self.project, self.ref_name)
    +    end
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/EventsHelper.html b/doc/app/EventsHelper.html new file mode 100644 index 00000000..4f7c0454 --- /dev/null +++ b/doc/app/EventsHelper.html @@ -0,0 +1,566 @@ + + + + + + +module EventsHelper - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    module EventsHelper

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + event_action_name(event) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/helpers/events_helper.rb, line 13
    +def event_action_name(event)
    +  target = if event.target_type
    +             event.target_type.titleize.downcase
    +           else
    +             'project'
    +           end
    +
    +  [event.action_name, target].join(" ")
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + event_image(event) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/helpers/events_helper.rb, line 23
    +def event_image event
    +  event_image_path = if event.push?
    +                 "event_push.png"
    +               elsif event.merged?
    +                 "event_mr_merged.png"
    +               end
    +
    +  return nil unless event_image_path
    +
    +  content_tag :div, class: 'event_icon' do
    +    image_tag event_image_path
    +  end
    +end
    +
    + +
    + + + + +
    + + + + + +
    + +
    + +
    + + + + diff --git a/doc/app/ExtractsPath.html b/doc/app/ExtractsPath.html new file mode 100644 index 00000000..e3825307 --- /dev/null +++ b/doc/app/ExtractsPath.html @@ -0,0 +1,614 @@ + + + + + + +module ExtractsPath - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    module ExtractsPath

    + +
    + +

    Module providing methods for dealing with separating a tree-ish string and +a file path string when combined in a request parameter

    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + assign_ref_vars() + click to toggle source +
    + + +
    + +

    Assigns common instance variables for views working with Git tree-ish +objects

    + +

    Assignments are:

    +
    • +

      @id - A string representing the joined ref and path

      +
    • +

      @ref - A string representing the ref (e.g., the branch, tag, or commit +SHA)

      +
    • +

      @path - A string representing the filesystem path

      +
    • +

      @commit - A CommitDecorator representing +the commit from the given ref

      +
    • +

      @tree - A TreeDecorator representing the +tree at the given ref/path

      +
    + +

    If the :id parameter appears to be requesting a specific response format, +that will be handled as well.

    + +

    Automatically renders `not_found!` if a valid tree path could not be +resolved (e.g., when a user inserts an invalid path or ref).

    + + + +
    +
    # File lib/extracts_path.rb, line 94
    +def assign_ref_vars
    +  # Handle formats embedded in the id
    +  if params[:id].ends_with?('.atom')
    +    params[:id].gsub!(%r\.atom$/, '')
    +    request.format = :atom
    +  end
    +
    +  @ref, @path = extract_ref(params[:id])
    +
    +  @id = File.join(@ref, @path)
    +
    +  @commit = CommitDecorator.decorate(@project.commit(@ref))
    +
    +  @tree = Tree.new(@commit.tree, @project, @ref, @path)
    +  @tree = TreeDecorator.new(@tree)
    +
    +  raise InvalidPathError if @tree.invalid?
    +rescue NoMethodError, InvalidPathError
    +  not_found!
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + extract_ref(input) + click to toggle source +
    + + +
    + +

    Given a string containing both a Git tree-ish, such as a branch or tag, and +a filesystem path joined by forward slashes, attempts to separate the two.

    + +

    Expects a @project instance variable to contain the active project. This is +used to check the input against a list of valid repository refs.

    + +

    Examples

    + +
    # No @project available
    +extract_ref('master')
    +# => ['', '']
    +
    +extract_ref('master')
    +# => ['master', '']
    +
    +extract_ref("f4b14494ef6abf3d144c28e4af0c20143383e062/CHANGELOG")
    +# => ['f4b14494ef6abf3d144c28e4af0c20143383e062', 'CHANGELOG']
    +
    +extract_ref("v2.0.0/README.md")
    +# => ['v2.0.0', 'README.md']
    +
    +extract_ref('issues/1234/app/models/project.rb')
    +# => ['issues/1234', 'app/models/project.rb']
    +
    +# Given an invalid branch, we fall back to just splitting on the first slash
    +extract_ref('non/existent/branch/README.md')
    +# => ['non', 'existent/branch/README.md']
    +
    + +

    Returns an Array where the first value is the tree-ish and the second is +the path

    + + + +
    +
    # File lib/extracts_path.rb, line 45
    +def extract_ref(input)
    +  pair = ['', '']
    +
    +  return pair unless @project
    +
    +  if input.match(%r^([[:alnum:]]{40})(.+)/)
    +    # If the ref appears to be a SHA, we're done, just split the string
    +    pair = $~.captures
    +  else
    +    # Otherwise, attempt to detect the ref using a list of the project's
    +    # branches and tags
    +
    +    # Append a trailing slash if we only get a ref and no file path
    +    id = input
    +    id += '/' unless id.ends_with?('/')
    +
    +    valid_refs = @project.ref_names
    +    valid_refs.select! { |v| id.start_with?("#{v}/") }
    +
    +    if valid_refs.length != 1
    +      # No exact ref match, so just try our best
    +      pair = id.match(%r([^\/]+)(.*)/).captures
    +    else
    +      # Partition the string into the ref and the path, ignoring the empty first value
    +      pair = id.partition(valid_refs.first)[1..-1]
    +    end
    +  end
    +
    +  # Remove ending slashes from path
    +  pair[1].gsub!(%r^\/|\/$/, '')
    +
    +  pair
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/ExtractsPath/InvalidPathError.html b/doc/app/ExtractsPath/InvalidPathError.html new file mode 100644 index 00000000..3c8c60fc --- /dev/null +++ b/doc/app/ExtractsPath/InvalidPathError.html @@ -0,0 +1,441 @@ + + + + + + +class ExtractsPath::InvalidPathError - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class ExtractsPath::InvalidPathError

    + +
    + +

    Raised when given an invalid file path

    + +
    + + + + +
    + + + + + + + + + + +
    + +
    + + + + diff --git a/doc/app/FileSizeValidator.html b/doc/app/FileSizeValidator.html new file mode 100644 index 00000000..5acdf291 --- /dev/null +++ b/doc/app/FileSizeValidator.html @@ -0,0 +1,652 @@ + + + + + + +class FileSizeValidator - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class FileSizeValidator

    + +
    + +
    + + + + +
    + + + + + + +
    +

    Constants

    +
    + +
    CHECKS + +
    + + +
    DEFAULT_TOKENIZER + +
    + + +
    MESSAGES + +
    + + +
    RESERVED_OPTIONS + +
    + + +
    +
    + + + + + + +
    +

    Public Class Methods

    + + +
    + +
    + new(options) + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/file_size_validator.rb, line 8
    +def initialize(options)
    +  if range = (options.delete(:in) || options.delete(:within))
    +    raise ArgumentError, ":in and :within must be a Range" unless range.is_a?(Range)
    +    options[:minimum], options[:maximum] = range.begin, range.end
    +    options[:maximum] -= 1 if range.exclude_end?
    +  end
    +
    +  super
    +end
    +
    + +
    + + + + +
    + + +
    + +
    +

    Public Instance Methods

    + + +
    + +
    + check_validity!() + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/file_size_validator.rb, line 18
    +def check_validity!
    +  keys = CHECKS.keys & options.keys
    +
    +  if keys.empty?
    +    raise ArgumentError, 'Range unspecified. Specify the :within, :maximum, :minimum, or :is option.'
    +  end
    +
    +  keys.each do |key|
    +    value = options[key]
    +
    +    unless value.is_a?(Integer) && value >= 0
    +      raise ArgumentError, ":#{key} must be a nonnegative Integer"
    +    end
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + help() + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/file_size_validator.rb, line 57
    +def help
    +  Helper.instance
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + validate_each(record, attribute, value) + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/file_size_validator.rb, line 34
    +def validate_each(record, attribute, value)
    +  raise(ArgumentError, "A CarrierWave::Uploader::Base object was expected") unless value.kind_of? CarrierWave::Uploader::Base
    +
    +  value = (options[:tokenizer] || DEFAULT_TOKENIZER).call(value) if value.kind_of?(String)
    +
    +  CHECKS.each do |key, validity_check|
    +    next unless check_value = options[key]
    +
    +    value ||= [] if key == :maximum
    +
    +    value_size = value.size
    +    next if value_size.send(validity_check, check_value)
    +
    +    errors_options = options.except(*RESERVED_OPTIONS)
    +    errors_options[:file_size] = help.number_to_human_size check_value
    +
    +    default_message = options[MESSAGES[key]]
    +    errors_options[:message] ||= default_message if default_message
    +
    +    record.errors.add(attribute, MESSAGES[key], errors_options)
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/FileSizeValidator/Helper.html b/doc/app/FileSizeValidator/Helper.html new file mode 100644 index 00000000..4fd146c2 --- /dev/null +++ b/doc/app/FileSizeValidator/Helper.html @@ -0,0 +1,455 @@ + + + + + + +class FileSizeValidator::Helper - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class FileSizeValidator::Helper

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    + +
    + + + + diff --git a/doc/app/GitHost.html b/doc/app/GitHost.html new file mode 100644 index 00000000..3197a73f --- /dev/null +++ b/doc/app/GitHost.html @@ -0,0 +1,479 @@ + + + + + + +module GitHost - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    module GitHost

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + git_host() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/git_host.rb, line 2
    +def git_host
    +  Gitlab::Gitolite.new
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/Gitlab.html b/doc/app/Gitlab.html new file mode 100644 index 00000000..0e28a24b --- /dev/null +++ b/doc/app/Gitlab.html @@ -0,0 +1,454 @@ + + + + + + +module Gitlab - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    module Gitlab

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    + +
    + + + + diff --git a/doc/app/Gitlab/API.html b/doc/app/Gitlab/API.html new file mode 100644 index 00000000..8fad1eec --- /dev/null +++ b/doc/app/Gitlab/API.html @@ -0,0 +1,452 @@ + + + + + + +class Gitlab::API - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Gitlab::API

    + +
    + +
    + + + + +
    + + + + + + +
    +

    Constants

    +
    + +
    VERSION + +
    + + +
    +
    + + + + + + +
    + +
    + + + + diff --git a/doc/app/Gitlab/APIHelpers.html b/doc/app/Gitlab/APIHelpers.html new file mode 100644 index 00000000..47bf3fb1 --- /dev/null +++ b/doc/app/Gitlab/APIHelpers.html @@ -0,0 +1,846 @@ + + + + + + +module Gitlab::APIHelpers - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    module Gitlab::APIHelpers

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + attributes_for_keys(keys) + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/api/helpers.rb, line 35
    +def attributes_for_keys(keys)
    +  attrs = {}
    +  keys.each do |key|
    +    attrs[key] = params[key] if params[key].present?
    +  end
    +  attrs
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + authenticate!() + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/api/helpers.rb, line 21
    +def authenticate!
    +  unauthorized! unless current_user
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + authenticated_as_admin!() + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/api/helpers.rb, line 25
    +def authenticated_as_admin!
    +  forbidden! unless current_user.is_admin?
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + authorize!(action, subject) + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/api/helpers.rb, line 29
    +def authorize! action, subject
    +  unless abilities.allowed?(current_user, action, subject)
    +    forbidden!
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + current_user() + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/api/helpers.rb, line 3
    +def current_user
    +  @current_user ||= User.find_by_authentication_token(params[:private_token] || env["HTTP_PRIVATE_TOKEN"])
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + forbidden!() + click to toggle source +
    + + +
    + +

    error helpers

    + + + +
    +
    # File lib/api/helpers.rb, line 45
    +def forbidden!
    +  render_api_error!('403 Forbidden', 403)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + not_allowed!() + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/api/helpers.rb, line 60
    +def not_allowed!
    +  render_api_error!('Method Not Allowed', 405)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + not_found!(resource = nil) + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/api/helpers.rb, line 49
    +def not_found!(resource = nil)
    +  message = ["404"]
    +  message << resource if resource
    +  message << "Not Found"
    +  render_api_error!(message.join(' '), 404)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + paginate(object) + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/api/helpers.rb, line 17
    +def paginate(object)
    +  object.page(params[:page]).per(params[:per_page].to_i)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + render_api_error!(message, status) + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/api/helpers.rb, line 64
    +def render_api_error!(message, status)
    +  error!({'message' => message}, status)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + unauthorized!() + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/api/helpers.rb, line 56
    +def unauthorized!
    +  render_api_error!('401 Unauthorized', 401)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + user_project() + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/api/helpers.rb, line 7
    +def user_project
    +  if @project ||= current_user.projects.find_by_id(params[:id]) ||
    +                  current_user.projects.find_by_code(params[:id])
    +  else
    +    not_found!
    +  end
    +
    +  @project
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/Gitlab/AppLogger.html b/doc/app/Gitlab/AppLogger.html new file mode 100644 index 00000000..e3a352af --- /dev/null +++ b/doc/app/Gitlab/AppLogger.html @@ -0,0 +1,523 @@ + + + + + + +class Gitlab::AppLogger - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Gitlab::AppLogger

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Class Methods

    + + +
    + +
    + file_name() + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/gitlab/app_logger.rb, line 3
    +def self.file_name
    +  'application.log'
    +end
    +
    + +
    + + + + +
    + + +
    + +
    +

    Public Instance Methods

    + + +
    + +
    + format_message(severity, timestamp, progname, msg) + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/gitlab/app_logger.rb, line 7
    +def format_message(severity, timestamp, progname, msg)
    +  "#{timestamp.to_s(:long)}: #{msg}\n"
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/Gitlab/Auth.html b/doc/app/Gitlab/Auth.html new file mode 100644 index 00000000..ac4fd556 --- /dev/null +++ b/doc/app/Gitlab/Auth.html @@ -0,0 +1,630 @@ + + + + + + +class Gitlab::Auth - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Gitlab::Auth

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + create_from_omniauth(auth, ldap = false) + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/gitlab/auth.rb, line 20
    +def create_from_omniauth(auth, ldap = false)
    +  provider = auth.provider
    +  uid = auth.info.uid || auth.uid
    +  name = auth.info.name.force_encoding("utf-8")
    +  email = auth.info.email.downcase unless auth.info.email.nil?
    +
    +  ldap_prefix = ldap ? '(LDAP) ' : ''
    +  raise OmniAuth::Error, "#{ldap_prefix}#{provider} does not provide an email"         " address" if auth.info.email.blank?
    +
    +  log.info "#{ldap_prefix}Creating user from #{provider} login"         " {uid => #{uid}, name => #{name}, email => #{email}}"
    +  password = Devise.friendly_token[0, 8].downcase
    +  @user = User.new({
    +    extern_uid: uid,
    +    provider: provider,
    +    name: name,
    +    email: email,
    +    password: password,
    +    password_confirmation: password,
    +    projects_limit: Gitlab.config.default_projects_limit,
    +  }, as: :admin)
    +  if Gitlab.config.omniauth['block_auto_created_users'] && !ldap
    +    @user.blocked = true
    +  end
    +  @user.save!
    +  @user
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + find_for_ldap_auth(auth, signed_in_resource = nil) + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/gitlab/auth.rb, line 3
    +def find_for_ldap_auth(auth, signed_in_resource = nil)
    +  uid = auth.info.uid
    +  provider = auth.provider
    +  email = auth.info.email.downcase unless auth.info.email.nil?
    +  raise OmniAuth::Error, "LDAP accounts must provide an uid and email address" if uid.nil? or email.nil?
    +
    +  if @user = User.find_by_extern_uid_and_provider(uid, provider)
    +    @user
    +  elsif @user = User.find_by_email(email)
    +    log.info "Updating legacy LDAP user #{email} with extern_uid => #{uid}"
    +    @user.update_attributes(:extern_uid => uid, :provider => provider)
    +    @user
    +  else
    +    create_from_omniauth(auth, true)
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + find_or_new_for_omniauth(auth) + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/gitlab/auth.rb, line 49
    +def find_or_new_for_omniauth(auth)
    +  provider, uid = auth.provider, auth.uid
    +  email = auth.info.email.downcase unless auth.info.email.nil?
    +
    +  if @user = User.find_by_provider_and_extern_uid(provider, uid)
    +    @user
    +  elsif @user = User.find_by_email(email)
    +    @user.update_attributes(:extern_uid => uid, :provider => provider)
    +    @user
    +  else
    +    if Gitlab.config.omniauth['allow_single_sign_on']
    +      @user = create_from_omniauth(auth)
    +      @user
    +    end
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + log() + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/gitlab/auth.rb, line 66
    +def log
    +  Gitlab::AppLogger
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/Gitlab/Encode.html b/doc/app/Gitlab/Encode.html new file mode 100644 index 00000000..387e0db2 --- /dev/null +++ b/doc/app/Gitlab/Encode.html @@ -0,0 +1,537 @@ + + + + + + +module Gitlab::Encode - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    module Gitlab::Encode

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + detect_encoding(message) + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/gitlab/encode.rb, line 34
    +def detect_encoding message
    +  return nil unless message
    +
    +  hash = CharlockHolmes::EncodingDetector.detect(message) rescue {}
    +  return hash[:encoding] ? hash[:encoding] : nil
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + utf8(message) + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/gitlab/encode.rb, line 7
    +def utf8 message
    +  # return nil if message is nil
    +  return nil unless message
    +
    +  message.force_encoding("utf-8")
    +  # return message if message type is binary
    +  detect = CharlockHolmes::EncodingDetector.detect(message)
    +  return message if detect[:type] == :binary
    +
    +  # if message is utf-8 encoding, just return it
    +  return message if message.valid_encoding?
    +
    +  # if message is not utf-8 encoding, convert it
    +  if detect[:encoding]
    +    message.force_encoding(detect[:encoding])
    +    message.encode!("utf-8", detect[:encoding], undef: :replace, replace: "", invalid: :replace)
    +  end
    +
    +  # ensure message encoding is utf8
    +  message.valid_encoding? ? message : raise
    +
    +# Prevent app from crash cause of encoding errors
    +rescue
    +  encoding = detect ? detect[:encoding] : "unknown"
    +  "--broken encoding: #{encoding}"
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/Gitlab/Entities.html b/doc/app/Gitlab/Entities.html new file mode 100644 index 00000000..8cd0d837 --- /dev/null +++ b/doc/app/Gitlab/Entities.html @@ -0,0 +1,433 @@ + + + + + + +module Gitlab::Entities - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    module Gitlab::Entities

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    + +
    + + + + diff --git a/doc/app/Gitlab/Entities/Hook.html b/doc/app/Gitlab/Entities/Hook.html new file mode 100644 index 00000000..c9eba832 --- /dev/null +++ b/doc/app/Gitlab/Entities/Hook.html @@ -0,0 +1,439 @@ + + + + + + +class Gitlab::Entities::Hook - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Gitlab::Entities::Hook

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    + +
    + + + + diff --git a/doc/app/Gitlab/Entities/Issue.html b/doc/app/Gitlab/Entities/Issue.html new file mode 100644 index 00000000..f713b7ab --- /dev/null +++ b/doc/app/Gitlab/Entities/Issue.html @@ -0,0 +1,439 @@ + + + + + + +class Gitlab::Entities::Issue - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Gitlab::Entities::Issue

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    + +
    + + + + diff --git a/doc/app/Gitlab/Entities/Milestone.html b/doc/app/Gitlab/Entities/Milestone.html new file mode 100644 index 00000000..f762ad29 --- /dev/null +++ b/doc/app/Gitlab/Entities/Milestone.html @@ -0,0 +1,439 @@ + + + + + + +class Gitlab::Entities::Milestone - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Gitlab::Entities::Milestone

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    + +
    + + + + diff --git a/doc/app/Gitlab/Entities/Project.html b/doc/app/Gitlab/Entities/Project.html new file mode 100644 index 00000000..725e6b75 --- /dev/null +++ b/doc/app/Gitlab/Entities/Project.html @@ -0,0 +1,439 @@ + + + + + + +class Gitlab::Entities::Project - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Gitlab::Entities::Project

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    + +
    + + + + diff --git a/doc/app/Gitlab/Entities/ProjectMember.html b/doc/app/Gitlab/Entities/ProjectMember.html new file mode 100644 index 00000000..468d3dc1 --- /dev/null +++ b/doc/app/Gitlab/Entities/ProjectMember.html @@ -0,0 +1,439 @@ + + + + + + +class Gitlab::Entities::ProjectMember - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Gitlab::Entities::ProjectMember

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    + +
    + + + + diff --git a/doc/app/Gitlab/Entities/ProjectSnippet.html b/doc/app/Gitlab/Entities/ProjectSnippet.html new file mode 100644 index 00000000..e19fad75 --- /dev/null +++ b/doc/app/Gitlab/Entities/ProjectSnippet.html @@ -0,0 +1,439 @@ + + + + + + +class Gitlab::Entities::ProjectSnippet - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Gitlab::Entities::ProjectSnippet

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    + +
    + + + + diff --git a/doc/app/Gitlab/Entities/RepoCommit.html b/doc/app/Gitlab/Entities/RepoCommit.html new file mode 100644 index 00000000..04c118a3 --- /dev/null +++ b/doc/app/Gitlab/Entities/RepoCommit.html @@ -0,0 +1,439 @@ + + + + + + +class Gitlab::Entities::RepoCommit - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Gitlab::Entities::RepoCommit

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    + +
    + + + + diff --git a/doc/app/Gitlab/Entities/RepoObject.html b/doc/app/Gitlab/Entities/RepoObject.html new file mode 100644 index 00000000..27f928c1 --- /dev/null +++ b/doc/app/Gitlab/Entities/RepoObject.html @@ -0,0 +1,439 @@ + + + + + + +class Gitlab::Entities::RepoObject - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Gitlab::Entities::RepoObject

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    + +
    + + + + diff --git a/doc/app/Gitlab/Entities/SSHKey.html b/doc/app/Gitlab/Entities/SSHKey.html new file mode 100644 index 00000000..43ed0f01 --- /dev/null +++ b/doc/app/Gitlab/Entities/SSHKey.html @@ -0,0 +1,439 @@ + + + + + + +class Gitlab::Entities::SSHKey - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Gitlab::Entities::SSHKey

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    + +
    + + + + diff --git a/doc/app/Gitlab/Entities/User.html b/doc/app/Gitlab/Entities/User.html new file mode 100644 index 00000000..fb978194 --- /dev/null +++ b/doc/app/Gitlab/Entities/User.html @@ -0,0 +1,439 @@ + + + + + + +class Gitlab::Entities::User - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Gitlab::Entities::User

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    + +
    + + + + diff --git a/doc/app/Gitlab/Entities/UserBasic.html b/doc/app/Gitlab/Entities/UserBasic.html new file mode 100644 index 00000000..dcb1e782 --- /dev/null +++ b/doc/app/Gitlab/Entities/UserBasic.html @@ -0,0 +1,439 @@ + + + + + + +class Gitlab::Entities::UserBasic - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Gitlab::Entities::UserBasic

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    + +
    + + + + diff --git a/doc/app/Gitlab/Entities/UserLogin.html b/doc/app/Gitlab/Entities/UserLogin.html new file mode 100644 index 00000000..fde77335 --- /dev/null +++ b/doc/app/Gitlab/Entities/UserLogin.html @@ -0,0 +1,439 @@ + + + + + + +class Gitlab::Entities::UserLogin - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Gitlab::Entities::UserLogin

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    + +
    + + + + diff --git a/doc/app/Gitlab/FileEditor.html b/doc/app/Gitlab/FileEditor.html new file mode 100644 index 00000000..0916d63c --- /dev/null +++ b/doc/app/Gitlab/FileEditor.html @@ -0,0 +1,647 @@ + + + + + + +class Gitlab::FileEditor - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Gitlab::FileEditor

    + +
    + +

    GitLab file editor

    + +

    It gives you ability to make changes to files & commit this changes +from GitLab UI.

    + +
    + + + + +
    + + + + + + + + +
    +

    Attributes

    + + +
    +
    + project[RW] +
    + +
    + + + +
    +
    + +
    +
    + ref[RW] +
    + +
    + + + +
    +
    + +
    +
    + user[RW] +
    + +
    + + + +
    +
    + +
    + + + + +
    +

    Public Class Methods

    + + +
    + +
    + new(user, project, ref) + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/gitlab/file_editor.rb, line 9
    +def initialize(user, project, ref)
    +  self.user = user
    +  self.project = project
    +  self.ref = ref
    +end
    +
    + +
    + + + + +
    + + +
    + +
    +

    Public Instance Methods

    + + +
    + +
    + update(path, content, commit_message, last_commit) + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/gitlab/file_editor.rb, line 15
    +def update(path, content, commit_message, last_commit)
    +  return false unless can_edit?(path, last_commit)
    +
    +  Grit::Git.with_timeout(10.seconds) do
    +    lock_file = Rails.root.join("tmp", "#{project.path}.lock")
    +
    +    File.open(lock_file, "w+") do |f|
    +      f.flock(File::LOCK_EX)
    +
    +      unless project.satellite.exists?
    +        raise "Satellite doesn't exist"
    +      end
    +
    +      project.satellite.clear
    +
    +      Dir.chdir(project.satellite.path) do
    +        r = Grit::Repo.new('.')
    +        r.git.sh "git reset --hard"
    +        r.git.sh "git fetch origin"
    +        r.git.sh "git config user.name \"#{user.name}\""
    +        r.git.sh "git config user.email \"#{user.email}\""
    +        r.git.sh "git checkout -b #{ref} origin/#{ref}"
    +        File.open(path, 'w'){|f| f.write(content)}
    +        r.git.sh "git add ."
    +        r.git.sh "git commit -am '#{commit_message}'"
    +        output = r.git.sh "git push origin #{ref}"
    +
    +        if output =~ %rreject/
    +          return false
    +        end
    +      end
    +    end
    +  end
    +  true
    +end
    +
    + +
    + + + + +
    + + +
    + +
    +

    Protected Instance Methods

    + + +
    + +
    + can_edit?(path, last_commit) + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/gitlab/file_editor.rb, line 53
    +def can_edit?(path, last_commit)
    +  current_last_commit = @project.last_commit_for(ref, path).sha
    +  last_commit == current_last_commit
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/Gitlab/GitLogger.html b/doc/app/Gitlab/GitLogger.html new file mode 100644 index 00000000..c37bce98 --- /dev/null +++ b/doc/app/Gitlab/GitLogger.html @@ -0,0 +1,523 @@ + + + + + + +class Gitlab::GitLogger - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Gitlab::GitLogger

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Class Methods

    + + +
    + +
    + file_name() + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/gitlab/git_logger.rb, line 3
    +def self.file_name
    +  'githost.log'
    +end
    +
    + +
    + + + + +
    + + +
    + +
    +

    Public Instance Methods

    + + +
    + +
    + format_message(severity, timestamp, progname, msg) + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/gitlab/git_logger.rb, line 7
    +def format_message(severity, timestamp, progname, msg)
    +  "#{timestamp.to_s(:long)} -> #{severity} -> #{msg}\n"
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/Gitlab/Gitolite.html b/doc/app/Gitlab/Gitolite.html new file mode 100644 index 00000000..60ef3b13 --- /dev/null +++ b/doc/app/Gitlab/Gitolite.html @@ -0,0 +1,716 @@ + + + + + + +class Gitlab::Gitolite - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Gitlab::Gitolite

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + config() + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/gitlab/backend/gitolite.rb, line 7
    +def config
    +  Gitlab::GitoliteConfig.new
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + create_repository(project) + click to toggle source +
    + + +
    + + + + + +
    + + + + +
    + Alias for: update_repository +
    + +
    + + +
    + +
    + enable_automerge() + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/gitlab/backend/gitolite.rb, line 37
    +def enable_automerge
    +  config.admin_all_repo!
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + remove_key(key_id, projects) + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/gitlab/backend/gitolite.rb, line 18
    +def remove_key key_id, projects
    +  config.apply do |config|
    +    config.rm_key(key_id)
    +    config.update_projects(projects)
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + remove_repository(project) + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/gitlab/backend/gitolite.rb, line 29
    +def remove_repository project
    +  config.destroy_project!(project)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + set_key(key_id, key_content, projects) + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/gitlab/backend/gitolite.rb, line 11
    +def set_key key_id, key_content, projects
    +  config.apply do |config|
    +    config.write_key(key_id, key_content)
    +    config.update_projects(projects)
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + update_repository(project) + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/gitlab/backend/gitolite.rb, line 25
    +def update_repository project
    +  config.update_project!(project.path, project)
    +end
    +
    + +
    + + +
    + Also aliased as: create_repository +
    + + + +
    + + +
    + +
    + url_to_repo(path) + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/gitlab/backend/gitolite.rb, line 33
    +def url_to_repo path
    +  Gitlab.config.ssh_path + "#{path}.git"
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/Gitlab/Gitolite/AccessDenied.html b/doc/app/Gitlab/Gitolite/AccessDenied.html new file mode 100644 index 00000000..64a3b17a --- /dev/null +++ b/doc/app/Gitlab/Gitolite/AccessDenied.html @@ -0,0 +1,439 @@ + + + + + + +class Gitlab::Gitolite::AccessDenied - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Gitlab::Gitolite::AccessDenied

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    + +
    + + + + diff --git a/doc/app/Gitlab/GitoliteConfig.html b/doc/app/Gitlab/GitoliteConfig.html new file mode 100644 index 00000000..19671089 --- /dev/null +++ b/doc/app/Gitlab/GitoliteConfig.html @@ -0,0 +1,994 @@ + + + + + + +class Gitlab::GitoliteConfig - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Gitlab::GitoliteConfig

    + +
    + +
    + + + + +
    + + + + + + + + +
    +

    Attributes

    + + +
    +
    + conf[R] +
    + +
    + + + +
    +
    + +
    +
    + config_tmp_dir[R] +
    + +
    + + + +
    +
    + +
    +
    + ga_repo[R] +
    + +
    + + + +
    +
    + +
    + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + admin_all_repo() + click to toggle source +
    + + +
    + +

    Enable access to all repos for gitolite admin. We use it for accept merge +request feature

    + + + +
    +
    # File lib/gitlab/backend/gitolite_config.rb, line 162
    +def admin_all_repo
    +  owner_name = Gitlab.config.gitolite_admin_key
    +
    +  # @ALL repos premission for gitolite owner
    +  repo_name = "@all"
    +  repo = if conf.has_repo?(repo_name)
    +           conf.get_repo(repo_name)
    +         else
    +           ::Gitolite::Config::Repo.new(repo_name)
    +         end
    +
    +  repo.add_permission("RW+", "", owner_name)
    +  conf.add_repo(repo, true)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + admin_all_repo!() + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/gitlab/backend/gitolite_config.rb, line 177
    +def admin_all_repo!
    +  apply { |config| config.admin_all_repo }
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + apply() { |self| ... } + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/gitlab/backend/gitolite_config.rb, line 20
    +def apply
    +  Timeout::timeout(30) do
    +    File.open(Rails.root.join('tmp', "gitlabhq-gitolite.lock"), "w+") do |f|
    +      begin
    +        # Set exclusive lock
    +        # to prevent race condition
    +        f.flock(File::LOCK_EX)
    +
    +        # Pull gitolite-admin repo
    +        # in tmp dir before do any changes
    +        pull(config_tmp_dir)
    +
    +        # Build ga_repo object and @conf
    +        # to access gitolite-admin configuration
    +        @conf = ga_repo.config
    +
    +        # Do any changes
    +        # in gitolite-admin
    +        # config here
    +        yield(self)
    +
    +        # Save changes in
    +        # gitolite-admin repo
    +        # before push it
    +        ga_repo.save
    +
    +        # Push gitolite-admin repo
    +        # to apply all changes
    +        push(config_tmp_dir)
    +      ensure
    +        # Remove tmp dir
    +        # removing the gitolite folder first is important to avoid
    +        # NFS issues.
    +        FileUtils.rm_rf(File.join(config_tmp_dir, 'gitolite'))
    +
    +        # Remove parent tmp dir
    +        FileUtils.rm_rf(config_tmp_dir)
    +
    +        # Unlock so other task can access
    +        # gitolite configuration
    +        f.flock(File::LOCK_UN)
    +      end
    +    end
    +  end
    +rescue PullError => ex
    +  log("Pull error ->  " + ex.message)
    +  raise Gitolite::AccessDenied, ex.message
    +
    +rescue PushError => ex
    +  log("Push error ->  " + " " + ex.message)
    +  raise Gitolite::AccessDenied, ex.message
    +
    +rescue Exception => ex
    +  log(ex.class.name + " " + ex.message)
    +  raise Gitolite::AccessDenied.new("gitolite timeout")
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + destroy_project(project) + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/gitlab/backend/gitolite_config.rb, line 81
    +def destroy_project(project)
    +  FileUtils.rm_rf(project.path_to_repo)
    +  conf.rm_repo(project.path)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + destroy_project!(project) + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/gitlab/backend/gitolite_config.rb, line 86
    +def destroy_project!(project)
    +  apply do |config|
    +    config.destroy_project(project)
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + log(message) + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/gitlab/backend/gitolite_config.rb, line 77
    +def log message
    +  Gitlab::GitLogger.error(message)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + rm_key(user) + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/gitlab/backend/gitolite_config.rb, line 98
    +def rm_key(user)
    +  key_path = File.join(config_tmp_dir, 'gitolite/keydir', "#{user}.pub")
    +  ga_key = ::Gitolite::SSHKey.from_file(key_path)
    +  ga_repo.rm_key(ga_key)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + update_project(repo_name, project) + click to toggle source +
    + + +
    + +

    update or create

    + + + +
    +
    # File lib/gitlab/backend/gitolite_config.rb, line 105
    +def update_project(repo_name, project)
    +  repo = update_project_config(project, conf)
    +  conf.add_repo(repo, true)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + update_project!(repo_name, project) + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/gitlab/backend/gitolite_config.rb, line 110
    +def update_project!(repo_name, project)
    +  apply do |config|
    +    config.update_project(repo_name, project)
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + update_project_config(project, conf) + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/gitlab/backend/gitolite_config.rb, line 125
    +def update_project_config(project, conf)
    +  repo_name = project.path
    +
    +  repo = if conf.has_repo?(repo_name)
    +           conf.get_repo(repo_name)
    +         else
    +           ::Gitolite::Config::Repo.new(repo_name)
    +         end
    +
    +  name_readers = project.repository_readers
    +  name_writers = project.repository_writers
    +  name_masters = project.repository_masters
    +
    +  pr_br = project.protected_branches.map(&:name).join("$ ")
    +
    +  repo.clean_permissions
    +
    +  # Deny access to protected branches for writers
    +  unless name_writers.blank? || pr_br.blank?
    +    repo.add_permission("-", pr_br.strip + "$ ", name_writers)
    +  end
    +
    +  # Add read permissions
    +  repo.add_permission("R", "", name_readers) unless name_readers.blank?
    +
    +  # Add write permissions
    +  repo.add_permission("RW+", "", name_writers) unless name_writers.blank?
    +  repo.add_permission("RW+", "", name_masters) unless name_masters.blank?
    +
    +  # Add sharedRepository config
    +  repo.set_git_config("core.sharedRepository", "0660")
    +
    +  repo
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + update_projects(projects) + click to toggle source +
    + + +
    + +

    Updates many projects and uses project.path as the repo path An order of +magnitude faster than #update_project

    + + + +
    +
    # File lib/gitlab/backend/gitolite_config.rb, line 118
    +def update_projects(projects)
    +  projects.each do |project|
    +    repo = update_project_config(project, conf)
    +    conf.add_repo(repo, true)
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + write_key(id, key) + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/gitlab/backend/gitolite_config.rb, line 92
    +def write_key(id, key)
    +  File.open(File.join(config_tmp_dir, 'gitolite/keydir',"#{id}.pub"), 'w') do |f|
    +    f.write(key.gsub(%r\n/,''))
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/Gitlab/GitoliteConfig/PullError.html b/doc/app/Gitlab/GitoliteConfig/PullError.html new file mode 100644 index 00000000..8e5a1510 --- /dev/null +++ b/doc/app/Gitlab/GitoliteConfig/PullError.html @@ -0,0 +1,439 @@ + + + + + + +class Gitlab::GitoliteConfig::PullError - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Gitlab::GitoliteConfig::PullError

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    + +
    + + + + diff --git a/doc/app/Gitlab/GitoliteConfig/PushError.html b/doc/app/Gitlab/GitoliteConfig/PushError.html new file mode 100644 index 00000000..c1541f49 --- /dev/null +++ b/doc/app/Gitlab/GitoliteConfig/PushError.html @@ -0,0 +1,439 @@ + + + + + + +class Gitlab::GitoliteConfig::PushError - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Gitlab::GitoliteConfig::PushError

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    + +
    + + + + diff --git a/doc/app/Gitlab/GraphCommit.html b/doc/app/Gitlab/GraphCommit.html new file mode 100644 index 00000000..b8ffa6f4 --- /dev/null +++ b/doc/app/Gitlab/GraphCommit.html @@ -0,0 +1,967 @@ + + + + + + +class Gitlab::GraphCommit - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Gitlab::GraphCommit

    + +
    + +
    + + + + +
    + + + + + + + + +
    +

    Attributes

    + + +
    +
    + refs[RW] +
    + +
    + + + +
    +
    + +
    +
    + space[RW] +
    + +
    + + + +
    +
    + +
    +
    + time[RW] +
    + +
    + + + +
    +
    + +
    + + + + +
    +

    Public Class Methods

    + + +
    + +
    + find_free_space(time_range) + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/gitlab/graph_commit.rb, line 113
    +def self.find_free_space(time_range)
    +  reserved = []
    +  for day in time_range
    +      reserved += @_reserved[day]
    +  end
    +  space = 1
    +  while reserved.include? space do
    +    space += 1
    +  end
    +  space
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + index_commits(commits) + click to toggle source +
    + + +
    + +

    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<GraphCommit>] comits to index

    + +

    @return [Array<TimeDate>] list of commit dates corelated with time on +commits

    + + + +
    +
    # File lib/gitlab/graph_commit.rb, line 33
    +def self.index_commits(commits)
    +  days, heads = [], []
    +  map = {}
    +
    +  commits.reverse.each_with_index do |c,i|
    +    c.time = i
    +    days[i] = c.committed_date
    +    map[c.id] = c
    +    heads += c.refs unless c.refs.nil?
    +  end
    +
    +  heads.select!{|h| h.is_a? Grit::Head or h.is_a? Grit::Remote}
    +  # sort heads so the master is top and current branches are closer
    +  heads.sort! do |a,b|
    +    if a.name == "master"
    +      -1
    +    elsif b.name == "master"
    +      1
    +    else
    +      b.commit.committed_date <=> a.commit.committed_date
    +    end
    +  end
    +
    +  @_reserved = {}
    +  days.each_index do |i|
    +    @_reserved[i] = []
    +  end
    +
    +  heads.each do |h|
    +    if map.include? h.commit.id then
    +      place_chain(map[h.commit.id], map)
    +    end
    +  end
    +  days
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + mark_reserved(time_range, space) + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/gitlab/graph_commit.rb, line 107
    +def self.mark_reserved(time_range, space)
    +  for day in time_range
    +    @_reserved[day].push(space)
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + new(commit) + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/gitlab/graph_commit.rb, line 148
    +def initialize(commit)
    +  @_commit = commit
    +  @time = -1
    +  @space = 0
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + place_chain(commit, map, parent_time = nil) + click to toggle source +
    + + +
    + +

    Add space mark on commit and its parents

    + +

    @param [GraphCommit] the commit object. @param +[Hash<String,GraphCommit>] map of commits

    + + + +
    +
    # File lib/gitlab/graph_commit.rb, line 73
    +def self.place_chain(commit, map, parent_time = nil)
    +  leaves = take_left_leaves(commit, map)
    +  if leaves.empty? then
    +    return
    +  end
    +  space = find_free_space(leaves.last.time..leaves.first.time)
    +  leaves.each{|l| l.space = space}
    +  # and mark it as reserved
    +  min_time = leaves.last.time
    +  parents = leaves.last.parents.collect
    +  parents.each do |p|
    +    if map.include? p.id then
    +      parent = map[p.id]
    +      if parent.time < min_time then
    +        min_time = parent.time
    +      end
    +    end
    +  end
    +  if parent_time.nil? then
    +    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 == 0}
    +    for p in parents
    +      place_chain(map[p.id], map, l.time)
    +    end
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + take_left_leaves(commit, map) + click to toggle source +
    + + +
    + +

    Takes most left subtree branch of commits which don’t have space mark yet.

    + +

    @param [GraphCommit] the commit object. @param +[Hash<String,GraphCommit>] map of commits

    + +

    @return [Array<GraphCommit>] list of branch commits

    + + + +
    +
    # File lib/gitlab/graph_commit.rb, line 132
    +def self.take_left_leaves(commit, map)
    +  leaves = []
    +  leaves.push(commit)  if commit.space == 0
    +  while true
    +    parent = commit.parents.collect
    +      .select{|p| map.include? p.id and map[p.id].space == 0}
    +    if parent.count == 0 then
    +      return leaves
    +    else
    +      commit = map[parent.first.id]
    +      leaves.push(commit)
    +    end
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + to_graph(project) + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/gitlab/graph_commit.rb, line 10
    +def self.to_graph(project)
    +  @repo = project.repo
    +  commits = Grit::Commit.find_all(@repo, nil, {max_count: 650})
    +
    +  ref_cache = {}
    +
    +  commits.map! {|c| GraphCommit.new(Commit.new(c))}
    +  commits.each { |commit| commit.add_refs(ref_cache, @repo) }
    +
    +  days = GraphCommit.index_commits(commits)
    +  @days_json = days.compact.collect{|d| [d.day, d.strftime("%b")] }.to_json
    +  @commits_json = commits.map(&:to_graph_hash).to_json
    +
    +  return @days_json, @commits_json
    +end
    +
    + +
    + + + + +
    + + +
    + +
    +

    Public Instance Methods

    + + +
    + +
    + add_refs(ref_cache, repo) + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/gitlab/graph_commit.rb, line 174
    +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
    +
    + +
    + + + + +
    + + +
    + +
    + method_missing(m, *args, &block) + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/gitlab/graph_commit.rb, line 154
    +def method_missing(m, *args, &block)
    +  @_commit.send(m, *args, &block)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + to_graph_hash() + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/gitlab/graph_commit.rb, line 158
    +def to_graph_hash
    +  h = {}
    +  h[:parents] = self.parents.collect do |p|
    +    [p.id,0,0]
    +  end
    +  h[:author]  = Gitlab::Encode.utf8(author.name)
    +  h[:time]    = time
    +  h[:space]   = space
    +  h[:refs]    = refs.collect{|r|r.name}.join(" ") unless refs.nil?
    +  h[:id]      = sha
    +  h[:date]    = date
    +  h[:message] = escape_once(Gitlab::Encode.utf8(message))
    +  h[:login]   = author.email
    +  h
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/Gitlab/InlineDiff.html b/doc/app/Gitlab/InlineDiff.html new file mode 100644 index 00000000..91eeae9c --- /dev/null +++ b/doc/app/Gitlab/InlineDiff.html @@ -0,0 +1,611 @@ + + + + + + +class Gitlab::InlineDiff - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Gitlab::InlineDiff

    + +
    + +
    + + + + +
    + + + + + + +
    +

    Constants

    +
    + +
    FINISH + +
    + + +
    START + +
    + + +
    +
    + + + + + + +
    +

    Public Class Methods

    + + +
    + +
    + _indexes_of_changed_lines(diff_arr) + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/gitlab/inline_diff.rb, line 42
    +def _indexes_of_changed_lines diff_arr
    +  chain_of_first_symbols = ""
    +  diff_arr.each_with_index do |line, i|
    +    chain_of_first_symbols += line[0]
    +  end
    +  chain_of_first_symbols.gsub!(%r[^\-\+]/, "#")
    +
    +  offset = 0
    +  indexes = []
    +  while index = chain_of_first_symbols.index("#-+#", offset)
    +    indexes << index
    +    offset = index + 1
    +  end
    +  indexes
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + processing(diff_arr) + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/gitlab/inline_diff.rb, line 8
    +def processing diff_arr
    +  indexes = _indexes_of_changed_lines diff_arr
    +
    +  indexes.each do |index|
    +    first_line = diff_arr[index+1]
    +    second_line = diff_arr[index+2]
    +    max_length = [first_line.size, second_line.size].max
    +
    +    first_the_same_symbols = 0
    +    (0..max_length + 1).each do |i|
    +      first_the_same_symbols = i - 1
    +      if first_line[i] != second_line[i] && i > 0
    +        break
    +      end
    +    end
    +    first_token = first_line[0..first_the_same_symbols][1..-1]
    +    diff_arr[index+1].sub!(first_token, first_token + START)
    +    diff_arr[index+2].sub!(first_token, first_token + START)
    +    last_the_same_symbols = 0
    +    (1..max_length + 1).each do |i|
    +      last_the_same_symbols = -i
    +      shortest_line = second_line.size > first_line.size ? first_line : second_line
    +      if ( first_line[-i] != second_line[-i] ) || "#{first_token}#{START}".size == shortest_line[1..-i].size
    +        break
    +      end
    +    end
    +    last_the_same_symbols += 1
    +    last_token = first_line[last_the_same_symbols..-1]
    +    diff_arr[index+1].sub!(%r#{Regexp.escape(last_token)}$/, FINISH + last_token)
    +    diff_arr[index+2].sub!(%r#{Regexp.escape(last_token)}$/, FINISH + last_token)
    +  end
    +  diff_arr
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + replace_markers(line) + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/gitlab/inline_diff.rb, line 58
    +def replace_markers line
    +  line.gsub!(START, "<span class='idiff'>")
    +  line.gsub!(FINISH, "</span>")
    +  line
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/Gitlab/Issues.html b/doc/app/Gitlab/Issues.html new file mode 100644 index 00000000..eda99ed1 --- /dev/null +++ b/doc/app/Gitlab/Issues.html @@ -0,0 +1,441 @@ + + + + + + +class Gitlab::Issues - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Gitlab::Issues

    + +
    + +

    Issues API

    + +
    + + + + +
    + + + + + + + + + + +
    + +
    + + + + diff --git a/doc/app/Gitlab/Logger.html b/doc/app/Gitlab/Logger.html new file mode 100644 index 00000000..8cc6cdea --- /dev/null +++ b/doc/app/Gitlab/Logger.html @@ -0,0 +1,583 @@ + + + + + + +class Gitlab::Logger - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Gitlab::Logger

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Class Methods

    + + +
    + +
    + build() + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/gitlab/logger.rb, line 17
    +def self.build
    +  new(Rails.root.join("log", file_name))
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + error(message) + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/gitlab/logger.rb, line 3
    +def self.error(message)
    +  build.error(message)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + info(message) + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/gitlab/logger.rb, line 7
    +def self.info(message)
    +  build.info(message)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + read_latest() + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/gitlab/logger.rb, line 11
    +def self.read_latest
    +  path = Rails.root.join("log", file_name)
    +  self.build unless File.exist?(path)
    +  logs = File.read(path).split("\n")
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/Gitlab/Markdown.html b/doc/app/Gitlab/Markdown.html new file mode 100644 index 00000000..12691455 --- /dev/null +++ b/doc/app/Gitlab/Markdown.html @@ -0,0 +1,580 @@ + + + + + + +module Gitlab::Markdown - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    module Gitlab::Markdown

    + +
    + +

    Custom parser for GitLab-flavored Markdown

    + +

    It replaces references in the text with links to the appropriate items in +GitLab.

    + +

    Supported reference formats are:

    + +
    * @foo for team members
    +* #123 for issues
    +* !123 for merge requests
    +* $123 for snippets
    +* 123456 for commits
    + +

    It also parses Emoji codes to insert images. See www.emoji-cheat-sheet.com/ for +a list of the supported icons.

    + +

    Examples

    + +
    >> gfm("Hey @david, can you fix this?")
    +=> "Hey <a href="/gitlab/team_members/1">@david</a>, can you fix this?"
    +
    +>> gfm("Commit 35d5f7c closes #1234")
    +=> "Commit <a href="/gitlab/commits/35d5f7c">35d5f7c</a> closes <a href="/gitlab/issues/1234">#1234</a>"
    +
    +>> gfm(":trollface:")
    +=> "<img alt=\":trollface:\" class=\"emoji\" src=\"/images/trollface.png" title=\":trollface:\" />
    +
    + +
    + + + + +
    + + + + + + +
    +

    Constants

    +
    + +
    EMOJI_PATTERN + +
    + + +
    REFERENCE_PATTERN + +
    + + +
    +
    + + + + +
    +

    Attributes

    + + +
    +
    + html_options[R] +
    + +
    + + + +
    +
    + +
    + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + gfm(text, html_options = {}) + click to toggle source +
    + + +
    + +

    Public: Parse the provided text with GitLab-Flavored Markdown

    + +

    text - the source text #html_options - extra +options for the reference links as given to link_to

    + +

    Note: reference links will only be generated if @project is set

    + + + +
    +
    # File lib/gitlab/markdown.rb, line 48
    +def gfm(text, html_options = {})
    +  return text if text.nil?
    +
    +  # Duplicate the string so we don't alter the original, then call to_str
    +  # to cast it back to a String instead of a SafeBuffer. This is required
    +  # for gsub calls to work as we need them to.
    +  text = text.dup.to_str
    +
    +  @html_options = html_options
    +
    +  # Extract pre blocks so they are not altered
    +  # from http://github.github.com/github-flavored-markdown/
    +  extractions = {}
    +  text.gsub!(%r{<pre>.*?</pre>|<code>.*?</code>}) do |match|
    +    md5 = Digest::MD5.hexdigest(match)
    +    extractions[md5] = match
    +    "{gfm-extraction-#{md5}}"
    +  end
    +
    +  # TODO: add popups with additional information
    +
    +  text = parse(text)
    +
    +  # Insert pre block extractions
    +  text.gsub!(%r\{gfm-extraction-(\h{32})\}/) do
    +    extractions[$1]
    +  end
    +
    +  sanitize text.html_safe, attributes: ActionView::Base.sanitized_allowed_attributes + %w(id class)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/Gitlab/Merge.html b/doc/app/Gitlab/Merge.html new file mode 100644 index 00000000..99a66cd5 --- /dev/null +++ b/doc/app/Gitlab/Merge.html @@ -0,0 +1,628 @@ + + + + + + +class Gitlab::Merge - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Gitlab::Merge

    + +
    + +
    + + + + +
    + + + + + + + + +
    +

    Attributes

    + + +
    +
    + merge_request[RW] +
    + +
    + + + +
    +
    + +
    +
    + project[RW] +
    + +
    + + + +
    +
    + +
    +
    + user[RW] +
    + +
    + + + +
    +
    + +
    + + + + +
    +

    Public Class Methods

    + + +
    + +
    + new(merge_request, user) + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/gitlab/merge.rb, line 5
    +def initialize(merge_request, user)
    +  @merge_request = merge_request
    +  @project = merge_request.project
    +  @user = user
    +end
    +
    + +
    + + + + +
    + + +
    + +
    +

    Public Instance Methods

    + + +
    + +
    + can_be_merged?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/gitlab/merge.rb, line 11
    +def can_be_merged?
    +  in_locked_and_timed_satellite do |merge_repo|
    +    merge_in_satellite!(merge_repo)
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + merge!() + click to toggle source +
    + + +
    + +

    Merges the source branch into the target branch in the satellite and pushes +it back to Gitolite. It also removes the source +branch if requested in the merge request.

    + +

    Returns false if the merge produced conflicts Returns false if pushing from +the satallite to Gitolite failed or was +rejected Returns true otherwise

    + + + +
    +
    # File lib/gitlab/merge.rb, line 24
    +def merge!
    +  in_locked_and_timed_satellite do |merge_repo|
    +    if merge_in_satellite!(merge_repo)
    +      # push merge back to Gitolite
    +      # will raise CommandFailed when push fails
    +      merge_repo.git.push({raise: true}, :origin, merge_request.target_branch)
    +
    +      # remove source branch
    +      if merge_request.should_remove_source_branch && !project.root_ref?(merge_request.source_branch)
    +        # will raise CommandFailed when push fails
    +        merge_repo.git.push({raise: true}, :origin, ":#{merge_request.source_branch}")
    +      end
    +
    +      # merge, push and branch removal successful
    +      true
    +    end
    +  end
    +rescue Grit::Git::CommandFailed
    +  false
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/Gitlab/Milestones.html b/doc/app/Gitlab/Milestones.html new file mode 100644 index 00000000..aac605b2 --- /dev/null +++ b/doc/app/Gitlab/Milestones.html @@ -0,0 +1,441 @@ + + + + + + +class Gitlab::Milestones - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Gitlab::Milestones

    + +
    + +

    Milestones API

    + +
    + + + + +
    + + + + + + + + + + +
    + +
    + + + + diff --git a/doc/app/Gitlab/Projects.html b/doc/app/Gitlab/Projects.html new file mode 100644 index 00000000..a44cba3c --- /dev/null +++ b/doc/app/Gitlab/Projects.html @@ -0,0 +1,441 @@ + + + + + + +class Gitlab::Projects - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Gitlab::Projects

    + +
    + +

    Projects API

    + +
    + + + + +
    + + + + + + + + + + +
    + +
    + + + + diff --git a/doc/app/Gitlab/Satellite.html b/doc/app/Gitlab/Satellite.html new file mode 100644 index 00000000..4615d1c6 --- /dev/null +++ b/doc/app/Gitlab/Satellite.html @@ -0,0 +1,664 @@ + + + + + + +class Gitlab::Satellite - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Gitlab::Satellite

    + +
    + +
    + + + + +
    + + + + + + +
    +

    Constants

    +
    + +
    PARKING_BRANCH + +
    + + +
    +
    + + + + +
    +

    Attributes

    + + +
    +
    + project[RW] +
    + +
    + + + +
    +
    + +
    + + + + +
    +

    Public Class Methods

    + + +
    + +
    + new(project) + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/gitlab/satellite.rb, line 8
    +def initialize project
    +  self.project = project
    +end
    +
    + +
    + + + + +
    + + +
    + +
    +

    Public Instance Methods

    + + +
    + +
    + clear() + click to toggle source +
    + + +
    + +

    will be deleted all branches except PARKING_BRANCH

    + + + +
    +
    # File lib/gitlab/satellite.rb, line 25
    +def clear
    +  Dir.chdir(path) do
    +    heads = Grit::Repo.new(".").heads.map{|head| head.name}
    +    if heads.include? PARKING_BRANCH
    +      %xgit checkout #{PARKING_BRANCH}`
    +    else
    +      %xgit checkout -b #{PARKING_BRANCH}`
    +    end
    +    heads.delete(PARKING_BRANCH)
    +    heads.each do |head|
    +      %xgit branch -D #{head}`
    +    end
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + create() + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/gitlab/satellite.rb, line 12
    +def create
    +  %xgit clone #{project.url_to_repo} #{path}`
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + exists?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/gitlab/satellite.rb, line 20
    +def exists?
    +  File.exists? path
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + path() + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/gitlab/satellite.rb, line 16
    +def path
    +  Rails.root.join("tmp", "repo_satellites", project.path)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/Gitlab/Session.html b/doc/app/Gitlab/Session.html new file mode 100644 index 00000000..12f3433e --- /dev/null +++ b/doc/app/Gitlab/Session.html @@ -0,0 +1,441 @@ + + + + + + +class Gitlab::Session - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Gitlab::Session

    + +
    + +

    Users API

    + +
    + + + + +
    + + + + + + + + + + +
    + +
    + + + + diff --git a/doc/app/Gitlab/Theme.html b/doc/app/Gitlab/Theme.html new file mode 100644 index 00000000..00b3e5f6 --- /dev/null +++ b/doc/app/Gitlab/Theme.html @@ -0,0 +1,493 @@ + + + + + + +class Gitlab::Theme - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Gitlab::Theme

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Class Methods

    + + +
    + +
    + css_class_by_id(id) + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/gitlab/theme.rb, line 3
    +def self.css_class_by_id(id)
    +  themes = { 
    +    1 => "ui_basic",
    +    2 => "ui_mars",
    +    3 => "ui_modern"
    +  }
    +
    +  id ||= 1
    +
    +  return themes[id]
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/Gitlab/Users.html b/doc/app/Gitlab/Users.html new file mode 100644 index 00000000..c9712874 --- /dev/null +++ b/doc/app/Gitlab/Users.html @@ -0,0 +1,441 @@ + + + + + + +class Gitlab::Users - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Gitlab::Users

    + +
    + +

    Users API

    + +
    + + + + +
    + + + + + + + + + + +
    + +
    + + + + diff --git a/doc/app/GitlabMarkdownHelper.html b/doc/app/GitlabMarkdownHelper.html new file mode 100644 index 00000000..6bf172ac --- /dev/null +++ b/doc/app/GitlabMarkdownHelper.html @@ -0,0 +1,559 @@ + + + + + + +module GitlabMarkdownHelper - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    module GitlabMarkdownHelper

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + + + + +
    + +
    + markdown(text) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/helpers/gitlab_markdown_helper.rb, line 25
    +def markdown(text)
    +  unless @markdown
    +    gitlab_renderer = Redcarpet::Render::GitlabHTML.new(self,
    +                        # see https://github.com/vmg/redcarpet#darling-i-packed-you-a-couple-renderers-for-lunch-
    +                        filter_html: true,
    +                        with_toc_data: true,
    +                        hard_wrap: true)
    +    @markdown = Redcarpet::Markdown.new(gitlab_renderer,
    +                    # see https://github.com/vmg/redcarpet#and-its-like-really-simple-to-use
    +                    no_intra_emphasis: true,
    +                    tables: true,
    +                    fenced_code_blocks: true,
    +                    autolink: true,
    +                    strikethrough: true,
    +                    lax_html_blocks: true,
    +                    space_after_headers: true,
    +                    superscript: true)
    +  end
    +
    +  @markdown.render(text).html_safe
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/Grack.html b/doc/app/Grack.html new file mode 100644 index 00000000..e3c5bbd4 --- /dev/null +++ b/doc/app/Grack.html @@ -0,0 +1,433 @@ + + + + + + +module Grack - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    module Grack

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    + +
    + + + + diff --git a/doc/app/Grack/Auth.html b/doc/app/Grack/Auth.html new file mode 100644 index 00000000..5da483f7 --- /dev/null +++ b/doc/app/Grack/Auth.html @@ -0,0 +1,734 @@ + + + + + + +class Grack::Auth - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Grack::Auth

    + +
    + +
    + + + + +
    + + + + + + + + +
    +

    Attributes

    + + +
    +
    + project[RW] +
    + +
    + + + +
    +
    + +
    +
    + user[RW] +
    + +
    + + + +
    +
    + +
    + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + can?(object, action, subject) + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/gitlab/backend/grack_auth.rb, line 56
    +def can?(object, action, subject)
    +  abilities.allowed?(object, action, subject)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + current_ref() + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/gitlab/backend/grack_auth.rb, line 60
    +def current_ref
    +  if @env["HTTP_CONTENT_ENCODING"] =~ %rgzip/
    +    input = Zlib::GzipReader.new(@request.body).read
    +  else
    +    input = @request.body.read
    +  end
    +  # Need to reset seek point
    +  @request.body.rewind
    +  %rrefs\/heads\/([\w-]+)/.match(input).to_a.first
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + valid?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/gitlab/backend/grack_auth.rb, line 5
    +def valid?
    +  # Authentication with username and password
    +  email, password = @auth.credentials
    +  self.user = User.find_by_email(email)
    +  return false unless user.try(:valid_password?, password)
    +
    +  # Set GL_USER env variable
    +  ENV['GL_USER'] = email
    +  # Pass Gitolite update hook
    +  ENV['GL_BYPASS_UPDATE_HOOK'] = "true"
    +
    +  # Need this patch due to the rails mount
    +  @env['PATH_INFO'] = @request.path
    +  @env['SCRIPT_NAME'] = ""
    +
    +  # Find project by PATH_INFO from env
    +  if m = %r^\/([\w-]+).git/.match(@request.path_info).to_a
    +    self.project = Project.find_by_path(m.last)
    +    return false unless project
    +  end
    +
    +  # Git upload and receive
    +  if @request.get?
    +    validate_get_request
    +  elsif @request.post?
    +    validate_post_request
    +  else
    +    false
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + validate_get_request() + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/gitlab/backend/grack_auth.rb, line 36
    +def validate_get_request
    +  true
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + validate_post_request() + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/gitlab/backend/grack_auth.rb, line 40
    +def validate_post_request
    +  if @request.path_info.end_with?('git-upload-pack')
    +    can?(user, :push_code, project)
    +  elsif @request.path_info.end_with?('git-receive-pack')
    +    action = if project.protected_branch?(current_ref)
    +               :push_code_to_protected_branches
    +             else
    +               :push_code
    +             end
    +
    +    can?(user, action, project)
    +  else
    +    false
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    +

    Protected Instance Methods

    + + +
    + +
    + abilities() + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/gitlab/backend/grack_auth.rb, line 73
    +def abilities
    +  @abilities ||= begin
    +                   abilities = Six.new
    +                   abilities << Ability
    +                   abilities
    +                 end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/Group.html b/doc/app/Group.html new file mode 100644 index 00000000..a23e1da6 --- /dev/null +++ b/doc/app/Group.html @@ -0,0 +1,555 @@ + + + + + + +class Group - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Group

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Class Methods

    + + + + + +
    + +
    +

    Public Instance Methods

    + + +
    + +
    + to_param() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/group.rb, line 17
    +def to_param
    +  code
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + users() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/group.rb, line 21
    +def users
    +  User.joins(:users_projects).where(users_projects: {project_id: project_ids}).uniq
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/GroupsController.html b/doc/app/GroupsController.html new file mode 100644 index 00000000..72e1022c --- /dev/null +++ b/doc/app/GroupsController.html @@ -0,0 +1,735 @@ + + + + + + +class GroupsController - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class GroupsController

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + issues() + click to toggle source +
    + + +
    + +

    Get only assigned issues

    + + + +
    +
    # File app/controllers/groups_controller.rb, line 26
    +def issues
    +  @user   = current_user
    +  @issues = current_user.assigned_issues.opened
    +  @issues = @issues.of_group(@group).recent.page(params[:page]).per(20)
    +  @issues = @issues.includes(:author, :project)
    +
    +  respond_to do |format|
    +    format.html
    +    format.atom { render layout: false }
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + merge_requests() + click to toggle source +
    + + +
    + +

    Get authored or assigned open merge requests

    + + + +
    +
    # File app/controllers/groups_controller.rb, line 20
    +def merge_requests
    +  @merge_requests = current_user.cared_merge_requests
    +  @merge_requests = @merge_requests.of_group(@group).recent.page(params[:page]).per(20)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + people() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/groups_controller.rb, line 46
    +def people
    +  @users = group.users.all
    +end
    +
    + +
    + + + + +
    + + + + + +
    + +
    + show() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/groups_controller.rb, line 8
    +def show
    +  @events = Event.in_projects(project_ids).limit(20).offset(params[:offset] || 0)
    +  @last_push = current_user.recent_push
    +
    +  respond_to do |format|
    +    format.html
    +    format.js
    +    format.atom { render layout: false }
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    +

    Protected Instance Methods

    + + +
    + +
    + group() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/groups_controller.rb, line 52
    +def group
    +  @group ||= Group.find_by_code(params[:id])
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + project_ids() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/groups_controller.rb, line 60
    +def project_ids
    +  projects.map(&:id)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + projects() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/groups_controller.rb, line 56
    +def projects
    +  @projects ||= current_user.projects_with_events.where(group_id: @group.id)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/HelpController.html b/doc/app/HelpController.html new file mode 100644 index 00000000..afbbeb0b --- /dev/null +++ b/doc/app/HelpController.html @@ -0,0 +1,484 @@ + + + + + + +class HelpController - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class HelpController

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + index() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/help_controller.rb, line 2
    +def index
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/HooksController.html b/doc/app/HooksController.html new file mode 100644 index 00000000..70fd89c3 --- /dev/null +++ b/doc/app/HooksController.html @@ -0,0 +1,595 @@ + + + + + + +class HooksController - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class HooksController

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + create() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/hooks_controller.rb, line 13
    +def create
    +  @hook = @project.hooks.new(params[:hook])
    +  @hook.save
    +
    +  if @hook.valid?
    +    redirect_to project_hooks_path(@project)
    +  else
    +    @hooks = @project.hooks.all
    +    render :index
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + destroy() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/hooks_controller.rb, line 31
    +def destroy
    +  @hook = @project.hooks.find(params[:id])
    +  @hook.destroy
    +
    +  redirect_to project_hooks_path(@project)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + index() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/hooks_controller.rb, line 8
    +def index
    +  @hooks = @project.hooks.all
    +  @hook = ProjectHook.new
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + test() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/hooks_controller.rb, line 25
    +def test
    +  TestHookContext.new(project, current_user, params).execute
    +
    +  redirect_to :back
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/Issue.html b/doc/app/Issue.html new file mode 100644 index 00000000..e8a7881c --- /dev/null +++ b/doc/app/Issue.html @@ -0,0 +1,501 @@ + + + + + + +class Issue - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Issue

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Class Methods

    + + +
    + +
    + open_for(user) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/issue.rb, line 14
    +def self.open_for(user)
    +  opened.assigned(user)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/IssueCommonality.html b/doc/app/IssueCommonality.html new file mode 100644 index 00000000..336580d8 --- /dev/null +++ b/doc/app/IssueCommonality.html @@ -0,0 +1,641 @@ + + + + + + +module IssueCommonality - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    module IssueCommonality

    + +
    + +

    Contains common functionality shared between Issues and MergeRequests

    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + is_assigned?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/issue_commonality.rb, line 50
    +def is_assigned?
    +  !!assignee_id
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + is_being_closed?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/issue_commonality.rb, line 58
    +def is_being_closed?
    +  closed_changed? && closed
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + is_being_reassigned?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/issue_commonality.rb, line 54
    +def is_being_reassigned?
    +  assignee_id_changed?
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + is_being_reopened?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/issue_commonality.rb, line 62
    +def is_being_reopened?
    +  closed_changed? && !closed
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + new?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/issue_commonality.rb, line 46
    +def new?
    +  today? && created_at == updated_at
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + today?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/issue_commonality.rb, line 42
    +def today?
    +  Date.today == created_at.to_date
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/IssueCommonality/ClassMethods.html b/doc/app/IssueCommonality/ClassMethods.html new file mode 100644 index 00000000..d4ab179d --- /dev/null +++ b/doc/app/IssueCommonality/ClassMethods.html @@ -0,0 +1,479 @@ + + + + + + +module IssueCommonality::ClassMethods - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    module IssueCommonality::ClassMethods

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + + + + +
    + +
    + +
    + + + + diff --git a/doc/app/IssueObserver.html b/doc/app/IssueObserver.html new file mode 100644 index 00000000..7f16a865 --- /dev/null +++ b/doc/app/IssueObserver.html @@ -0,0 +1,571 @@ + + + + + + +class IssueObserver - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class IssueObserver

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + after_create(issue) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/observers/issue_observer.rb, line 4
    +def after_create(issue)
    +  if issue.assignee && issue.assignee != current_user
    +    Notify.new_issue_email(issue.id).deliver 
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + after_update(issue) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/observers/issue_observer.rb, line 10
    +def after_update(issue)
    +  send_reassigned_email(issue) if issue.is_being_reassigned?
    +
    +  status = nil
    +  status = 'closed' if issue.is_being_closed?
    +  status = 'reopened' if issue.is_being_reopened?
    +  if status
    +    Note.create_status_change_note(issue, current_user, status) 
    +    [issue.author, issue.assignee].compact.each do |recipient| 
    +      Notify.issue_status_changed_email(recipient.id, issue.id, status, current_user)
    +    end
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    +

    Protected Instance Methods

    + + +
    + +
    + send_reassigned_email(issue) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/observers/issue_observer.rb, line 26
    +def send_reassigned_email(issue)
    +  recipient_ids = [issue.assignee_id, issue.assignee_id_was].keep_if {|id| id && id != current_user.id }
    +
    +  recipient_ids.each do |recipient_id|
    +    Notify.reassigned_issue_email(recipient_id, issue.id, issue.assignee_id_was).deliver
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/IssuesBulkUpdateContext.html b/doc/app/IssuesBulkUpdateContext.html new file mode 100644 index 00000000..1d87a757 --- /dev/null +++ b/doc/app/IssuesBulkUpdateContext.html @@ -0,0 +1,503 @@ + + + + + + +class IssuesBulkUpdateContext - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class IssuesBulkUpdateContext

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + execute() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/contexts/issues_bulk_update_context.rb, line 2
    +def execute
    +  update_data = params[:update]
    +
    +  issues_ids   = update_data[:issues_ids].split(",")
    +  milestone_id = update_data[:milestone_id]
    +  assignee_id  = update_data[:assignee_id]
    +  status       = update_data[:status]
    +
    +  opts = {} 
    +  opts[:milestone_id] = milestone_id if milestone_id.present?
    +  opts[:assignee_id] = assignee_id if assignee_id.present?
    +  opts[:closed] = (status == "closed") if status.present?
    +
    +  issues = Issue.where(id: issues_ids).all
    +  issues = issues.select { |issue| can?(current_user, :modify_issue, issue) }
    +  issues.each { |issue| issue.update_attributes(opts) }
    +  { 
    +    count: issues.count,
    +    success: !issues.count.zero?
    +  }
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/IssuesController.html b/doc/app/IssuesController.html new file mode 100644 index 00000000..6b3edbb6 --- /dev/null +++ b/doc/app/IssuesController.html @@ -0,0 +1,996 @@ + + + + + + +class IssuesController - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class IssuesController

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + bulk_update() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/issues_controller.rb, line 111
    +def bulk_update
    +  result = IssuesBulkUpdateContext.new(project, current_user, params).execute
    +  redirect_to :back, notice: "#{result[:count]} issues updated"
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + create() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/issues_controller.rb, line 48
    +def create
    +  @issue = @project.issues.new(params[:issue])
    +  @issue.author = current_user
    +  @issue.save
    +
    +  respond_to do |format|
    +    format.html do
    +      if @issue.valid?
    +        redirect_to project_issue_path(@project, @issue)
    +      else
    +        render :new
    +      end
    +    end
    +    format.js
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + destroy() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/issues_controller.rb, line 80
    +def destroy
    +  @issue.destroy
    +
    +  respond_to do |format|
    +    format.html { redirect_to project_issues_path }
    +    format.js { render nothing: true }
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + edit() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/issues_controller.rb, line 35
    +def edit
    +  respond_with(@issue)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + index() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/issues_controller.rb, line 19
    +def index
    +  @issues = issues_filtered
    +  @issues = @issues.page(params[:page]).per(20)
    +
    +  respond_to do |format|
    +    format.html # index.html.erb
    +    format.js
    +    format.atom { render layout: false }
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + new() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/issues_controller.rb, line 30
    +def new
    +  @issue = @project.issues.new(params[:issue])
    +  respond_with(@issue)
    +end
    +
    + +
    + + + + +
    + + + + + +
    + +
    + show() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/issues_controller.rb, line 39
    +def show
    +  @note = @project.notes.new(noteable: @issue)
    +
    +  respond_to do |format|
    +    format.html
    +    format.js
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + sort() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/issues_controller.rb, line 89
    +def sort
    +  return render_404 unless can?(current_user, :admin_issue, @project)
    +
    +  @issues = @project.issues.where(id: params['issue'])
    +  @issues.each do |issue|
    +    issue.position = params['issue'].index(issue.id.to_s) + 1
    +    issue.save
    +  end
    +
    +  render nothing: true
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + update() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/issues_controller.rb, line 65
    +def update
    +  @issue.update_attributes(params[:issue].merge(author_id_of_changes: current_user.id))
    +
    +  respond_to do |format|
    +    format.js
    +    format.html do
    +      if @issue.valid?
    +        redirect_to [@project, @issue]
    +      else
    +        render :edit
    +      end
    +    end
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    +

    Protected Instance Methods

    + + +
    + +
    + authorize_admin_issue!() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/issues_controller.rb, line 126
    +def authorize_admin_issue!
    +  return render_404 unless can?(current_user, :admin_issue, @issue)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + authorize_modify_issue!() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/issues_controller.rb, line 122
    +def authorize_modify_issue!
    +  return render_404 unless can?(current_user, :modify_issue, @issue)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + issue() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/issues_controller.rb, line 118
    +def issue
    +  @issue ||= @project.issues.find(params[:id])
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + issues_filtered() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/issues_controller.rb, line 134
    +def issues_filtered
    +  @issues = IssuesListContext.new(project, current_user, params).execute
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + module_enabled() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/issues_controller.rb, line 130
    +def module_enabled
    +  return render_404 unless @project.issues_enabled
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/IssuesHelper.html b/doc/app/IssuesHelper.html new file mode 100644 index 00000000..d58a5f7f --- /dev/null +++ b/doc/app/IssuesHelper.html @@ -0,0 +1,698 @@ + + + + + + +module IssuesHelper - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    module IssuesHelper

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + issue_css_classes(issue) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/helpers/issues_helper.rb, line 29
    +def issue_css_classes issue
    +  classes = "issue"
    +  classes << " closed" if issue.closed
    +  classes << " today" if issue.today?
    +  classes
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + issue_tags() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/helpers/issues_helper.rb, line 36
    +def issue_tags
    +  @project.issues.tag_counts_on(:labels).map(&:name)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + issues_filter() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/helpers/issues_helper.rb, line 47
    +def issues_filter
    +  {
    +    all: "all",
    +    closed: "closed",
    +    to_me: "assigned-to-me",
    +    open: "open"
    +  }
    +end
    +
    + +
    + + + + +
    + + + + + + + + +
    + +
    + project_issues_filter_path(project, params = {}) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/helpers/issues_helper.rb, line 2
    +def project_issues_filter_path project, params = {}
    +  params[:f] ||= cookies['issue_filter']
    +  project_issues_path project, params
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + unassigned_filter() + click to toggle source +
    + + +
    + +

    Returns an OpenStruct object suitable for use by +options_from_collection_for_select to allow filtering issues +by an unassigned User or Milestone

    + + + +
    +
    # File app/helpers/issues_helper.rb, line 42
    +def unassigned_filter
    +  # Milestone uses :title, Issue uses :name
    +  OpenStruct.new(id: 0, title: 'Unspecified', name: 'Unassigned')
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/IssuesListContext.html b/doc/app/IssuesListContext.html new file mode 100644 index 00000000..01acec45 --- /dev/null +++ b/doc/app/IssuesListContext.html @@ -0,0 +1,537 @@ + + + + + + +class IssuesListContext - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class IssuesListContext

    + +
    + +
    + + + + +
    + + + + + + + + +
    +

    Attributes

    + + +
    +
    + issues[RW] +
    + +
    + + + +
    +
    + +
    + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + execute() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/contexts/issues_list_context.rb, line 6
    +def execute
    +  @issues = case params[:f]
    +            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)
    +            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")
    +
    +  # Filter by specific assignee_id (or lack thereof)?
    +  if params[:assignee_id].present?
    +    @issues = @issues.where(assignee_id: (params[:assignee_id] == '0' ? nil : params[:assignee_id]))
    +  end
    +
    +  # Filter by specific milestone_id (or lack thereof)?
    +  if params[:milestone_id].present?
    +    @issues = @issues.where(milestone_id: (params[:milestone_id] == '0' ? nil : params[:milestone_id]))
    +  end
    +
    +  @issues
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/Key.html b/doc/app/Key.html new file mode 100644 index 00000000..afbcea93 --- /dev/null +++ b/doc/app/Key.html @@ -0,0 +1,700 @@ + + + + + + +class Key - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Key

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + fingerprintable_key() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/key.rb, line 30
    +def fingerprintable_key
    +  return true unless key # Don't test if there is no key.
    +  # `ssh-keygen -lf /dev/stdin <<< "#{key}"` errors with: redirection unexpected
    +  file = Tempfile.new('key_file')
    +  begin
    +    file.puts key
    +    file.rewind
    +    fingerprint_output = %xssh-keygen -lf #{file.path} 2>&1` # Catch stderr.
    +  ensure
    +    file.close
    +    file.unlink # deletes the temp file
    +  end
    +  errors.add(:key, "can't be fingerprinted") if fingerprint_output.match("failed")
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + is_deploy_key() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/key.rb, line 53
    +def is_deploy_key
    +  true if project_id
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + last_deploy?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/key.rb, line 66
    +def last_deploy?
    +  Key.where(identifier: identifier).count == 0
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + projects() + click to toggle source +
    + + +
    + +

    projects that has this key

    + + + +
    +
    # File app/models/key.rb, line 58
    +def projects
    +  if is_deploy_key
    +    [project]
    +  else
    +    user.projects
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + set_identifier() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/key.rb, line 45
    +def set_identifier
    +  if is_deploy_key
    +    self.identifier = "deploy_#{Digest::MD5.hexdigest(key)}"
    +  else
    +    self.identifier = "#{user.identifier}_#{Time.now.to_i}"
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + strip_white_space() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/key.rb, line 18
    +def strip_white_space
    +  self.key = self.key.strip unless self.key.blank?
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + unique_key() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/key.rb, line 22
    +def unique_key
    +  query = Key.where(key: key)
    +  query = query.where('(project_id IS NULL OR project_id = ?)', project_id) if project_id
    +  if (query.count > 0)
    +    errors.add :key, 'already exist.'
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/KeyObserver.html b/doc/app/KeyObserver.html new file mode 100644 index 00000000..bdaf9f84 --- /dev/null +++ b/doc/app/KeyObserver.html @@ -0,0 +1,530 @@ + + + + + + +class KeyObserver - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class KeyObserver

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + after_destroy(key) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/observers/key_observer.rb, line 8
    +def after_destroy(key)
    +  return if key.is_deploy_key && !key.last_deploy?
    +  git_host.remove_key(key.identifier, key.projects)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + after_save(key) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/observers/key_observer.rb, line 4
    +def after_save(key)
    +  git_host.set_key(key.identifier, key.key, key.projects)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/KeysController.html b/doc/app/KeysController.html new file mode 100644 index 00000000..13fac402 --- /dev/null +++ b/doc/app/KeysController.html @@ -0,0 +1,624 @@ + + + + + + +class KeysController - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class KeysController

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + create() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/keys_controller.rb, line 19
    +def create
    +  @key = current_user.keys.new(params[:key])
    +  @key.save
    +
    +  respond_with(@key)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + destroy() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/keys_controller.rb, line 26
    +def destroy
    +  @key = current_user.keys.find(params[:id])
    +  @key.destroy
    +
    +  respond_to do |format|
    +    format.html { redirect_to keys_url }
    +    format.js { render nothing: true }
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + index() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/keys_controller.rb, line 5
    +def index
    +  @keys = current_user.keys.all
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + new() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/keys_controller.rb, line 13
    +def new
    +  @key = current_user.keys.new
    +
    +  respond_with(@key)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + show() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/keys_controller.rb, line 9
    +def show
    +  @key = current_user.keys.find(params[:id])
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/LabelsController.html b/doc/app/LabelsController.html new file mode 100644 index 00000000..7dece840 --- /dev/null +++ b/doc/app/LabelsController.html @@ -0,0 +1,523 @@ + + + + + + +class LabelsController - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class LabelsController

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + index() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/labels_controller.rb, line 9
    +def index
    +  @labels = @project.issues_labels.order('count DESC')
    +end
    +
    + +
    + + + + +
    + + +
    + +
    +

    Protected Instance Methods

    + + +
    + +
    + module_enabled() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/labels_controller.rb, line 15
    +def module_enabled
    +  return render_404 unless @project.issues_enabled
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/MergeRequest.html b/doc/app/MergeRequest.html new file mode 100644 index 00000000..7dbe6f4f --- /dev/null +++ b/doc/app/MergeRequest.html @@ -0,0 +1,1478 @@ + + + + + + +class MergeRequest - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class MergeRequest

    + +
    + +
    + + + + +
    + + + + + + +
    +

    Constants

    +
    + +
    BROKEN_DIFF + +
    + + +
    CANNOT_BE_MERGED + +
    + + +
    CAN_BE_MERGED + +
    + + +
    UNCHECKED + +
    + + +
    +
    + + + + +
    +

    Attributes

    + + +
    +
    + should_remove_source_branch[RW] +
    + +
    + + + +
    +
    + +
    + + + + +
    +

    Public Class Methods

    + + +
    + +
    + find_all_by_branch(branch_name) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/merge_request.rb, line 25
    +def self.find_all_by_branch(branch_name)
    +  where("source_branch LIKE :branch OR target_branch LIKE :branch", branch: branch_name)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    +

    Public Instance Methods

    + + +
    + +
    + automerge!(current_user) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/merge_request.rb, line 169
    +def automerge!(current_user)
    +  if Gitlab::Merge.new(self, current_user).merge! && self.unmerged_commits.empty?
    +    self.merge!(current_user.id)
    +    true
    +  end
    +rescue
    +  self.mark_as_unmergable
    +  false
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + broken_diffs?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/merge_request.rb, line 86
    +def broken_diffs?
    +  diffs == [BROKEN_DIFF]
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + can_be_merged?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/merge_request.rb, line 58
    +def can_be_merged?
    +  state == CAN_BE_MERGED
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + check_if_can_be_merged() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/merge_request.rb, line 62
    +def check_if_can_be_merged
    +  self.state = if Gitlab::Merge.new(self, self.author).can_be_merged?
    +                 CAN_BE_MERGED
    +               else
    +                 CANNOT_BE_MERGED
    +               end
    +  self.save
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + closed_event() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/merge_request.rb, line 114
    +def closed_event
    +  self.project.events.where(target_id: self.id, target_type: "MergeRequest", action: Event::Closed).last
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + commits() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/merge_request.rb, line 118
    +def commits
    +  st_commits || []
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + diffs() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/merge_request.rb, line 71
    +def diffs
    +  st_diffs || []
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + human_state() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/merge_request.rb, line 29
    +def human_state
    +  states = {
    +    CAN_BE_MERGED =>  "can_be_merged",
    +    CANNOT_BE_MERGED => "cannot_be_merged",
    +    UNCHECKED => "unchecked"
    +  }
    +  states[self.state]
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + last_commit() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/merge_request.rb, line 102
    +def last_commit
    +  commits.first
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + mark_as_merged!() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/merge_request.rb, line 131
    +def mark_as_merged!
    +  self.merged = true
    +  self.closed = true
    +  save
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + mark_as_unchecked() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/merge_request.rb, line 53
    +def mark_as_unchecked
    +  self.state = UNCHECKED
    +  self.save
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + mark_as_unmergable() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/merge_request.rb, line 137
    +def mark_as_unmergable
    +  self.state = CANNOT_BE_MERGED
    +  self.save
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + merge!(user_id) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/merge_request.rb, line 158
    +def merge!(user_id)
    +  self.mark_as_merged!
    +  Event.create(
    +    project: self.project,
    +    action: Event::Merged,
    +    target_id: self.id,
    +    target_type: "MergeRequest",
    +    author_id: user_id
    +  )
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + merge_event() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/merge_request.rb, line 110
    +def merge_event
    +  self.project.events.where(target_id: self.id, target_type: "MergeRequest", action: Event::Merged).last
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + merged?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/merge_request.rb, line 106
    +def merged?
    +  merged && merge_event
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + mr_and_commit_notes() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/merge_request.rb, line 191
    +def mr_and_commit_notes
    +  commit_ids = commits.map(&:id)
    +  Note.where("(noteable_type = 'MergeRequest' AND noteable_id = :mr_id) OR (noteable_type = 'Commit' AND noteable_id IN (:commit_ids))", mr_id: id, commit_ids: commit_ids)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + open?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/merge_request.rb, line 127
    +def open?
    +  !closed
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + probably_merged?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/merge_request.rb, line 122
    +def probably_merged?
    +  unmerged_commits.empty? &&
    +    commits.any? && open?
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + reload_code() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/merge_request.rb, line 44
    +def reload_code
    +  self.reloaded_commits
    +  self.reloaded_diffs
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + reloaded_commits() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/merge_request.rb, line 142
    +def reloaded_commits
    +  if open? && unmerged_commits.any?
    +    self.st_commits = unmerged_commits
    +    save
    +  end
    +  commits
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + reloaded_diffs() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/merge_request.rb, line 75
    +def reloaded_diffs
    +  if open? && unmerged_diffs.any?
    +    self.st_diffs = unmerged_diffs
    +    self.save
    +  end
    +
    +rescue Grit::Git::GitTimeout
    +  self.st_diffs = [BROKEN_DIFF]
    +  self.save
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + to_raw() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/merge_request.rb, line 179
    +def to_raw
    +  FileUtils.mkdir_p(Rails.root.join("tmp", "patches"))
    +  patch_path = Rails.root.join("tmp", "patches", "merge_request_#{self.id}.patch")
    +
    +  from = commits.last.id
    +  to = source_branch
    +
    +  project.repo.git.run('', "format-patch" , " > #{patch_path.to_s}", {}, ["#{from}..#{to}", "--stdout"])
    +
    +  patch_path
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + unchecked?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/merge_request.rb, line 49
    +def unchecked?
    +  state == UNCHECKED
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + unmerged_commits() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/merge_request.rb, line 150
    +def unmerged_commits
    +  self.project.repo.
    +    commits_between(self.target_branch, self.source_branch).
    +    map {|c| Commit.new(c)}.
    +    sort_by(&:created_at).
    +    reverse
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + unmerged_diffs() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/merge_request.rb, line 94
    +def unmerged_diffs
    +  # Only show what is new in the source branch compared to the target branch, not the other way around.
    +  # The linex below with merge_base is equivalent to diff with three dots (git diff branch1...branch2)
    +  # From the git documentation: "git diff A...B" is equivalent to "git diff $(git-merge-base A B) B"
    +  common_commit = project.repo.git.native(:merge_base, {}, [target_branch, source_branch]).strip
    +  diffs = project.repo.diff(common_commit, source_branch)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + valid_diffs?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/merge_request.rb, line 90
    +def valid_diffs?
    +  !broken_diffs?
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + validate_branches() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/merge_request.rb, line 38
    +def validate_branches
    +  if target_branch == source_branch
    +    errors.add :base, "You can not use same branch for source and target branches"
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/MergeRequestObserver.html b/doc/app/MergeRequestObserver.html new file mode 100644 index 00000000..f2d0dbd7 --- /dev/null +++ b/doc/app/MergeRequestObserver.html @@ -0,0 +1,569 @@ + + + + + + +class MergeRequestObserver - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class MergeRequestObserver

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + after_create(merge_request) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/observers/merge_request_observer.rb, line 4
    +def after_create(merge_request)
    +  if merge_request.assignee && merge_request.assignee != current_user
    +    Notify.new_merge_request_email(merge_request.id).deliver
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + after_update(merge_request) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/observers/merge_request_observer.rb, line 10
    +def after_update(merge_request)
    +  send_reassigned_email(merge_request) if merge_request.is_being_reassigned?
    +
    +  status = nil
    +  status = 'closed' if merge_request.is_being_closed?
    +  status = 'reopened' if merge_request.is_being_reopened?
    +  if status
    +    Note.create_status_change_note(merge_request, current_user, status)
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    +

    Protected Instance Methods

    + + +
    + +
    + send_reassigned_email(merge_request) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/observers/merge_request_observer.rb, line 23
    +def send_reassigned_email(merge_request)
    +  recipients_ids = merge_request.assignee_id_was, merge_request.assignee_id
    +  recipients_ids.delete current_user.id
    +
    +  recipients_ids.each do |recipient_id|
    +    Notify.reassigned_merge_request_email(recipient_id, merge_request.id, merge_request.assignee_id_was).deliver
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/MergeRequestsController.html b/doc/app/MergeRequestsController.html new file mode 100644 index 00000000..ef257c6c --- /dev/null +++ b/doc/app/MergeRequestsController.html @@ -0,0 +1,1114 @@ + + + + + + +class MergeRequestsController - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class MergeRequestsController

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + automerge() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/merge_requests_controller.rb, line 79
    +def automerge
    +  return access_denied! unless can?(current_user, :accept_mr, @project)
    +  if @merge_request.open? && @merge_request.can_be_merged?
    +    @merge_request.should_remove_source_branch = params[:should_remove_source_branch]
    +    @merge_request.automerge!(current_user)
    +    @status = true
    +  else
    +    @status = false
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + automerge_check() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/merge_requests_controller.rb, line 72
    +def automerge_check
    +  if @merge_request.unchecked?
    +    @merge_request.check_if_can_be_merged
    +  end
    +  render json: {state: @merge_request.human_state}
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + branch_from() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/merge_requests_controller.rb, line 98
    +def branch_from
    +  @commit = project.commit(params[:ref])
    +  @commit = CommitDecorator.decorate(@commit)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + branch_to() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/merge_requests_controller.rb, line 103
    +def branch_to
    +  @commit = project.commit(params[:ref])
    +  @commit = CommitDecorator.decorate(@commit)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + create() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/merge_requests_controller.rb, line 50
    +def create
    +  @merge_request = @project.merge_requests.new(params[:merge_request])
    +  @merge_request.author = current_user
    +
    +  if @merge_request.save
    +    @merge_request.reload_code
    +    redirect_to [@project, @merge_request], notice: 'Merge request was successfully created.'
    +  else
    +    render action: "new"
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + destroy() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/merge_requests_controller.rb, line 90
    +def destroy
    +  @merge_request.destroy
    +
    +  respond_to do |format|
    +    format.html { redirect_to project_merge_requests_url(@project) }
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + diffs() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/merge_requests_controller.rb, line 35
    +def diffs
    +  @diffs = @merge_request.diffs
    +  @commit = @merge_request.last_commit
    +
    +  @comments_allowed = true
    +  @line_notes = @merge_request.notes.where("line_code is not null")
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + edit() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/merge_requests_controller.rb, line 47
    +def edit
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + index() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/merge_requests_controller.rb, line 20
    +def index
    +  @merge_requests = MergeRequestsLoadContext.new(project, current_user, params).execute
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + new() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/merge_requests_controller.rb, line 43
    +def new
    +  @merge_request = @project.merge_requests.new(params[:merge_request])
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + raw() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/merge_requests_controller.rb, line 31
    +def raw
    +  send_file @merge_request.to_raw
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + show() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/merge_requests_controller.rb, line 24
    +def show
    +  respond_to do |format|
    +    format.html
    +    format.js
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + update() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/merge_requests_controller.rb, line 62
    +def update
    +  if @merge_request.update_attributes(params[:merge_request].merge(author_id_of_changes: current_user.id))
    +    @merge_request.reload_code
    +    @merge_request.mark_as_unchecked
    +    redirect_to [@project, @merge_request], notice: 'Merge request was successfully updated.'
    +  else
    +    render action: "edit"
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    +

    Protected Instance Methods

    + + +
    + +
    + authorize_admin_merge_request!() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/merge_requests_controller.rb, line 118
    +def authorize_admin_merge_request!
    +  return render_404 unless can?(current_user, :admin_merge_request, @merge_request)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + authorize_modify_merge_request!() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/merge_requests_controller.rb, line 114
    +def authorize_modify_merge_request!
    +  return render_404 unless can?(current_user, :modify_merge_request, @merge_request)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + define_show_vars() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/merge_requests_controller.rb, line 135
    +def define_show_vars
    +  # Build a note object for comment form
    +  @note = @project.notes.new(noteable: @merge_request)
    +
    +  # Get commits from repository
    +  # or from cache if already merged
    +  @commits = @merge_request.commits
    +  @commits = CommitDecorator.decorate(@commits)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + merge_request() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/merge_requests_controller.rb, line 110
    +def merge_request
    +  @merge_request ||= @project.merge_requests.find(params[:id])
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + module_enabled() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/merge_requests_controller.rb, line 122
    +def module_enabled
    +  return render_404 unless @project.merge_requests_enabled
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + validates_merge_request() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/merge_requests_controller.rb, line 126
    +def validates_merge_request
    +  # Show git not found page if target branch doesnt exist
    +  return git_not_found! unless @project.repo.heads.map(&:name).include?(@merge_request.target_branch)
    +
    +  # Show git not found page if source branch doesnt exist
    +  # and there is no saved commits between source & target branch
    +  return git_not_found! if !@project.repo.heads.map(&:name).include?(@merge_request.source_branch) && @merge_request.commits.blank?
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/MergeRequestsHelper.html b/doc/app/MergeRequestsHelper.html new file mode 100644 index 00000000..fec9b0e0 --- /dev/null +++ b/doc/app/MergeRequestsHelper.html @@ -0,0 +1,599 @@ + + + + + + +module MergeRequestsHelper - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    module MergeRequestsHelper

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + + + + + + + +
    + +
    + mr_css_classes(mr) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/helpers/merge_requests_helper.rb, line 35
    +def mr_css_classes mr
    +  classes = "merge_request"
    +  classes << " closed" if mr.closed
    +  classes << " merged" if mr.merged?
    +  classes
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + new_mr_path_from_push_event(event) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/helpers/merge_requests_helper.rb, line 24
    +def new_mr_path_from_push_event(event)
    +  new_project_merge_request_path(
    +    event.project,
    +    merge_request: {
    +      source_branch: event.branch_name,
    +      target_branch: event.project.root_ref,
    +      title: event.branch_name.titleize
    +    }
    +  )
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/MergeRequestsLoadContext.html b/doc/app/MergeRequestsLoadContext.html new file mode 100644 index 00000000..5c1b8829 --- /dev/null +++ b/doc/app/MergeRequestsLoadContext.html @@ -0,0 +1,496 @@ + + + + + + +class MergeRequestsLoadContext - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class MergeRequestsLoadContext

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + execute() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/contexts/merge_requests_load_context.rb, line 2
    +def execute
    +  type = params[:f]
    +
    +  merge_requests = project.merge_requests
    +
    +  merge_requests = case type
    +                   when 'all' then merge_requests
    +                   when 'closed' then merge_requests.closed
    +                   when 'assigned-to-me' then merge_requests.opened.assigned(current_user)
    +                   else merge_requests.opened
    +                   end.page(params[:page]).per(20)
    +
    +  merge_requests.includes(:author, :project).order("closed, created_at desc")
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/Milestone.html b/doc/app/Milestone.html new file mode 100644 index 00000000..cc3bdc58 --- /dev/null +++ b/doc/app/Milestone.html @@ -0,0 +1,589 @@ + + + + + + +class Milestone - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Milestone

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Class Methods

    + + +
    + +
    + active() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/milestone.rb, line 10
    +def self.active
    +  where("due_date > ? OR due_date IS NULL", Date.today)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    +

    Public Instance Methods

    + + +
    + +
    + expires_at() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/milestone.rb, line 24
    +def expires_at
    +  "expires at #{due_date.stamp("Aug 21, 2011")}" if due_date
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + participants() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/milestone.rb, line 14
    +def participants
    +  User.where(id: issues.pluck(:assignee_id))
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + percent_complete() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/milestone.rb, line 18
    +def percent_complete
    +  ((self.issues.closed.count * 100) / self.issues.count).abs
    +rescue ZeroDivisionError
    +  100
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/MilestonesController.html b/doc/app/MilestonesController.html new file mode 100644 index 00000000..e15d6fef --- /dev/null +++ b/doc/app/MilestonesController.html @@ -0,0 +1,816 @@ + + + + + + +class MilestonesController - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class MilestonesController

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + create() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/milestones_controller.rb, line 42
    +def create
    +  @milestone = @project.milestones.new(params[:milestone])
    +
    +  if @milestone.save
    +    redirect_to project_milestone_path(@project, @milestone)
    +  else
    +    render "new"
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + destroy() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/milestones_controller.rb, line 67
    +def destroy
    +  return access_denied! unless can?(current_user, :admin_milestone, @milestone)
    +
    +  @milestone.destroy
    +
    +  respond_to do |format|
    +    format.html { redirect_to project_milestones_path }
    +    format.js { render nothing: true }
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + edit() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/milestones_controller.rb, line 28
    +def edit
    +  respond_with(@milestone)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + index() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/milestones_controller.rb, line 13
    +def index
    +  @milestones = case params[:f]
    +                when 'all'; @project.milestones
    +                else @project.milestones.active
    +                end
    +
    +  @milestones = @milestones.includes(:project).order("due_date")
    +  @milestones = @milestones.page(params[:page]).per(20)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + new() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/milestones_controller.rb, line 23
    +def new
    +  @milestone = @project.milestones.new
    +  respond_with(@milestone)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + show() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/milestones_controller.rb, line 32
    +def show
    +  @issues = @milestone.issues
    +  @users = @milestone.participants
    +
    +  respond_to do |format|
    +    format.html
    +    format.js
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + update() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/milestones_controller.rb, line 52
    +def update
    +  @milestone.update_attributes(params[:milestone])
    +
    +  respond_to do |format|
    +    format.js
    +    format.html do
    +      if @milestone.valid?
    +        redirect_to [@project, @milestone]
    +      else
    +        render :edit
    +      end
    +    end
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    +

    Protected Instance Methods

    + + +
    + +
    + authorize_admin_milestone!() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/milestones_controller.rb, line 84
    +def authorize_admin_milestone!
    +  return render_404 unless can?(current_user, :admin_milestone, @project)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + milestone() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/milestones_controller.rb, line 80
    +def milestone
    +  @milestone ||= @project.milestones.find(params[:id])
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + module_enabled() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/milestones_controller.rb, line 88
    +def module_enabled
    +  return render_404 unless @project.issues_enabled
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/Note.html b/doc/app/Note.html new file mode 100644 index 00000000..24a474b0 --- /dev/null +++ b/doc/app/Note.html @@ -0,0 +1,777 @@ + + + + + + +class Note - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Note

    + +
    + +
    + + + + +
    + + + + + + + + +
    +

    Attributes

    + + +
    +
    + notify[RW] +
    + +
    + + + +
    +
    + +
    +
    + notify_author[RW] +
    + +
    + + + +
    +
    + +
    + + + + +
    +

    Public Class Methods

    + + +
    + +
    + create_status_change_note(noteable, author, status) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/note.rb, line 34
    +def self.create_status_change_note(noteable, author, status)
    +  create({
    +    noteable: noteable,
    +    project: noteable.project,
    +    author: author,
    +    note: "_Status changed to #{status}_"
    +  }, without_protection: true)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    +

    Public Instance Methods

    + + +
    + +
    + commit_author() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/note.rb, line 90
    +def commit_author
    +  @commit_author ||=
    +    project.users.find_by_email(noteable.author_email) ||
    +    project.users.find_by_name(noteable.author_name)
    +rescue
    +  nil
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + downvote?() + click to toggle source +
    + + +
    + +

    Returns true if this is a downvote note, otherwise false is returned

    + + + +
    +
    # File app/models/note.rb, line 106
    +def downvote?
    +  note.start_with?('-1') || note.start_with?(':-1:')
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + for_commit?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/note.rb, line 82
    +def for_commit?
    +  noteable_type == "Commit"
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + for_diff_line?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/note.rb, line 86
    +def for_diff_line?
    +  line_code.present?
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + noteable() + click to toggle source +
    + + +
    + +

    override to return commits, which are not active record

    + + + +
    +
    # File app/models/note.rb, line 52
    +def noteable
    +  if for_commit?
    +    project.commit(noteable_id)
    +  else
    +    super
    +  end
    +# Temp fix to prevent app crash
    +# if note commit id doesnt exist
    +rescue
    +  nil
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + notify_only_author?(user) + click to toggle source +
    + + +
    + +

    Check if we can notify commit author with email about our comment

    + +

    If commit author email exist in project and commit author is not passed +user we can send email to him

    + +

    params:

    + +
    user - current user
    + +

    return:

    + +
    Boolean
    + + + +
    +
    # File app/models/note.rb, line 77
    +def notify_only_author?(user)
    +  for_commit? && commit_author &&
    +    commit_author.email != user.email
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + upvote?() + click to toggle source +
    + + +
    + +

    Returns true if this is an upvote note, otherwise false is returned

    + + + +
    +
    # File app/models/note.rb, line 100
    +def upvote?
    +  note.start_with?('+1') || note.start_with?(':+1:')
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/NoteObserver.html b/doc/app/NoteObserver.html new file mode 100644 index 00000000..d0528833 --- /dev/null +++ b/doc/app/NoteObserver.html @@ -0,0 +1,603 @@ + + + + + + +class NoteObserver - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class NoteObserver

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + after_create(note) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/observers/note_observer.rb, line 3
    +def after_create(note)
    +  send_notify_mails(note)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    +

    Protected Instance Methods

    + + +
    + +
    + notify_team(note) + click to toggle source +
    + + +
    + +

    Notifies the whole team except the author of note

    + + + +
    +
    # File app/observers/note_observer.rb, line 22
    +def notify_team(note)
    +  # Note: wall posts are not "attached" to anything, so fall back to "Wall"
    +  noteable_type = note.noteable_type || "Wall"
    +  notify_method = "note_#{noteable_type.underscore}_email".to_sym
    +
    +  if Notify.respond_to? notify_method
    +    team_without_note_author(note).map do |u|
    +      Notify.send(notify_method, u.id, note.id).deliver
    +    end
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + send_notify_mails(note) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/observers/note_observer.rb, line 9
    +def send_notify_mails(note)
    +  if note.notify
    +    notify_team(note)
    +  elsif note.notify_author
    +    # Notify only author of resource
    +    Notify.note_commit_email(note.commit_author.id, note.id).deliver
    +  else
    +    # Otherwise ignore it
    +    nil
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + team_without_note_author(note) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/observers/note_observer.rb, line 34
    +def team_without_note_author(note)
    +  note.project.users.reject { |u| u.id == note.author.id }
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/Notes.html b/doc/app/Notes.html new file mode 100644 index 00000000..d6bcc540 --- /dev/null +++ b/doc/app/Notes.html @@ -0,0 +1,434 @@ + + + + + + +module Notes - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    module Notes

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    + +
    + + + + diff --git a/doc/app/Notes/CreateContext.html b/doc/app/Notes/CreateContext.html new file mode 100644 index 00000000..6d4c8449 --- /dev/null +++ b/doc/app/Notes/CreateContext.html @@ -0,0 +1,490 @@ + + + + + + +class Notes::CreateContext - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Notes::CreateContext

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + execute() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/contexts/notes/create_context.rb, line 3
    +def execute
    +  note = project.notes.new(params[:note])
    +  note.author = current_user
    +  note.notify = true if params[:notify] == '1'
    +  note.notify_author = true if params[:notify_author] == '1'
    +  note.save
    +  note
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/Notes/LoadContext.html b/doc/app/Notes/LoadContext.html new file mode 100644 index 00000000..273fc0f0 --- /dev/null +++ b/doc/app/Notes/LoadContext.html @@ -0,0 +1,513 @@ + + + + + + +class Notes::LoadContext - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Notes::LoadContext

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + execute() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/contexts/notes/load_context.rb, line 3
    +def execute
    +  target_type = params[:target_type]
    +  target_id   = params[:target_id]
    +  after_id    = params[:after_id]
    +  before_id   = params[:before_id]
    +
    +
    +  @notes = case target_type
    +           when "commit"
    +             project.commit_notes(project.commit(target_id)).fresh.limit(20)
    +           when "issue"
    +             project.issues.find(target_id).notes.inc_author.fresh.limit(20)
    +           when "merge_request"
    +             project.merge_requests.find(target_id).mr_and_commit_notes.inc_author.fresh.limit(20)
    +           when "snippet"
    +             project.snippets.find(target_id).notes.fresh
    +           when "wall"
    +             # this is the only case, where the order is DESC
    +             project.common_notes.order("created_at DESC, id DESC").limit(50)
    +           when "wiki"
    +             project.wiki_notes.limit(20)
    +           end
    +
    +  @notes = if after_id
    +             @notes.where("id > ?", after_id)
    +           elsif before_id
    +             @notes.where("id < ?", before_id)
    +           else
    +             @notes
    +           end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/NotesController.html b/doc/app/NotesController.html new file mode 100644 index 00000000..5623d4a2 --- /dev/null +++ b/doc/app/NotesController.html @@ -0,0 +1,636 @@ + + + + + + +class NotesController - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class NotesController

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + create() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/notes_controller.rb, line 18
    +def create
    +  @note = Notes::CreateContext.new(project, current_user, params).execute
    +
    +  respond_to do |format|
    +    format.html {redirect_to :back}
    +    format.js
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + destroy() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/notes_controller.rb, line 27
    +def destroy
    +  @note = @project.notes.find(params[:id])
    +  return access_denied! unless can?(current_user, :admin_note, @note)
    +  @note.destroy
    +
    +  respond_to do |format|
    +    format.js { render nothing: true }
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + index() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/notes_controller.rb, line 8
    +def index
    +  notes
    +  if params[:target_type] == "merge_request"
    +    @mixed_targets = true
    +    @main_target_type = params[:target_type].camelize
    +  end
    +
    +  respond_with(@notes)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + preview() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/notes_controller.rb, line 37
    +def preview
    +  render text: view_context.markdown(params[:note])
    +end
    +
    + +
    + + + + +
    + + +
    + +
    +

    Protected Instance Methods

    + + +
    + +
    + notes() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/notes_controller.rb, line 43
    +def notes
    +  @notes = Notes::LoadContext.new(project, current_user, params).execute
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/NotesHelper.html b/doc/app/NotesHelper.html new file mode 100644 index 00000000..c2940f8d --- /dev/null +++ b/doc/app/NotesHelper.html @@ -0,0 +1,581 @@ + + + + + + +module NotesHelper - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    module NotesHelper

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + + + + +
    + +
    + loading_more_notes?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/helpers/notes_helper.rb, line 2
    +def loading_more_notes?
    +  params[:loading_more].present?
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + loading_new_notes?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/helpers/notes_helper.rb, line 6
    +def loading_new_notes?
    +  params[:loading_new].present?
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + note_for_main_target?(note) + click to toggle source +
    + + +
    + +

    Helps to distinguish e.g. commit notes in mr notes list

    + + + +
    +
    # File app/helpers/notes_helper.rb, line 11
    +def note_for_main_target?(note)
    +  !@mixed_targets || @main_target_type == note.noteable_type
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/Notify.html b/doc/app/Notify.html new file mode 100644 index 00000000..933e1cbc --- /dev/null +++ b/doc/app/Notify.html @@ -0,0 +1,883 @@ + + + + + + +class Notify - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Notify

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + issue_status_changed_email(recipient_id, issue_id, status, updated_by_user_id) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/mailers/notify.rb, line 31
    +def issue_status_changed_email(recipient_id, issue_id, status, updated_by_user_id)
    +  @issue = Issue.find issue_id
    +  @issue_status = status
    +  @updated_by = User.find updated_by_user_id
    +  mail(to: recipient(recipient_id),
    +      subject: subject("changed issue ##{@issue.id}", @issue.title))
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + new_issue_email(issue_id) + click to toggle source +
    + + +
    + +

    Issue

    + + + +
    +
    # File app/mailers/notify.rb, line 18
    +def new_issue_email(issue_id)
    +  @issue = Issue.find(issue_id)
    +  @project = @issue.project
    +  mail(to: @issue.assignee_email, subject: subject("new issue ##{@issue.id}", @issue.title))
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + new_merge_request_email(merge_request_id) + click to toggle source +
    + + +
    + +

    Merge Request

    + + + +
    +
    # File app/mailers/notify.rb, line 45
    +def new_merge_request_email(merge_request_id)
    +  @merge_request = MergeRequest.find(merge_request_id)
    +  @project = @merge_request.project
    +  mail(to: @merge_request.assignee_email, subject: subject("new merge request !#{@merge_request.id}", @merge_request.title))
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + new_user_email(user_id, password) + click to toggle source +
    + + +
    + +

    User

    + + + +
    +
    # File app/mailers/notify.rb, line 118
    +def new_user_email(user_id, password)
    +  @user = User.find(user_id)
    +  @password = password
    +  mail(to: @user.email, subject: subject("Account was created for you"))
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + note_commit_email(recipient_id, note_id) + click to toggle source +
    + + +
    + +

    Note

    + + + +
    +
    # File app/mailers/notify.rb, line 64
    +def note_commit_email(recipient_id, note_id)
    +  @note = Note.find(note_id)
    +  @commit = @note.noteable
    +  @commit = CommitDecorator.decorate(@commit)
    +  @project = @note.project
    +  mail(to: recipient(recipient_id), subject: subject("note for commit #{@commit.short_id}", @commit.title))
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + note_issue_email(recipient_id, note_id) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/mailers/notify.rb, line 72
    +def note_issue_email(recipient_id, note_id)
    +  @note = Note.find(note_id)
    +  @issue = @note.noteable
    +  @project = @note.project
    +  mail(to: recipient(recipient_id), subject: subject("note for issue ##{@issue.id}"))
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + note_merge_request_email(recipient_id, note_id) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/mailers/notify.rb, line 79
    +def note_merge_request_email(recipient_id, note_id)
    +  @note = Note.find(note_id)
    +  @merge_request = @note.noteable
    +  @project = @note.project
    +  mail(to: recipient(recipient_id), subject: subject("note for merge request !#{@merge_request.id}"))
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + note_wall_email(recipient_id, note_id) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/mailers/notify.rb, line 86
    +def note_wall_email(recipient_id, note_id)
    +  @note = Note.find(note_id)
    +  @project = @note.project
    +  mail(to: recipient(recipient_id), subject: subject)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + note_wiki_email(recipient_id, note_id) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/mailers/notify.rb, line 92
    +def note_wiki_email(recipient_id, note_id)
    +  @note = Note.find(note_id)
    +  @wiki = @note.noteable
    +  @project = @note.project
    +  mail(to: recipient(recipient_id), subject: subject("note for wiki"))
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + project_access_granted_email(user_project_id) + click to toggle source +
    + + +
    + +

    Project

    + + + +
    +
    # File app/mailers/notify.rb, line 105
    +def project_access_granted_email(user_project_id)
    +  @users_project = UsersProject.find user_project_id
    +  @project = @users_project.project
    +  mail(to: @users_project.user.email, 
    +       subject: subject("access to project was granted"))
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + reassigned_issue_email(recipient_id, issue_id, previous_assignee_id) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/mailers/notify.rb, line 24
    +def reassigned_issue_email(recipient_id, issue_id, previous_assignee_id)
    +  @issue = Issue.find(issue_id)
    +  @previous_assignee ||= User.find(previous_assignee_id)
    +  @project = @issue.project
    +  mail(to: recipient(recipient_id), subject: subject("changed issue ##{@issue.id}", @issue.title))
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + reassigned_merge_request_email(recipient_id, merge_request_id, previous_assignee_id) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/mailers/notify.rb, line 51
    +def reassigned_merge_request_email(recipient_id, merge_request_id, previous_assignee_id)
    +  @merge_request = MergeRequest.find(merge_request_id)
    +  @previous_assignee ||= User.find(previous_assignee_id)
    +  @project = @merge_request.project
    +  mail(to: recipient(recipient_id), subject: subject("changed merge request !#{@merge_request.id}", @merge_request.title))
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/OmniauthCallbacksController.html b/doc/app/OmniauthCallbacksController.html new file mode 100644 index 00000000..d40a16a3 --- /dev/null +++ b/doc/app/OmniauthCallbacksController.html @@ -0,0 +1,527 @@ + + + + + + +class OmniauthCallbacksController - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class OmniauthCallbacksController

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + failure_message() + click to toggle source +
    + + +
    + +

    Extend the standard message generation to accept our custom exception

    + + + +
    +
    # File app/controllers/omniauth_callbacks_controller.rb, line 9
    +def failure_message
    +  exception = env["omniauth.error"]
    +  error   = exception.error_reason if exception.respond_to?(:error_reason)
    +  error ||= exception.error        if exception.respond_to?(:error)
    +  error ||= exception.message      if exception.respond_to?(:message)
    +  error ||= env["omniauth.error.type"].to_s
    +  error.to_s.humanize if error
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + ldap() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/omniauth_callbacks_controller.rb, line 18
    +def ldap
    +  # We only find ourselves here if the authentication to LDAP was successful.
    +  @user = User.find_for_ldap_auth(request.env["omniauth.auth"], current_user)
    +  if @user.persisted?
    +    @user.remember_me = true
    +  end
    +  sign_in_and_redirect @user
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/PostReceive.html b/doc/app/PostReceive.html new file mode 100644 index 00000000..11468fd9 --- /dev/null +++ b/doc/app/PostReceive.html @@ -0,0 +1,495 @@ + + + + + + +class PostReceive - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class PostReceive

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Class Methods

    + + +
    + +
    + perform(reponame, oldrev, newrev, ref, identifier) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/workers/post_receive.rb, line 4
    +def self.perform(reponame, oldrev, newrev, ref, identifier)
    +  project = Project.find_by_path(reponame)
    +  return false if project.nil?
    +
    +  # Ignore push from non-gitlab users
    +  if %r^[A-Z0-9._%a-z\-]+@(?:[A-Z0-9a-z\-]+\.)+[A-Za-z]{2,4}$/.match(identifier)
    +    return false unless user = User.find_by_email(identifier)
    +  else
    +    return false unless user = Key.find_by_identifier(identifier).try(:user)
    +  end
    +
    +  project.trigger_post_receive(oldrev, newrev, ref, user)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/ProfileController.html b/doc/app/ProfileController.html new file mode 100644 index 00000000..deb4b0bc --- /dev/null +++ b/doc/app/ProfileController.html @@ -0,0 +1,683 @@ + + + + + + +class ProfileController - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class ProfileController

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + design() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/profile_controller.rb, line 7
    +def design
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + history() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/profile_controller.rb, line 34
    +def history
    +  @events = current_user.recent_events.page(params[:page]).per(20)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + password_update() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/profile_controller.rb, line 18
    +def password_update
    +  params[:user].reject!{ |k, v| k != "password" && k != "password_confirmation"}
    +
    +  if @user.update_attributes(params[:user])
    +    flash[:notice] = "Password was successfully updated. Please login with it"
    +    redirect_to new_user_session_path
    +  else
    +    render action: "password"
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + reset_private_token() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/profile_controller.rb, line 29
    +def reset_private_token
    +  current_user.reset_authentication_token!
    +  redirect_to profile_account_path
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + show() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/profile_controller.rb, line 4
    +def show
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + token() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/profile_controller.rb, line 15
    +def token
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + update() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/profile_controller.rb, line 10
    +def update
    +  @user.update_attributes(params[:user])
    +  redirect_to :back
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/ProfileHelper.html b/doc/app/ProfileHelper.html new file mode 100644 index 00000000..ab7773ba --- /dev/null +++ b/doc/app/ProfileHelper.html @@ -0,0 +1,481 @@ + + + + + + +module ProfileHelper - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    module ProfileHelper

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + oauth_active_class(provider) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/helpers/profile_helper.rb, line 2
    +def oauth_active_class provider
    +  if current_user.provider == provider.to_s
    +    'active'
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/Project.html b/doc/app/Project.html new file mode 100644 index 00000000..55868ef1 --- /dev/null +++ b/doc/app/Project.html @@ -0,0 +1,1202 @@ + + + + + + +class Project - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Project

    + +
    + +
    + + + + +
    + + + + + + + + +
    +

    Attributes

    + + +
    +
    + error_code[RW] +
    + +
    + + + +
    +
    + +
    + + + + +
    +

    Public Class Methods

    + + +
    + +
    + access_options() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/project.rb, line 85
    +def access_options
    +  UsersProject.access_roles
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + active() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/project.rb, line 52
    +def active
    +  joins(:issues, :notes, :merge_requests).order("issues.created_at, notes.created_at, merge_requests.created_at DESC")
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + create_by_user(params, user) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/project.rb, line 60
    +def create_by_user(params, user)
    +  project = Project.new params
    +
    +  Project.transaction do
    +    project.owner = user
    +    project.save!
    +
    +    # Add user as project master
    +    project.users_projects.create!(project_access: UsersProject::MASTER, user: user)
    +
    +    # when project saved no team member exist so
    +    # project repository should be updated after first user add
    +    project.update_repository
    +  end
    +
    +  project
    +rescue Gitlab::Gitolite::AccessDenied => ex
    +  project.error_code = :gitolite
    +  project
    +rescue => ex
    +  project.error_code = :db
    +  project.errors.add(:base, "Can't save project. Please try again later")
    +  project
    +end
    +
    + +
    + + + + +
    + + + + + +
    + +
    +

    Public Instance Methods

    + + +
    + +
    + build_commit_note(commit) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/project.rb, line 124
    +def build_commit_note(commit)
    +  notes.new(noteable_id: commit.id, noteable_type: "Commit")
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + check_limit() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/project.rb, line 98
    +def check_limit
    +  unless owner.can_create_project?
    +    errors[:base] << ("Your own projects limit is #{owner.projects_limit}! Please contact administrator to increase it")
    +  end
    +rescue
    +  errors[:base] << ("Can't check your ability to create project")
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + commit_line_notes(commit) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/project.rb, line 132
    +def commit_line_notes(commit)
    +  notes.where(noteable_id: commit.id, noteable_type: "Commit").where("line_code IS NOT NULL")
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + commit_notes(commit) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/project.rb, line 128
    +def commit_notes(commit)
    +  notes.where(noteable_id: commit.id, noteable_type: "Commit", line_code: nil)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + common_notes() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/project.rb, line 120
    +def common_notes
    +  notes.where(noteable_type: ["", nil]).inc_author_project
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + git_error?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/project.rb, line 90
    +def git_error?
    +  error_code == :gitolite
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + issues_labels() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/project.rb, line 160
    +def issues_labels
    +  issues.tag_counts_on(:labels)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + last_activity() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/project.rb, line 144
    +def last_activity
    +  last_event
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + last_activity_date() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/project.rb, line 148
    +def last_activity_date
    +  last_event.try(:created_at) || updated_at
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + private?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/project.rb, line 140
    +def private?
    +  private_flag
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + project_id() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/project.rb, line 156
    +def project_id
    +  self.id
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + public?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/project.rb, line 136
    +def public?
    +  !private_flag
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + repo_name() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/project.rb, line 106
    +def repo_name
    +  if path == "gitolite-admin"
    +    errors.add(:path, " like 'gitolite-admin' is not allowed")
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + saved?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/project.rb, line 94
    +def saved?
    +  id && valid?
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + to_param() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/project.rb, line 112
    +def to_param
    +  code
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + web_url() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/project.rb, line 116
    +def web_url
    +  [Gitlab.config.url, code].join("/")
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + wiki_notes() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/project.rb, line 152
    +def wiki_notes
    +  Note.where(noteable_id: wikis.pluck(:id), noteable_type: 'Wiki', project_id: self.id)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/ProjectHook.html b/doc/app/ProjectHook.html new file mode 100644 index 00000000..e5d17ef0 --- /dev/null +++ b/doc/app/ProjectHook.html @@ -0,0 +1,439 @@ + + + + + + +class ProjectHook - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class ProjectHook

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    + +
    + + + + diff --git a/doc/app/ProjectObserver.html b/doc/app/ProjectObserver.html new file mode 100644 index 00000000..d4ce7824 --- /dev/null +++ b/doc/app/ProjectObserver.html @@ -0,0 +1,589 @@ + + + + + + +class ProjectObserver - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class ProjectObserver

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + after_create(project) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/observers/project_observer.rb, line 12
    +def after_create project
    +  log_info("#{project.owner.name} created a new project \"#{project.name}\"")
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + after_destroy(project) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/observers/project_observer.rb, line 6
    +def after_destroy(project)
    +  log_info("Project \"#{project.name}\" was removed")
    +
    +  project.destroy_repository
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + after_save(project) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/observers/project_observer.rb, line 2
    +def after_save(project)
    +  project.update_repository
    +end
    +
    + +
    + + + + +
    + + +
    + +
    +

    Protected Instance Methods

    + + +
    + +
    + log_info(message) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/observers/project_observer.rb, line 18
    +def log_info message
    +  Gitlab::AppLogger.info message
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/ProjectResourceController.html b/doc/app/ProjectResourceController.html new file mode 100644 index 00000000..aa972617 --- /dev/null +++ b/doc/app/ProjectResourceController.html @@ -0,0 +1,439 @@ + + + + + + +class ProjectResourceController - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class ProjectResourceController

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    + +
    + + + + diff --git a/doc/app/ProjectsController.html b/doc/app/ProjectsController.html new file mode 100644 index 00000000..6982deab --- /dev/null +++ b/doc/app/ProjectsController.html @@ -0,0 +1,785 @@ + + + + + + +class ProjectsController - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class ProjectsController

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + create() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/projects_controller.rb, line 20
    +def create
    +  @project = Project.create_by_user(params[:project], current_user)
    +
    +  respond_to do |format|
    +    format.html do
    +      if @project.saved?
    +        redirect_to(@project, notice: 'Project was successfully created.')
    +      else
    +        render action: "new"
    +      end
    +    end
    +    format.js
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + destroy() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/projects_controller.rb, line 85
    +def destroy
    +  # Disable the UsersProject update_repository call, otherwise it will be
    +  # called once for every person removed from the project
    +  UsersProject.skip_callback(:destroy, :after, :update_repository)
    +  project.destroy
    +  UsersProject.set_callback(:destroy, :after, :update_repository)
    +
    +  respond_to do |format|
    +    format.html { redirect_to root_path }
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + edit() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/projects_controller.rb, line 17
    +def edit
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + files() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/projects_controller.rb, line 64
    +def files
    +  @notes = @project.notes.where("attachment != 'NULL'").order("created_at DESC").limit(100)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + graph() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/projects_controller.rb, line 81
    +def graph
    +  @days_json, @commits_json = Gitlab::GraphCommit.to_graph(project)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + new() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/projects_controller.rb, line 13
    +def new
    +  @project = Project.new
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + show() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/projects_controller.rb, line 47
    +def show
    +  limit = (params[:limit] || 20).to_i
    +  @events = @project.events.recent.limit(limit).offset(params[:offset] || 0)
    +
    +  respond_to do |format|
    +    format.html do
    +       unless @project.empty_repo?
    +         @last_push = current_user.recent_push(@project.id)
    +         render :show
    +       else
    +         render "projects/empty"
    +       end
    +    end
    +    format.js
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + update() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/projects_controller.rb, line 35
    +def update
    +  respond_to do |format|
    +    if project.update_attributes(params[:project])
    +      format.html { redirect_to edit_project_path(project), notice: 'Project was successfully updated.' }
    +      format.js
    +    else
    +      format.html { render action: "edit" }
    +      format.js
    +    end
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + wall() + click to toggle source +
    + + +
    + +

    Wall

    + + + +
    +
    # File app/controllers/projects_controller.rb, line 72
    +def wall
    +  return render_404 unless @project.wall_enabled
    +  @note = Note.new
    +
    +  respond_to do |format|
    +    format.html
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/ProjectsHelper.html b/doc/app/ProjectsHelper.html new file mode 100644 index 00000000..5edd4592 --- /dev/null +++ b/doc/app/ProjectsHelper.html @@ -0,0 +1,543 @@ + + + + + + +module ProjectsHelper - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    module ProjectsHelper

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + grouper_project_members(project) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/helpers/projects_helper.rb, line 2
    +def grouper_project_members(project)
    +  @project.users_projects.sort_by(&:project_access).reverse.group_by(&:project_access)
    +end
    +
    + +
    + + + + +
    + + + + + +
    + +
    + remove_from_team_message(project, member) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/helpers/projects_helper.rb, line 6
    +def remove_from_team_message(project, member)
    +  "You are going to remove #{member.user_name} from #{project.name}. Are you sure?"
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/ProtectedBranch.html b/doc/app/ProtectedBranch.html new file mode 100644 index 00000000..900646dc --- /dev/null +++ b/doc/app/ProtectedBranch.html @@ -0,0 +1,529 @@ + + + + + + +class ProtectedBranch - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class ProtectedBranch

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + commit() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/protected_branch.rb, line 17
    +def commit
    +  project.commit(self.name)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + update_repository() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/protected_branch.rb, line 13
    +def update_repository
    +  git_host.update_repository(project)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/ProtectedBranchesController.html b/doc/app/ProtectedBranchesController.html new file mode 100644 index 00000000..7eb2b1fd --- /dev/null +++ b/doc/app/ProtectedBranchesController.html @@ -0,0 +1,556 @@ + + + + + + +class ProtectedBranchesController - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class ProtectedBranchesController

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + create() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/protected_branches_controller.rb, line 13
    +def create
    +  @project.protected_branches.create(params[:protected_branch])
    +  redirect_to project_protected_branches_path(@project)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + destroy() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/protected_branches_controller.rb, line 18
    +def destroy
    +  @project.protected_branches.find(params[:id]).destroy
    +
    +  respond_to do |format|
    +    format.html { redirect_to project_protected_branches_path }
    +    format.js { render nothing: true }
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + index() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/protected_branches_controller.rb, line 8
    +def index
    +  @branches = @project.protected_branches.all
    +  @protected_branch = @project.protected_branches.new
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/PushEvent.html b/doc/app/PushEvent.html new file mode 100644 index 00000000..002866a6 --- /dev/null +++ b/doc/app/PushEvent.html @@ -0,0 +1,1105 @@ + + + + + + +module PushEvent - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    module PushEvent

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + branch?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/push_event.rb, line 12
    +def branch?
    +  data[:ref]["refs/heads"]
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + branch_name() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/push_event.rb, line 48
    +def branch_name
    +  @branch_name ||= data[:ref].gsub("refs/heads/", "")
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + commit_from() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/push_event.rb, line 32
    +def commit_from
    +  data[:before]
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + commit_to() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/push_event.rb, line 36
    +def commit_to
    +  data[:after]
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + commits() + click to toggle source +
    + + +
    + +

    Max 20 commits from push DESC

    + + + +
    +
    # File app/roles/push_event.rb, line 57
    +def commits
    +  @commits ||= data[:commits].map { |commit| project.commit(commit[:id]) }.reverse
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + commits_count() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/push_event.rb, line 61
    +def commits_count 
    +  data[:total_commits_count] || commits.count || 0
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + last_commit() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/push_event.rb, line 85
    +def last_commit
    +  project.commit(commit_to)
    +rescue => ex
    +  nil
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + last_push_to_non_root?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/push_event.rb, line 97
    +def last_push_to_non_root?
    +  branch? && project.default_branch != branch_name
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + md_ref?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/push_event.rb, line 28
    +def md_ref?
    +  !(rm_ref? || new_ref?)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + new_branch?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/push_event.rb, line 16
    +def new_branch?
    +  commit_from =~ %r^00000/
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + new_ref?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/push_event.rb, line 20
    +def new_ref?
    +  commit_from =~ %r^00000/
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + parent_commit() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/push_event.rb, line 79
    +def parent_commit
    +  project.commit(commit_from)
    +rescue => ex
    +  nil
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + push_action_name() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/push_event.rb, line 69
    +def push_action_name
    +  if new_ref?
    +    "pushed new"
    +  elsif rm_ref?
    +    "deleted"
    +  else
    +    "pushed to"
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + push_with_commits?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/push_event.rb, line 91
    +def push_with_commits? 
    +  md_ref? && commits.any? && parent_commit && last_commit
    +rescue Grit::NoSuchPathError
    +  false
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + ref_name() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/push_event.rb, line 40
    +def ref_name
    +  if tag?
    +    tag_name
    +  else
    +    branch_name
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + ref_type() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/push_event.rb, line 65
    +def ref_type
    +  tag? ? "tag" : "branch"
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + rm_ref?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/push_event.rb, line 24
    +def rm_ref?
    +  commit_to =~ %r^00000/
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + tag?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/push_event.rb, line 8
    +def tag?
    +  data[:ref]["refs/tags"]
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + tag_name() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/push_event.rb, line 52
    +def tag_name
    +  @tag_name ||= data[:ref].gsub("refs/tags/", "")
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + valid_push?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/push_event.rb, line 2
    +def valid_push?
    +  data[:ref]
    +rescue => ex
    +  false
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/PushObserver.html b/doc/app/PushObserver.html new file mode 100644 index 00000000..804447a6 --- /dev/null +++ b/doc/app/PushObserver.html @@ -0,0 +1,699 @@ + + + + + + +module PushObserver - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    module PushObserver

    + +
    + +

    Includes methods for handling Git Push events

    + +

    Triggered by PostReceive job

    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + execute_hooks(oldrev, newrev, ref, user) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/push_observer.rb, line 33
    +def execute_hooks(oldrev, newrev, ref, user)
    +  ref_parts = ref.split('/')
    +
    +  # Return if this is not a push to a branch (e.g. new commits)
    +  return if ref_parts[1] !~ %rheads/ || oldrev == "00000000000000000000000000000000"
    +
    +  data = post_receive_data(oldrev, newrev, ref, user)
    +
    +  hooks.each { |hook| hook.execute(data) }
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + observe_push(oldrev, newrev, ref, user) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/push_observer.rb, line 5
    +def observe_push(oldrev, newrev, ref, user)
    +  data = post_receive_data(oldrev, newrev, ref, user)
    +
    +  Event.create(
    +    project: self,
    +    action: Event::Pushed,
    +    data: data,
    +    author_id: data[:user_id]
    +  )
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + post_receive_data(oldrev, newrev, ref, user) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/push_observer.rb, line 44
    +def post_receive_data(oldrev, newrev, ref, user)
    +
    +  push_commits = commits_between(oldrev, newrev)
    +
    +  # Total commits count
    +  push_commits_count = push_commits.size
    +
    +  # Get latest 20 commits ASC
    +  push_commits_limited = push_commits.last(20)
    +
    +  # Hash to be passed as post_receive_data
    +  data = {
    +    before: oldrev,
    +    after: newrev,
    +    ref: ref,
    +    user_id: user.id,
    +    user_name: user.name,
    +    repository: {
    +      name: name,
    +      url: web_url,
    +      description: description,
    +      homepage: web_url,
    +    },
    +    commits: [],
    +    total_commits_count: push_commits_count
    +  }
    +
    +  # For perfomance purposes maximum 20 latest commits
    +  # will be passed as post receive hook data.
    +  #
    +  push_commits_limited.each do |commit|
    +    data[:commits] << {
    +      id: commit.id,
    +      message: commit.safe_message,
    +      timestamp: commit.date.xmlschema,
    +      url: "#{Gitlab.config.url}/#{code}/commits/#{commit.id}",
    +      author: {
    +        name: commit.author_name,
    +        email: commit.author_email
    +      }
    +    }
    +  end
    +
    +  data
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + trigger_post_receive(oldrev, newrev, ref, user) + click to toggle source +
    + + +
    + +

    This method will be called after each post receive and only if the provided +user is present in GitLab.

    + +

    All callbacks for post receive should be placed here.

    + + + +
    +
    # File app/roles/push_observer.rb, line 94
    +def trigger_post_receive(oldrev, newrev, ref, user)
    +  # Create push event
    +  self.observe_push(oldrev, newrev, ref, user)
    +
    +  # Close merged MR
    +  self.update_merge_requests(oldrev, newrev, ref, user)
    +
    +  # Execute web hooks
    +  self.execute_hooks(oldrev, newrev, ref, user)
    +
    +  # Create satellite
    +  self.satellite.create unless self.satellite.exists?
    +
    +  # Discover the default branch, but only if it hasn't already been set to
    +  # something else
    +  if default_branch.nil?
    +    update_attributes(default_branch: discover_default_branch)
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + update_merge_requests(oldrev, newrev, ref, user) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/push_observer.rb, line 16
    +def update_merge_requests(oldrev, newrev, ref, user)
    +  return true unless ref =~ %rheads/
    +  branch_name = ref.gsub("refs/heads/", "")
    +  c_ids = self.commits_between(oldrev, newrev).map(&:id)
    +
    +  # Update code for merge requests
    +  mrs = self.merge_requests.opened.find_all_by_branch(branch_name).all
    +  mrs.each { |merge_request| merge_request.reload_code; merge_request.mark_as_unchecked }
    +
    +  # Close merge requests
    +  mrs = self.merge_requests.opened.where(target_branch: branch_name).all
    +  mrs = mrs.select(&:last_commit).select { |mr| c_ids.include?(mr.last_commit.id) }
    +  mrs.each { |merge_request| merge_request.merge!(user.id) }
    +
    +  true
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/Redcarpet.html b/doc/app/Redcarpet.html new file mode 100644 index 00000000..238b7f25 --- /dev/null +++ b/doc/app/Redcarpet.html @@ -0,0 +1,432 @@ + + + + + + +module Redcarpet - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    module Redcarpet

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    + +
    + + + + diff --git a/doc/app/Redcarpet/Render.html b/doc/app/Redcarpet/Render.html new file mode 100644 index 00000000..acb7ba3c --- /dev/null +++ b/doc/app/Redcarpet/Render.html @@ -0,0 +1,432 @@ + + + + + + +module Redcarpet::Render - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    module Redcarpet::Render

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    + +
    + + + + diff --git a/doc/app/Redcarpet/Render/GitlabHTML.html b/doc/app/Redcarpet/Render/GitlabHTML.html new file mode 100644 index 00000000..93212eab --- /dev/null +++ b/doc/app/Redcarpet/Render/GitlabHTML.html @@ -0,0 +1,594 @@ + + + + + + +class Redcarpet::Render::GitlabHTML - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Redcarpet::Render::GitlabHTML

    + +
    + +
    + + + + +
    + + + + + + + + +
    +

    Attributes

    + + +
    +
    + h[R] +
    + +
    + + + +
    +
    + +
    +
    + template[R] +
    + +
    + + + +
    +
    + +
    + + + + +
    +

    Public Class Methods

    + + +
    + +
    + new(template, options = {}) + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/redcarpet/render/gitlab_html.rb, line 6
    +def initialize(template, options = {})
    +  @template = template
    +  @project = @template.instance_variable_get("@project")
    +  super options
    +end
    +
    + +
    + + + + +
    + + +
    + +
    +

    Public Instance Methods

    + + +
    + +
    + block_code(code, language) + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/redcarpet/render/gitlab_html.rb, line 12
    +def block_code(code, language)
    +  if Pygments::Lexer.find(language)
    +    Pygments.highlight(code, lexer: language, options: {encoding: 'utf-8'})
    +  else
    +    Pygments.highlight(code, options: {encoding: 'utf-8'})
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + postprocess(full_document) + click to toggle source +
    + + +
    + + + + + +
    +
    # File lib/redcarpet/render/gitlab_html.rb, line 20
    +def postprocess(full_document)
    +  h.gfm(full_document)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/RefsController.html b/doc/app/RefsController.html new file mode 100644 index 00000000..a8248da0 --- /dev/null +++ b/doc/app/RefsController.html @@ -0,0 +1,638 @@ + + + + + + +class RefsController - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class RefsController

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + logs_tree() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/refs_controller.rb, line 31
    +def logs_tree
    +  contents = @tree.contents
    +  @logs = contents.map do |content|
    +    file = params[:path] ? File.join(params[:path], content.name) : content.name
    +    last_commit = @project.commits(@commit.id, file, 1).last
    +    last_commit = CommitDecorator.decorate(last_commit)
    +    {
    +      file_name: content.name,
    +      commit: last_commit
    +    }
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + switch() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/refs_controller.rb, line 12
    +def switch
    +  respond_to do |format|
    +    format.html do
    +      new_path = if params[:destination] == "tree"
    +                   project_tree_path(@project, @ref)
    +                 else
    +                   project_commits_path(@project, @ref)
    +                 end
    +
    +      redirect_to new_path
    +    end
    +    format.js do
    +      @ref = params[:ref]
    +      define_tree_vars
    +      render "tree"
    +    end
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    +

    Protected Instance Methods

    + + +
    + +
    + define_tree_vars() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/refs_controller.rb, line 46
    +def define_tree_vars
    +  params[:path] = nil if params[:path].blank?
    +
    +  @repo = project.repo
    +  @commit = project.commit(@ref)
    +  @commit = CommitDecorator.decorate(@commit)
    +  @tree = Tree.new(@commit.tree, project, @ref, params[:path])
    +  @tree = TreeDecorator.new(@tree)
    +  @hex_path = Digest::SHA1.hexdigest(params[:path] || "")
    +
    +  if params[:path]
    +    @logs_path = logs_file_project_ref_path(@project, @ref, params[:path])
    +  else
    +    @logs_path = logs_tree_project_ref_path(@project, @ref)
    +  end
    +rescue
    +  return render_404
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + ref() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/refs_controller.rb, line 65
    +def ref
    +  @ref = params[:id] || params[:ref]
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/RepositoriesController.html b/doc/app/RepositoriesController.html new file mode 100644 index 00000000..3a22f71e --- /dev/null +++ b/doc/app/RepositoriesController.html @@ -0,0 +1,593 @@ + + + + + + +class RepositoriesController - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class RepositoriesController

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + archive() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/repositories_controller.rb, line 19
    +def archive
    +  unless can?(current_user, :download_code, @project)
    +    render_404 and return 
    +  end
    +
    +
    +  file_path = @project.archive_repo(params[:ref])
    +
    +  if file_path
    +    # Send file to user
    +    send_file file_path
    +  else
    +    render_404
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + branches() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/repositories_controller.rb, line 11
    +def branches
    +  @branches = @project.branches
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + show() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/repositories_controller.rb, line 7
    +def show
    +  @activities = @project.commits_with_refs(20)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + tags() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/repositories_controller.rb, line 15
    +def tags
    +  @tags = @project.tags
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/Repository.html b/doc/app/Repository.html new file mode 100644 index 00000000..cb68146c --- /dev/null +++ b/doc/app/Repository.html @@ -0,0 +1,1566 @@ + + + + + + +module Repository - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    module Repository

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + archive_repo(ref) + click to toggle source +
    + + +
    + +

    Archive Project to .tar.gz

    + +

    Already packed repo archives stored at +app_root/tmp/repositories/project_name/project_name-commit-id.tag.gz

    + + + +
    +
    # File app/roles/repository.rb, line 157
    +def archive_repo(ref)
    +  ref = ref || self.root_ref
    +  commit = self.commit(ref)
    +  return nil unless commit
    +
    +  # Build file path
    +  file_name = self.code + "-" + commit.id.to_s + ".tar.gz"
    +  storage_path = Rails.root.join("tmp", "repositories", self.code)
    +  file_path = File.join(storage_path, file_name)
    +
    +  # Put files into a directory before archiving
    +  prefix = self.code + "/"
    +
    +  # Create file if not exists
    +  unless File.exists?(file_path)
    +    FileUtils.mkdir_p storage_path
    +    file = self.repo.archive_to_file(ref, prefix,  file_path)
    +  end
    +
    +  file_path
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + branch_names() + click to toggle source +
    + + +
    + +

    Returns an Array of branch names

    + + + +
    +
    # File app/roles/repository.rb, line 53
    +def branch_names
    +  repo.branches.collect(&:name).sort
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + branches() + click to toggle source +
    + + +
    + +

    Returns an Array of Branches

    + + + +
    +
    # File app/roles/repository.rb, line 58
    +def branches
    +  repo.branches.sort_by(&:name)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + commit(commit_id = nil) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/repository.rb, line 15
    +def commit(commit_id = nil)
    +  Commit.find_or_first(repo, commit_id, root_ref)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + commits(ref, path = nil, limit = nil, offset = nil) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/repository.rb, line 31
    +def commits(ref, path = nil, limit = nil, offset = nil)
    +  Commit.commits(repo, ref, path, limit, offset)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + commits_between(from, to) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/repository.rb, line 39
    +def commits_between(from, to)
    +  Commit.commits_between(repo, from, to)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + commits_since(date) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/repository.rb, line 27
    +def commits_since(date)
    +  Commit.commits_since(repo, date)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + commits_with_refs(n = 20) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/repository.rb, line 23
    +def commits_with_refs(n = 20)
    +  Commit.commits_with_refs(repo, n)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + destroy_repository() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/repository.rb, line 93
    +def destroy_repository
    +  git_host.remove_repository(self)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + discover_default_branch() + click to toggle source +
    + + +
    + +

    Discovers the default branch based on the repository’s available branches

    +
    • +

      If no branches are present, returns nil

      +
    • +

      If one branch is present, returns its name

      +
    • +

      If two or more branches are present, returns the one that has a name +matching #root_ref +(default_branch or ‘master’ if default_branch is nil)

      +
    + + + +
    +
    # File app/roles/repository.rb, line 128
    +def discover_default_branch
    +  if branch_names.length == 0
    +    nil
    +  elsif branch_names.length == 1
    +    branch_names.first
    +  else
    +    branch_names.select { |v| v == root_ref }.first
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + empty_repo?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/repository.rb, line 11
    +def empty_repo?
    +  !repo_exists? || !has_commits?
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + fresh_commits(n = 10) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/repository.rb, line 19
    +def fresh_commits(n = 10)
    +  Commit.fresh_commits(repo, n)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + has_commits?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/repository.rb, line 138
    +def has_commits?
    +  !!commit
    +rescue Grit::NoSuchPathError
    +  false
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + has_post_receive_file?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/repository.rb, line 47
    +def has_post_receive_file?
    +  hook_file = File.join(path_to_repo, 'hooks', 'post-receive')
    +  File.exists?(hook_file)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + heads() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/repository.rb, line 103
    +def heads
    +  @heads ||= repo.heads
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + http_url_to_repo() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/repository.rb, line 183
    +def http_url_to_repo
    +  http_url = [Gitlab.config.url, "/", path, ".git"].join('')
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + last_commit_for(ref, path = nil) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/repository.rb, line 35
    +def last_commit_for(ref, path = nil)
    +  commits(ref, path, 1).first
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + open_branches() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/repository.rb, line 113
    +def open_branches
    +  if protected_branches.empty?
    +    self.repo.heads
    +  else
    +    pnames = protected_branches.map(&:name)
    +    self.repo.heads.reject { |h| pnames.include?(h.name) }
    +  end.sort_by(&:name)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + path_to_repo() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/repository.rb, line 85
    +def path_to_repo
    +  File.join(Gitlab.config.git_base_path, "#{path}.git")
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + protected_branch?(branch_name) + click to toggle source +
    + + +
    + +

    Check if current branch name is marked as protected in the system

    + + + +
    +
    # File app/roles/repository.rb, line 188
    +def protected_branch? branch_name
    +  protected_branches.map(&:name).include?(branch_name)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + ref_names() + click to toggle source +
    + + +
    + +

    Returns an Array of branch and tag names

    + + + +
    +
    # File app/roles/repository.rb, line 73
    +def ref_names
    +  [branch_names + tag_names].flatten
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + repo() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/repository.rb, line 77
    +def repo
    +  @repo ||= Grit::Repo.new(path_to_repo)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + repo_exists?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/repository.rb, line 97
    +def repo_exists?
    +  @repo_exists ||= (repo && !repo.branches.empty?)
    +rescue
    +  @repo_exists = false
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + root_ref() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/repository.rb, line 144
    +def root_ref
    +  default_branch || "master"
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + root_ref?(branch) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/repository.rb, line 148
    +def root_ref?(branch)
    +  root_ref == branch
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + satellite() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/repository.rb, line 43
    +def satellite
    +  @satellite ||= Gitlab::Satellite.new(self)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + ssh_url_to_repo() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/repository.rb, line 179
    +def ssh_url_to_repo
    +  url_to_repo
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + tag_names() + click to toggle source +
    + + +
    + +

    Returns an Array of tag names

    + + + +
    +
    # File app/roles/repository.rb, line 63
    +def tag_names
    +  repo.tags.collect(&:name).sort.reverse
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + tags() + click to toggle source +
    + + +
    + +

    Returns an Array of Tags

    + + + +
    +
    # File app/roles/repository.rb, line 68
    +def tags
    +  repo.tags.sort_by(&:name).reverse
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + tree(fcommit, path = nil) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/repository.rb, line 107
    +def tree(fcommit, path = nil)
    +  fcommit = commit if fcommit == :head
    +  tree = fcommit.tree
    +  path ? (tree / path) : tree
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + update_repository() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/repository.rb, line 89
    +def update_repository
    +  git_host.update_repository(self)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + url_to_repo() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/repository.rb, line 81
    +def url_to_repo
    +  git_host.url_to_repo(path)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + valid_repo?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/repository.rb, line 4
    +def valid_repo?
    +  repo
    +rescue
    +  errors.add(:path, "Invalid repository path")
    +  false
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/SearchContext.html b/doc/app/SearchContext.html new file mode 100644 index 00000000..6a3d2bd5 --- /dev/null +++ b/doc/app/SearchContext.html @@ -0,0 +1,599 @@ + + + + + + +class SearchContext - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class SearchContext

    + +
    + +
    + + + + +
    + + + + + + + + +
    +

    Attributes

    + + +
    +
    + params[RW] +
    + +
    + + + +
    +
    + +
    +
    + project_ids[RW] +
    + +
    + + + +
    +
    + +
    + + + + +
    +

    Public Class Methods

    + + +
    + +
    + new(project_ids, params) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/contexts/search_context.rb, line 4
    +def initialize(project_ids, params)
    +  @project_ids, @params = project_ids, params.dup
    +end
    +
    + +
    + + + + +
    + + +
    + +
    +

    Public Instance Methods

    + + +
    + +
    + execute() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/contexts/search_context.rb, line 8
    +def execute
    +  query = params[:search]
    +
    +  return result unless query.present?
    +
    +  result[:projects] = Project.where(id: project_ids).search(query).limit(10)
    +  result[:merge_requests] = MergeRequest.where(project_id: project_ids).search(query).limit(10)
    +  result[:issues] = Issue.where(project_id: project_ids).search(query).limit(10)
    +  result
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + result() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/contexts/search_context.rb, line 19
    +def result
    +  @result ||= {
    +    projects: [],
    +    merge_requests: [],
    +    issues: []
    +  }
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/SearchController.html b/doc/app/SearchController.html new file mode 100644 index 00000000..0c14ea8f --- /dev/null +++ b/doc/app/SearchController.html @@ -0,0 +1,489 @@ + + + + + + +class SearchController - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class SearchController

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + show() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/search_controller.rb, line 2
    +def show
    +  result = SearchContext.new(current_user.project_ids, params).execute
    +
    +  @projects       = result[:projects]
    +  @merge_requests = result[:merge_requests]
    +  @issues         = result[:issues]
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/Snippet.html b/doc/app/Snippet.html new file mode 100644 index 00000000..a24e4494 --- /dev/null +++ b/doc/app/Snippet.html @@ -0,0 +1,667 @@ + + + + + + +class Snippet - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Snippet

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Class Methods

    + + +
    + +
    + content_types() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/snippet.rb, line 23
    +def self.content_types
    +  [
    +    ".rb", ".py", ".pl", ".scala", ".c", ".cpp", ".java",
    +    ".haml", ".html", ".sass", ".scss", ".xml", ".php", ".erb",
    +    ".js", ".sh", ".coffee", ".yml", ".md"
    +  ]
    +end
    +
    + +
    + + + + +
    + + +
    + +
    +

    Public Instance Methods

    + + +
    + +
    + data() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/snippet.rb, line 31
    +def data
    +  content
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + expired?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/snippet.rb, line 47
    +def expired?
    +  expires_at && expires_at < Time.current
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + mode() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/snippet.rb, line 43
    +def mode
    +  nil
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + name() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/snippet.rb, line 39
    +def name
    +  file_name
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + size() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/snippet.rb, line 35
    +def size
    +  0
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/SnippetsController.html b/doc/app/SnippetsController.html new file mode 100644 index 00000000..7585a295 --- /dev/null +++ b/doc/app/SnippetsController.html @@ -0,0 +1,833 @@ + + + + + + +class SnippetsController - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class SnippetsController

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + create() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/snippets_controller.rb, line 26
    +def create
    +  @snippet = @project.snippets.new(params[:snippet])
    +  @snippet.author = current_user
    +  @snippet.save
    +
    +  if @snippet.valid?
    +    redirect_to [@project, @snippet]
    +  else
    +    respond_with(@snippet)
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + destroy() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/snippets_controller.rb, line 55
    +def destroy
    +  return access_denied! unless can?(current_user, :admin_snippet, @snippet)
    +
    +  @snippet.destroy
    +
    +  redirect_to project_snippets_path(@project)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + edit() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/snippets_controller.rb, line 38
    +def edit
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + index() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/snippets_controller.rb, line 18
    +def index
    +  @snippets = @project.snippets
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + new() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/snippets_controller.rb, line 22
    +def new
    +  @snippet = @project.snippets.new
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + raw() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/snippets_controller.rb, line 63
    +def raw 
    +  send_data(
    +    @snippet.content,
    +    type: "text/plain",
    +    disposition: 'inline',
    +    filename: @snippet.file_name
    +  )
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + show() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/snippets_controller.rb, line 51
    +def show
    +  @note = @project.notes.new(noteable: @snippet)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + update() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/snippets_controller.rb, line 41
    +def update
    +  @snippet.update_attributes(params[:snippet])
    +
    +  if @snippet.valid?
    +    redirect_to [@project, @snippet]
    +  else
    +    respond_with(@snippet)
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    +

    Protected Instance Methods

    + + +
    + +
    + authorize_admin_snippet!() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/snippets_controller.rb, line 82
    +def authorize_admin_snippet!
    +  return render_404 unless can?(current_user, :admin_snippet, @snippet)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + authorize_modify_snippet!() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/snippets_controller.rb, line 78
    +def authorize_modify_snippet!
    +  return render_404 unless can?(current_user, :modify_snippet, @snippet)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + snippet() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/snippets_controller.rb, line 74
    +def snippet
    +  @snippet ||= @project.snippets.find(params[:id])
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/SnippetsHelper.html b/doc/app/SnippetsHelper.html new file mode 100644 index 00000000..99c08d51 --- /dev/null +++ b/doc/app/SnippetsHelper.html @@ -0,0 +1,485 @@ + + + + + + +module SnippetsHelper - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    module SnippetsHelper

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + lifetime_select_options() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/helpers/snippets_helper.rb, line 2
    +def lifetime_select_options
    +  options = [
    +      ['forever', nil],
    +      ['1 day',   "#{Date.current + 1.day}"],
    +      ['1 week',  "#{Date.current + 1.week}"],
    +      ['1 month', "#{Date.current + 1.month}"]
    +  ]
    +  options_for_select(options)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/StaticModel.html b/doc/app/StaticModel.html new file mode 100644 index 00000000..9dd1617c --- /dev/null +++ b/doc/app/StaticModel.html @@ -0,0 +1,648 @@ + + + + + + +module StaticModel - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    module StaticModel

    + +
    + +

    Provides an ActiveRecord-like interface to a model whose data is not +persisted to a database.

    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + ==(other) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/static_model.rb, line 40
    +def ==(other)
    +  if other.is_a? StaticModel
    +    id == other.id
    +  else
    +    super
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + [](key) + click to toggle source +
    + + +
    + +

    Used by AR for fetching attributes

    + +

    Pass it along if we respond to it.

    + + + +
    +
    # File app/roles/static_model.rb, line 20
    +def [](key)
    +  send(key) if respond_to?(key)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + destroyed?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/static_model.rb, line 36
    +def destroyed?
    +  false
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + new_record?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/static_model.rb, line 28
    +def new_record?
    +  false
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + persisted?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/static_model.rb, line 32
    +def persisted?
    +  false
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + to_param() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/static_model.rb, line 24
    +def to_param
    +  id
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/StaticModel/ClassMethods.html b/doc/app/StaticModel/ClassMethods.html new file mode 100644 index 00000000..27ee1373 --- /dev/null +++ b/doc/app/StaticModel/ClassMethods.html @@ -0,0 +1,511 @@ + + + + + + +module StaticModel::ClassMethods - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    module StaticModel::ClassMethods

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + base_class() + click to toggle source +
    + + +
    + +

    Used by ActiveRecord’s polymorphic association to set object_type

    + + + +
    +
    # File app/roles/static_model.rb, line 12
    +def base_class
    +  self
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + primary_key() + click to toggle source +
    + + +
    + +

    Used by ActiveRecord’s polymorphic association to set object_id

    + + + +
    +
    # File app/roles/static_model.rb, line 7
    +def primary_key
    +  'id'
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/SystemHook.html b/doc/app/SystemHook.html new file mode 100644 index 00000000..8e35bf27 --- /dev/null +++ b/doc/app/SystemHook.html @@ -0,0 +1,525 @@ + + + + + + +class SystemHook - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class SystemHook

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Class Methods

    + + +
    + +
    + all_hooks_fire(data) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/system_hook.rb, line 2
    +def self.all_hooks_fire(data)
    +  SystemHook.all.each do |sh|
    +    sh.async_execute data
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    +

    Public Instance Methods

    + + +
    + +
    + async_execute(data) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/system_hook.rb, line 8
    +def async_execute(data)
    +  Resque.enqueue(SystemHookWorker, id, data)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/SystemHookObserver.html b/doc/app/SystemHookObserver.html new file mode 100644 index 00000000..20d81963 --- /dev/null +++ b/doc/app/SystemHookObserver.html @@ -0,0 +1,573 @@ + + + + + + +class SystemHookObserver - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class SystemHookObserver

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + after_create(model) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/observers/system_hook_observer.rb, line 4
    +def after_create(model)
    +  if model.kind_of? Project
    +    SystemHook.all_hooks_fire({
    +      event_name: "project_create",
    +      name: model.name,
    +      path: model.path,
    +      project_id: model.id,
    +      owner_name: model.owner.name,
    +      owner_email: model.owner.email,
    +      created_at: model.created_at
    +    })
    +  elsif model.kind_of? User 
    +    SystemHook.all_hooks_fire({
    +      event_name: "user_create",
    +      name: model.name,
    +      email: model.email,
    +      created_at: model.created_at
    +    })
    +
    +  elsif model.kind_of? UsersProject
    +    SystemHook.all_hooks_fire({
    +      event_name: "user_add_to_team",
    +      project_name: model.project.name,
    +      project_path: model.project.path,
    +      project_id: model.project_id,
    +      user_name: model.user.name,
    +      user_email: model.user.email,
    +      project_access: model.repo_access_human,
    +      created_at: model.created_at
    +    })
    +
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + after_destroy(model) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/observers/system_hook_observer.rb, line 38
    +def after_destroy(model)
    +  if model.kind_of? Project
    +    SystemHook.all_hooks_fire({
    +      event_name: "project_destroy",
    +      name: model.name,
    +      path: model.path,
    +      project_id: model.id,
    +      owner_name: model.owner.name,
    +      owner_email: model.owner.email,
    +    })
    +  elsif model.kind_of? User
    +    SystemHook.all_hooks_fire({
    +      event_name: "user_destroy",
    +      name: model.name,
    +      email: model.email
    +    })
    +
    +  elsif model.kind_of? UsersProject
    +    SystemHook.all_hooks_fire({
    +      event_name: "user_remove_from_team",
    +      project_name: model.project.name,
    +      project_path: model.project.path,
    +      project_id: model.project_id,
    +      user_name: model.user.name,
    +      user_email: model.user.email,
    +      project_access: model.repo_access_human
    +    })
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/SystemHookWorker.html b/doc/app/SystemHookWorker.html new file mode 100644 index 00000000..153eb577 --- /dev/null +++ b/doc/app/SystemHookWorker.html @@ -0,0 +1,485 @@ + + + + + + +class SystemHookWorker - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class SystemHookWorker

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Class Methods

    + + +
    + +
    + perform(hook_id, data) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/workers/system_hook_worker.rb, line 4
    +def self.perform(hook_id, data)
    +  SystemHook.find(hook_id).execute data
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/TabHelper.html b/doc/app/TabHelper.html new file mode 100644 index 00000000..43cc08a4 --- /dev/null +++ b/doc/app/TabHelper.html @@ -0,0 +1,621 @@ + + + + + + +module TabHelper - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    module TabHelper

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + branches_tab_class() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/helpers/tab_helper.rb, line 80
    +def branches_tab_class
    +  if current_page?(branches_project_repository_path(@project)) ||
    +    current_controller?(:protected_branches) ||
    +    current_page?(project_repository_path(@project))
    +    'active'
    +  end
    +end
    +
    + +
    + + + + +
    + + + + + +
    + +
    + project_tab_class() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/helpers/tab_helper.rb, line 70
    +def project_tab_class
    +  [:show, :files, :edit, :update].each do |action|
    +    return "active" if current_page?(controller: "projects", action: action, id: @project)
    +  end
    +
    +  if ['snippets', 'hooks', 'deploy_keys', 'team_members'].include? controller.controller_name
    +   "active"
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/TagsHelper.html b/doc/app/TagsHelper.html new file mode 100644 index 00000000..3a98d61f --- /dev/null +++ b/doc/app/TagsHelper.html @@ -0,0 +1,516 @@ + + + + + + +module TagsHelper - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    module TagsHelper

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + tag_list(project) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/helpers/tags_helper.rb, line 6
    +def tag_list project
    +  html = ''
    +  project.tag_list.each do |tag|
    +    html += link_to tag, tag_path(tag)
    +  end
    +
    +  html.html_safe
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + tag_path(tag) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/helpers/tags_helper.rb, line 2
    +def tag_path tag
    +  "/tags/#{tag}"
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/Team.html b/doc/app/Team.html new file mode 100644 index 00000000..3a583694 --- /dev/null +++ b/doc/app/Team.html @@ -0,0 +1,710 @@ + + + + + + +module Team - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    module Team

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + add_user_id_to_team(user_id, access_role) + click to toggle source +
    + + +
    + +

    Add user to project with passed access role by user id

    + + + +
    +
    # File app/roles/team.rb, line 26
    +def add_user_id_to_team(user_id, access_role)
    +  users_projects.create(
    +    user_id: user_id,
    +    project_access: access_role
    +  )
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + add_user_to_team(user, access_role) + click to toggle source +
    + + +
    + +

    Add user to project with passed access role

    + + + +
    +
    # File app/roles/team.rb, line 14
    +def add_user_to_team(user, access_role)
    +  add_user_id_to_team(user.id, access_role)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + add_users_ids_to_team(users_ids, access_role) + click to toggle source +
    + + +
    + +

    Add multiple users to project with same access role by user ids

    + + + +
    +
    # File app/roles/team.rb, line 35
    +def add_users_ids_to_team(users_ids, access_role)
    +  UsersProject.bulk_import(self, users_ids, access_role)
    +  self.update_repository
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + add_users_to_team(users, access_role) + click to toggle source +
    + + +
    + +

    Add multiple users to project with same access role

    + + + +
    +
    # File app/roles/team.rb, line 20
    +def add_users_to_team(users, access_role)
    +  add_users_ids_to_team(users.map(&:id), access_role)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + delete_users_ids_from_team(users_ids) + click to toggle source +
    + + +
    + +

    Delete multiple users from project by user ids

    + + + +
    +
    # File app/roles/team.rb, line 48
    +def delete_users_ids_from_team(users_ids)
    +  UsersProject.bulk_delete(self, users_ids)
    +  self.update_repository
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + team_member_by_id(user_id) + click to toggle source +
    + + +
    + +

    Get Team Member record by user id

    + + + +
    +
    # File app/roles/team.rb, line 8
    +def team_member_by_id(user_id)
    +  users_projects.find_by_user_id(user_id)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + team_member_by_name_or_email(name = nil, email = nil) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/team.rb, line 2
    +def team_member_by_name_or_email(name = nil, email = nil)
    +  user = users.where("name like ? or email like ?", name, email).first
    +  users_projects.where(user: user) if user
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + update_users_ids_to_role(users_ids, access_role) + click to toggle source +
    + + +
    + +

    Update multiple project users to same access role by user ids

    + + + +
    +
    # File app/roles/team.rb, line 42
    +def update_users_ids_to_role(users_ids, access_role)
    +  UsersProject.bulk_update(self, users_ids, access_role)
    +  self.update_repository
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/TeamMembersController.html b/doc/app/TeamMembersController.html new file mode 100644 index 00000000..46af7c9f --- /dev/null +++ b/doc/app/TeamMembersController.html @@ -0,0 +1,698 @@ + + + + + + +class TeamMembersController - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class TeamMembersController

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + apply_import() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/team_members_controller.rb, line 47
    +def apply_import
    +  giver = Project.find(params[:source_project_id])
    +  status = UsersProject.import_team(giver, project)
    +  notice = status ? "Succesfully imported" : "Import failed"
    +
    +  redirect_to project_team_members_path(project), notice: notice
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + create() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/team_members_controller.rb, line 18
    +def create
    +  @project.add_users_ids_to_team(
    +    params[:user_ids],
    +    params[:project_access]
    +  )
    +
    +  redirect_to project_team_index_path(@project)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + destroy() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/team_members_controller.rb, line 37
    +def destroy
    +  @team_member = project.users_projects.find(params[:id])
    +  @team_member.destroy
    +
    +  respond_to do |format|
    +    format.html { redirect_to project_team_index_path(@project) }
    +    format.js { render nothing: true }
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + index() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/team_members_controller.rb, line 6
    +def index
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + new() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/team_members_controller.rb, line 14
    +def new
    +  @team_member = project.users_projects.new
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + show() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/team_members_controller.rb, line 9
    +def show
    +  @team_member = project.users_projects.find(params[:id])
    +  @events = @team_member.user.recent_events.where(:project_id => @project.id).limit(7)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + update() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/team_members_controller.rb, line 27
    +def update
    +  @team_member = project.users_projects.find(params[:id])
    +  @team_member.update_attributes(params[:team_member])
    +
    +  unless @team_member.valid?
    +    flash[:alert] = "User should have at least one role"
    +  end
    +  redirect_to project_team_index_path(@project)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/TestHookContext.html b/doc/app/TestHookContext.html new file mode 100644 index 00000000..e18701cb --- /dev/null +++ b/doc/app/TestHookContext.html @@ -0,0 +1,488 @@ + + + + + + +class TestHookContext - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class TestHookContext

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + execute() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/contexts/test_hook_context.rb, line 2
    +def execute
    +  hook = project.hooks.find(params[:id])
    +  commits = project.commits(project.default_branch, nil, 3)
    +  data = project.post_receive_data(commits.last.id, commits.first.id, "refs/heads/#{project.default_branch}", current_user)
    +  hook.execute(data)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/Tree.html b/doc/app/Tree.html new file mode 100644 index 00000000..f1f414a4 --- /dev/null +++ b/doc/app/Tree.html @@ -0,0 +1,663 @@ + + + + + + +class Tree - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Tree

    + +
    + +
    + + + + +
    + + + + + + + + +
    +

    Attributes

    + + +
    +
    + path[RW] +
    + +
    + + + +
    +
    + +
    +
    + project[RW] +
    + +
    + + + +
    +
    + +
    +
    + ref[RW] +
    + +
    + + + +
    +
    + +
    +
    + tree[RW] +
    + +
    + + + +
    +
    + +
    + + + + +
    +

    Public Class Methods

    + + +
    + +
    + new(raw_tree, project, ref = nil, path = nil) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/tree.rb, line 8
    +def initialize(raw_tree, project, ref = nil, path = nil)
    +  @project, @ref, @path = project, ref, path
    +  @tree = if path.present?
    +            raw_tree / path.dup.force_encoding('ascii-8bit')
    +          else
    +            raw_tree
    +          end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    +

    Public Instance Methods

    + + +
    + +
    + empty?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/tree.rb, line 25
    +def empty?
    +  data.blank?
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + invalid?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/tree.rb, line 21
    +def invalid?
    +  tree.nil?
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + is_blob?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/tree.rb, line 17
    +def is_blob?
    +  tree.is_a?(Grit::Blob)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/TreeController.html b/doc/app/TreeController.html new file mode 100644 index 00000000..1adba0ec --- /dev/null +++ b/doc/app/TreeController.html @@ -0,0 +1,583 @@ + + + + + + +class TreeController - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class TreeController

    + +
    + +

    Controller for viewing a repository’s file structure

    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + edit() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/tree_controller.rb, line 24
    +def edit
    +  @last_commit = @project.last_commit_for(@ref, @path).sha
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + show() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/tree_controller.rb, line 13
    +def show
    +  @hex_path  = Digest::SHA1.hexdigest(@path)
    +  @logs_path = logs_file_project_ref_path(@project, @ref, @path)
    +
    +  respond_to do |format|
    +    format.html
    +    # Disable cache so browser history works
    +    format.js { no_cache_headers }
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + update() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/tree_controller.rb, line 28
    +def update
    +  file_editor = Gitlab::FileEditor.new(current_user, @project, @ref)
    +  update_status = file_editor.update(
    +    @path,
    +    params[:content],
    +    params[:commit_message],
    +    params[:last_commit]
    +  )
    +
    +  if update_status
    +    redirect_to project_tree_path(@project, @id), notice: "Your changes have been successfully commited"
    +  else
    +    flash[:notice] = "Your changes could not be commited, because the file has been changed"
    +    render :edit
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/TreeDecorator.html b/doc/app/TreeDecorator.html new file mode 100644 index 00000000..e6bdc0fa --- /dev/null +++ b/doc/app/TreeDecorator.html @@ -0,0 +1,597 @@ + + + + + + +class TreeDecorator - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class TreeDecorator

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + breadcrumbs(max_links = 2) { |link_to("..", "| ... } + click to toggle source +
    + + +
    + + + + + + + +
    + + + + +
    + + +
    + +
    + readme() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/decorators/tree_decorator.rb, line 32
    +def readme
    +  @readme ||= contents.find { |c| c.is_a?(Grit::Blob) and c.name =~ %r^readme/ }
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + up_dir?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/decorators/tree_decorator.rb, line 23
    +def up_dir?
    +  path.present?
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + up_dir_path() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/decorators/tree_decorator.rb, line 27
    +def up_dir_path
    +  file = File.join(path, "..")
    +  h.project_tree_path(project, h.tree_join(ref, file))
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/TreeHelper.html b/doc/app/TreeHelper.html new file mode 100644 index 00000000..e185ba72 --- /dev/null +++ b/doc/app/TreeHelper.html @@ -0,0 +1,736 @@ + + + + + + +module TreeHelper - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    module TreeHelper

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + allowed_tree_edit?() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/helpers/tree_helper.rb, line 63
    +def allowed_tree_edit?
    +  if @project.protected_branch? @ref
    +    can?(current_user, :push_code_to_protected_branches, @project)
    +  else
    +    can?(current_user, :push_code, @project)
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + gitlab_markdown?(filename) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/helpers/tree_helper.rb, line 50
    +def gitlab_markdown?(filename)
    +  filename.end_with?(*%w(.mdown .md .markdown))
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + markup?(filename) + click to toggle source +
    + + +
    + +

    Public: Determines if a given filename is compatible with GitHub::Markup.

    + +

    filename - Filename string to check

    + +

    Returns boolean

    + + + +
    +
    # File app/helpers/tree_helper.rb, line 45
    +def markup?(filename)
    +  filename.end_with?(*%w(.textile .rdoc .org .creole
    +                         .mediawiki .rst .asciidoc .pod))
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + plain_text_readme?(filename) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/helpers/tree_helper.rb, line 54
    +def plain_text_readme? filename
    +  filename == 'README'
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + render_tree(contents) + click to toggle source +
    + + +
    + +

    Sorts a repository’s tree so that folders are before files and renders +their corresponding partials

    + +

    contents - A Grit::Tree object for the current tree

    + + + +
    +
    # File app/helpers/tree_helper.rb, line 6
    +def render_tree(contents)
    +  # Render Folders before Files/Submodules
    +  folders, files = contents.partition { |v| v.kind_of?(Grit::Tree) }
    +
    +  tree = ""
    +
    +  # Render folders if we have any
    +  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
    +  end
    +
    +  tree.html_safe
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + tree_hex_class(content) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/helpers/tree_helper.rb, line 36
    +def tree_hex_class(content)
    +  "file_#{hexdigest(content.name)}"
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + tree_icon(type) + click to toggle source +
    + + +
    + +

    Return an image icon depending on the file type

    + +

    type - String type of the tree item; either ‘folder’ or ‘file’

    + + + +
    +
    # File app/helpers/tree_helper.rb, line 31
    +def tree_icon(type)
    +  image = type == 'folder' ? 'file_dir.png' : 'file_txt.png'
    +  image_tag(image, size: '16x16')
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + tree_join(*args) + click to toggle source +
    + + +
    + +

    Simple shortcut to File.join

    + + + +
    +
    # File app/helpers/tree_helper.rb, line 59
    +def tree_join(*args)
    +  File.join(*args)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/User.html b/doc/app/User.html new file mode 100644 index 00000000..513f9644 --- /dev/null +++ b/doc/app/User.html @@ -0,0 +1,755 @@ + + + + + + +class User - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class User

    + +
    + +
    + + + + +
    + + + + + + + + +
    +

    Attributes

    + + +
    +
    + force_random_password[RW] +
    + +
    + + + +
    +
    + +
    + + + + +
    +

    Public Class Methods

    + + +
    + +
    + create_from_omniauth(auth, ldap = false) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/user.rb, line 55
    +def create_from_omniauth(auth, ldap = false)
    +  gitlab_auth.create_from_omniauth(auth, ldap)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + filter(filter_name) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/user.rb, line 41
    +def filter filter_name
    +  case filter_name
    +  when "admins"; self.admins
    +  when "blocked"; self.blocked
    +  when "wop"; self.without_projects
    +  else
    +    self.active
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + find_for_ldap_auth(auth, signed_in_resource = nil) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/user.rb, line 63
    +def find_for_ldap_auth(auth, signed_in_resource = nil)
    +  gitlab_auth.find_for_ldap_auth(auth, signed_in_resource)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + find_or_new_for_omniauth(auth) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/user.rb, line 59
    +def find_or_new_for_omniauth(auth)
    +  gitlab_auth.find_or_new_for_omniauth(auth)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + gitlab_auth() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/user.rb, line 67
    +def gitlab_auth
    +  Gitlab::Auth.new
    +end
    +
    + +
    + + + + +
    + + + + + +
    + +
    + without_projects() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/user.rb, line 51
    +def without_projects
    +  where('id NOT IN (SELECT DISTINCT(user_id) FROM users_projects)')
    +end
    +
    + +
    + + + + +
    + + +
    + +
    +

    Public Instance Methods

    + + +
    + +
    + generate_password() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/user.rb, line 76
    +def generate_password
    +  if self.force_random_password
    +    self.password = self.password_confirmation = Devise.friendly_token.first(8)
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/UserObserver.html b/doc/app/UserObserver.html new file mode 100644 index 00000000..95c7249d --- /dev/null +++ b/doc/app/UserObserver.html @@ -0,0 +1,557 @@ + + + + + + +class UserObserver - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class UserObserver

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + after_create(user) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/observers/user_observer.rb, line 2
    +def after_create(user)
    +  log_info("User \"#{user.name}\" (#{user.email}) was created")
    +
    +  Notify.new_user_email(user.id, user.password).deliver
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + after_destroy(user) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/observers/user_observer.rb, line 8
    +def after_destroy user
    +  log_info("User \"#{user.name}\" (#{user.email})  was removed")
    +end
    +
    + +
    + + + + +
    + + +
    + +
    +

    Protected Instance Methods

    + + +
    + +
    + log_info(message) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/observers/user_observer.rb, line 14
    +def log_info message
    +  Gitlab::AppLogger.info message
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/UsersProject.html b/doc/app/UsersProject.html new file mode 100644 index 00000000..f157832b --- /dev/null +++ b/doc/app/UsersProject.html @@ -0,0 +1,904 @@ + + + + + + +class UsersProject - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class UsersProject

    + +
    + +
    + + + + +
    + + + + + + +
    +

    Constants

    +
    + +
    DEVELOPER + +
    + + +
    GUEST + +
    + + +
    MASTER + +
    + + +
    REPORTER + +
    + + +
    +
    + + + + + + +
    +

    Public Class Methods

    + + +
    + +
    + access_roles() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/users_project.rb, line 96
    +def access_roles
    +  {
    +    "Guest"     => GUEST,
    +    "Reporter"  => REPORTER,
    +    "Developer" => DEVELOPER,
    +    "Master"    => MASTER
    +  }
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + bulk_delete(project, user_ids) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/users_project.rb, line 53
    +def bulk_delete(project, user_ids)
    +  UsersProject.transaction do
    +    UsersProject.where(:user_id => user_ids, :project_id => project.id).each do |users_project|
    +      users_project.destroy
    +    end
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + bulk_import(project, user_ids, project_access) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/users_project.rb, line 70
    +def bulk_import(project, user_ids, project_access)
    +  UsersProject.transaction do
    +    user_ids.each do |user_id|
    +      users_project = UsersProject.new(
    +        project_access: project_access,
    +        user_id: user_id
    +      )
    +      users_project.project = project
    +      users_project.save
    +    end
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + bulk_update(project, user_ids, project_access) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/users_project.rb, line 61
    +def bulk_update(project, user_ids, project_access)
    +  UsersProject.transaction do
    +    UsersProject.where(:user_id => user_ids, :project_id => project.id).each do |users_project|
    +      users_project.project_access = project_access
    +      users_project.save
    +    end
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + import_team(source_project, target_project) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/users_project.rb, line 24
    +def import_team(source_project, target_project)
    +  UsersProject.without_repository_callback do
    +    UsersProject.transaction do
    +      team = source_project.users_projects.all
    +
    +      team.each do |tm|
    +        # Skip if user already present in team
    +        next if target_project.users.include?(tm.user)
    +
    +        new_tm = tm.dup
    +        new_tm.id = nil
    +        new_tm.project_id = target_project.id
    +        new_tm.save
    +      end
    +    end
    +  end
    +
    +  target_project.update_repository
    +  true
    +rescue
    +  false
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + user_bulk_import(user, project_ids, project_access) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/users_project.rb, line 83
    +def user_bulk_import(user, project_ids, project_access)
    +  UsersProject.transaction do
    +    project_ids.each do |project_id|
    +      users_project = UsersProject.new(
    +        project_access: project_access,
    +      )
    +      users_project.project_id = project_id
    +      users_project.user_id = user.id
    +      users_project.save
    +    end
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + without_repository_callback() { || ... } + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/users_project.rb, line 47
    +def without_repository_callback
    +  UsersProject.skip_callback(:destroy, :after, :update_repository)
    +  yield
    +  UsersProject.set_callback(:destroy, :after, :update_repository)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    +

    Public Instance Methods

    + + +
    + +
    + project_access_human() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/users_project.rb, line 114
    +def project_access_human
    +  Project.access_options.key(self.project_access)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + repo_access_human() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/users_project.rb, line 118
    +def repo_access_human
    +  self.class.access_roles.invert[self.project_access]
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + role_access() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/users_project.rb, line 106
    +def role_access
    +  project_access
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + update_repository() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/users_project.rb, line 110
    +def update_repository
    +  git_host.update_repository(project)
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/UsersProjectObserver.html b/doc/app/UsersProjectObserver.html new file mode 100644 index 00000000..7c7b90a0 --- /dev/null +++ b/doc/app/UsersProjectObserver.html @@ -0,0 +1,558 @@ + + + + + + +class UsersProjectObserver - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class UsersProjectObserver

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + after_commit(users_project) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/observers/users_project_observer.rb, line 2
    +def after_commit(users_project)
    +  return if users_project.destroyed?
    +  Notify.project_access_granted_email(users_project.id).deliver
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + after_create(users_project) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/observers/users_project_observer.rb, line 7
    +def after_create(users_project)
    +  Event.create(
    +    project_id: users_project.project.id,
    +    action: Event::Joined,
    +    author_id: users_project.user.id
    +  )
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + after_destroy(users_project) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/observers/users_project_observer.rb, line 15
    +def after_destroy(users_project)
    +  Event.create(
    +    project_id: users_project.project.id,
    +    action: Event::Left,
    +    author_id: users_project.user.id
    +  )
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/Votes.html b/doc/app/Votes.html new file mode 100644 index 00000000..9fa2a0b7 --- /dev/null +++ b/doc/app/Votes.html @@ -0,0 +1,615 @@ + + + + + + +module Votes - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    module Votes

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + downvotes() + click to toggle source +
    + + +
    + +

    Return the number of -1 comments (downvotes)

    + + + +
    +
    # File app/roles/votes.rb, line 16
    +def downvotes
    +  notes.select(&:downvote?).size
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + downvotes_in_percent() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/votes.rb, line 20
    +def downvotes_in_percent
    +  if votes_count.zero?
    +    0
    +  else
    +    100.0 - upvotes_in_percent
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + upvotes() + click to toggle source +
    + + +
    + +

    Return the number of +1 comments (upvotes)

    + + + +
    +
    # File app/roles/votes.rb, line 3
    +def upvotes
    +  notes.select(&:upvote?).size
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + upvotes_in_percent() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/roles/votes.rb, line 7
    +def upvotes_in_percent
    +  if votes_count.zero?
    +    0
    +  else
    +    100.0 / votes_count * upvotes
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + votes_count() + click to toggle source +
    + + +
    + +

    Return the total number of votes

    + + + +
    +
    # File app/roles/votes.rb, line 29
    +def votes_count
    +  upvotes + downvotes
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/WebHook.html b/doc/app/WebHook.html new file mode 100644 index 00000000..44a89adb --- /dev/null +++ b/doc/app/WebHook.html @@ -0,0 +1,506 @@ + + + + + + +class WebHook - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class WebHook

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + execute(data) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/web_hook.rb, line 12
    +def execute(data)
    +  parsed_url = URI.parse(url)
    +  if parsed_url.userinfo.blank?
    +    WebHook.post(url, body: data.to_json, headers: { "Content-Type" => "application/json" })
    +  else
    +    post_url = url.gsub("#{parsed_url.userinfo}@", "")
    +    WebHook.post(post_url,
    +                 body: data.to_json,
    +                 headers: {"Content-Type" => "application/json"},
    +                 basic_auth: {username: parsed_url.user, password: parsed_url.password})
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/Wiki.html b/doc/app/Wiki.html new file mode 100644 index 00000000..c2bf5b61 --- /dev/null +++ b/doc/app/Wiki.html @@ -0,0 +1,567 @@ + + + + + + +class Wiki - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class Wiki

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Protected Class Methods

    + + +
    + +
    + regenerate_from(wiki) + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/wiki.rb, line 20
    +def self.regenerate_from wiki
    +  regenerated_field = [:slug, :content, :title]
    +
    +  new_wiki = Wiki.new
    +  regenerated_field.each do |field|
    +    new_wiki.send("#{field}=", wiki.send(field))
    +  end
    +  new_wiki
    +end
    +
    + +
    + + + + +
    + + +
    + +
    +

    Public Instance Methods

    + + +
    + +
    + to_param() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/wiki.rb, line 14
    +def to_param
    +  slug
    +end
    +
    + +
    + + + + +
    + + +
    + +
    +

    Protected Instance Methods

    + + +
    + +
    + set_slug() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/models/wiki.rb, line 30
    +def set_slug
    +  self.slug = self.title.parameterize
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/WikisController.html b/doc/app/WikisController.html new file mode 100644 index 00000000..4a4e930c --- /dev/null +++ b/doc/app/WikisController.html @@ -0,0 +1,676 @@ + + + + + + +class WikisController - Rails Application Documentation + + + + + + + + + + + + + + + + +
    +

    class WikisController

    + +
    + +
    + + + + +
    + + + + + + + + + + +
    +

    Public Instance Methods

    + + +
    + +
    + create() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/wikis_controller.rb, line 36
    +def create
    +  @wiki = @project.wikis.new(params[:wiki])
    +  @wiki.user = current_user
    +
    +  respond_to do |format|
    +    if @wiki.save
    +      format.html { redirect_to [@project, @wiki], notice: 'Wiki was successfully updated.' }
    +    else
    +      format.html { render action: "edit" }
    +    end
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + destroy() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/wikis_controller.rb, line 53
    +def destroy
    +  @wikis = @project.wikis.where(slug: params[:id]).delete_all
    +
    +  respond_to do |format|
    +    format.html { redirect_to project_wiki_path(@project, :index), notice: "Page was successfully deleted" }
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + edit() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/wikis_controller.rb, line 31
    +def edit
    +  @wiki = @project.wikis.where(slug: params[:id]).order("created_at").last
    +  @wiki = Wiki.regenerate_from @wiki
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + history() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/wikis_controller.rb, line 49
    +def history
    +  @wikis = @project.wikis.where(slug: params[:id]).order("created_at")
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + pages() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/wikis_controller.rb, line 6
    +def pages
    +  @wikis = @project.wikis.group(:slug).order("created_at")
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + show() + click to toggle source +
    + + +
    + + + + + +
    +
    # File app/controllers/wikis_controller.rb, line 10
    +def show
    +  if params[:old_page_id]
    +    @wiki = @project.wikis.find(params[:old_page_id])
    +  else
    +    @wiki = @project.wikis.where(slug: params[:id]).order("created_at").last
    +  end
    +
    +  @note = @project.notes.new(noteable: @wiki)
    +
    +  if @wiki
    +    render 'show'
    +  else
    +    if can?(current_user, :write_wiki, @project)
    +      @wiki = @project.wikis.new(slug: params[:id])
    +      render 'edit'
    +    else
    +      render 'empty'
    +    end
    +  end
    +end
    +
    + +
    + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/doc/app/created.rid b/doc/app/created.rid new file mode 100644 index 00000000..972a7e6f --- /dev/null +++ b/doc/app/created.rid @@ -0,0 +1,135 @@ +Thu, 25 Oct 2012 11:45:21 +0300 +doc/README_FOR_APP Wed, 06 Jun 2012 13:56:49 +0300 +app/observers/issue_observer.rb Fri, 21 Sep 2012 15:47:07 +0300 +app/observers/key_observer.rb Fri, 21 Sep 2012 15:47:07 +0300 +app/observers/project_observer.rb Fri, 21 Sep 2012 15:47:07 +0300 +app/observers/activity_observer.rb Wed, 22 Aug 2012 14:52:27 +0300 +app/observers/user_observer.rb Fri, 21 Sep 2012 15:47:07 +0300 +app/observers/merge_request_observer.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/observers/note_observer.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/observers/system_hook_observer.rb Wed, 22 Aug 2012 14:52:27 +0300 +app/observers/users_project_observer.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/contexts/test_hook_context.rb Wed, 22 Aug 2012 14:52:27 +0300 +app/contexts/commit_load_context.rb Mon, 22 Oct 2012 16:08:19 +0300 +app/contexts/notes/create_context.rb Wed, 22 Aug 2012 14:52:27 +0300 +app/contexts/notes/load_context.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/contexts/search_context.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/contexts/issues_bulk_update_context.rb Wed, 22 Aug 2012 14:52:27 +0300 +app/contexts/base_context.rb Wed, 22 Aug 2012 14:52:27 +0300 +app/contexts/merge_requests_load_context.rb Mon, 22 Oct 2012 16:08:19 +0300 +app/contexts/issues_list_context.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/mailers/notify.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/roles/team.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/roles/push_event.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/roles/votes.rb Fri, 21 Sep 2012 15:47:07 +0300 +app/roles/issue_commonality.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/roles/git_host.rb Fri, 21 Sep 2012 15:47:07 +0300 +app/roles/authority.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/roles/push_observer.rb Fri, 21 Sep 2012 15:47:07 +0300 +app/roles/account.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/roles/static_model.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/roles/repository.rb Wed, 24 Oct 2012 12:43:00 +0300 +app/workers/post_receive.rb Wed, 22 Aug 2012 14:52:27 +0300 +app/workers/system_hook_worker.rb Wed, 22 Aug 2012 14:52:27 +0300 +app/controllers/issues_controller.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/controllers/labels_controller.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/controllers/project_resource_controller.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/controllers/dashboard_controller.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/controllers/projects_controller.rb Tue, 23 Oct 2012 11:29:23 +0300 +app/controllers/repositories_controller.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/controllers/milestones_controller.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/controllers/hooks_controller.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/controllers/blob_controller.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/controllers/merge_requests_controller.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/controllers/admin/logs_controller.rb Fri, 21 Sep 2012 15:47:07 +0300 +app/controllers/admin/dashboard_controller.rb Fri, 21 Sep 2012 15:47:07 +0300 +app/controllers/admin/projects_controller.rb Fri, 21 Sep 2012 15:47:07 +0300 +app/controllers/admin/hooks_controller.rb Fri, 21 Sep 2012 15:47:07 +0300 +app/controllers/admin/team_members_controller.rb Fri, 21 Sep 2012 15:47:07 +0300 +app/controllers/admin/groups_controller.rb Wed, 24 Oct 2012 12:43:00 +0300 +app/controllers/admin/users_controller.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/controllers/admin/resque_controller.rb Fri, 21 Sep 2012 15:47:07 +0300 +app/controllers/blame_controller.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/controllers/keys_controller.rb Wed, 26 Sep 2012 13:10:32 +0300 +app/controllers/admin_controller.rb Wed, 26 Sep 2012 13:10:32 +0300 +app/controllers/errors_controller.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/controllers/help_controller.rb Wed, 06 Jun 2012 13:56:49 +0300 +app/controllers/profile_controller.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/controllers/commits_controller.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/controllers/team_members_controller.rb Wed, 24 Oct 2012 16:14:03 +0300 +app/controllers/omniauth_callbacks_controller.rb Fri, 21 Sep 2012 15:47:07 +0300 +app/controllers/protected_branches_controller.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/controllers/snippets_controller.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/controllers/groups_controller.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/controllers/compare_controller.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/controllers/wikis_controller.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/controllers/search_controller.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/controllers/deploy_keys_controller.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/controllers/tree_controller.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/controllers/commit_controller.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/controllers/refs_controller.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/controllers/application_controller.rb Wed, 24 Oct 2012 14:17:22 +0300 +app/controllers/notes_controller.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/uploaders/attachment_uploader.rb Wed, 22 Aug 2012 14:52:27 +0300 +app/models/tree.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/models/project.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/models/issue.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/models/project_hook.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/models/group.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/models/key.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/models/snippet.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/models/user.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/models/note.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/models/wiki.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/models/users_project.rb Wed, 24 Oct 2012 16:14:03 +0300 +app/models/web_hook.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/models/ability.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/models/milestone.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/models/commit.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/models/protected_branch.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/models/merge_request.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/models/event.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/models/system_hook.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/decorators/commit_decorator.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/decorators/event_decorator.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/decorators/application_decorator.rb Fri, 21 Sep 2012 15:40:04 +0300 +app/decorators/tree_decorator.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/helpers/tags_helper.rb Wed, 06 Jun 2012 13:56:49 +0300 +app/helpers/application_helper.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/helpers/gitlab_markdown_helper.rb Fri, 21 Sep 2012 15:47:07 +0300 +app/helpers/notes_helper.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/helpers/profile_helper.rb Fri, 21 Sep 2012 15:47:07 +0300 +app/helpers/tree_helper.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/helpers/commits_helper.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/helpers/projects_helper.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/helpers/events_helper.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/helpers/issues_helper.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/helpers/tab_helper.rb Mon, 22 Oct 2012 16:08:18 +0300 +app/helpers/snippets_helper.rb Wed, 06 Jun 2012 13:56:49 +0300 +app/helpers/merge_requests_helper.rb Mon, 22 Oct 2012 16:08:18 +0300 +lib/file_size_validator.rb Wed, 22 Aug 2012 14:52:27 +0300 +lib/api.rb Fri, 21 Sep 2012 15:47:07 +0300 +lib/gitlab/encode.rb Wed, 22 Aug 2012 14:52:27 +0300 +lib/gitlab/graph_commit.rb Fri, 21 Sep 2012 15:47:07 +0300 +lib/gitlab/theme.rb Tue, 26 Jun 2012 16:08:37 +0300 +lib/gitlab/app_logger.rb Fri, 21 Sep 2012 15:47:07 +0300 +lib/gitlab/inline_diff.rb Mon, 22 Oct 2012 16:08:19 +0300 +lib/gitlab/merge.rb Mon, 22 Oct 2012 16:08:19 +0300 +lib/gitlab/backend/gitolite.rb Fri, 21 Sep 2012 15:47:07 +0300 +lib/gitlab/backend/grack_auth.rb Mon, 22 Oct 2012 19:02:02 +0300 +lib/gitlab/backend/gitolite_config.rb Mon, 22 Oct 2012 16:08:19 +0300 +lib/gitlab/git_logger.rb Fri, 21 Sep 2012 15:47:07 +0300 +lib/gitlab/markdown.rb Mon, 22 Oct 2012 16:08:19 +0300 +lib/gitlab/file_editor.rb Mon, 22 Oct 2012 16:08:19 +0300 +lib/gitlab/logger.rb Mon, 22 Oct 2012 16:08:19 +0300 +lib/gitlab/satellite.rb Mon, 22 Oct 2012 16:08:19 +0300 +lib/gitlab/auth.rb Mon, 22 Oct 2012 16:08:19 +0300 +lib/extracts_path.rb Mon, 22 Oct 2012 16:08:19 +0300 +lib/api/issues.rb Fri, 21 Sep 2012 15:47:07 +0300 +lib/api/projects.rb Mon, 22 Oct 2012 16:08:18 +0300 +lib/api/helpers.rb Mon, 22 Oct 2012 16:08:18 +0300 +lib/api/entities.rb Fri, 21 Sep 2012 15:47:07 +0300 +lib/api/session.rb Fri, 21 Sep 2012 15:47:07 +0300 +lib/api/users.rb Mon, 22 Oct 2012 16:08:19 +0300 +lib/api/milestones.rb Fri, 21 Sep 2012 15:47:07 +0300 +lib/redcarpet/render/gitlab_html.rb Wed, 22 Aug 2012 14:52:27 +0300 diff --git a/doc/app/doc/README_FOR_APP.html b/doc/app/doc/README_FOR_APP.html new file mode 100644 index 00000000..9456512e --- /dev/null +++ b/doc/app/doc/README_FOR_APP.html @@ -0,0 +1,399 @@ + + + + + + +README_FOR_APP - Rails Application Documentation + + + + + + + + + + + + + + + + +
    + +

    Use this README file to introduce your application and point to useful +places in the API for learning more. Run “rake doc:app” to generate API +documentation for your models, controllers, helpers, and libraries.

    + +
    + + + + + diff --git a/doc/app/images/add.png b/doc/app/images/add.png new file mode 100755 index 00000000..6332fefe Binary files /dev/null and b/doc/app/images/add.png differ diff --git a/doc/app/images/brick.png b/doc/app/images/brick.png new file mode 100644 index 00000000..7851cf34 Binary files /dev/null and b/doc/app/images/brick.png differ diff --git a/doc/app/images/brick_link.png b/doc/app/images/brick_link.png new file mode 100644 index 00000000..9ebf013a Binary files /dev/null and b/doc/app/images/brick_link.png differ diff --git a/doc/app/images/bug.png b/doc/app/images/bug.png new file mode 100644 index 00000000..2d5fb90e Binary files /dev/null and b/doc/app/images/bug.png differ diff --git a/doc/app/images/bullet_black.png b/doc/app/images/bullet_black.png new file mode 100644 index 00000000..57619706 Binary files /dev/null and b/doc/app/images/bullet_black.png differ diff --git a/doc/app/images/bullet_toggle_minus.png b/doc/app/images/bullet_toggle_minus.png new file mode 100644 index 00000000..b47ce55f Binary files /dev/null and b/doc/app/images/bullet_toggle_minus.png differ diff --git a/doc/app/images/bullet_toggle_plus.png b/doc/app/images/bullet_toggle_plus.png new file mode 100644 index 00000000..9ab4a896 Binary files /dev/null and b/doc/app/images/bullet_toggle_plus.png differ diff --git a/doc/app/images/date.png b/doc/app/images/date.png new file mode 100644 index 00000000..783c8335 Binary files /dev/null and b/doc/app/images/date.png differ diff --git a/doc/app/images/delete.png b/doc/app/images/delete.png new file mode 100755 index 00000000..08f24936 Binary files /dev/null and b/doc/app/images/delete.png differ diff --git a/doc/app/images/find.png b/doc/app/images/find.png new file mode 100644 index 00000000..15474796 Binary files /dev/null and b/doc/app/images/find.png differ diff --git a/doc/app/images/loadingAnimation.gif b/doc/app/images/loadingAnimation.gif new file mode 100644 index 00000000..82290f48 Binary files /dev/null and b/doc/app/images/loadingAnimation.gif differ diff --git a/doc/app/images/macFFBgHack.png b/doc/app/images/macFFBgHack.png new file mode 100644 index 00000000..c6473b32 Binary files /dev/null and b/doc/app/images/macFFBgHack.png differ diff --git a/doc/app/images/package.png b/doc/app/images/package.png new file mode 100644 index 00000000..da3c2a2d Binary files /dev/null and b/doc/app/images/package.png differ diff --git a/doc/app/images/page_green.png b/doc/app/images/page_green.png new file mode 100644 index 00000000..de8e003f Binary files /dev/null and b/doc/app/images/page_green.png differ diff --git a/doc/app/images/page_white_text.png b/doc/app/images/page_white_text.png new file mode 100644 index 00000000..813f712f Binary files /dev/null and b/doc/app/images/page_white_text.png differ diff --git a/doc/app/images/page_white_width.png b/doc/app/images/page_white_width.png new file mode 100644 index 00000000..1eb88094 Binary files /dev/null and b/doc/app/images/page_white_width.png differ diff --git a/doc/app/images/plugin.png b/doc/app/images/plugin.png new file mode 100644 index 00000000..6187b15a Binary files /dev/null and b/doc/app/images/plugin.png differ diff --git a/doc/app/images/ruby.png b/doc/app/images/ruby.png new file mode 100644 index 00000000..f763a168 Binary files /dev/null and b/doc/app/images/ruby.png differ diff --git a/doc/app/images/tag_blue.png b/doc/app/images/tag_blue.png new file mode 100755 index 00000000..3f02b5f8 Binary files /dev/null and b/doc/app/images/tag_blue.png differ diff --git a/doc/app/images/tag_green.png b/doc/app/images/tag_green.png new file mode 100644 index 00000000..83ec984b Binary files /dev/null and b/doc/app/images/tag_green.png differ diff --git a/doc/app/images/transparent.png b/doc/app/images/transparent.png new file mode 100644 index 00000000..d665e179 Binary files /dev/null and b/doc/app/images/transparent.png differ diff --git a/doc/app/images/wrench.png b/doc/app/images/wrench.png new file mode 100644 index 00000000..5c8213fe Binary files /dev/null and b/doc/app/images/wrench.png differ diff --git a/doc/app/images/wrench_orange.png b/doc/app/images/wrench_orange.png new file mode 100644 index 00000000..565a9330 Binary files /dev/null and b/doc/app/images/wrench_orange.png differ diff --git a/doc/app/images/zoom.png b/doc/app/images/zoom.png new file mode 100644 index 00000000..908612e3 Binary files /dev/null and b/doc/app/images/zoom.png differ diff --git a/doc/app/index.html b/doc/app/index.html new file mode 100644 index 00000000..a46b4fbc --- /dev/null +++ b/doc/app/index.html @@ -0,0 +1,392 @@ + + + + + + +Rails Application Documentation + + + + + + + + + + + + + + + + +

    This is the API documentation for Rails Application Documentation. + + +

    + diff --git a/doc/app/js/darkfish.js b/doc/app/js/darkfish.js new file mode 100644 index 00000000..4be722fa --- /dev/null +++ b/doc/app/js/darkfish.js @@ -0,0 +1,153 @@ +/** + * + * Darkfish Page Functions + * $Id: darkfish.js 53 2009-01-07 02:52:03Z deveiant $ + * + * Author: Michael Granger + * + */ + +/* Provide console simulation for firebug-less environments */ +if (!("console" in window) || !("firebug" in console)) { + var names = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml", + "group", "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"]; + + window.console = {}; + for (var i = 0; i < names.length; ++i) + window.console[names[i]] = function() {}; +}; + + +/** + * Unwrap the first element that matches the given @expr@ from the targets and return them. + */ +$.fn.unwrap = function( expr ) { + return this.each( function() { + $(this).parents( expr ).eq( 0 ).after( this ).remove(); + }); +}; + + +function showSource( e ) { + var target = e.target; + var codeSections = $(target). + parents('.method-detail'). + find('.method-source-code'); + + $(target). + parents('.method-detail'). + find('.method-source-code'). + slideToggle(); +}; + +function hookSourceViews() { + $('.method-heading').click( showSource ); +}; + +function toggleDebuggingSection() { + $('.debugging-section').slideToggle(); +}; + +function hookDebuggingToggle() { + $('#debugging-toggle img').click( toggleDebuggingSection ); +}; + +function hookTableOfContentsToggle() { + $('.indexpage li .toc-toggle').each( function() { + $(this).click( function() { + $(this).toggleClass('open'); + }); + + var section = $(this).next(); + + $(this).click( function() { + section.slideToggle(); + }); + }); +} + +function hookSearch() { + var input = $('#search-field').eq(0); + var result = $('#search-results').eq(0); + $(result).show(); + + var search_section = $('#search-section').get(0); + $(search_section).show(); + + var search = new Search(search_data, input, result); + + search.renderItem = function(result) { + var li = document.createElement('li'); + var html = ''; + + // TODO add relative path to + + + + + + + + + + +

    Table of Contents - Rails Application Documentation

    + +

    Pages

    + + +

    Classes/Modules

    + + +

    Methods

    + + + + + diff --git a/doc/development.md b/doc/development.md new file mode 100644 index 00000000..b7213adc --- /dev/null +++ b/doc/development.md @@ -0,0 +1,36 @@ +## Development tips: + + +### Installation + +Install the Gitlab development in a virtual machine with the [Gitlab Vagrant virtual machine](https://github.com/gitlabhq/gitlab-vagrant-vm). Installing it in a virtual machine makes it much easier to set up all the dependencies for integration testing. + + +### Start application in development mode + +#### 1. Via foreman + + bundle exec foreman start -p 3000 + +#### 2. Manually + + bundle exec rails s + bundle exec rake environment resque:work QUEUE=* VVERBOSE=1 + + +### Test DB setup & seed + + bundle exec rake db:setup RAILS_ENV=test + bundle exec rake db:seed_fu RAILS_ENV=test + + +### Run the Tests + + # All in one + bundle exec rake gitlab:test + + # Rspec + bundle exec rake spec + + # Spinach + bundle exec rake spinach diff --git a/doc/install/databases.md b/doc/install/databases.md index fade5d4f..b7beff26 100644 --- a/doc/install/databases.md +++ b/doc/install/databases.md @@ -1,51 +1,71 @@ -# Setup Database +# Databases: -GitLab supports the following databases: +GitLab use mysql as default database but you are free to use PostgreSQL or SQLite. -* MySQL (preferred) -* PostgreSQL +## SQLite + + sudo apt-get install -y sqlite3 libsqlite3-dev ## MySQL - # Install the database packages sudo apt-get install -y mysql-server mysql-client libmysqlclient-dev # Login to MySQL - mysql -u root -p - - # Create a user for GitLab. (change $password to a real password) - mysql> CREATE USER 'gitlab'@'localhost' IDENTIFIED BY '$password'; + $ mysql -u root -p # Create the GitLab production database mysql> CREATE DATABASE IF NOT EXISTS `gitlabhq_production` DEFAULT CHARACTER SET `utf8` COLLATE `utf8_unicode_ci`; - # Grant the GitLab user necessary permissopns on the table. + # Create the MySQL User change $password to a real password + mysql> CREATE USER 'gitlab'@'localhost' IDENTIFIED BY '$password'; + + # Grant proper permissions to the MySQL User mysql> GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER ON `gitlabhq_production`.* TO 'gitlab'@'localhost'; - # Quit the database session - mysql> \q - - # Try connecting to the new database with the new user - sudo -u git -H mysql -u gitlab -p -D gitlabhq_production ## PostgreSQL - # Install the database packages - sudo apt-get install -y postgresql-9.1 libpq-dev + sudo apt-get install -y postgresql-9.1 postgresql-server-dev-9.1 - # Login to PostgreSQL + # Connect to database server sudo -u postgres psql -d template1 - # Create a user for GitLab. (change $password to a real password) - template1=# CREATE USER git WITH PASSWORD '$password'; + # Add a user called gitlab. Change $password to a real password + template1=# CREATE USER gitlab WITH PASSWORD '$password'; # Create the GitLab production database & grant all privileges on database - template1=# CREATE DATABASE gitlabhq_production OWNER git; + template1=# CREATE DATABASE gitlabhq_production OWNER gitlab; - # Quit the database session + # Quit from PostgreSQL server template1=# \q - # Try connecting to the new database with the new user - sudo -u git -H psql -d gitlabhq_production + # Try connect to new database + sudo -u gitlab psql -d gitlabhq_production + + + +#### Select the database you want to use + + # SQLite + sudo -u gitlab cp config/database.yml.sqlite config/database.yml + + # Mysql + sudo -u gitlab cp config/database.yml.mysql config/database.yml + + # PostgreSQL + sudo -u gitlab cp config/database.yml.postgresql config/database.yml + + # make sure to update username/password in config/database.yml + +#### Install gems + + # mysql + sudo -u gitlab -H bundle install --without development test sqlite postgres --deployment + + # or postgres + sudo -u gitlab -H bundle install --without development test sqlite mysql --deployment + + # or sqlite + sudo -u gitlab -H bundle install --without development test mysql postgres --deployment diff --git a/doc/install/installation.md b/doc/install/installation.md index a9ae4b1e..07ed0b0f 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -1,318 +1,283 @@ -# Important notes +_This installation guide created for Debian/Ubuntu and properly tested._ -This installation guide was created for and tested on **Debian/Ubuntu** operating systems. Please read [`doc/install/requirements.md`](./requirements.md) for hardware and operating system requirements. +_Checkout requirements before setup_ -This is the official installation guide to set up a production server. To set up a **development installation** or for many other installation options please consult [the installation section in the readme](https://github.com/gitlabhq/gitlabhq#installation). -The following steps have been known to work. Please **use caution when you deviate** from this guide. Make sure you don't violate any assumptions GitLab makes about its environment. +### IMPORTANT -If you find a bug/error in this guide please **submit a pull request** following the [`contributing guide`](../../CONTRIBUTING.md). +Please make sure you have followed all the steps below before posting to the mailing list with installation and configuration questions. + +Only create a GitHub Issue if you want a specific part of this installation guide updated. + +Also read the [Read this before you submit an issue](https://github.com/gitlabhq/gitlabhq/wiki/Read-this-before-you-submit-an-issue) wiki page. - - - -# Overview +# Basic setup -The GitLab installation consists of setting up the following components: +The basic installation will provide you a GitLab setup with options: -1. Packages / Dependencies +1. ruby 1.9.3 +2. mysql as main db +3. gitolite v3 fork by gitlab +4. nginx + unicorn + +The installation consists of next steps: + +1. Packages / dependencies 2. Ruby -3. System Users -4. GitLab shell -5. Database -6. GitLab -7. Nginx +3. Users +4. Gitolite +5. Mysql +6. GitLab. +7. Nginx -# 1. Packages / Dependencies +# 1. Packages / dependencies -`sudo` is not installed on Debian by default. Make sure your system is -up-to-date and install it. +*Keep in mind that `sudo` is not installed on Debian by default. You should install it as root:* - # run as root - apt-get update - apt-get upgrade - apt-get install sudo + apt-get update && apt-get upgrade && apt-get install sudo -**Note:** -Vim is an editor that is used here whenever there are files that need to be -edited by hand. But, you can use any editor you like instead. +Now install the required packages: - # Install vim - sudo apt-get install -y vim + sudo apt-get update + sudo apt-get upgrade -Install the required packages: + sudo apt-get install -y wget curl gcc checkinstall libxml2-dev libxslt-dev libcurl4-openssl-dev libreadline6-dev libc6-dev libssl-dev libmysql++-dev make build-essential zlib1g-dev libicu-dev redis-server openssh-server git-core python-dev python-pip libyaml-dev postfix libpq-dev - sudo apt-get install -y build-essential zlib1g-dev libyaml-dev libssl-dev libgdbm-dev libreadline-dev libncurses5-dev libffi-dev curl git-core openssh-server redis-server postfix checkinstall libxml2-dev libxslt-dev libcurl4-openssl-dev libicu-dev - -Make sure you have the right version of Python installed. - - # Install Python - sudo apt-get install python - - # Make sure that Python is 2.5+ (3.x is not supported at the moment) - python --version - - # If it's Python 3 you might need to install Python 2 separately - sudo apt-get install python2.7 - - # Make sure you can access Python via python2 - python2 --version - - # If you get a "command not found" error create a link to the python binary - sudo ln -s /usr/bin/python /usr/bin/python2 + sudo pip install pygments -# 2. Ruby +# 2. Install Ruby -Download and compile it: - - mkdir /tmp/ruby && cd /tmp/ruby - curl --progress http://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.3-p392.tar.gz | tar xz - cd ruby-1.9.3-p392 + wget http://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.3-p194.tar.gz + tar xfvz ruby-1.9.3-p194.tar.gz + cd ruby-1.9.3-p194 ./configure make sudo make install -Install the Bundler Gem: +# 3. Users - sudo gem install bundler +Create user for git: + + sudo adduser \ + --system \ + --shell /bin/sh \ + --gecos 'git version control' \ + --group \ + --disabled-password \ + --home /home/git \ + git + +Create user for GitLab: + + # ubuntu/debian + sudo adduser --disabled-login --gecos 'gitlab system' gitlab + +Add your users to groups: + + sudo usermod -a -G git gitlab + sudo usermod -a -G gitlab git + +Generate key: + + sudo -H -u gitlab ssh-keygen -q -N '' -t rsa -f /home/gitlab/.ssh/id_rsa -# 3. System Users +# 4. Gitolite -Create a `git` user for Gitlab: +Clone GitLab's fork of the Gitolite source code: - sudo adduser --disabled-login --gecos 'GitLab' git + sudo -H -u git git clone -b gl-v304 https://github.com/gitlabhq/gitolite.git /home/git/gitolite +Setup: -# 4. GitLab shell - -GitLab Shell is a ssh access and repository management software developed specially for GitLab. - - # Login as git - sudo su git - - # Go to home directory cd /home/git + sudo -u git -H mkdir bin + sudo -u git sh -c 'echo -e "PATH=\$PATH:/home/git/bin\nexport PATH" >> /home/git/.profile' + sudo -u git sh -c 'gitolite/install -ln /home/git/bin' - # Clone gitlab shell - git clone https://github.com/gitlabhq/gitlab-shell.git + sudo cp /home/gitlab/.ssh/id_rsa.pub /home/git/gitlab.pub + sudo chmod 0444 /home/git/gitlab.pub - cd gitlab-shell - cp config.yml.example config.yml + sudo -u git -H sh -c "PATH=/home/git/bin:$PATH; gitolite setup -pk /home/git/gitlab.pub" + - # Edit config and replace gitlab_url - # with something like 'http://domain.com/' - vim config.yml +Permissions: - # Do setup - ./bin/install + sudo chmod -R g+rwX /home/git/repositories/ + sudo chown -R git:git /home/git/repositories/ + + # clone admin repo to add localhost to known_hosts + # & be sure your user has access to gitolite + sudo -u gitlab -H git clone git@localhost:gitolite-admin.git /tmp/gitolite-admin + + # if succeed you can remove it + sudo rm -rf /tmp/gitolite-admin + +**IMPORTANT! If you can't clone `gitolite-admin` repository - DO NOT PROCEED WITH INSTALLATION** +Check the [Trouble Shooting Guide](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Trouble-Shooting-Guide) +and ensure you have followed all of the above steps carefully. -# 5. Database +# 5. Mysql database -To setup the MySQL/PostgreSQL database and dependencies please see [`doc/install/databases.md`](./databases.md). + sudo apt-get install -y mysql-server mysql-client libmysqlclient-dev + + # Login to MySQL + $ mysql -u root -p + + # Create the GitLab production database + mysql> CREATE DATABASE IF NOT EXISTS `gitlabhq_production` DEFAULT CHARACTER SET `utf8` COLLATE `utf8_unicode_ci`; + + # Create the MySQL User change $password to a real password + mysql> CREATE USER 'gitlab'@'localhost' IDENTIFIED BY '$password'; + + # Grant proper permissions to the MySQL User + mysql> GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER ON `gitlabhq_production`.* TO 'gitlab'@'localhost'; # 6. GitLab - # We'll install GitLab into home directory of the user "git" - cd /home/git + cd /home/gitlab -## Clone the Source - # Clone GitLab repository - sudo -u git -H git clone https://github.com/gitlabhq/gitlabhq.git gitlab +#### Get source code - # Go to gitlab dir - cd /home/git/gitlab + # Get gitlab code. Use this for stable setup + sudo -H -u gitlab git clone -b stable https://github.com/gitlabhq/gitlabhq.git gitlab - # Checkout to stable release - sudo -u git -H git checkout 5-0-stable + # Skip this for stable setup. + # Master branch (recent changes, less stable) + sudo -H -u gitlab git clone -b master https://github.com/gitlabhq/gitlabhq.git gitlab -**Note:** -You can change `5-0-stable` to `master` if you want the *bleeding edge* version, but -do so with caution! -## Configure it +#### Copy configs + + cd gitlab - cd /home/git/gitlab + # Rename config files + # + sudo -u gitlab cp config/gitlab.yml.example config/gitlab.yml - # Copy the example GitLab config - sudo -u git -H cp config/gitlab.yml.example config/gitlab.yml + # Copy mysql db config + # + # make sure to update username/password in config/database.yml + # + sudo -u gitlab cp config/database.yml.mysql config/database.yml - # Make sure to change "localhost" to the fully-qualified domain name of your - # host serving GitLab where necessary - sudo -u git -H vim config/gitlab.yml + # Copy unicorn config + # + sudo -u gitlab cp config/unicorn.rb.example config/unicorn.rb - # Make sure GitLab can write to the log/ and tmp/ directories - sudo chown -R git log/ - sudo chown -R git tmp/ - sudo chmod -R u+rwX log/ - sudo chmod -R u+rwX tmp/ +#### Install gems - # Create directory for satellites - sudo -u git -H mkdir /home/git/gitlab-satellites - - # 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 - -**Important Note:** -Make sure to edit both files to match your setup. - -## Configure GitLab DB settings - - # Mysql - sudo -u git cp config/database.yml.mysql config/database.yml - - # PostgreSQL - sudo -u git cp config/database.yml.postgresql config/database.yml - -Make sure to update username/password in config/database.yml. - -## Install Gems - - cd /home/git/gitlab + cd /home/gitlab/gitlab sudo gem install charlock_holmes --version '0.6.9' + sudo gem install bundler + sudo -u gitlab -H bundle install --without development test sqlite postgres --deployment - # For MySQL (note, the option says "without") - sudo -u git -H bundle install --deployment --without development test postgres +#### Configure git client - # Or for PostgreSQL - sudo -u git -H bundle install --deployment --without development test mysql +Gitlab needs to be able to commit and push changes to gitolite. +Git requires a username and email in order to be able to do that. + + sudo -u gitlab -H git config --global user.email "gitlab@localhost" + sudo -u gitlab -H git config --global user.name "Gitlab" + +#### Setup application + + sudo -u gitlab bundle exec rake gitlab:app:setup RAILS_ENV=production -## Initialise Database and Activate Advanced Features +#### Setup GitLab hooks - sudo -u git -H bundle exec rake gitlab:setup RAILS_ENV=production + sudo cp ./lib/hooks/post-receive /home/git/.gitolite/hooks/common/post-receive + sudo chown git:git /home/git/.gitolite/hooks/common/post-receive + +#### Check application status + +Checking status: + + sudo -u gitlab bundle exec rake gitlab:app:status RAILS_ENV=production -## Install Init Script + # OUTPUT EXAMPLE + Starting diagnostic + config/database.yml............exists + config/gitlab.yml............exists + /home/git/repositories/............exists + /home/git/repositories/ is writable?............YES + remote: Counting objects: 603, done. + remote: Compressing objects: 100% (466/466), done. + remote: Total 603 (delta 174), reused 0 (delta 0) + Receiving objects: 100% (603/603), 53.29 KiB, done. + Resolving deltas: 100% (174/174), done. + Can clone gitolite-admin?............YES + UMASK for .gitolite.rc is 0007? ............YES + /home/git/share/gitolite/hooks/common/post-receive exists? ............YES -Download the init script (will be /etc/init.d/gitlab): +If you got all YES - congratulations! You can run a GitLab app. - sudo curl --output /etc/init.d/gitlab https://raw.github.com/gitlabhq/gitlab-recipes/master/init.d/gitlab +#### init script + +Create init script in /etc/init.d/gitlab: + + sudo wget https://raw.github.com/gitlabhq/gitlab-recipes/master/init.d/gitlab -P /etc/init.d/ sudo chmod +x /etc/init.d/gitlab -Make GitLab start on boot: +GitLab autostart: sudo update-rc.d gitlab defaults 21 - -## Check Application Status - -Check if GitLab and its environment are configured correctly: - - sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production - -To make sure you didn't miss anything run a more thorough check with: - - sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production - -If all items are green, then congratulations on successfully installing GitLab! -However there are still a few steps left. - -## Start Your GitLab Instance +#### Now you should start GitLab application: sudo service gitlab start - # or - sudo /etc/init.d/gitlab restart # 7. Nginx -**Note:** -If you can't or don't want to use Nginx as your web server, have a look at the -[`Advanced Setup Tips`](./installation.md#advanced-setup-tips) section. - -## Installation + # Install first sudo apt-get install nginx -## Site Configuration - -Download an example site config: - - sudo curl --output /etc/nginx/sites-available/gitlab https://raw.github.com/gitlabhq/gitlab-recipes/master/nginx/gitlab + # Add GitLab to nginx sites & change with your host specific settings + sudo wget https://raw.github.com/gitlabhq/gitlab-recipes/master/nginx/gitlab -P /etc/nginx/sites-available/ sudo ln -s /etc/nginx/sites-available/gitlab /etc/nginx/sites-enabled/gitlab -Make sure to edit the config file to match your setup: - # Change **YOUR_SERVER_IP** and **YOUR_SERVER_FQDN** # to the IP address and fully-qualified domain name - # of your host serving GitLab - sudo vim /etc/nginx/sites-available/gitlab + # of the host serving GitLab. + sudo vim /etc/nginx/sites-enabled/gitlab -## Restart - - sudo service nginx restart + # Restart nginx: + sudo /etc/init.d/nginx restart -# Done! +# Done! Visit YOUR_SERVER for gitlab instance -Visit YOUR_SERVER for your first GitLab login. -The setup has created an admin account for you. You can use it to log in: +You can login via web using admin generated with setup: admin@local.host 5iveL!fe -**Important Note:** -Please go over to your profile page and immediately chage the password, so -nobody can access your GitLab by using this login information later on. - -**Enjoy!** - - - - -# Advanced Setup Tips +# Advanced setup tips: -## Custom Redis Connection +_Checkout databases.md for postgres or sqlite_ + +## Customizing Resque's Redis connection If you'd like Resque to connect to a Redis server on a non-standard port or on -a different host, you can configure its connection string via the -`config/resque.yml` file. +a different host, you can configure its connection string in the +**config/resque.yml** file: - # example - production: redis://redis.example.tld:6379 + production: redis.example.com:6379 -## 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 - port 2222 # Your port number - hostname 127.0.0.1; # Your server name or IP - -You also need to change the corresponding options (e.g. ssh_user, ssh_host, admin_uri) in the `config\gitlab.yml` file. - -## LDAP authentication - -You can configure LDAP authentication in config/gitlab.yml. Please restart GitLab after editing this file. - -## Using Custom Omniauth Providers - -GitLab uses [Omniauth](http://www.omniauth.org/) for authentication and already ships with a few providers preinstalled (e.g. LDAP, GitHub, Twitter). But sometimes that is not enough and you need to integrate with other authentication solutions. For these cases you can use the Omniauth provider. - -### Steps - -These steps are fairly general and you will need to figure out the exact details from the Omniauth provider's documentation. - -* Add `gem "omniauth-your-auth-provider"` to the [Gemfile](https://github.com/gitlabhq/gitlabhq/blob/master/Gemfile#L18) -* Run `sudo -u gitlab -H bundle install` to install the new gem(s) -* Add provider specific configuration options to your `config/gitlab.yml` (you can use the [auth providers section of the example config](https://github.com/gitlabhq/gitlabhq/blob/master/config/gitlab.yml.example#L53) as a reference) -* Add icons for the new provider into the [vendor/assets/images/authbuttons](https://github.com/gitlabhq/gitlabhq/tree/master/vendor/assets/images/authbuttons) directory (you can find some more popular ones over at https://github.com/intridea/authbuttons) -* Restart GitLab - -### Examples - -If you have successfully set up a provider that is not shipped with GitLab itself, please let us know. -You can help others by reporting successful configurations and probably share a few insights or provide warnings for common errors or pitfalls by sharing your experience [in the public Wiki](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Working-Custom-Omniauth-Provider-Configurations). -While we can't officially support every possible auth mechanism out there, we'd like to at least help those with special needs. +**Ok - we have a working application now. ** +**But keep going - there are some things that should be done ** diff --git a/doc/install/requirements.md b/doc/install/requirements.md index 9209fad5..75b02d64 100644 --- a/doc/install/requirements.md +++ b/doc/install/requirements.md @@ -1,63 +1,28 @@ -# Operating Systems +## Platform requirements: -## Linux +**The project is designed for the Linux operating system.** -GitLab is developed for the Linux operating system. +It may work on FreeBSD and Mac OS, but we don't test our application for these systems and can't guarantee stability and full functionality. -GitLab officially supports (recent versions of) these Linux distributions: +We officially support (recent versions of) these Linux distributions: - Ubuntu Linux - Debian/GNU Linux -It should also work on (though they are not officially supported): +It should work on: -- Arch -- CentOS - Fedora -- Gentoo +- CentOs - RedHat -## Other Unix Systems +You might have some luck using these, but no guarantees: -There is nothing that prevents GitLab from running on other Unix operating -systems. This means you may get it to work on systems running FreeBSD or OS X. -**If you want to try, please proceed with caution!** +- FreeBSD will likely work, see https://github.com/gitlabhq/gitlabhq/issues/796 +- MacOS X will likely work, see https://groups.google.com/forum/#!topic/gitlabhq/5IXHbPkjKLA -## Windows - -GitLab does **not** run on Windows and we have no plans of supporting it in the -near future. Please consider using a virtual machine to run GitLab. +GitLab does **not** run on Windows and we have no plans of making GitLab compatible. -# Rubies +## Hardware: -GitLab requires Ruby (MRI) 1.9.3 and several Gems with native components. -While it is generally possible to use other Rubies (like -[JRuby](http://jruby.org/) or [Rubinius](http://rubini.us/)) it might require -some work on your part. - - -# Hardware requirements - -## CPU - -We recommend a processor with **4 cores**. At a minimum you need a processor with 2 cores to responsively run an unmodified installation. - -## Memory - -We recommend you to run GitLab on a server with at least **1GB of RAM** memory. You can use it with 512MB of memory but you need to setup unicorn to use only 1 worker and you need at least 200MB of swap. The minimal requirement for an unmodified installation is 768MB. With 1.5GB of memory you should be able to support 1000+ users. - -## Storage - -The necessary hard drive space largely depends on the size of the repos you want -to store in GitLab. But as a *rule of thumb* you should have at least twice as much -free space as your all repos combined take up. You need twice the storage because [GitLab satellites](https://github.com/gitlabhq/gitlabhq/blob/master/doc/install/structure.md) contain an extra copy of each repo. Apart from a local hard drive you can also mount a volume that supports the network file system (NFS) protocol. This volume might be located on a file server, a network attached storage (NAS) device, a storage area network (SAN) or on an Amazon Web Services (AWS) Elastic Block Store (EBS) volume. - -If you have enough RAM memory and a recent CPU the speed of GitLab is mainly limited by hard drive seek times. Having a fast drive (7200 RPM and up) or a solid state drive (SSD) will improve the responsiveness of GitLab. - - -# Installation troubles and reporting success or failure - -If you have troubles installing GitLab following the official installation guide -or want to share your experience installing GitLab on a not officially supported -platform, please follow the the contribution guide (see CONTRIBUTING.md). +We recommend to use server with at least 1GB RAM for gitlab instance. diff --git a/doc/install/structure.md b/doc/install/structure.md deleted file mode 100644 index f580ea15..00000000 --- a/doc/install/structure.md +++ /dev/null @@ -1,27 +0,0 @@ -# GitLab directory structure - -This is the directory structure you will end up with following the instructions in the Installation Guide. - - |-- home - | |-- git - | |-- .ssh - | |-- gitlab - | |-- gitlab-satellites - | |-- gitlab-shell - | |-- repositories - - -**/home/git/.ssh** - -**/home/git/gitlab** - This is where GitLab lives. - -**/home/git/gitlab-satellites** - Contains a copy of all repositories with a working tree. - It's used for merge requests, editing files, etc. - -**/home/git/repositories** - Holds all your repositories in bare format. - This is the place Git uses when you pull/push to your projects. - -You can change them in your `config/gitlab.yml` file. diff --git a/doc/raketasks/backup_restore.md b/doc/raketasks/backup_restore.md deleted file mode 100644 index d2da64f3..00000000 --- a/doc/raketasks/backup_restore.md +++ /dev/null @@ -1,80 +0,0 @@ -### Create a backup of the GitLab system - -Creates a backup archive of the database and all repositories. This archive will be saved in backup_path (see `config/gitlab.yml`). -The filename will be `[TIMESTAMP]_gitlab_backup.tar`. This timestamp can be used to restore an specific backup. - -``` -bundle exec rake gitlab:backup:create RAILS_ENV=production -``` - -Example output: - -``` -Dumping database tables: -- Dumping table events... [DONE] -- Dumping table issues... [DONE] -- Dumping table keys... [DONE] -- Dumping table merge_requests... [DONE] -- Dumping table milestones... [DONE] -- Dumping table namespaces... [DONE] -- Dumping table notes... [DONE] -- Dumping table projects... [DONE] -- Dumping table protected_branches... [DONE] -- Dumping table schema_migrations... [DONE] -- Dumping table services... [DONE] -- Dumping table snippets... [DONE] -- Dumping table taggings... [DONE] -- Dumping table tags... [DONE] -- Dumping table users... [DONE] -- Dumping table users_projects... [DONE] -- Dumping table web_hooks... [DONE] -- Dumping table wikis... [DONE] -Dumping repositories: -- Dumping repository abcd... [DONE] -Creating backup archive: $TIMESTAMP_gitlab_backup.tar [DONE] -Deleting tmp directories...[DONE] -Deleting old backups... [SKIPPING] -``` - -### Restore a previously created backup - -``` -bundle exec rake gitlab:backup:restore RAILS_ENV=production -``` - -Options: - -``` -BACKUP=timestamp_of_backup (required if more than one backup exists) -``` - -Example output: - -``` -Unpacking backup... [DONE] -Restoring database tables: --- create_table("events", {:force=>true}) - -> 0.2231s -[...] -- Loading fixture events...[DONE] -- Loading fixture issues...[DONE] -- Loading fixture keys...[SKIPPING] -- Loading fixture merge_requests...[DONE] -- Loading fixture milestones...[DONE] -- Loading fixture namespaces...[DONE] -- Loading fixture notes...[DONE] -- Loading fixture projects...[DONE] -- Loading fixture protected_branches...[SKIPPING] -- Loading fixture schema_migrations...[DONE] -- Loading fixture services...[SKIPPING] -- Loading fixture snippets...[SKIPPING] -- Loading fixture taggings...[SKIPPING] -- Loading fixture tags...[SKIPPING] -- Loading fixture users...[DONE] -- Loading fixture users_projects...[DONE] -- Loading fixture web_hooks...[SKIPPING] -- Loading fixture wikis...[SKIPPING] -Restoring repositories: -- Restoring repository abcd... [DONE] -Deleting tmp directories...[DONE] -``` diff --git a/doc/raketasks/cleanup.md b/doc/raketasks/cleanup.md deleted file mode 100644 index 30a5d362..00000000 --- a/doc/raketasks/cleanup.md +++ /dev/null @@ -1,14 +0,0 @@ -### Remove grabage from filesystem. Important! Data loss! - -Remove namespaces(dirs) from /home/git/repositories if they dont exist in GitLab database - -``` -bundle exec rake gitlab:cleanup:dirs RAILS_ENV=production -``` - -Remove repositories (global only for now) from /home/git/repositories if they dont exist in GitLab database - -``` -bundle exec rake gitlab:cleanup:repos RAILS_ENV=production -``` - diff --git a/doc/raketasks/features.md b/doc/raketasks/features.md deleted file mode 100644 index 018817d2..00000000 --- a/doc/raketasks/features.md +++ /dev/null @@ -1,36 +0,0 @@ -### Enable usernames and namespaces for user projects - -This command will enable the namespaces feature introduced in v4.0. It will move every project in its namespace folder. - -Note: - -* Because the **repository location will change**, you will need to **update all your git url's** to point to the new location. -* Username can be changed at [Profile / Account](/profile/account) - -**Example:** - -Old path: `git@example.org:myrepo.git` -New path: `git@example.org:username/myrepo.git` or `git@example.org:groupname/myrepo.git` - -``` -bundle exec rake gitlab:enable_namespaces RAILS_ENV=production -``` - - -### Rebuild project satellites - -This command will build missing satellites for projects. After this you will be able to **merge a merge request** via GitLab and use the **online editor**. - -``` -bundle exec rake gitlab:satellites:create RAILS_ENV=production -``` - -Example output: - -``` -Creating satellite for abcd.git -[git clone output] -Creating satellite for abcd2.git -[git clone output] -done -``` diff --git a/doc/raketasks/maintenance.md b/doc/raketasks/maintenance.md deleted file mode 100644 index b5514705..00000000 --- a/doc/raketasks/maintenance.md +++ /dev/null @@ -1,154 +0,0 @@ -### Gather information about GitLab and the system it runs on - -This command gathers information about your GitLab installation and the System -it runs on. These may be useful when asking for help or reporting issues. - -``` -bundle exec rake gitlab:env:info RAILS_ENV=production -``` - -Example output: - -``` -System information -System: Debian 6.0.6 -Current User: gitlab -Using RVM: yes -RVM Version: 1.17.2 -Ruby Version: ruby-1.9.3-p392 -Gem Version: 1.8.24 -Bundler Version:1.2.3 -Rake Version: 10.0.1 - -GitLab information -Version: 3.1.0 -Resivion: fd5141d -Directory: /home/gitlab/gitlab -DB Adapter: mysql2 -URL: http://localhost:3000 -HTTP Clone URL: http://localhost:3000/some-project.git -SSH Clone URL: git@localhost:some-project.git -Using LDAP: no -Using Omniauth: no - -GitLab Shell -Version: 1.0.4 -Repositories: /home/git/repositories/ -Hooks: /home/git/gitlab-shell/hooks/ -Git: /usr/bin/git -``` - - -### Check GitLab configuration - -Runs the following rake tasks: - -* gitlab:env:check -* gitlab:gitlab_shell:check -* gitlab:sidekiq:check -* gitlab:app:check - -It will check that each component was setup according to the installation guide and suggest fixes for issues found. - -You may also have a look at our [Trouble Shooting Guide](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Trouble-Shooting-Guide). - -``` -bundle exec rake gitlab:check RAILS_ENV=production -``` - -Example output: - -``` -Checking Environment ... - -gitlab user is in git group? ... yes -Has no "-e" in ~git/.profile ... yes -Git configured for gitlab user? ... yes -Has python2? ... yes -python2 is supported version? ... yes - -Checking Environment ... Finished - -Checking Gitolite ... - -Using recommended version ... yes -Config directory exists? ... yes -Config directory owned by git:git? ... yes -Config directory access is drwxr-x---? ... yes -Repo base directory exists? ... yes -Repo base owned by git:git? ... yes -Repo base access is drwxrws---? ... yes -post-receive hook exists? ... yes -post-receive hook up-to-date? ... yes -post-receive hooks in repos are links: ... -GitLab ... ok -Non-Ascii Files Test ... ok -Touch Commit Test ... ok -Without Master Test ... ok -Git config in repos: ... -GitLab ... ok -Non-Ascii Files Test ... ok -Touch Commit Test ... ok -Without Master Test ... ok - -Checking Gitolite ... Finished - -Checking Resque ... - -Running? ... yes - -Checking Resque ... Finished - -Checking GitLab ... - -Database config exists? ... yes -Database is not SQLite ... yes -All migrations up? ... yes -GitLab config exists? ... yes -GitLab config not outdated? ... yes -Log directory writable? ... yes -Tmp directory writable? ... yes -Init script exists? ... yes -Init script up-to-date? ... yes -Projects have satellites? ... -GitLab ... yes -Non-Ascii Files Test ... yes -Touch Commit Test ... yes -Without Master Test ... yes - -Checking GitLab ... Finished -``` - - -### (Re-)Create satellite repos - -This will create satellite repos for all your projects. -If necessary, remove the `tmp/repo_satellites` directory and rerun the command below. - -``` -bundle exec rake gitlab:satellites:create RAILS_ENV=production -``` - -### Import bare repositories into GitLab project instance - -Notes: - -* project owner will be a first admin -* existing projects will be skipped - -How to use: - -1. copy your bare repos under git base_path (see `config/gitlab.yml` git_host -> base_path) -2. run the command below - -``` -bundle exec rake gitlab:import:repos RAILS_ENV=production -``` - -Example output: - -``` -Processing abcd.git - * Created abcd (abcd.git) -[...] -``` diff --git a/doc/raketasks/user_management.md b/doc/raketasks/user_management.md deleted file mode 100644 index 021ce359..00000000 --- a/doc/raketasks/user_management.md +++ /dev/null @@ -1,16 +0,0 @@ -### Add user to as a developer to all projects - -``` -bundle exec rake gitlab:import:user_to_projects[username@domain.tld] -``` - - -### Add all users to all projects - -Notes: - -* admin users are added as masters - -``` -bundle exec rake gitlab:import:all_users_to_all_projects -``` diff --git a/features/admin/active_tab.feature b/features/admin/active_tab.feature index 226d3d5d..fce85ce9 100644 --- a/features/admin/active_tab.feature +++ b/features/admin/active_tab.feature @@ -12,11 +12,6 @@ Feature: Admin active tab Then the active main tab should be Projects And no other main tabs should be active - Scenario: On Admin Groups - Given I visit admin groups page - Then the active main tab should be Groups - And no other main tabs should be active - Scenario: On Admin Users Given I visit admin users page Then the active main tab should be Users diff --git a/features/admin/groups.feature b/features/admin/groups.feature index 28f35e3a..e5eab8e6 100644 --- a/features/admin/groups.feature +++ b/features/admin/groups.feature @@ -1,8 +1,6 @@ Feature: Admin Groups Background: Given I sign in as an admin - And I have group with projects - And Create gitlab user "John" And I visit admin groups page Scenario: Create a group @@ -10,8 +8,3 @@ Feature: Admin Groups And submit form with new group info Then I should be redirected to group page And I should see newly created group - - Scenario: Add user into projects in group - When I visit admin group page - When I select user "John" from user list as "Reporter" - Then I should see "John" in team list in every project as "Reporter" diff --git a/features/admin/logs.feature b/features/admin/logs.feature deleted file mode 100644 index d07f0048..00000000 --- a/features/admin/logs.feature +++ /dev/null @@ -1,7 +0,0 @@ -Feature: Admin Logs - Background: - Given I sign in as an admin - - Scenario: On Admin Logs - Given I visit admin logs page - Then I should see tabs with available logs diff --git a/features/admin/projects.feature b/features/admin/projects.feature deleted file mode 100644 index 4a4ee1c1..00000000 --- a/features/admin/projects.feature +++ /dev/null @@ -1,13 +0,0 @@ -Feature: Admin Projects - Background: - Given I sign in as an admin - And there are projects in system - - Scenario: Projects list - When I visit admin projects page - Then I should see all projects - - Scenario: Projects show - When I visit admin projects page - And I click on first project - Then I should see project details diff --git a/features/admin/teams.feature b/features/admin/teams.feature deleted file mode 100644 index 6a15fddc..00000000 --- a/features/admin/teams.feature +++ /dev/null @@ -1,70 +0,0 @@ -Feature: Admin Teams - Background: - Given I sign in as an admin - And Create gitlab user "John" - - Scenario: Create a team - When I visit admin teams page - And I click new team link - And submit form with new team info - Then I should be redirected to team page - And I should see newly created team - - Scenario: Add user to team - When I visit admin teams page - When I have clean "HardCoders" team - And I visit "HardCoders" team page - When I click to "Add members" link - When I select user "John" from user list as "Developer" - And submit form with new team member info - Then I should see "John" in teams members list as "Developer" - - Scenario: Assign team to existing project - When I visit admin teams page - When I have "HardCoders" team with "John" member with "Developer" role - When I have "Shop" project - And I visit "HardCoders" team page - Then I should see empty projects table - When I click to "Add projects" link - When I select project "Shop" with max access "Reporter" - And submit form with new team project info - Then I should see "Shop" project in projects list - When I visit "Shop" project admin page - Then I should see "John" user with role "Reporter" in team table - - Scenario: Add user to team with ptojects - When I visit admin teams page - When I have "HardCoders" team with "John" member with "Developer" role - And "HardCoders" team assigned to "Shop" project with "Developer" max role access - When I have gitlab user "Jimm" - And I visit "HardCoders" team page - Then I should see members table without "Jimm" member - When I click to "Add members" link - When I select user "Jimm" ub team members list as "Master" - And submit form with new team member info - Then I should see "Jimm" in teams members list as "Master" - - Scenario: Remove member from team - Given I have users team "HardCoders" - And gitlab user "John" is a member "HardCoders" team - And gitlab user "Jimm" is a member "HardCoders" team - And "HardCoders" team is assigned to "Shop" project - When I visit admin teams page - When I visit "HardCoders" team admin page - Then I shoould see "John" in members list - And I should see "Jimm" in members list - And I should see "Shop" in projects list - When I click on remove "Jimm" user link - Then I should be redirected to "HardCoders" team admin page - And I should not to see "Jimm" user in members list - - Scenario: Remove project from team - Given I have users team "HardCoders" - And gitlab user "John" is a member "HardCoders" team - And gitlab user "Jimm" is a member "HardCoders" team - And "HardCoders" team is assigned to "Shop" project - When I visit admin teams page - When I visit "HardCoders" team admin page - Then I should see "Shop" project in projects list - When I click on "Relegate" link on "Shop" project - Then I should see projects liston team page without "Shop" project diff --git a/features/admin/users.feature b/features/admin/users.feature deleted file mode 100644 index 4c951df9..00000000 --- a/features/admin/users.feature +++ /dev/null @@ -1,16 +0,0 @@ -Feature: Admin Users - Background: - Given I sign in as an admin - And system has users - - Scenario: On Admin Users - Given I visit admin users page - Then I should see all users - - Scenario: Edit user and change username to non ascii char - When I visit admin users page - And Click edit - And Input non ascii char in username - And Click save - Then See username error message - And Not chenged form action url diff --git a/features/dashboard/dashboard.feature b/features/dashboard/dashboard.feature index 695148b5..24296f46 100644 --- a/features/dashboard/dashboard.feature +++ b/features/dashboard/dashboard.feature @@ -5,7 +5,6 @@ Feature: Dashboard And project "Shop" has push event And I visit dashboard page - @javascript Scenario: I should see projects list Then I should see "New Project" link Then I should see "Shop" project link @@ -21,13 +20,11 @@ Feature: Dashboard And I click "Create Merge Request" link Then I see prefilled new Merge Request page - @javascript Scenario: I should see User joined Project event Given user with name "John Doe" joined project "Shop" When I visit dashboard page Then I should see "John Doe joined project at Shop" event - @javascript Scenario: I should see User left Project event Given user with name "John Doe" joined project "Shop" And user with name "John Doe" left project "Shop" diff --git a/features/dashboard/event_filters.feature b/features/dashboard/event_filters.feature deleted file mode 100644 index e0c6b84b..00000000 --- a/features/dashboard/event_filters.feature +++ /dev/null @@ -1,51 +0,0 @@ -Feature: Event filters - Background: - Given I sign in as a user - And I own a project - And this project has push event - And this project has new member event - And this project has merge request event - And I visit dashboard page - - @javascript - Scenario: I should see all events - Then I should see push event - And I should see new member event - And I should see merge request event - - @javascript - Scenario: I should see only pushed events - When I click "push" event filter - Then I should see push event - And I should not see new member event - And I should not see merge request event - - @javascript - Scenario: I should see only joined events - When I click "team" event filter - Then I should see new member event - And I should not see push event - And I should not see merge request event - - @javascript - Scenario: I should see only merged events - When I click "merge" event filter - Then I should see merge request event - And I should not see push event - And I should not see new member event - - @javascript - Scenario: I should see only selected events while page reloaded - When I click "push" event filter - And I visit dashboard page - Then I should see push event - And I should not see new member event - When I click "team" event filter - And I visit dashboard page - Then I should see push event - And I should see new member event - And I should not see merge request event - When I click "push" event filter - Then I should not see push event - And I should see new member event - And I should not see merge request event diff --git a/features/dashboard/projects.feature b/features/dashboard/projects.feature deleted file mode 100644 index 7e617bd1..00000000 --- a/features/dashboard/projects.feature +++ /dev/null @@ -1,12 +0,0 @@ -Feature: Dashboard projects - Background: - Given I sign in as a user - And I own project "Shop" - And I visit dashboard projects page - - Scenario: I should see projects list - Then I should see projects list - - Scenario: I should see project I am looking for - Given I search for "Sho" - Then I should see "Shop" project link diff --git a/features/group/create_group.feature b/features/group/create_group.feature deleted file mode 100644 index b77f3599..00000000 --- a/features/group/create_group.feature +++ /dev/null @@ -1,11 +0,0 @@ -Feature: Groups - Background: - Given I sign in as a user - - Scenario: Create a group from dasboard - Given I have group with projects - And I visit dashboard page - When I click new group link - And submit form with new group info - Then I should be redirected to group page - And I should see newly created group diff --git a/features/group/group.feature b/features/group/group.feature index a48affe8..07308112 100644 --- a/features/group/group.feature +++ b/features/group/group.feature @@ -3,7 +3,6 @@ Feature: Groups Given I sign in as a user And I have group with projects - @javascript Scenario: I should see group dashboard list When I visit group page Then I should see projects list @@ -18,15 +17,3 @@ Feature: Groups Given project from group has merge requests assigned to me When I visit group merge requests page Then I should see merge requests from this group assigned to me - - Scenario: I should add user to projects in Group - Given I have new user "John" - When I visit group people page - And I select user "John" from list with role "Reporter" - Then I should see user "John" in team list - - Scenario: I should see edit group page - When I visit group settings page - And I change group name - Then I should see new group name - diff --git a/features/project/active_tab.feature b/features/project/active_tab.feature index f33e4b9e..2d3e41d3 100644 --- a/features/project/active_tab.feature +++ b/features/project/active_tab.feature @@ -49,52 +49,51 @@ Feature: Project active tab Scenario: On Project Home/Show Given I visit my project's home page - Then the active main tab should be Home - And no other main tabs should be active + Then the active sub tab should be Show + And no other sub tabs should be active + And the active main tab should be Home - #Scenario: On Project Settings/Attachments - #Given I visit my project's home page - #And I click the "Attachments" tab - #Then the active sub tab should be Attachments - #And no other sub tabs should be active - #And the active main tab should be Home - - #Scenario: On Project Settings/Snippets - #Given I visit my project's home page - #And I click the "Snippets" tab - #Then the active sub tab should be Snippets - #And no other sub tabs should be active - #And the active main tab should be Home - - # Sub Tabs: Settings - - Scenario: On Project Settings/Team - Given I visit my project's settings page + Scenario: On Project Home/Team + Given I visit my project's home page And I click the "Team" tab Then the active sub tab should be Team And no other sub tabs should be active - And the active main tab should be Settings + And the active main tab should be Home - Scenario: On Project Settings/Edit - Given I visit my project's settings page + Scenario: On Project Home/Attachments + Given I visit my project's home page + And I click the "Attachments" tab + Then the active sub tab should be Attachments + And no other sub tabs should be active + And the active main tab should be Home + + Scenario: On Project Home/Snippets + Given I visit my project's home page + And I click the "Snippets" tab + Then the active sub tab should be Snippets + And no other sub tabs should be active + And the active main tab should be Home + + Scenario: On Project Home/Edit + Given I visit my project's home page And I click the "Edit" tab Then the active sub tab should be Edit And no other sub tabs should be active - And the active main tab should be Settings + And the active main tab should be Home - Scenario: On Project Settings/Hooks - Given I visit my project's settings page + Scenario: On Project Home/Hooks + Given I visit my project's home page And I click the "Hooks" tab Then the active sub tab should be Hooks And no other sub tabs should be active - And the active main tab should be Settings + And the active main tab should be Home - Scenario: On Project Settings/Deploy Keys - Given I visit my project's settings page + Scenario: On Project Home/Deploy Keys + Given I visit my project's home page And I click the "Deploy Keys" tab Then the active sub tab should be Deploy Keys And no other sub tabs should be active - And the active main tab should be Settings + And the active main tab should be Home # Sub Tabs: Commits diff --git a/features/project/commits/commit_comments.feature b/features/project/commits/commit_comments.feature index a1aa745a..5acf541a 100644 --- a/features/project/commits/commit_comments.feature +++ b/features/project/commits/commit_comments.feature @@ -1,48 +1,10 @@ -Feature: Comments on commits +Feature: Project Comment commit Background: Given I sign in as a user And I own project "Shop" - And I visit project commit page + Given I visit project commit page @javascript - Scenario: I can comment on a commit + Scenario: I comment commit Given I leave a comment like "XML attached" - Then I should see a comment saying "XML attached" - - @javascript - Scenario: I can't cancel the main form - Then I should not see the cancel comment button - - @javascript - Scenario: I can't preview without text - Given I haven't written any comment text - Then I should not see the comment preview button - - @javascript - Scenario: I can preview with text - Given I write a comment like "Nice" - Then I should see the comment preview button - - @javascript - Scenario: I preview a comment - Given I preview a comment text like "Bug fixed :smile:" - Then I should see the comment preview - And I should not see the comment text field - - @javascript - Scenario: I can edit after preview - Given I preview a comment text like "Bug fixed :smile:" - Then I should see the comment edit button - - @javascript - Scenario: I have a reset form after posting from preview - Given I preview a comment text like "Bug fixed :smile:" - And I submit the comment - Then I should see an empty comment text field - And I should not see the comment preview - - @javascript - Scenario: I can delete a comment - Given I leave a comment like "XML attached" - And I delete a comment - Then I should not see a comment saying "XML attached" + Then I should see comment "XML attached" diff --git a/features/project/commits/commit_diff_comments.feature b/features/project/commits/commit_diff_comments.feature deleted file mode 100644 index 884fab52..00000000 --- a/features/project/commits/commit_diff_comments.feature +++ /dev/null @@ -1,92 +0,0 @@ -Feature: Comments on commit diffs - Background: - Given I sign in as a user - And I own project "Shop" - And I visit project commit page - - @javascript - Scenario: I can access add diff comment buttons - Then I should see add a diff comment button - - @javascript - Scenario: I can comment on a commit diff - Given I leave a diff comment like "Typo, please fix" - Then I should see a diff comment saying "Typo, please fix" - - @javascript - Scenario: I get a temporary form for the first comment on a diff line - Given I open a diff comment form - Then I should see a temporary diff comment form - - @javascript - Scenario: I have a cancel button on the diff form - Given I open a diff comment form - Then I should see the cancel comment button - - @javascript - Scenario: I can cancel a diff form - Given I open a diff comment form - And I cancel the diff comment - Then I should not see the diff comment form - - @javascript - Scenario: I can't open a second form for a diff line - Given I open a diff comment form - And I open a diff comment form - Then I should only see one diff form - - @javascript - Scenario: I can have multiple forms - Given I open a diff comment form - And I write a diff comment like ":-1: I don't like this" - And I open another diff comment form - Then I should see a diff comment form with ":-1: I don't like this" - And I should see an empty diff comment form - - @javascript - Scenario: I can preview multiple forms separately - Given I preview a diff comment text like "Should fix it :smile:" - And I preview another diff comment text like "DRY this up" - Then I should see two separate previews - - @javascript - Scenario: I have a reply button in discussions - Given I leave a diff comment like "Typo, please fix" - Then I should see a discussion reply button - - @javascript - Scenario: I can't preview without text - Given I open a diff comment form - And I haven't written any diff comment text - Then I should not see the diff comment preview button - - @javascript - Scenario: I can preview with text - Given I open a diff comment form - And I write a diff comment like ":-1: I don't like this" - Then I should see the diff comment preview button - - @javascript - Scenario: I preview a diff comment - Given I preview a diff comment text like "Should fix it :smile:" - Then I should see the diff comment preview - And I should not see the diff comment text field - - @javascript - Scenario: I can edit after preview - Given I preview a diff comment text like "Should fix it :smile:" - Then I should see the diff comment edit button - - @javascript - Scenario: The form gets removed after posting - Given I preview a diff comment text like "Should fix it :smile:" - And I submit the diff comment - Then I should not see the diff comment form - And I should see a discussion reply button - - - #@wip @javascript - #Scenario: I can delete a discussion comment - # Given I leave a diff comment like "Typo, please fix" - # And I delete a diff comment - # Then I should not see a diff comment saying "Typo, please fix" diff --git a/features/project/issues/issues.feature b/features/project/issues/issues.feature index d6ef384c..596e8bd7 100644 --- a/features/project/issues/issues.feature +++ b/features/project/issues/issues.feature @@ -24,9 +24,11 @@ Feature: Project Issues Given I click link "Release 0.4" Then I should see issue "Release 0.4" + @javascript Scenario: I submit new unassigned issue Given I click link "New Issue" And I submit new issue "500 error on profile" + Given I click link "500 error on profile" Then I should see issue "500 error on profile" @javascript @@ -55,19 +57,25 @@ Feature: Project Issues Then I should see "Release 0.3" in issues And I should not see "Release 0.4" in issues - # Disable this two cause of random failing - # TODO: fix after v4.0 released - #@javascript - #Scenario: I create Issue with pre-selected milestone - #Given project "Shop" has milestone "v2.2" - #And project "Shop" has milestone "v3.0" - #And I visit project "Shop" issues page - #When I select milestone "v3.0" - #And I click link "New Issue" - #Then I should see selected milestone with title "v3.0" + @javascript + Scenario: I clear search + Given I click link "All" + And I fill in issue search with "Something" + And I fill in issue search with "" + Then I should see "Release 0.4" in issues + And I should see "Release 0.3" in issues - #@javascript - #Scenario: I create Issue with pre-selected assignee - #When I select first assignee from "Shop" project - #And I click link "New Issue" - #Then I should see first assignee from "Shop" as selected assignee + @javascript + Scenario: I create Issue with pre-selected milestone + Given project "Shop" has milestone "v2.2" + And project "Shop" has milestone "v3.0" + And I visit project "Shop" issues page + When I select milestone "v3.0" + And I click link "New Issue" + Then I should see selected milestone with title "v3.0" + + @javascript + Scenario: I create Issue with pre-selected assignee + When I select first assignee from "Shop" project + And I click link "New Issue" + Then I should see first assignee from "Shop" as selected assignee diff --git a/features/project/merge_requests.feature b/features/project/merge_requests.feature index 5b8becbb..80f00986 100644 --- a/features/project/merge_requests.feature +++ b/features/project/merge_requests.feature @@ -35,34 +35,8 @@ Feature: Project Merge Requests Then I should see merge request "Wiki Feature" @javascript - Scenario: I comment on a merge request + Scenario: I comment merge request Given I visit merge request page "Bug NS-04" And I leave a comment like "XML attached" Then I should see comment "XML attached" - @javascript - Scenario: I comment on a merge request diff - Given project "Shop" have "Bug NS-05" open merge request with diffs inside - And I visit merge request page "Bug NS-05" - And I switch to the diff tab - And I leave a comment like "Line is wrong" on line 185 of the first file - And I switch to the merge request's comments tab - Then I should see a discussion has started on line 185 - - @javascript - Scenario: I comment on a line of a commit in merge request - Given project "Shop" have "Bug NS-05" open merge request with diffs inside - And I visit merge request page "Bug NS-05" - And I click on the first commit in the merge request - And I leave a comment like "Line is wrong" on line 185 of the first file - And I switch to the merge request's comments tab - Then I should see a discussion has started on commit bcf03b5de6c:L185 - - @javascript - Scenario: I comment on a commit in merge request - Given project "Shop" have "Bug NS-05" open merge request with diffs inside - And I visit merge request page "Bug NS-05" - And I click on the first commit in the merge request - And I leave a comment on the diff page - And I switch to the merge request's comments tab - Then I should see a discussion has started on commit bcf03b5de6c diff --git a/features/project/network.feature b/features/project/network.feature index a6cbd2c4..31ce5ad3 100644 --- a/features/project/network.feature +++ b/features/project/network.feature @@ -7,19 +7,3 @@ 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 diff --git a/features/project/project.feature b/features/project/project.feature index 0c8c97c8..1c9f201d 100644 --- a/features/project/project.feature +++ b/features/project/project.feature @@ -1,20 +1,14 @@ -Feature: Project Feature +Feature: Projects Background: - Given I sign in as a user + Given I signin as a user And I own project "Shop" - And project "Shop" has push event And I visit project "Shop" page - Scenario: I should see project activity - When I visit project "Shop" page - Then I should see project "Shop" activity feed + # @wip + # Scenario: I should see project activity - Scenario: I visit edit project - When I visit edit project "Shop" page - Then I should see project settings + # @wip + # Scenario: I edit project - Scenario: I edit project - When I visit edit project "Shop" page - And change project settings - And I save project - Then I should see project with new settings + # @wip + # Scenario: I visit attachments diff --git a/features/project/service.feature b/features/project/service.feature deleted file mode 100644 index ca8a4756..00000000 --- a/features/project/service.feature +++ /dev/null @@ -1,14 +0,0 @@ -Feature: Project Services - Background: - Given I sign in as a user - And I own project "Shop" - - Scenario: I should see project services - When I visit project "Shop" services page - Then I should see list of available services - - Scenario: Activate gitlab-ci service - When I visit project "Shop" services page - And I click gitlab-ci service link - And I fill gitlab-ci settings - Then I should see service settings saved diff --git a/features/project/source/browse_files.feature b/features/project/source/browse_files.feature index ee26f537..0b8495ff 100644 --- a/features/project/source/browse_files.feature +++ b/features/project/source/browse_files.feature @@ -12,7 +12,7 @@ Feature: Project Browse files Then I should see files from repository for "8470d70" Scenario: I browse file content - Given I click on "Gemfile.lock" file in repo + Given I click on "Gemfile" file in repo Then I should see it content Scenario: I browse raw file @@ -22,6 +22,6 @@ Feature: Project Browse files @javascript Scenario: I can edit file - Given I click on "Gemfile.lock" file in repo + Given I click on "Gemfile" file in repo And I click button "edit" Then I can edit code diff --git a/features/project/source/git_blame.feature b/features/project/source/git_blame.feature index 3b20437a..93ed20a8 100644 --- a/features/project/source/git_blame.feature +++ b/features/project/source/git_blame.feature @@ -5,6 +5,6 @@ Feature: Project Browse git repo Given I visit project source page Scenario: I blame file - Given I click on "Gemfile.lock" file in repo + Given I click on "Gemfile" file in repo And I click blame button Then I should see git file blame diff --git a/features/project/team_management.feature b/features/project/team_management.feature index 04545a08..0ac37620 100644 --- a/features/project/team_management.feature +++ b/features/project/team_management.feature @@ -11,7 +11,6 @@ Feature: Project Team management Then I should be able to see myself in team And I should see "Sam" in team list - @javascript Scenario: Add user to project Given I click link "New Team Member" And I select "Mike" as "Reporter" @@ -24,8 +23,13 @@ Feature: Project Team management Then I visit project "Shop" team page And I should see "Sam" in team list as "Reporter" + Scenario: View team member profile + Given I click link "Sam" + Then I should see "Sam" team profile + Scenario: Cancel team member - Given I click cancel link for "Sam" + Given I click link "Sam" + And I click link "Remove from team" Then I visit project "Shop" team page And I should not see "Sam" in team list diff --git a/features/project/wiki.feature b/features/project/wiki.feature index 90eb2b79..51370565 100644 --- a/features/project/wiki.feature +++ b/features/project/wiki.feature @@ -5,43 +5,11 @@ Feature: Project Wiki Given I visit project wiki page Scenario: Add new page - Given I create the Wiki Home page - Then I should see the newly created wiki page + Given I create Wiki page + Then I should see newly created wiki page - Scenario: Pressing Cancel while editing a brand new Wiki - Given I click on the Cancel button - Then I should be redirected back to the Edit Home Wiki page - - Scenario: Edit existing page - Given I have an existing Wiki page - And I browse to that Wiki page - And I click on the Edit button - And I change the content - Then I should see the updated content - - Scenario: Pressing Cancel while editing an existing Wiki page - Given I have an existing Wiki page - And I browse to that Wiki page - And I click on the Edit button - And I click on the Cancel button - Then I should be redirected back to that Wiki page - - Scenario: View page history - Given I have an existing wiki page - And That page has two revisions - And I browse to that Wiki page - And I click the History button - Then I should see both revisions - - Scenario: Destroy Wiki page - Given I have an existing wiki page - And I browse to that Wiki page - And I click on the Edit button - And I click on the "Delete this page" button - Then The page should be deleted - - Scenario: View all pages - Given I have an existing wiki page - And I browse to that Wiki page - And I click on the "Pages" button - Then I should see the existing page in the pages list + @javascript + Scenario: I comment wiki page + Given I create Wiki page + And I leave a comment like "XML attached" + Then I should see comment "XML attached" diff --git a/features/steps/admin/admin_active_tab.rb b/features/steps/admin/admin_active_tab.rb index f14c5f39..29290892 100644 --- a/features/steps/admin/admin_active_tab.rb +++ b/features/steps/admin/admin_active_tab.rb @@ -4,17 +4,13 @@ class AdminActiveTab < Spinach::FeatureSteps include SharedActiveTab Then 'the active main tab should be Home' do - ensure_active_main_tab('Home') + ensure_active_main_tab('Stats') end Then 'the active main tab should be Projects' do ensure_active_main_tab('Projects') end - Then 'the active main tab should be Groups' do - ensure_active_main_tab('Groups') - end - Then 'the active main tab should be Users' do ensure_active_main_tab('Users') end @@ -28,6 +24,6 @@ class AdminActiveTab < Spinach::FeatureSteps end Then 'the active main tab should be Resque' do - ensure_active_main_tab('Background Jobs') + ensure_active_main_tab('Resque') end end diff --git a/features/steps/admin/admin_groups.rb b/features/steps/admin/admin_groups.rb index 167763b6..e1759013 100644 --- a/features/steps/admin/admin_groups.rb +++ b/features/steps/admin/admin_groups.rb @@ -3,59 +3,22 @@ class AdminGroups < Spinach::FeatureSteps include SharedPaths include SharedActiveTab - When 'I visit admin group page' do - visit admin_group_path(current_group) - end - When 'I click new group link' do click_link "New Group" end - And 'I have group with projects' do - @group = create(:group) - @project = create(:project, group: @group) - @event = create(:closed_issue_event, project: @project) - - @project.team << [current_user, :master] - end - - And 'Create gitlab user "John"' do - create(:user, :name => "John") - end - 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" + fill_in 'group_code', :with => 'gitlab' + click_button "Save 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 current_path.should == admin_group_path(Group.last) end - - When 'I select user "John" from user list as "Reporter"' do - user = User.find_by_name("John") - within "#new_team_member" do - select user.name, :from => "user_ids" - select "Reporter", :from => "project_access" - end - click_button "Add user to projects in group" - end - - Then 'I should see "John" in team list in every project as "Reporter"' do - user = User.find_by_name("John") - projects_with_access = find(".user_#{user.id} .projects_access") - projects_with_access.should have_link("Reporter") - end - - protected - - def current_group - @group ||= Group.first - end end + diff --git a/features/steps/admin/admin_logs.rb b/features/steps/admin/admin_logs.rb deleted file mode 100644 index 83958545..00000000 --- a/features/steps/admin/admin_logs.rb +++ /dev/null @@ -1,11 +0,0 @@ -class AdminLogs < Spinach::FeatureSteps - include SharedAuthentication - include SharedPaths - include SharedAdmin - - Then 'I should see tabs with available logs' do - page.should have_content 'production.log' - page.should have_content 'githost.log' - page.should have_content 'application.log' - end -end diff --git a/features/steps/admin/admin_projects.rb b/features/steps/admin/admin_projects.rb deleted file mode 100644 index b410b238..00000000 --- a/features/steps/admin/admin_projects.rb +++ /dev/null @@ -1,22 +0,0 @@ -class AdminProjects < Spinach::FeatureSteps - include SharedAuthentication - include SharedPaths - include SharedAdmin - - And 'I should see all projects' do - Project.all.each do |p| - page.should have_content p.name_with_namespace - end - end - - And 'I click on first project' do - click_link Project.first.name_with_namespace - end - - Then 'I should see project details' do - project = Project.first - current_path.should == admin_project_path(project) - page.should have_content(project.name_with_namespace) - page.should have_content(project.creator.name) - end -end diff --git a/features/steps/admin/admin_teams.rb b/features/steps/admin/admin_teams.rb deleted file mode 100644 index 65c7e485..00000000 --- a/features/steps/admin/admin_teams.rb +++ /dev/null @@ -1,236 +0,0 @@ -class AdminTeams < Spinach::FeatureSteps - include SharedAuthentication - include SharedPaths - include SharedActiveTab - include SharedAdmin - - And 'I have own project' do - create :project - end - - And 'Create gitlab user "John"' do - @user = create(:user, name: "John") - end - - And 'I click new team link' do - click_link "New Team" - end - - 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 - - Then 'I should be redirected to team page' do - current_path.should == admin_team_path(UserTeam.last) - end - - 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 - visit admin_teams_path - end - - When 'I have clean "HardCoders" team' do - @team = create :user_team, name: "HardCoders", owner: current_user - end - - And 'I visit "HardCoders" team page' do - visit admin_team_path(UserTeam.find_by_name("HardCoders")) - end - - Then 'I should see only me in members table' do - members_list = find("#members_list .member") - members_list.should have_content(current_user.name) - members_list.should have_content(current_user.username) - end - - When 'I select user "John" from user list as "Developer"' do - @user ||= User.find_by_name("John") - within "#team_members" do - select "#{@user.name} (#{@user.username})", from: "user_ids" - select "Developer", from: "default_project_access" - end - end - - And 'submit form with new team member info' do - click_button 'add_members_to_team' - end - - Then 'I should see "John" in teams members list as "Developer"' do - @user ||= User.find_by_name("John") - find_in_list("#members_list .member", @user).must_equal true - end - - When 'I visit "John" user admin page' do - pending 'step not implemented' - end - - Then 'I should see "HardCoders" team in teams table' do - pending 'step not implemented' - end - - When 'I have "HardCoders" team with "John" member with "Developer" role' do - @team = create :user_team, name: "HardCoders", owner: current_user - @user ||= User.find_by_name("John") - @team.add_member(@user, UserTeam.access_roles["Developer"], group_admin: false) - end - - When 'I have "Shop" project' do - @project = create :project, name: "Shop" - end - - Then 'I should see empty projects table' do - page.has_no_css?("#projects_list").must_equal true - end - - When 'I select project "Shop" with max access "Reporter"' do - @project ||= Project.find_by_name("Shop") - within "#assign_projects" do - select @project.name, from: "project_ids" - select "Reporter", from: "greatest_project_access" - end - - end - - And 'submit form with new team project info' do - click_button 'assign_projects_to_team' - end - - Then 'I should see "Shop" project in projects list' do - project = Project.find_by_name("Shop") - find_in_list("#projects_list .project", project).must_equal true - end - - When 'I visit "Shop" project admin page' do - project = Project.find_by_name("Shop") - visit admin_project_path(project) - end - - And '"HardCoders" team assigned to "Shop" project with "Developer" max role access' do - @team = UserTeam.find_by_name("HardCoders") - @project = create :project, name: "Shop" - @team.assign_to_project(@project, UserTeam.access_roles["Developer"]) - end - - When 'I have gitlab user "Jimm"' do - create :user, name: "Jimm" - end - - Then 'I should see members table without "Jimm" member' do - user = User.find_by_name("Jimm") - find_in_list("#members_list .member", user).must_equal false - end - - When 'I select user "Jimm" ub team members list as "Master"' do - user = User.find_by_name("Jimm") - within "#team_members" do - select "#{user.name} (#{user.username})", from: "user_ids" - select "Developer", from: "default_project_access" - end - end - - Then 'I should see "Jimm" in teams members list as "Master"' do - user = User.find_by_name("Jimm") - find_in_list("#members_list .member", user).must_equal true - end - - Given 'I have users team "HardCoders"' do - @team = create :user_team, name: "HardCoders" - end - - And 'gitlab user "John" is a member "HardCoders" team' do - @team = UserTeam.find_by_name("HardCoders") - @user = User.find_by_name("John") - @user = create :user, name: "John" unless @user - @team.add_member(@user, UserTeam.access_roles["Master"], group_admin: false) - end - - And 'gitlab user "Jimm" is a member "HardCoders" team' do - @team = UserTeam.find_by_name("HardCoders") - @user = User.find_by_name("Jimm") - @user = create :user, name: "Jimm" unless @user - @team.add_member(@user, UserTeam.access_roles["Master"], group_admin: false) - end - - And '"HardCoders" team is assigned to "Shop" project' do - @team = UserTeam.find_by_name("HardCoders") - @project = create :project, name: "Shop" - @team.assign_to_project(@project, UserTeam.access_roles["Developer"]) - end - - When 'I visit "HardCoders" team admin page' do - visit admin_team_path(UserTeam.find_by_name("HardCoders")) - end - - Then 'I shoould see "John" in members list' do - user = User.find_by_name("John") - find_in_list("#members_list .member", user).must_equal true - end - - And 'I should see "Jimm" in members list' do - user = User.find_by_name("Jimm") - find_in_list("#members_list .member", user).must_equal true - end - - And 'I should see "Shop" in projects list' do - project = Project.find_by_name("Shop") - find_in_list("#projects_list .project", project).must_equal true - end - - When 'I click on remove "Jimm" user link' do - user = User.find_by_name("Jimm") - click_link "remove_member_#{user.id}" - end - - Then 'I should be redirected to "HardCoders" team admin page' do - current_path.should == admin_team_path(UserTeam.find_by_name("HardCoders")) - end - - And 'I should not to see "Jimm" user in members list' do - user = User.find_by_name("Jimm") - find_in_list("#members_list .member", user).must_equal false - end - - When 'I click on "Relegate" link on "Shop" project' do - project = Project.find_by_name("Shop") - click_link "relegate_project_#{project.id}" - end - - Then 'I should see projects liston team page without "Shop" project' do - project = Project.find_by_name("Shop") - find_in_list("#projects_list .project", project).must_equal false - end - - Then 'I should see "John" user with role "Reporter" in team table' do - user = User.find_by_name("John") - find_in_list(".team_members", user).must_equal true - end - - When 'I click to "Add members" link' do - click_link "Add members" - end - - When 'I click to "Add projects" link' do - click_link "Add projects" - end - - protected - - def current_team - @team ||= Team.first - end - - def find_in_list(selector, item) - members_list = all(selector) - entered = false - members_list.each do |member_item| - entered = true if member_item.has_content?(item.name) - end - entered - end -end diff --git a/features/steps/admin/admin_users.rb b/features/steps/admin/admin_users.rb deleted file mode 100644 index 61b3ed91..00000000 --- a/features/steps/admin/admin_users.rb +++ /dev/null @@ -1,34 +0,0 @@ -class AdminUsers < Spinach::FeatureSteps - include SharedAuthentication - include SharedPaths - include SharedAdmin - - Then 'I should see all users' do - User.all.each do |user| - page.should have_content user.name - end - end - - And 'Click edit' do - @user = User.first - find("#edit_user_#{@user.id}").click - end - - And 'Input non ascii char in username' do - fill_in 'user_username', with: "\u3042\u3044" - end - - And 'Click save' do - click_button("Save") - end - - Then 'See username error message' do - within "#error_explanation" do - page.should have_content "Username" - end - end - - And 'Not chenged form action url' do - page.should have_selector %(form[action="/admin/users/#{@user.username}"]) - end -end diff --git a/features/steps/dashboard/dashboard.rb b/features/steps/dashboard/dashboard.rb index c6832056..99c48738 100644 --- a/features/steps/dashboard/dashboard.rb +++ b/features/steps/dashboard/dashboard.rb @@ -1,7 +1,6 @@ class Dashboard < Spinach::FeatureSteps include SharedAuthentication include SharedPaths - include SharedProject Then 'I should see "New Project" link' do page.should have_link "New Project" @@ -11,6 +10,11 @@ class Dashboard < Spinach::FeatureSteps page.should have_link "Shop" end + Then 'I should see project "Shop" activity feed' do + project = Project.find_by_name("Shop") + page.should have_content "#{@user.name} pushed new branch new_design at #{project.name}" + end + Then 'I should see last push widget' do page.should have_content "You pushed to new_design" page.should have_link "Create Merge Request" @@ -33,7 +37,7 @@ class Dashboard < Spinach::FeatureSteps Event.create( project: project, author_id: user.id, - action: Event::JOINED + action: Event::Joined ) end @@ -47,7 +51,7 @@ class Dashboard < Spinach::FeatureSteps Event.create( project: project, author_id: user.id, - action: Event::LEFT + action: Event::Left ) end @@ -55,18 +59,43 @@ class Dashboard < Spinach::FeatureSteps page.should have_content "John Doe left project at Shop" end + And 'I own project "Shop"' do + @project = create :project, name: 'Shop' + @project.add_access(@user, :admin) + end + And 'I have group with projects' do @group = create(:group) @project = create(:project, group: @group) @event = create(:closed_issue_event, project: @project) - @project.team << [current_user, :master] + @project.add_access current_user, :admin end - Then 'I should see projects list' do - @user.authorized_projects.all.each do |project| - page.should have_link project.name_with_namespace - end + And 'project "Shop" has push event' do + @project = Project.find_by_name("Shop") + + data = { + before: "0000000000000000000000000000000000000000", + after: "0220c11b9a3e6c69dc8fd35321254ca9a7b98f7e", + ref: "refs/heads/new_design", + user_id: @user.id, + user_name: @user.name, + repository: { + name: @project.name, + url: "localhost/rubinius", + description: "", + homepage: "localhost/rubinius", + private: true + } + } + + @event = Event.create( + project: @project, + action: Event::Pushed, + data: data, + author_id: @user.id + ) end Then 'I should see groups list' do @@ -74,13 +103,4 @@ class Dashboard < Spinach::FeatureSteps page.should have_link group.name end end - - And 'group has a projects that does not belongs to me' do - @forbidden_project1 = create(:project, group: @group) - @forbidden_project2 = create(:project, group: @group) - end - - Then 'I should see 1 project at group list' do - page.find('span.last_activity/span').should have_content('1') - end end diff --git a/features/steps/dashboard/dashboard_event_filters.rb b/features/steps/dashboard/dashboard_event_filters.rb deleted file mode 100644 index afa15c31..00000000 --- a/features/steps/dashboard/dashboard_event_filters.rb +++ /dev/null @@ -1,87 +0,0 @@ -class EventFilters < Spinach::FeatureSteps - include SharedAuthentication - include SharedPaths - include SharedProject - - Then 'I should see push event' do - page.should have_selector('span.pushed') - end - - Then 'I should not see push event' do - page.should_not have_selector('span.pushed') - end - - Then 'I should see new member event' do - page.should have_selector('span.joined') - end - - And 'I should not see new member event' do - page.should_not have_selector('span.joined') - end - - Then 'I should see merge request event' do - page.should have_selector('span.merged') - end - - And 'I should not see merge request event' do - page.should_not have_selector('span.merged') - end - - And 'this project has push event' do - data = { - before: "0000000000000000000000000000000000000000", - after: "0220c11b9a3e6c69dc8fd35321254ca9a7b98f7e", - ref: "refs/heads/new_design", - user_id: @user.id, - user_name: @user.name, - repository: { - name: @project.name, - url: "localhost/rubinius", - description: "", - homepage: "localhost/rubinius", - private: true - } - } - - @event = Event.create( - project: @project, - action: Event::PUSHED, - data: data, - author_id: @user.id - ) - end - - And 'this project has new member event' do - user = create(:user, {name: "John Doe"}) - Event.create( - project: @project, - author_id: user.id, - action: Event::JOINED - ) - end - - And 'this project has merge request event' do - merge_request = create :merge_request, author: @user, project: @project - Event.create( - project: @project, - action: Event::MERGED, - target_id: merge_request.id, - target_type: "MergeRequest", - author_id: @user.id - ) - end - - When 'I click "push" event filter' do - click_link("push_event_filter") - end - - When 'I click "team" event filter' do - click_link("team_event_filter") - end - - When 'I click "merge" event filter' do - click_link("merged_event_filter") - end - -end - diff --git a/features/steps/dashboard/dashboard_issues.rb b/features/steps/dashboard/dashboard_issues.rb index fcf4296a..e5caf905 100644 --- a/features/steps/dashboard/dashboard_issues.rb +++ b/features/steps/dashboard/dashboard_issues.rb @@ -7,13 +7,12 @@ class DashboardIssues < Spinach::FeatureSteps issues.each do |issue| page.should have_content(issue.title[0..10]) page.should have_content(issue.project.name) - page.should have_link(issue.project.name) end end And 'I have assigned issues' do project = create :project - project.team << [@user, :master] + project.add_access(@user, :read, :write) 2.times { create :issue, author: @user, assignee: @user, project: project } end diff --git a/features/steps/dashboard/dashboard_merge_requests.rb b/features/steps/dashboard/dashboard_merge_requests.rb index 7cfa8a13..485a4ccc 100644 --- a/features/steps/dashboard/dashboard_merge_requests.rb +++ b/features/steps/dashboard/dashboard_merge_requests.rb @@ -14,8 +14,8 @@ class DashboardMergeRequests < Spinach::FeatureSteps project1 = create :project project2 = create :project - project1.team << [@user, :master] - project2.team << [@user, :master] + project1.add_access(@user, :read, :write) + project2.add_access(@user, :read, :write) merge_request1 = create :merge_request, author: @user, project: project1 merge_request2 = create :merge_request, author: @user, project: project2 diff --git a/features/steps/dashboard/dashboard_projects.rb b/features/steps/dashboard/dashboard_projects.rb deleted file mode 100644 index 670e25a9..00000000 --- a/features/steps/dashboard/dashboard_projects.rb +++ /dev/null @@ -1,23 +0,0 @@ -class DashboardProjects < Spinach::FeatureSteps - include SharedAuthentication - include SharedPaths - include SharedProject - - Then 'I should see projects list' do - @user.authorized_projects.all.each do |project| - page.should have_link project.name_with_namespace - end - end - - Given 'I search for "Sho"' do - fill_in "dashboard_projects_search", with: "Sho" - - within ".dashboard-search-filter" do - find('button').click - end - end - - Then 'I should see "Shop" project link' do - page.should have_link "Shop" - end -end diff --git a/features/steps/dashboard/dashboard_search.rb b/features/steps/dashboard/dashboard_search.rb index 9c8c8794..a34c14d0 100644 --- a/features/steps/dashboard/dashboard_search.rb +++ b/features/steps/dashboard/dashboard_search.rb @@ -1,7 +1,6 @@ class DashboardSearch < Spinach::FeatureSteps include SharedAuthentication include SharedPaths - include SharedProject Given 'I search for "Sho"' do fill_in "dashboard_search", with: "Sho" @@ -12,6 +11,11 @@ class DashboardSearch < Spinach::FeatureSteps page.should have_link "Shop" end + And 'I own project "Shop"' do + @project = create(:project, :name => "Shop") + @project.add_access(@user, :admin) + end + Given 'I search for "Contibuting"' do fill_in "dashboard_search", with: "Contibuting" click_button "Search" diff --git a/features/steps/group/group.rb b/features/steps/group/group.rb index 5ac958e3..4de260ec 100644 --- a/features/steps/group/group.rb +++ b/features/steps/group/group.rb @@ -3,17 +3,17 @@ class Groups < Spinach::FeatureSteps include SharedPaths Then 'I should see projects list' do - current_user.authorized_projects.each do |project| + current_user.projects.each do |project| page.should have_link project.name end end And 'I have group with projects' do - @group = create(:group, owner: current_user) + @group = create(:group) @project = create(:project, group: @group) @event = create(:closed_issue_event, project: @project) - @project.team << [current_user, :master] + @project.add_access current_user, :admin end And 'I should see projects activity feed' do @@ -28,28 +28,10 @@ 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[0..80] + page.should have_content issue.title end end - Given 'I have new user "John"' do - create(:user, name: "John") - end - - And 'I select user "John" from list with role "Reporter"' do - user = User.find_by_name("John") - within "#new_team_member" do - select user.name, :from => "user_ids" - select "Reporter", :from => "project_access" - end - click_button "Add" - end - - Then 'I should see user "John" in team list' do - projects_with_access = find(".ui-box .well-list") - projects_with_access.should have_content("John") - end - Given 'project from group has issues assigned to me' do create :issue, project: project, @@ -64,37 +46,6 @@ class Groups < Spinach::FeatureSteps author: current_user end - When 'I click new group link' do - click_link "New Group" - end - - And 'submit form with new group info' do - 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 - - Then 'I should be redirected to group page' do - current_path.should == group_path(Group.last) - end - - And 'I change group name' do - fill_in 'group_name', :with => 'new-name' - click_button "Save group" - end - - Then 'I should see new group name' do - within ".navbar-gitlab" do - page.should have_content "group: new-name" - end - end - protected def current_group diff --git a/features/steps/profile/profile.rb b/features/steps/profile/profile.rb index a02ed057..b6833f2b 100644 --- a/features/steps/profile/profile.rb +++ b/features/steps/profile/profile.rb @@ -23,19 +23,15 @@ class Profile < Spinach::FeatureSteps end Then 'I change my password' do - within '.update-password' do - fill_in "user_password", :with => "222333" - fill_in "user_password_confirmation", :with => "222333" - click_button "Save" - end + fill_in "user_password", :with => "222333" + fill_in "user_password_confirmation", :with => "222333" + click_button "Save" end When 'I unsuccessfully change my password' do - within '.update-password' do - fill_in "user_password", with: "password" - fill_in "user_password_confirmation", with: "confirmation" - click_button "Save" - end + fill_in "user_password", with: "password" + fill_in "user_password_confirmation", with: "confirmation" + click_button "Save" end Then "I should see a password error message" do @@ -47,10 +43,8 @@ class Profile < Spinach::FeatureSteps end Then 'I reset my token' do - within '.update-token' do - @old_token = @user.private_token - click_button "Reset" - end + @old_token = @user.private_token + click_button "Reset" end And 'I should see new token' do @@ -67,15 +61,11 @@ class Profile < Spinach::FeatureSteps end When "I change my application theme" do - within '.application-theme' do - choose "Violet" - end + choose "Violet" end When "I change my code preview theme" do - within '.code-preview-theme' do - choose "Solarized Dark" - end + choose "Dark code preview" end Then "I should see the theme change immediately" do diff --git a/features/steps/profile/profile_active_tab.rb b/features/steps/profile/profile_active_tab.rb index ee9f5f20..1924a6fa 100644 --- a/features/steps/profile/profile_active_tab.rb +++ b/features/steps/profile/profile_active_tab.rb @@ -4,7 +4,7 @@ class ProfileActiveTab < Spinach::FeatureSteps include SharedActiveTab Then 'the active main tab should be Home' do - ensure_active_main_tab('Home') + ensure_active_main_tab('Profile') end Then 'the active main tab should be Account' do diff --git a/features/steps/profile/profile_ssh_keys.rb b/features/steps/profile/profile_ssh_keys.rb index fbb92077..8ae1fa91 100644 --- a/features/steps/profile/profile_ssh_keys.rb +++ b/features/steps/profile/profile_ssh_keys.rb @@ -43,6 +43,6 @@ class ProfileSshKeys < Spinach::FeatureSteps end And 'I have ssh key "ssh-rsa Work"' do - create(:key, :user => @user, :title => "ssh-rsa Work", :key => "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC+L3TbFegm3k8QjejSwemk4HhlRh+DuN679Pc5ckqE/MPhVtE/+kZQDYCTB284GiT2aIoGzmZ8ee9TkaoejAsBwlA+Wz2Q3vhz65X6sMgalRwpdJx8kSEUYV8ZPV3MZvPo8KdNg993o4jL6G36GDW4BPIyO6FPZhfsawdf6liVD0Xo5kibIK7B9VoE178cdLQtLpS2YolRwf5yy6XR6hbbBGQR+6xrGOdP16eGZDb1CE2bMvvJijjloFqPscGktWOqW+nfh5txwFfBzlfARDTBsS8WZtg3Yoj1kn33kPsWRlgHfNutFRAIynDuDdQzQq8tTtVwm+Yi75RfcPHW8y3P Work") + create(:key, :user => @user, :title => "ssh-rsa Work", :key => "jfKLJDFKSFJSHFJssh-rsa Work") end end diff --git a/features/steps/project/comments_on_commit_diffs.rb b/features/steps/project/comments_on_commit_diffs.rb deleted file mode 100644 index fc397a4f..00000000 --- a/features/steps/project/comments_on_commit_diffs.rb +++ /dev/null @@ -1,6 +0,0 @@ -class CommentsOnCommitDiffs < Spinach::FeatureSteps - include SharedAuthentication - include SharedDiffNote - include SharedPaths - include SharedProject -end diff --git a/features/steps/project/create_project.rb b/features/steps/project/create_project.rb index 0d972773..6d2ca3f9 100644 --- a/features/steps/project/create_project.rb +++ b/features/steps/project/create_project.rb @@ -3,13 +3,15 @@ class CreateProject < Spinach::FeatureSteps include SharedPaths And 'fill project form with valid data' do - fill_in 'project_name', with: 'Empty' + fill_in 'project_name', :with => 'NewProject' + fill_in 'project_code', :with => 'NPR' + fill_in 'project_path', :with => 'newproject' click_button "Create project" end Then 'I should see project page' do current_path.should == project_path(Project.last) - page.should have_content "Empty" + page.should have_content "NewProject" end And 'I should see empty project instuctions' do diff --git a/features/steps/project/project.rb b/features/steps/project/project.rb index e9ef1495..f33f12eb 100644 --- a/features/steps/project/project.rb +++ b/features/steps/project/project.rb @@ -1,18 +1,5 @@ -class ProjectFeature < Spinach::FeatureSteps +class Projects < Spinach::FeatureSteps include SharedAuthentication include SharedProject include SharedPaths - - And 'change project settings' do - fill_in 'project_name', with: 'NewName' - uncheck 'project_issues_enabled' - end - - And 'I save project' do - click_button 'Save' - end - - Then 'I should see project with new settings' do - find_field('project_name').value.should == 'NewName' - end end diff --git a/features/steps/project/project_active_tab.rb b/features/steps/project/project_active_tab.rb index 4c6d890f..a5c80353 100644 --- a/features/steps/project/project_active_tab.rb +++ b/features/steps/project/project_active_tab.rb @@ -7,11 +7,7 @@ class ProjectActiveTab < Spinach::FeatureSteps # Main Tabs Then 'the active main tab should be Home' do - ensure_active_main_tab('Home') - end - - Then 'the active main tab should be Settings' do - ensure_active_main_tab('Settings') + ensure_active_main_tab(@project.name) end Then 'the active main tab should be Files' do diff --git a/features/steps/project/project_browse_commits.rb b/features/steps/project/project_browse_commits.rb index b4c595fa..6bf164e2 100644 --- a/features/steps/project/project_browse_commits.rb +++ b/features/steps/project/project_browse_commits.rb @@ -4,7 +4,7 @@ class ProjectBrowseCommits < Spinach::FeatureSteps include SharedPaths Then 'I see project commits' do - commit = @project.repository.commit + commit = @project.commit page.should have_content(@project.name) page.should have_content(commit.message) page.should have_content(commit.id.to_s[0..5]) @@ -15,7 +15,7 @@ class ProjectBrowseCommits < Spinach::FeatureSteps end Then 'I see commits atom feed' do - commit = @project.repository.commit + commit = CommitDecorator.decorate(@project.commit) page.response_headers['Content-Type'].should have_content("application/atom+xml") page.body.should have_selector("title", :text => "Recent commits to #{@project.name}") page.body.should have_selector("author email", :text => commit.author_email) @@ -32,8 +32,8 @@ class ProjectBrowseCommits < Spinach::FeatureSteps end And 'I fill compare fields with refs' do - fill_in "from", with: "8716fc78f3c65bbf7bcf7b574febd583bc5d2812" - fill_in "to", with: "bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a" + fill_in "from", with: "bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a" + fill_in "to", with: "8716fc78f3c65bbf7bcf7b574febd583bc5d2812" click_button "Compare" end @@ -48,7 +48,7 @@ class ProjectBrowseCommits < Spinach::FeatureSteps page.should have_selector('ul.breadcrumb span.divider', count: 3) page.should have_selector('ul.breadcrumb a', count: 4) - find('ul.breadcrumb li:first a')['href'].should match(/#{@project.path_with_namespace}\/commits\/master\z/) + find('ul.breadcrumb li:first a')['href'].should match(/#{@project.path}\/commits\/master\z/) find('ul.breadcrumb li:last a')['href'].should match(%r{master/app/models/project\.rb\z}) end diff --git a/features/steps/project/project_browse_files.rb b/features/steps/project/project_browse_files.rb index 71360fb6..4efce0dc 100644 --- a/features/steps/project/project_browse_files.rb +++ b/features/steps/project/project_browse_files.rb @@ -16,12 +16,12 @@ class ProjectBrowseFiles < Spinach::FeatureSteps page.should have_content "Gemfile" end - Given 'I click on "Gemfile.lock" file in repo' do - click_link "Gemfile.lock" + Given 'I click on "Gemfile" file in repo' do + click_link "Gemfile" end Then 'I should see it content' do - page.should have_content "DEPENDENCIES" + page.should have_content "rubygems.org" end And 'I click link "raw"' do diff --git a/features/steps/project/project_browse_git_repo.rb b/features/steps/project/project_browse_git_repo.rb index cd9a60f4..e966f407 100644 --- a/features/steps/project/project_browse_git_repo.rb +++ b/features/steps/project/project_browse_git_repo.rb @@ -3,8 +3,8 @@ class ProjectBrowseGitRepo < Spinach::FeatureSteps include SharedProject include SharedPaths - Given 'I click on "Gemfile.lock" file in repo' do - click_link "Gemfile.lock" + Given 'I click on "Gemfile" file in repo' do + click_link "Gemfile" end And 'I click blame button' do @@ -12,8 +12,8 @@ class ProjectBrowseGitRepo < Spinach::FeatureSteps end Then 'I should see git file blame' do - page.should have_content "DEPENDENCIES" + page.should have_content "rubygems.org" page.should have_content "Dmitriy Zaporozhets" - page.should have_content "Moving to rails 3.2" + page.should have_content "bc3735004cb Moving to rails 3.2" end end diff --git a/features/steps/project/comments_on_commits.rb b/features/steps/project/project_comment_commit.rb similarity index 66% rename from features/steps/project/comments_on_commits.rb rename to features/steps/project/project_comment_commit.rb index 56bb12a8..cb8385e1 100644 --- a/features/steps/project/comments_on_commits.rb +++ b/features/steps/project/project_comment_commit.rb @@ -1,6 +1,6 @@ -class CommentsOnCommits < Spinach::FeatureSteps +class ProjectCommentCommit < Spinach::FeatureSteps include SharedAuthentication + include SharedProject include SharedNote include SharedPaths - include SharedProject end diff --git a/features/steps/project/project_issues.rb b/features/steps/project/project_issues.rb index 7d540099..88bfac63 100644 --- a/features/steps/project/project_issues.rb +++ b/features/steps/project/project_issues.rb @@ -73,6 +73,7 @@ class ProjectIssues < Spinach::FeatureSteps end And 'I fill in issue search with ""' do + page.execute_script("$('.issue_search').val('').keyup();"); fill_in 'issue_search', with: "" end @@ -95,7 +96,7 @@ class ProjectIssues < Spinach::FeatureSteps end Then 'I should see selected milestone with title "v3.0"' do - issues_milestone_selector = "#issue_milestone_id_chzn > a" + issues_milestone_selector = "#milestone_id_chzn > a" page.find(issues_milestone_selector).should have_content("v3.0") end @@ -106,7 +107,7 @@ class ProjectIssues < Spinach::FeatureSteps end Then 'I should see first assignee from "Shop" as selected assignee' do - issues_assignee_selector = "#issue_assignee_id_chzn > a" + issues_assignee_selector = "#assignee_id_chzn > a" project = Project.find_by_name "Shop" assignee_name = project.users.first.name page.find(issues_assignee_selector).should have_content(assignee_name) @@ -122,9 +123,10 @@ class ProjectIssues < Spinach::FeatureSteps And 'project "Shop" have "Release 0.3" closed issue' do project = Project.find_by_name("Shop") - create(:closed_issue, + create(:issue, :title => "Release 0.3", :project => project, - :author => project.users.first) + :author => project.users.first, + :closed => true) end end diff --git a/features/steps/project/project_merge_requests.rb b/features/steps/project/project_merge_requests.rb index 4c22119b..d153ad28 100644 --- a/features/steps/project/project_merge_requests.rb +++ b/features/steps/project/project_merge_requests.rb @@ -4,148 +4,77 @@ class ProjectMergeRequests < Spinach::FeatureSteps include SharedNote include SharedPaths - Given 'I click link "New Merge Request"' do - click_link "New Merge Request" - end - - Given 'I click link "Bug NS-04"' do - click_link "Bug NS-04" - end - - Given 'I click link "All"' do - click_link "All" - end - - Given 'I click link "Closed"' do - click_link "Closed" - end - - Then 'I should see merge request "Wiki Feature"' do - page.should have_content "Wiki Feature" - end - - Then 'I should see closed merge request "Bug NS-04"' do - merge_request = MergeRequest.find_by_title!("Bug NS-04") - merge_request.closed?.should be_true - page.should have_content "Closed by" - end - - Then 'I should see merge request "Bug NS-04"' do - page.should have_content "Bug NS-04" - end - Then 'I should see "Bug NS-04" in merge requests' do page.should have_content "Bug NS-04" end - Then 'I should see "Feature NS-03" in merge requests' do - page.should have_content "Feature NS-03" - end - And 'I should not see "Feature NS-03" in merge requests' do page.should_not have_content "Feature NS-03" end + Given 'I click link "Closed"' do + click_link "Closed" + end + + Then 'I should see "Feature NS-03" in merge requests' do + page.should have_content "Feature NS-03" + end And 'I should not see "Bug NS-04" in merge requests' do page.should_not have_content "Bug NS-04" end + Given 'I click link "All"' do + click_link "All" + end + + Given 'I click link "Bug NS-04"' do + click_link "Bug NS-04" + end + + Then 'I should see merge request "Bug NS-04"' do + page.should have_content "Bug NS-04" + end + And 'I click link "Close"' do click_link "Close" 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 + page.should have_content "Closed by" + end + + Given 'I click link "New Merge Request"' do + click_link "New Merge Request" + end + And 'I submit new merge request "Wiki Feature"' do fill_in "merge_request_title", :with => "Wiki Feature" select "master", :from => "merge_request_source_branch" select "stable", :from => "merge_request_target_branch" - click_button "Submit merge request" + click_button "Save" + end + + Then 'I should see merge request "Wiki Feature"' do + page.should have_content "Wiki Feature" 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, - author: project.users.first) - end - - And 'project "Shop" have "Bug NS-05" open merge request with diffs inside' do - create(:merge_request_with_diffs, - title: "Bug NS-05", - project: project, - author: project.users.first) + :title => "Bug NS-04", + :project => project, + :author => project.users.first) end And 'project "Shop" have "Feature NS-03" closed merge request' do - create(:closed_merge_request, - title: "Feature NS-03", - project: project, - author: project.users.first) - end - - And 'I switch to the diff tab' do - visit diffs_project_merge_request_path(project, merge_request) - end - - And 'I switch to the merge request\'s comments tab' do - visit project_merge_request_path(project, merge_request) - end - - And 'I click on the first commit in the merge request' do - - click_link merge_request.commits.first.short_id(8) - end - - And 'I leave a comment on the diff page' do - find("#4735dfc552ad7bf15ca468adc3cad9d05b624490_185_185 .add-diff-note").click - - within('.js-temp-notes-holder') do - fill_in "note_note", with: "One comment to rule them all" - click_button "Add Comment" - end - end - - And 'I leave a comment like "Line is wrong" on line 185 of the first file' do - find("#4735dfc552ad7bf15ca468adc3cad9d05b624490_185_185 .add-diff-note").click - - within(".js-temp-notes-holder") do - fill_in "note_note", with: "Line is wrong" - click_button "Add Comment" - sleep 0.05 - end - end - - Then 'I should see a discussion has started on line 185' do - 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" - page.should have_content "Line is wrong" - end - - Then 'I should see a discussion has started on commit bcf03b5de6c:L185' do - 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) - page.should have_content "#{first_diff.b_path}:L185" - page.should have_content "Line is wrong" - end - - Then 'I should see a discussion has started on commit bcf03b5de6c' do - 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") + project = Project.find_by_name("Shop") + create(:merge_request, + :title => "Feature NS-03", + :project => project, + :author => project.users.first, + :closed => true) end end diff --git a/features/steps/project/project_milestones.rb b/features/steps/project/project_milestones.rb index 1350938e..1c9ad6da 100644 --- a/features/steps/project/project_milestones.rb +++ b/features/steps/project/project_milestones.rb @@ -50,12 +50,12 @@ class ProjectMilestones < Spinach::FeatureSteps end Then "I should see 3 issues" do - page.should have_selector('.milestone-issue-filter .well-list li', count: 4) - page.should have_selector('.milestone-issue-filter .well-list li.hide', count: 1) + page.should have_selector('.milestone-issue-filter tbody tr', count: 4) + page.should have_selector('.milestone-issue-filter tbody tr.hide', count: 1) end Then "I should see 4 issues" do - page.should have_selector('.milestone-issue-filter .well-list li', count: 4) - page.should_not have_selector('.milestone-issue-filter .well-list li.hide') + page.should have_selector('.milestone-issue-filter tbody tr', count: 4) + page.should_not have_selector('.milestone-issue-filter tbody tr.hide') end end diff --git a/features/steps/project/project_network_graph.rb b/features/steps/project/project_network_graph.rb index cf5fa751..6fde532f 100644 --- a/features/steps/project/project_network_graph.rb +++ b/features/steps/project/project_network_graph.rb @@ -4,53 +4,17 @@ class ProjectNetworkGraph < Spinach::FeatureSteps Then 'page should have network graph' do page.should have_content "Project Network Graph" - page.should have_selector ".graph" + within ".graph" do + page.should have_content "master" + page.should have_content "scss_refactor..." + end end - When 'I visit project "Shop" network page' do - # Stub Graph max_size to speed up test (10 commits vs. 650) - Network::Graph.stub(max_count: 10) + And '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) 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 - - When 'I switch ref to "stable"' do - page.select 'stable', :from => 'ref' - sleep 2 - 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 - - When '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 - sleep 2 - end - - And 'page should have "v2.1.0" on graph' do - within '.graph' do - page.should have_content 'v2.1.0' - end + visit graph_project_path(project) end end diff --git a/features/steps/project/project_services.rb b/features/steps/project/project_services.rb deleted file mode 100644 index b1668ff7..00000000 --- a/features/steps/project/project_services.rb +++ /dev/null @@ -1,30 +0,0 @@ -class ProjectServices < Spinach::FeatureSteps - include SharedAuthentication - include SharedProject - include SharedPaths - - When 'I visit project "Shop" services page' do - visit project_services_path(@project) - end - - Then 'I should see list of available services' do - page.should have_content 'Services' - page.should have_content 'Jenkins' - page.should have_content 'GitLab CI' - end - - And 'I click gitlab-ci service link' do - click_link 'GitLab CI' - end - - And 'I fill gitlab-ci settings' do - check 'Active' - fill_in 'Project URL', with: 'http://ci.gitlab.org/projects/3' - fill_in 'CI Project token', with: 'verySecret' - click_button 'Save' - end - - Then 'I should see service settings saved' do - find_field('Project URL').value.should == 'http://ci.gitlab.org/projects/3' - end -end diff --git a/features/steps/project/project_team_management.rb b/features/steps/project/project_team_management.rb index e8e05435..6bde0b64 100644 --- a/features/steps/project/project_team_management.rb +++ b/features/steps/project/project_team_management.rb @@ -2,17 +2,16 @@ class ProjectTeamManagement < Spinach::FeatureSteps include SharedAuthentication include SharedProject include SharedPaths - include Select2Helper Then 'I should be able to see myself in team' do page.should have_content(@user.name) - page.should have_content(@user.username) + page.should have_content(@user.email) end And 'I should see "Sam" in team list' do user = User.find_by_name("Sam") page.should have_content(user.name) - page.should have_content(user.username) + page.should have_content(user.email) end Given 'I click link "New Team Member"' do @@ -21,12 +20,11 @@ class ProjectTeamManagement < Spinach::FeatureSteps And 'I select "Mike" as "Reporter"' do user = User.find_by_name("Mike") - - select2(user.id, from: "#user_ids", multiple: true) within "#new_team_member" do + select user.name, :from => "user_ids" select "Reporter", :from => "project_access" end - click_button "Add users" + click_button "Save" end Then 'I should see "Mike" in team list as "Reporter"' do @@ -54,6 +52,17 @@ class ProjectTeamManagement < Spinach::FeatureSteps role_id.should == UsersProject.access_roles["Reporter"].to_s end + Given 'I click link "Sam"' do + click_link "Sam" + end + + Then 'I should see "Sam" team profile' do + user = User.find_by_name("Sam") + page.should have_content(user.name) + page.should have_content(user.email) + page.should have_content("To team list") + end + And 'I click link "Remove from team"' do click_link "Remove from team" end @@ -61,7 +70,7 @@ class ProjectTeamManagement < Spinach::FeatureSteps And 'I should not see "Sam" in team list' do user = User.find_by_name("Sam") page.should_not have_content(user.name) - page.should_not have_content(user.username) + page.should_not have_content(user.email) end And 'gitlab user "Mike"' do @@ -75,18 +84,18 @@ class ProjectTeamManagement < Spinach::FeatureSteps And '"Sam" is "Shop" developer' do user = User.find_by_name("Sam") project = Project.find_by_name("Shop") - project.team << [user, :developer] + project.add_access(user, :write) end Given 'I own project "Website"' do @project = create(:project, :name => "Website") - @project.team << [@user, :master] + @project.add_access(@user, :admin) end And '"Mike" is "Website" reporter' do user = User.find_by_name("Mike") project = Project.find_by_name("Website") - project.team << [user, :reporter] + project.add_access(user, :read) end And 'I click link "Import team from another project"' do @@ -97,10 +106,4 @@ class ProjectTeamManagement < Spinach::FeatureSteps select 'Website', from: 'source_project_id' click_button 'Import' end - - step 'I click cancel link for "Sam"' do - within "#user_#{User.find_by_name('Sam').id}" do - click_link('Remove user from team') - end - end end diff --git a/features/steps/project/project_wall.rb b/features/steps/project/project_wall.rb index 7c61580e..ba9d3533 100644 --- a/features/steps/project/project_wall.rb +++ b/features/steps/project/project_wall.rb @@ -3,16 +3,4 @@ class ProjectWall < Spinach::FeatureSteps include SharedProject include SharedNote include SharedPaths - - - Given 'I write new comment "my special test message"' do - within(".wall-note-form") do - fill_in "note[note]", with: "my special test message" - click_button "Add Comment" - end - end - - Then 'I should see project wall note "my special test message"' do - page.should have_content "my special test message" - end end diff --git a/features/steps/project/project_wiki.rb b/features/steps/project/project_wiki.rb index 745e9ede..902e9ce1 100644 --- a/features/steps/project/project_wiki.rb +++ b/features/steps/project/project_wiki.rb @@ -4,89 +4,17 @@ class ProjectWiki < Spinach::FeatureSteps include SharedNote include SharedPaths - Given 'I click on the Cancel button' do - within(:css, ".actions") do - click_on "Cancel" - end - end - - Then 'I should be redirected back to the Edit Home Wiki page' do - url = URI.parse(current_url) - url.path.should == project_wiki_path(project, :home) - end - - Given 'I create the Wiki Home page' do + Given 'I create Wiki page' do + fill_in "Title", :with => 'Test title' fill_in "Content", :with => '[link test](test)' click_on "Save" end - Then 'I should see the newly created wiki page' do - page.should have_content "Home" + Then 'I should see newly created wiki page' do + page.should have_content "Test title" page.should have_content "link test" click_link "link test" page.should have_content "Editing page" end - - Given 'I have an existing Wiki page' do - wiki.create_page("existing", "content", :markdown, "first commit") - @page = wiki.find_page("existing") - end - - And 'I browse to that Wiki page' do - visit project_wiki_path(project, @page) - end - - And 'I click on the Edit button' do - click_on "Edit" - end - - And 'I change the content' do - fill_in "Content", :with => 'Updated Wiki Content' - click_on "Save" - end - - Then 'I should see the updated content' do - page.should have_content "Updated Wiki Content" - end - - Then 'I should be redirected back to that Wiki page' do - url = URI.parse(current_url) - url.path.should == project_wiki_path(project, @page) - end - - And 'That page has two revisions' do - @page.update("new content", :markdown, "second commit") - end - - And 'I click the History button' do - click_on "History" - end - - Then 'I should see both revisions' do - page.should have_content current_user.name - page.should have_content "first commit" - page.should have_content "second commit" - end - - And 'I click on the "Delete this page" button' do - click_on "Delete this page" - end - - Then 'The page should be deleted' do - page.should have_content "Page was successfully deleted" - end - - And 'I click on the "Pages" button' do - click_on "Pages" - end - - Then 'I should see the existing page in the pages list' do - page.should have_content current_user.name - page.should have_content @page.title.titleize - end - - def wiki - @gollum_wiki = GollumWiki.new(project, current_user) - end end diff --git a/features/steps/shared/active_tab.rb b/features/steps/shared/active_tab.rb index 617a077b..884f2d5f 100644 --- a/features/steps/shared/active_tab.rb +++ b/features/steps/shared/active_tab.rb @@ -2,11 +2,7 @@ module SharedActiveTab include Spinach::DSL def ensure_active_main_tab(content) - if content == "Home" - page.find('.main-nav li.active').should have_css('i.icon-home') - else - page.find('.main-nav li.active').should have_content(content) - end + page.find('ul.main_menu li.active').should have_content(content) end def ensure_active_sub_tab(content) @@ -14,7 +10,7 @@ module SharedActiveTab end And 'no other main tabs should be active' do - page.should have_selector('.main-nav li.active', count: 1) + page.should have_selector('ul.main_menu li.active', count: 1) end And 'no other sub tabs should be active' do diff --git a/features/steps/shared/admin.rb b/features/steps/shared/admin.rb deleted file mode 100644 index 1b712dc6..00000000 --- a/features/steps/shared/admin.rb +++ /dev/null @@ -1,12 +0,0 @@ -module SharedAdmin - include Spinach::DSL - - And 'there are projects in system' do - 2.times { create(:project) } - end - - And 'system has users' do - 2.times { create(:user) } - end -end - diff --git a/features/steps/shared/diff_note.rb b/features/steps/shared/diff_note.rb deleted file mode 100644 index 9dbbc553..00000000 --- a/features/steps/shared/diff_note.rb +++ /dev/null @@ -1,160 +0,0 @@ -module SharedDiffNote - include Spinach::DSL - - Given 'I cancel the diff comment' do - within(".file") do - find(".js-close-discussion-note-form").trigger("click") - end - end - - Given 'I delete a diff comment' do - sleep 1 - within(".file") do - first(".js-note-delete").trigger("click") - end - end - - Given 'I haven\'t written any diff comment text' do - within(".file") do - fill_in "note[note]", with: "" - end - end - - Given 'I leave a diff comment like "Typo, please fix"' do - find("#586fb7c4e1add2d4d24e27566ed7064680098646_29_14.line_holder .js-add-diff-note-button").trigger("click") - within(".file form[rel$='586fb7c4e1add2d4d24e27566ed7064680098646_29_14']") do - fill_in "note[note]", with: "Typo, please fix" - #click_button("Add Comment") - find(".js-comment-button").trigger("click") - sleep 0.05 - end - end - - Given 'I preview a diff comment text like "Should fix it :smile:"' do - find("#586fb7c4e1add2d4d24e27566ed7064680098646_29_14.line_holder .js-add-diff-note-button").trigger("click") - within(".file form[rel$='586fb7c4e1add2d4d24e27566ed7064680098646_29_14']") do - fill_in "note[note]", with: "Should fix it :smile:" - find(".js-note-preview-button").trigger("click") - end - end - - Given 'I preview another diff comment text like "DRY this up"' do - find("#586fb7c4e1add2d4d24e27566ed7064680098646_57_41.line_holder .js-add-diff-note-button").trigger("click") - within(".file form[rel$='586fb7c4e1add2d4d24e27566ed7064680098646_57_41']") do - fill_in "note[note]", with: "DRY this up" - find(".js-note-preview-button").trigger("click") - end - end - - Given 'I open a diff comment form' do - find("#586fb7c4e1add2d4d24e27566ed7064680098646_29_14.line_holder .js-add-diff-note-button").trigger("click") - end - - Given 'I open another diff comment form' do - find("#586fb7c4e1add2d4d24e27566ed7064680098646_57_41.line_holder .js-add-diff-note-button").trigger("click") - end - - Given 'I write a diff comment like ":-1: I don\'t like this"' do - within(".file") do - fill_in "note[note]", with: ":-1: I don\'t like this" - end - end - - Given 'I submit the diff comment' do - within(".file") do - click_button("Add Comment") - end - end - - - - Then 'I should not see the diff comment form' do - within(".file") do - page.should_not have_css("form.new_note") - end - end - - Then 'I should not see the diff comment preview button' do - within(".file") do - page.should have_css(".js-note-preview-button", visible: false) - end - end - - Then 'I should not see the diff comment text field' do - within(".file") do - page.should have_css(".js-note-text", visible: false) - end - end - - Then 'I should only see one diff form' do - within(".file") do - page.should have_css("form.new_note", count: 1) - end - end - - Then 'I should see a diff comment form with ":-1: I don\'t like this"' do - within(".file") do - page.should have_field("note[note]", with: ":-1: I don\'t like this") - end - end - - Then 'I should see a diff comment saying "Typo, please fix"' do - within(".file .note") do - page.should have_content("Typo, please fix") - end - end - - Then 'I should see a discussion reply button' do - within(".file") do - page.should have_link("Reply") - end - end - - Then 'I should see a temporary diff comment form' do - within(".file") do - page.should have_css(".js-temp-notes-holder form.new_note") - end - end - - Then 'I should see add a diff comment button' do - page.should have_css(".js-add-diff-note-button", visible: false) - end - - Then 'I should see an empty diff comment form' do - within(".file") do - page.should have_field("note[note]", with: "") - end - end - - Then 'I should see the cancel comment button' do - within(".file form") do - page.should have_css(".js-close-discussion-note-form", text: "Cancel") - end - end - - Then 'I should see the diff comment preview' do - within(".file form") do - page.should have_css(".js-note-preview", visible: false) - end - end - - Then 'I should see the diff comment edit button' do - within(".file") do - page.should have_css(".js-note-edit-button", visible: true) - end - end - - Then 'I should see the diff comment preview button' do - within(".file") do - page.should have_css(".js-note-preview-button", visible: true) - end - end - - Then 'I should see two separate previews' do - within(".file") do - page.should have_css(".js-note-preview", visible: true, count: 2) - page.should have_content("Should fix it") - page.should have_content("DRY this up") - end - end -end diff --git a/features/steps/shared/note.rb b/features/steps/shared/note.rb index e0ff52a7..923e69b6 100644 --- a/features/steps/shared/note.rb +++ b/features/steps/shared/note.rb @@ -1,105 +1,21 @@ module SharedNote include Spinach::DSL - Given 'I delete a comment' do - sleep 1 - first(".js-note-delete").trigger("click") - end - - Given 'I haven\'t written any comment text' do - within(".js-main-target-form") do - fill_in "note[note]", with: "" - end - end - Given 'I leave a comment like "XML attached"' do - within(".js-main-target-form") do - fill_in "note[note]", with: "XML attached" - click_button "Add Comment" - sleep 0.05 - end - end - - Given 'I preview a comment text like "Bug fixed :smile:"' do - within(".js-main-target-form") do - fill_in "note[note]", with: "Bug fixed :smile:" - find(".js-note-preview-button").trigger("click") - end - end - - Given 'I submit the comment' do - within(".js-main-target-form") do - click_button "Add Comment" - end - end - - Given 'I write a comment like "Nice"' do - within(".js-main-target-form") do - fill_in "note[note]", with: "Nice" - end - end - - Then 'I should not see a comment saying "XML attached"' do - page.should_not have_css(".note") - end - - Then 'I should not see the cancel comment button' do - within(".js-main-target-form") do - should_not have_link("Cancel") - end - end - - Then 'I should not see the comment preview' do - within(".js-main-target-form") do - page.should have_css(".js-note-preview", visible: false) - end - end - - Then 'I should not see the comment preview button' do - within(".js-main-target-form") do - page.should have_css(".js-note-preview-button", visible: false) - end - end - - Then 'I should not see the comment text field' do - within(".js-main-target-form") do - page.should have_css(".js-note-text", visible: false) - end - end - - Then 'I should see a comment saying "XML attached"' do - within(".note") do - page.should have_content("XML attached") - end - end - - Then 'I should see an empty comment text field' do - within(".js-main-target-form") do - page.should have_field("note[note]", with: "") - end - end - - Then 'I should see the comment edit button' do - within(".js-main-target-form") do - page.should have_css(".js-note-edit-button", visible: true) - end - end - - Then 'I should see the comment preview' do - within(".js-main-target-form") do - page.should have_css(".js-note-preview", visible: true) - end - end - - Then 'I should see the comment preview button' do - within(".js-main-target-form") do - page.should have_css(".js-note-preview-button", visible: true) - end + fill_in "note_note", :with => "XML attached" + click_button "Add Comment" end Then 'I should see comment "XML attached"' do - within(".note") do - page.should have_content("XML attached") - end + page.should have_content "XML attached" + end + + Given 'I write new comment "my special test message"' do + fill_in "note_note", :with => "my special test message" + click_button "Add Comment" + end + + Then 'I should see project wall note "my special test message"' do + page.should have_content "my special test message" end end diff --git a/features/steps/shared/paths.rb b/features/steps/shared/paths.rb index 1af8b478..33a94027 100644 --- a/features/steps/shared/paths.rb +++ b/features/steps/shared/paths.rb @@ -21,14 +21,6 @@ module SharedPaths visit merge_requests_group_path(current_group) end - When 'I visit group people page' do - visit people_group_path(current_group) - end - - When 'I visit group settings page' do - visit edit_group_path(current_group) - end - # ---------------------------------------- # Dashboard # ---------------------------------------- @@ -37,16 +29,12 @@ module SharedPaths visit dashboard_path end - Given 'I visit dashboard projects page' do - visit projects_dashboard_path - end - Given 'I visit dashboard issues page' do - visit issues_dashboard_path + visit dashboard_issues_path end Given 'I visit dashboard merge requests page' do - visit merge_requests_dashboard_path + visit dashboard_merge_requests_path end Given 'I visit dashboard search page' do @@ -66,7 +54,7 @@ module SharedPaths end Given 'I visit profile account page' do - visit account_profile_path + visit profile_account_path end Given 'I visit profile SSH keys page' do @@ -74,11 +62,15 @@ module SharedPaths end Given 'I visit profile design page' do - visit design_profile_path + visit profile_design_path end Given 'I visit profile history page' do - visit history_profile_path + visit profile_history_path + end + + Given 'I visit profile token page' do + visit profile_token_path end # ---------------------------------------- @@ -113,10 +105,6 @@ module SharedPaths visit admin_groups_path end - When 'I visit admin teams page' do - visit admin_teams_path - end - # ---------------------------------------- # Generic Project # ---------------------------------------- @@ -125,20 +113,16 @@ module SharedPaths visit project_path(@project) end - Given "I visit my project's settings page" do - visit edit_project_path(@project) - end - Given "I visit my project's files page" do - visit project_tree_path(@project, root_ref) + visit project_tree_path(@project, @project.root_ref) end Given "I visit my project's commits page" do - visit project_commits_path(@project, root_ref, {limit: 5}) + visit project_commits_path(@project, @project.root_ref, {limit: 5}) end Given "I visit my project's commits page for a specific path" do - visit project_commits_path(@project, root_ref + "/app/models/project.rb", {limit: 5}) + visit project_commits_path(@project, @project.root_ref + "/app/models/project.rb", {limit: 5}) end Given 'I visit my project\'s commits stats page' do @@ -146,10 +130,10 @@ module SharedPaths end Given "I visit my project's network page" do - # Stub Graph max_size to speed up test (10 commits vs. 650) - Network::Graph.stub(max_count: 10) + # Stub Graph::JsonBuilder max_size to speed up test (10 commits vs. 650) + Gitlab::Graph::JsonBuilder.stub(max_count: 10) - visit project_graph_path(@project, root_ref) + visit graph_project_path(@project) end Given "I visit my project's issues page" do @@ -161,11 +145,11 @@ module SharedPaths end Given "I visit my project's wall page" do - visit project_wall_path(@project) + visit wall_project_path(@project) end Given "I visit my project's wiki page" do - visit project_wiki_path(@project, :home) + visit project_wiki_path(@project, :index) end When 'I visit project hooks page' do @@ -177,13 +161,10 @@ module SharedPaths # ---------------------------------------- And 'I visit project "Shop" page' do + project = Project.find_by_name("Shop") visit project_path(project) end - When 'I visit edit project "Shop" page' do - visit edit_project_path(project) - end - Given 'I visit project branches page' do visit branches_project_repository_path(@project) end @@ -193,7 +174,7 @@ module SharedPaths end Given 'I visit project commits page' do - visit project_commits_path(@project, root_ref, {limit: 5}) + visit project_commits_path(@project, @project.root_ref, {limit: 5}) end Given 'I visit project commits page for stable branch' do @@ -201,7 +182,7 @@ module SharedPaths end Given 'I visit project source page' do - visit project_tree_path(@project, root_ref) + visit project_tree_path(@project, @project.root_ref) end Given 'I visit blob file from repo' do @@ -221,7 +202,7 @@ module SharedPaths end And 'I visit project "Shop" issues page' do - visit project_issues_path(project) + visit project_issues_path(Project.find_by_name("Shop")) end Given 'I visit issue page "Release 0.4"' do @@ -230,7 +211,7 @@ module SharedPaths end Given 'I visit project "Shop" labels page' do - visit project_labels_path(project) + visit project_labels_path(Project.find_by_name("Shop")) end Given 'I visit merge request page "Bug NS-04"' do @@ -238,36 +219,25 @@ module SharedPaths visit project_merge_request_path(mr.project, mr) end - Given 'I visit merge request page "Bug NS-05"' do - mr = MergeRequest.find_by_title("Bug NS-05") - visit project_merge_request_path(mr.project, mr) - end - And 'I visit project "Shop" merge requests page' do - visit project_merge_requests_path(project) + visit project_merge_requests_path(Project.find_by_name("Shop")) end Given 'I visit project "Shop" milestones page' do - visit project_milestones_path(project) + @project = Project.find_by_name("Shop") + visit project_milestones_path(@project) end Then 'I visit project "Shop" team page' do - visit project_team_index_path(project) + visit project_team_index_path(Project.find_by_name("Shop")) end Then 'I visit project "Shop" wall page' do - visit project_wall_path(project) + project = Project.find_by_name("Shop") + visit wall_project_path(project) end Given 'I visit project wiki page' do - visit project_wiki_path(@project, :home) - end - - def root_ref - @project.repository.root_ref - end - - def project - project = Project.find_by_name!("Shop") + visit project_wiki_path(@project, :index) end end diff --git a/features/steps/shared/project.rb b/features/steps/shared/project.rb index b16032a8..dfc8ce9d 100644 --- a/features/steps/shared/project.rb +++ b/features/steps/shared/project.rb @@ -3,52 +3,14 @@ module SharedProject # Create a project without caring about what it's called And "I own a project" do - @project = create(:project_with_code) - @project.team << [@user, :master] + @project = create(:project) + @project.add_access(@user, :admin) end # Create a specific project called "Shop" And 'I own project "Shop"' do - @project = Project.find_by_name "Shop" - @project ||= create(:project_with_code, name: "Shop") - @project.team << [@user, :master] - end - - And 'project "Shop" has push event' do - @project = Project.find_by_name("Shop") - - data = { - before: "0000000000000000000000000000000000000000", - after: "0220c11b9a3e6c69dc8fd35321254ca9a7b98f7e", - ref: "refs/heads/new_design", - user_id: @user.id, - user_name: @user.name, - repository: { - name: @project.name, - url: "localhost/rubinius", - description: "", - homepage: "localhost/rubinius", - private: true - } - } - - @event = Event.create( - project: @project, - action: Event::PUSHED, - data: data, - author_id: @user.id - ) - end - - Then 'I should see project "Shop" activity feed' do - project = Project.find_by_name("Shop") - page.should have_content "#{@user.name} pushed new branch new_design at #{project.name}" - end - - Then 'I should see project settings' do - current_path.should == edit_project_path(@project) - page.should have_content("Project name is") - page.should have_content("Features:") + @project = create(:project, :name => "Shop") + @project.add_access(@user, :admin) end def current_project diff --git a/features/steps/userteams/userteams.rb b/features/steps/userteams/userteams.rb deleted file mode 100644 index 9a86572e..00000000 --- a/features/steps/userteams/userteams.rb +++ /dev/null @@ -1,257 +0,0 @@ -class Userteams < Spinach::FeatureSteps - include SharedAuthentication - include SharedPaths - include SharedProject - include Select2Helper - - When 'I do not have teams with me' do - UserTeam.with_member(current_user).destroy_all - end - - Then 'I should see dashboard page without teams info block' do - page.has_no_css?(".teams-box").must_equal true - end - - When 'I have teams with my membership' do - team = create :user_team, owner: current_user - team.add_member(current_user, UserTeam.access_roles["Master"], true) - end - - Then 'I should see dashboard page with teams information block' do - page.should have_css(".teams-box") - end - - When 'exist user teams' do - team = create :user_team - team.add_member(current_user, UserTeam.access_roles["Master"], true) - end - - And 'I click on "All teams" link' do - click_link("All Teams") - end - - Then 'I should see "All teams" page' do - current_path.should == teams_path - end - - And 'I should see exist teams in teams list' do - team = UserTeam.last - find_in_list(".teams_list tr", team).must_equal true - end - - When 'I click to "New team" link' do - click_link("New Team") - end - - 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) - end - - When 'I have teams with projects and members' do - team = create :user_team, owner: current_user - @project = create :project - team.add_member(current_user, UserTeam.access_roles["Master"], true) - team.assign_to_project(@project, UserTeam.access_roles["Master"]) - @event = create(:closed_issue_event, project: @project) - end - - When 'I visit team page' do - visit team_path(UserTeam.last) - end - - Then 'I should see projects list' do - within(".side .ui-box") do - page.should have_content(@project.name) - end - end - - And 'project from team has issues assigned to me' do - team = UserTeam.last - team.projects.each do |project| - project.issues << create(:issue, assignee: current_user) - end - end - - When 'I visit team issues page' do - team = UserTeam.last - visit issues_team_path(team) - end - - Then 'I should see issues from this team assigned to me' do - team = UserTeam.last - team.projects.each do |project| - project.issues.assigned(current_user).each do |issue| - page.should have_content issue.title - end - end - end - - Given 'I have team with projects and members' do - team = create :user_team, owner: current_user - project = create :project - user = create :user - team.add_member(current_user, UserTeam.access_roles["Master"], true) - team.add_member(user, UserTeam.access_roles["Developer"], false) - team.assign_to_project(project, UserTeam.access_roles["Master"]) - end - - Given 'project from team has issues assigned to teams members' do - team = UserTeam.last - team.projects.each do |project| - team.members.each do |member| - project.issues << create(:issue, assignee: member) - end - end - end - - Then 'I should see issues from this team assigned to teams members' do - team = UserTeam.last - team.projects.each do |project| - team.members.each do |member| - project.issues.assigned(member).each do |issue| - page.should have_content issue.title - end - end - end - end - - Given 'project from team has merge requests assigned to me' do - team = UserTeam.last - team.projects.each do |project| - team.members.each do |member| - 3.times { create(:merge_request, assignee: member, project: project) } - end - end - end - - When 'I visit team merge requests page' do - team = UserTeam.last - visit merge_requests_team_path(team) - end - - Then 'I should see merge requests from this team assigned to me' do - team = UserTeam.last - team.projects.each do |project| - team.members.each do |member| - project.issues.assigned(member).each do |merge_request| - page.should have_content merge_request.title - end - end - end - end - - Given 'project from team has merge requests assigned to team members' do - team = UserTeam.last - team.projects.each do |project| - team.members.each do |member| - 3.times { create(:merge_request, assignee: member, project: project) } - end - end - end - - Then 'I should see merge requests from this team assigned to me' do - team = UserTeam.last - team.projects.each do |project| - team.members.each do |member| - project.issues.assigned(member).each do |merge_request| - page.should have_content merge_request.title - end - end - end - end - - Given 'I have new user "John"' do - create :user, name: "John" - end - - When 'I visit team people page' do - team = UserTeam.last - visit team_members_path(team) - end - - And 'I select user "John" from list with role "Reporter"' do - user = User.find_by_name("John") - select2(user.id, from: "#user_ids", multiple: true) - within "#team_members" do - select "Reporter", from: "default_project_access" - end - click_button "Add" - end - - Then 'I should see user "John" in team list' do - user = User.find_by_name("John") - team_members_list = find(".team-table") - team_members_list.should have_content user.name - end - - And 'I have my own project without teams' do - @project = create :project, namespace: current_user.namespace - end - - And 'I visit my team page' do - team = UserTeam.where(owner_id: current_user.id).last - visit team_path(team) - end - - When 'I click on link "Assign Project"' do - click_link "Assign Project" - end - - Then 'I should see form with my own project in available projects list' do - projects_select = find("#project_ids") - projects_select.should have_content(@project.name) - end - - When 'I submit form with selected project and max access' do - within "#assign_projects" do - select @project.name_with_namespace, from: "project_ids" - select "Reporter", from: "greatest_project_access" - end - click_button "Add" - end - - Then 'I should see my own project in team projects list' do - projects = find(".projects-table") - projects.should have_content(@project.name) - end - - When 'I click link "New Team Member"' do - click_link "New Team Member" - end - - protected - - def current_team - @user_team ||= UserTeam.first - end - - def project - current_team.projects.first - end - - def assigned_to_user key, user - project.send(key).where(assignee_id: user) - end - - def find_in_list(selector, item) - members_list = all(selector) - entered = false - members_list.each do |member_item| - entered = true if member_item.has_content?(item.name) - end - entered - end -end diff --git a/features/support/env.rb b/features/support/env.rb index 08b627f5..1a72d765 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -1,26 +1,23 @@ -require 'simplecov' unless ENV['CI'] - -if ENV['TRAVIS'] - require 'coveralls' - Coveralls.wear! -end - ENV['RAILS_ENV'] = 'test' require './config/environment' require 'rspec' require 'database_cleaner' require 'spinach/capybara' -require 'sidekiq/testing/inline' - -%w(valid_commit select2_helper test_env).each do |f| +%w(gitolite_stub stubbed_repository valid_commit).each do |f| require Rails.root.join('spec', 'support', f) end Dir["#{Rails.root}/features/steps/shared/*.rb"].each {|file| require file} +# +# Stub gitolite +# +include GitoliteStub + WebMock.allow_net_connect! + # # JS driver # @@ -28,15 +25,13 @@ require 'capybara/poltergeist' Capybara.javascript_driver = :poltergeist Spinach.hooks.on_tag("javascript") do ::Capybara.current_driver = ::Capybara.javascript_driver + ::Capybara.default_wait_time = 5 end -Capybara.default_wait_time = 10 DatabaseCleaner.strategy = :truncation Spinach.hooks.before_scenario do - TestEnv.init - DatabaseCleaner.start end @@ -48,4 +43,6 @@ Spinach.hooks.before_run do RSpec::Mocks::setup self include FactoryGirl::Syntax::Methods + + stub_gitolite! end diff --git a/features/teams/team.feature b/features/teams/team.feature deleted file mode 100644 index ac34ed76..00000000 --- a/features/teams/team.feature +++ /dev/null @@ -1,65 +0,0 @@ -Feature: UserTeams - Background: - Given I sign in as a user - And I own project "Shop" - And project "Shop" has push event - - Scenario: I should see teams info block - When I have teams with my membership - And I visit dashboard page - Then I should see dashboard page with teams information block - - Scenario: I should can create new team - When I have teams with my membership - And I visit dashboard page - 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 - When I visit team page - Then I should see projects list - - Scenario: I should see team issues list - Given I have team with projects and members - And project from team has issues assigned to me - When I visit team issues page - Then I should see issues from this team assigned to me - - Scenario: I should see teams members issues list - Given I have team with projects and members - Given project from team has issues assigned to teams members - When I visit team issues page - Then I should see issues from this team assigned to teams members - - Scenario: I should see team merge requests list - Given I have team with projects and members - Given project from team has merge requests assigned to me - When I visit team merge requests page - Then I should see merge requests from this team assigned to me - - Scenario: I should see teams members merge requests list - Given I have team with projects and members - Given project from team has merge requests assigned to team members - When I visit team merge requests page - Then I should see merge requests from this team assigned to me - - @javascript - Scenario: I should add user to projects in Team - Given I have team with projects and members - Given I have new user "John" - When I visit team people page - When I click link "New Team Member" - And I select user "John" from list with role "Reporter" - Then I should see user "John" in team list - - Scenario: I should assign my team to my own project - Given I have team with projects and members - And I have my own project without teams - And I visit my team page - When I click on link "Assign Project" - Then I should see form with my own project in available projects list - When I submit form with selected project and max access - Then I should see my own project in team projects list diff --git a/lib/api.rb b/lib/api.rb index d241f9b7..7a184544 100644 --- a/lib/api.rb +++ b/lib/api.rb @@ -2,37 +2,22 @@ Dir["#{Rails.root}/lib/api/*.rb"].each {|file| require file} module Gitlab class API < Grape::API - version 'v3', using: :path + VERSION = 'v2' + version VERSION, using: :path rescue_from ActiveRecord::RecordNotFound do rack_response({'message' => '404 Not found'}.to_json, 404) end - rescue_from :all do |exception| - # lifted from https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb#L60 - # why is this not wrapped in something reusable? - trace = exception.backtrace - - message = "\n#{exception.class} (#{exception.message}):\n" - message << exception.annoted_source_code.to_s if exception.respond_to?(:annoted_source_code) - message << " " << trace.join("\n ") - - API.logger.add Logger::FATAL, message - rack_response({'message' => '500 Internal Server Error'}, 500) - end - format :json + error_format :json helpers APIHelpers - - mount Groups + mount Users mount Projects mount Issues mount Milestones mount Session mount MergeRequests - mount Notes - mount Internal - mount SystemHooks end end diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 3fe4abc3..9e605a60 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -1,60 +1,37 @@ module Gitlab module Entities class User < Grape::Entity - expose :id, :username, :email, :name, :bio, :skype, :linkedin, :twitter, - :dark_scheme, :theme_id, :state, :created_at, :extern_uid, :provider - end - - class UserSafe < Grape::Entity - expose :name + expose :id, :email, :name, :bio, :skype, :linkedin, :twitter, + :dark_scheme, :theme_id, :blocked, :created_at end class UserBasic < Grape::Entity - expose :id, :username, :email, :name, :state, :created_at + expose :id, :email, :name, :blocked, :created_at end - class UserLogin < User + class UserLogin < UserBasic expose :private_token - expose :is_admin?, as: :is_admin - expose :can_create_group?, as: :can_create_group - expose :can_create_project?, as: :can_create_project - expose :can_create_team?, as: :can_create_team end class Hook < Grape::Entity - expose :id, :url, :created_at + expose :id, :url end class Project < Grape::Entity - expose :id, :name, :description, :default_branch + expose :id, :code, :name, :description, :path, :default_branch expose :owner, using: Entities::UserBasic - expose :public - expose :path, :path_with_namespace + expose :private_flag, as: :private expose :issues_enabled, :merge_requests_enabled, :wall_enabled, :wiki_enabled, :created_at - expose :namespace end class ProjectMember < UserBasic - expose :project_access, as: :access_level do |user, options| + expose :project_access, :as => :access_level do |user, options| options[:project].users_projects.find_by_user_id(user.id).project_access end end - class Group < Grape::Entity - expose :id, :name, :path, :owner_id - end - - class GroupDetail < Group - expose :projects, using: Entities::Project - end - class RepoObject < Grape::Entity expose :name, :commit - expose :protected do |repo, options| - if options[:project] - options[:project].protected_branch? repo.name - end - end end class RepoCommit < Grape::Entity @@ -70,7 +47,7 @@ module Gitlab class Milestone < Grape::Entity expose :id expose (:project_id) {|milestone| milestone.project.id} - expose :title, :description, :due_date, :state, :updated_at, :created_at + expose :title, :description, :due_date, :closed, :updated_at, :created_at end class Issue < Grape::Entity @@ -80,29 +57,21 @@ module Gitlab expose :label_list, as: :labels expose :milestone, using: Entities::Milestone expose :assignee, :author, using: Entities::UserBasic - expose :state, :updated_at, :created_at + expose :closed, :updated_at, :created_at end class SSHKey < Grape::Entity - expose :id, :title, :key, :created_at + expose :id, :title, :key end class MergeRequest < Grape::Entity - expose :id, :target_branch, :source_branch, :project_id, :title, :state + expose :id, :target_branch, :source_branch, :project_id, :title, :closed, :merged expose :author, :assignee, using: Entities::UserBasic end class Note < Grape::Entity - expose :id - expose :note, as: :body - expose :attachment_identifier, as: :attachment expose :author, using: Entities::UserBasic - expose :created_at - end - - class MRNote < Grape::Entity expose :note - expose :author, using: Entities::UserBasic end end end diff --git a/lib/api/groups.rb b/lib/api/groups.rb deleted file mode 100644 index 52fa8eff..00000000 --- a/lib/api/groups.rb +++ /dev/null @@ -1,76 +0,0 @@ -module Gitlab - # groups API - class Groups < Grape::API - before { authenticate! } - - resource :groups do - # Get a groups list - # - # Example Request: - # GET /groups - get do - if current_user.admin - @groups = paginate Group - else - @groups = paginate current_user.groups - end - present @groups, with: Entities::Group - end - - # Create group. Available only for admin - # - # Parameters: - # name (required) - The name of the group - # path (required) - The path of the group - # Example Request: - # POST /groups - post do - authenticated_as_admin! - required_attributes! [:name, :path] - - attrs = attributes_for_keys [:name, :path] - @group = Group.new(attrs) - @group.owner = current_user - - if @group.save - present @group, with: Entities::Group - else - not_found! - end - end - - # Get a single group, with containing projects - # - # Parameters: - # id (required) - The ID of a group - # Example Request: - # GET /groups/:id - get ":id" do - @group = Group.find(params[:id]) - if current_user.admin or current_user.groups.include? @group - present @group, with: Entities::GroupDetail - else - not_found! - end - end - - # Transfer a project to the Group namespace - # - # Parameters: - # id - group id - # project_id - project id - # Example Request: - # POST /groups/:id/projects/:project_id - post ":id/projects/:project_id" do - authenticated_as_admin! - @group = Group.find(params[:id]) - project = Project.find(params[:project_id]) - if project.transfer(@group) - present @group - else - not_found! - end - end - end - end -end diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index f12fb5fd..a339ec4a 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -5,18 +5,13 @@ module Gitlab end def user_project - @project ||= find_project - @project || not_found! - end - - def find_project - project = Project.find_by_id(params[:id]) || Project.find_with_namespace(params[:id]) - - if project && can?(current_user, :read_project, project) - project + if @project ||= current_user.projects.find_by_id(params[:id]) || + current_user.projects.find_by_code(params[:id]) else - nil + not_found! end + + @project end def paginate(object) @@ -37,21 +32,6 @@ module Gitlab end end - def can?(object, action, subject) - abilities.allowed?(object, action, subject) - end - - # Checks the occurrences of required attributes, each attribute must be present in the params hash - # or a Bad Request error is invoked. - # - # Parameters: - # keys (required) - A hash consisting of keys that must be present - def required_attributes!(keys) - keys.each do |key| - bad_request!(key) unless params[key].present? - end - end - def attributes_for_keys(keys) attrs = {} keys.each do |key| @@ -66,12 +46,6 @@ module Gitlab render_api_error!('403 Forbidden', 403) end - def bad_request!(attribute) - message = ["400 (Bad request)"] - message << "\"" + attribute.to_s + "\" not given" - render_api_error!(message.join(' '), 400) - end - def not_found!(resource = nil) message = ["404"] message << resource if resource diff --git a/lib/api/internal.rb b/lib/api/internal.rb deleted file mode 100644 index 81451638..00000000 --- a/lib/api/internal.rb +++ /dev/null @@ -1,68 +0,0 @@ -module Gitlab - # Internal access API - class Internal < Grape::API - namespace 'internal' do - # - # Check if ssh key has access to project code - # - # Params: - # key_id - SSH Key id - # project - project path with namespace - # action - git action (git-upload-pack or git-receive-pack) - # ref - branch name - # - get "/allowed" do - # Check for *.wiki repositories. - # Strip out the .wiki from the pathname before finding the - # project. This applies the correct project permissions to - # the wiki repository as well. - project_path = params[:project] - project_path.gsub!(/\.wiki/,'') if project_path =~ /\.wiki/ - - key = Key.find(params[:key_id]) - project = Project.find_with_namespace(project_path) - git_cmd = params[:action] - - - if key.is_deploy_key - project == key.project && git_cmd == 'git-upload-pack' - else - user = key.user - - return false if user.blocked? - - action = case git_cmd - when 'git-upload-pack' - then :download_code - when 'git-receive-pack' - then - if project.protected_branch?(params[:ref]) - :push_code_to_protected_branches - else - :push_code - end - end - - user.can?(action, project) - end - end - - # - # Discover user by ssh key - # - get "/discover" do - key = Key.find(params[:key_id]) - present key.user, with: Entities::UserSafe - end - - get "/check" do - { - api_version: Gitlab::API.version, - gitlab_version: Gitlab::VERSION, - gitlab_rev: Gitlab::REVISION, - } - end - end - end -end - diff --git a/lib/api/issues.rb b/lib/api/issues.rb index 500a8551..4ee2d11f 100644 --- a/lib/api/issues.rb +++ b/lib/api/issues.rb @@ -17,7 +17,7 @@ module Gitlab # Get a list of project issues # # Parameters: - # id (required) - The ID of a project + # id (required) - The ID or code name of a project # Example Request: # GET /projects/:id/issues get ":id/issues" do @@ -27,7 +27,7 @@ module Gitlab # Get a single project issue # # Parameters: - # id (required) - The ID of a project + # id (required) - The ID or code name of a project # issue_id (required) - The ID of a project issue # Example Request: # GET /projects/:id/issues/:issue_id @@ -39,7 +39,7 @@ module Gitlab # Create a new project issue # # Parameters: - # id (required) - The ID of a project + # id (required) - The ID or code name of a project # title (required) - The title of an issue # description (optional) - The description of an issue # assignee_id (optional) - The ID of a user to assign issue @@ -48,7 +48,6 @@ module Gitlab # Example Request: # POST /projects/:id/issues post ":id/issues" do - required_attributes! [:title] attrs = attributes_for_keys [:title, :description, :assignee_id, :milestone_id] attrs[:label_list] = params[:labels] if params[:labels].present? @issue = user_project.issues.new attrs @@ -63,23 +62,22 @@ module Gitlab # Update an existing issue # # Parameters: - # id (required) - The ID of a project + # id (required) - The ID or code name of a project # issue_id (required) - The ID of a project issue # title (optional) - The title of an issue # description (optional) - The description of an issue # assignee_id (optional) - The ID of a user to assign issue # milestone_id (optional) - The ID of a milestone to assign issue # labels (optional) - The labels of an issue - # state (optional) - The state of an issue (close|reopen) + # closed (optional) - The state of an issue (0 = false, 1 = true) # Example Request: # PUT /projects/:id/issues/:issue_id put ":id/issues/:issue_id" do @issue = user_project.issues.find(params[:issue_id]) authorize! :modify_issue, @issue - attrs = attributes_for_keys [:title, :description, :assignee_id, :milestone_id, :state_event] + attrs = attributes_for_keys [:title, :description, :assignee_id, :milestone_id, :closed] attrs[:label_list] = params[:labels] if params[:labels].present? - IssueObserver.current_user = current_user if @issue.update_attributes attrs present @issue, with: Entities::Issue else @@ -90,7 +88,7 @@ module Gitlab # Delete a project issue (deprecated) # # Parameters: - # id (required) - The ID of a project + # id (required) - The ID or code name of a project # issue_id (required) - The ID of a project issue # Example Request: # DELETE /projects/:id/issues/:issue_id diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb index d5595d5f..d8f2c512 100644 --- a/lib/api/merge_requests.rb +++ b/lib/api/merge_requests.rb @@ -4,45 +4,35 @@ module Gitlab before { authenticate! } resource :projects do - helpers do - def handle_merge_request_errors!(errors) - if errors[:project_access].any? - error!(errors[:project_access], 422) - elsif errors[:branch_conflict].any? - error!(errors[:branch_conflict], 422) - end - not_found! - end - end - + # List merge requests - # + # # Parameters: - # id (required) - The ID of a project + # id (required) - The ID or code name of a project # # Example: # GET /projects/:id/merge_requests # get ":id/merge_requests" do authorize! :read_merge_request, user_project - + present paginate(user_project.merge_requests), with: Entities::MergeRequest end - + # Show MR - # + # # Parameters: - # id (required) - The ID of a project + # id (required) - The ID or code name of a project # merge_request_id (required) - The ID of MR - # + # # Example: # GET /projects/:id/merge_request/:merge_request_id # get ":id/merge_request/:merge_request_id" do merge_request = user_project.merge_requests.find(params[:merge_request_id]) - + authorize! :read_merge_request, merge_request - + present merge_request, with: Entities::MergeRequest end @@ -50,79 +40,74 @@ module Gitlab # # Parameters: # - # id (required) - The ID of a project + # id (required) - The ID or code name of a project # source_branch (required) - The source branch # target_branch (required) - The target branch # assignee_id - Assignee user ID # title (required) - Title of MR - # + # # Example: # POST /projects/:id/merge_requests # post ":id/merge_requests" do authorize! :write_merge_request, user_project - required_attributes! [:source_branch, :target_branch, :title] - + attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title] merge_request = user_project.merge_requests.new(attrs) merge_request.author = current_user - + if merge_request.save merge_request.reload_code present merge_request, with: Entities::MergeRequest else - handle_merge_request_errors! merge_request.errors + not_found! end end # Update MR # # Parameters: - # id (required) - The ID of a project + # id (required) - The ID or code name of a project # merge_request_id (required) - ID of MR # source_branch - The source branch # target_branch - The target branch # assignee_id - Assignee user ID # title - Title of MR - # state_event - Status of MR. (close|reopen|merge) + # closed - Status of MR. true - closed # Example: # PUT /projects/:id/merge_request/:merge_request_id # put ":id/merge_request/:merge_request_id" do - attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title, :state_event] + attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title, :closed] merge_request = user_project.merge_requests.find(params[:merge_request_id]) - + authorize! :modify_merge_request, merge_request - - MergeRequestObserver.current_user = current_user - + if merge_request.update_attributes attrs merge_request.reload_code merge_request.mark_as_unchecked present merge_request, with: Entities::MergeRequest else - handle_merge_request_errors! merge_request.errors + not_found! end end # Post comment to merge request # # Parameters: - # id (required) - The ID of a project + # id (required) - The ID or code name of a project # merge_request_id (required) - ID of MR # note (required) - Text of comment - # Examples: + # Examples: # POST /projects/:id/merge_request/:merge_request_id/comments # post ":id/merge_request/:merge_request_id/comments" do - required_attributes! [:note] - merge_request = user_project.merge_requests.find(params[:merge_request_id]) note = merge_request.notes.new(note: params[:note], project_id: user_project.id) note.author = current_user if note.save - present note, with: Entities::MRNote + present note, with: Entities::Note else not_found! end diff --git a/lib/api/milestones.rb b/lib/api/milestones.rb index 1adeefec..f55dfd04 100644 --- a/lib/api/milestones.rb +++ b/lib/api/milestones.rb @@ -7,7 +7,7 @@ module Gitlab # Get a list of project milestones # # Parameters: - # id (required) - The ID of a project + # id (required) - The ID or code name of a project # Example Request: # GET /projects/:id/milestones get ":id/milestones" do @@ -19,7 +19,7 @@ module Gitlab # Get a single project milestone # # Parameters: - # id (required) - The ID of a project + # id (required) - The ID or code name of a project # milestone_id (required) - The ID of a project milestone # Example Request: # GET /projects/:id/milestones/:milestone_id @@ -33,7 +33,7 @@ module Gitlab # Create a new project milestone # # Parameters: - # id (required) - The ID of the project + # id (required) - The ID or code name of the project # title (required) - The title of the milestone # description (optional) - The description of the milestone # due_date (optional) - The due date of the milestone @@ -41,7 +41,6 @@ module Gitlab # POST /projects/:id/milestones post ":id/milestones" do authorize! :admin_milestone, user_project - required_attributes! [:title] attrs = attributes_for_keys [:title, :description, :due_date] @milestone = user_project.milestones.new attrs @@ -55,19 +54,19 @@ module Gitlab # Update an existing project milestone # # Parameters: - # id (required) - The ID of a project + # id (required) - The ID or code name of a project # milestone_id (required) - The ID of a project milestone # title (optional) - The title of a milestone # description (optional) - The description of a milestone # due_date (optional) - The due date of a milestone - # state (optional) - The status of the milestone (close|activate) + # closed (optional) - The status of the milestone # Example Request: # PUT /projects/:id/milestones/:milestone_id put ":id/milestones/:milestone_id" do authorize! :admin_milestone, user_project @milestone = user_project.milestones.find(params[:milestone_id]) - attrs = attributes_for_keys [:title, :description, :due_date, :state_event] + attrs = attributes_for_keys [:title, :description, :due_date, :closed] if @milestone.update_attributes attrs present @milestone, with: Entities::Milestone else diff --git a/lib/api/notes.rb b/lib/api/notes.rb deleted file mode 100644 index 450faae5..00000000 --- a/lib/api/notes.rb +++ /dev/null @@ -1,116 +0,0 @@ -module Gitlab - # Notes API - class Notes < Grape::API - before { authenticate! } - - NOTEABLE_TYPES = [Issue, MergeRequest, Snippet] - - resource :projects do - # Get a list of project wall notes - # - # Parameters: - # id (required) - The ID of a project - # Example Request: - # GET /projects/:id/notes - get ":id/notes" do - @notes = user_project.notes.common - - # Get recent notes if recent = true - @notes = @notes.order('id DESC') if params[:recent] - - present paginate(@notes), with: Entities::Note - end - - # Get a single project wall note - # - # Parameters: - # id (required) - The ID of a project - # note_id (required) - The ID of a note - # Example Request: - # GET /projects/:id/notes/:note_id - get ":id/notes/:note_id" do - @note = user_project.notes.common.find(params[:note_id]) - present @note, with: Entities::Note - end - - # Create a new project wall note - # - # Parameters: - # id (required) - The ID of a project - # body (required) - The content of a note - # Example Request: - # POST /projects/:id/notes - post ":id/notes" do - required_attributes! [:body] - - @note = user_project.notes.new(note: params[:body]) - @note.author = current_user - - if @note.save - present @note, with: Entities::Note - else - # :note is exposed as :body, but :note is set on error - bad_request!(:note) if @note.errors[:note].any? - not_found! - end - end - - NOTEABLE_TYPES.each do |noteable_type| - noteables_str = noteable_type.to_s.underscore.pluralize - noteable_id_str = "#{noteable_type.to_s.underscore}_id" - - # Get a list of project +noteable+ notes - # - # Parameters: - # id (required) - The ID of a project - # noteable_id (required) - The ID of an issue or snippet - # Example Request: - # GET /projects/:id/issues/:noteable_id/notes - # GET /projects/:id/snippets/:noteable_id/notes - get ":id/#{noteables_str}/:#{noteable_id_str}/notes" do - @noteable = user_project.send(:"#{noteables_str}").find(params[:"#{noteable_id_str}"]) - present paginate(@noteable.notes), with: Entities::Note - end - - # Get a single +noteable+ note - # - # Parameters: - # id (required) - The ID of a project - # noteable_id (required) - The ID of an issue or snippet - # note_id (required) - The ID of a note - # Example Request: - # GET /projects/:id/issues/:noteable_id/notes/:note_id - # GET /projects/:id/snippets/:noteable_id/notes/:note_id - get ":id/#{noteables_str}/:#{noteable_id_str}/notes/:note_id" do - @noteable = user_project.send(:"#{noteables_str}").find(params[:"#{noteable_id_str}"]) - @note = @noteable.notes.find(params[:note_id]) - present @note, with: Entities::Note - end - - # Create a new +noteable+ note - # - # Parameters: - # id (required) - The ID of a project - # noteable_id (required) - The ID of an issue or snippet - # body (required) - The content of a note - # Example Request: - # POST /projects/:id/issues/:noteable_id/notes - # POST /projects/:id/snippets/:noteable_id/notes - post ":id/#{noteables_str}/:#{noteable_id_str}/notes" do - required_attributes! [:body] - - @noteable = user_project.send(:"#{noteables_str}").find(params[:"#{noteable_id_str}"]) - @note = @noteable.notes.new(note: params[:body]) - @note.author = current_user - @note.project = user_project - - if @note.save - present @note, with: Entities::Note - else - not_found! - end - end - end - end - end -end diff --git a/lib/api/projects.rb b/lib/api/projects.rb index ce94c34b..ac20bbec 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -4,28 +4,19 @@ module Gitlab before { authenticate! } resource :projects do - helpers do - def handle_project_member_errors(errors) - if errors[:project_access].any? - error!(errors[:project_access], 422) - end - not_found! - end - end - # Get a projects list for authenticated user # # Example Request: # GET /projects get do - @projects = paginate current_user.authorized_projects + @projects = paginate current_user.projects present @projects, with: Entities::Project end # Get a single project # # Parameters: - # id (required) - The ID of a project + # id (required) - The ID or code name of a project # Example Request: # GET /projects/:id get ":id" do @@ -36,60 +27,29 @@ module Gitlab # # Parameters: # name (required) - name for new project + # code (optional) - code for new project, uses project name if not set + # path (optional) - path for new project, uses project name if not set # description (optional) - short project description # default_branch (optional) - 'master' by default # issues_enabled (optional) - enabled by default # wall_enabled (optional) - enabled by default # merge_requests_enabled (optional) - enabled by default # wiki_enabled (optional) - enabled by default - # namespace_id (optional) - defaults to user namespace # Example Request # POST /projects post do - required_attributes! [:name] - attrs = attributes_for_keys [:name, - :description, - :default_branch, - :issues_enabled, - :wall_enabled, - :merge_requests_enabled, - :wiki_enabled, - :namespace_id] - @project = ::Projects::CreateContext.new(current_user, attrs).execute - if @project.saved? - present @project, with: Entities::Project - else - if @project.errors[:limit_reached].present? - error!(@project.errors[:limit_reached], 403) - end - not_found! - end - end - - # Create new project for a specified user. Only available to admin users. - # - # Parameters: - # user_id (required) - The ID of a user - # name (required) - name for new project - # description (optional) - short project description - # default_branch (optional) - 'master' by default - # issues_enabled (optional) - enabled by default - # wall_enabled (optional) - enabled by default - # merge_requests_enabled (optional) - enabled by default - # wiki_enabled (optional) - enabled by default - # Example Request - # POST /projects/user/:user_id - post "user/:user_id" do - authenticated_as_admin! - user = User.find(params[:user_id]) - attrs = attributes_for_keys [:name, + params[:code] ||= params[:name] + params[:path] ||= params[:name] + attrs = attributes_for_keys [:code, + :path, + :name, :description, :default_branch, :issues_enabled, :wall_enabled, :merge_requests_enabled, :wiki_enabled] - @project = ::Projects::CreateContext.new(user, attrs).execute + @project = Project.create_by_user(attrs, current_user) if @project.saved? present @project, with: Entities::Project else @@ -97,27 +57,21 @@ module Gitlab end end - # Get a project team members # # Parameters: - # id (required) - The ID of a project - # query - Query string + # id (required) - The ID or code name of a project # Example Request: # GET /projects/:id/members get ":id/members" do - if params[:query].present? - @members = paginate user_project.users.where("username LIKE ?", "%#{params[:query]}%") - else - @members = paginate user_project.users - end + @members = paginate user_project.users present @members, with: Entities::ProjectMember, project: user_project end # Get a project team members # # Parameters: - # id (required) - The ID of a project + # id (required) - The ID or code name of a project # user_id (required) - The ID of a user # Example Request: # GET /projects/:id/members/:user_id @@ -129,76 +83,63 @@ module Gitlab # Add a new project team member # # Parameters: - # id (required) - The ID of a project + # id (required) - The ID or code name of a project # user_id (required) - The ID of a user # access_level (required) - Project access level # Example Request: # POST /projects/:id/members post ":id/members" do authorize! :admin_project, user_project - required_attributes! [:user_id, :access_level] + users_project = user_project.users_projects.new( + user_id: params[:user_id], + project_access: params[:access_level] + ) - # either the user is already a team member or a new one - team_member = user_project.team_member_by_id(params[:user_id]) - if team_member.nil? - team_member = user_project.users_projects.new( - user_id: params[:user_id], - project_access: params[:access_level] - ) - end - - if team_member.save - @member = team_member.user + if users_project.save + @member = users_project.user present @member, with: Entities::ProjectMember, project: user_project else - handle_project_member_errors team_member.errors + not_found! end end # Update project team member # # Parameters: - # id (required) - The ID of a project + # id (required) - The ID or code name of a project # user_id (required) - The ID of a team member # access_level (required) - Project access level # Example Request: # PUT /projects/:id/members/:user_id put ":id/members/:user_id" do authorize! :admin_project, user_project - required_attributes! [:access_level] + users_project = user_project.users_projects.find_by_user_id params[:user_id] - team_member = user_project.users_projects.find_by_user_id(params[:user_id]) - not_found!("User can not be found") if team_member.nil? - - if team_member.update_attributes(project_access: params[:access_level]) - @member = team_member.user + if users_project.update_attributes(project_access: params[:access_level]) + @member = users_project.user present @member, with: Entities::ProjectMember, project: user_project else - handle_project_member_errors team_member.errors + not_found! end end # Remove a team member from project # # Parameters: - # id (required) - The ID of a project + # id (required) - The ID or code name of a project # user_id (required) - The ID of a team member # Example Request: # DELETE /projects/:id/members/:user_id delete ":id/members/:user_id" do authorize! :admin_project, user_project - team_member = user_project.users_projects.find_by_user_id(params[:user_id]) - unless team_member.nil? - team_member.destroy - else - {message: "Access revoked", id: params[:user_id].to_i} - end + users_project = user_project.users_projects.find_by_user_id params[:user_id] + users_project.destroy end # Get project hooks # # Parameters: - # id (required) - The ID of a project + # id (required) - The ID or code name of a project # Example Request: # GET /projects/:id/hooks get ":id/hooks" do @@ -210,12 +151,11 @@ module Gitlab # Get a project hook # # Parameters: - # id (required) - The ID of a project + # id (required) - The ID or code name of a project # hook_id (required) - The ID of a project hook # Example Request: # GET /projects/:id/hooks/:hook_id get ":id/hooks/:hook_id" do - authorize! :admin_project, user_project @hook = user_project.hooks.find(params[:hook_id]) present @hook, with: Entities::Hook end @@ -224,29 +164,24 @@ module Gitlab # Add hook to project # # Parameters: - # id (required) - The ID of a project + # id (required) - The ID or code name of a project # url (required) - The hook URL # Example Request: # POST /projects/:id/hooks post ":id/hooks" do authorize! :admin_project, user_project - required_attributes! [:url] - @hook = user_project.hooks.new({"url" => params[:url]}) if @hook.save present @hook, with: Entities::Hook else - if @hook.errors[:url].present? - error!("Invalid url given", 422) - end - not_found! + error!({'message' => '404 Not found'}, 404) end end # Update an existing project hook # # Parameters: - # id (required) - The ID of a project + # id (required) - The ID or code name of a project # hook_id (required) - The ID of a project hook # url (required) - The hook URL # Example Request: @@ -254,103 +189,55 @@ module Gitlab put ":id/hooks/:hook_id" do @hook = user_project.hooks.find(params[:hook_id]) authorize! :admin_project, user_project - required_attributes! [:url] attrs = attributes_for_keys [:url] + if @hook.update_attributes attrs present @hook, with: Entities::Hook else - if @hook.errors[:url].present? - error!("Invalid url given", 422) - end not_found! end end - # Deletes project hook. This is an idempotent function. + # Delete project hook # # Parameters: - # id (required) - The ID of a project + # id (required) - The ID or code name of a project # hook_id (required) - The ID of hook to delete # Example Request: - # DELETE /projects/:id/hooks/:hook_id + # DELETE /projects/:id/hooks delete ":id/hooks" do authorize! :admin_project, user_project - required_attributes! [:hook_id] - - begin - @hook = ProjectHook.find(params[:hook_id]) - @hook.destroy - rescue - # ProjectHook can raise Error if hook_id not found - end + @hook = user_project.hooks.find(params[:hook_id]) + @hook.destroy end # Get a project repository branches # # Parameters: - # id (required) - The ID of a project + # id (required) - The ID or code name of a project # Example Request: # GET /projects/:id/repository/branches get ":id/repository/branches" do - present user_project.repo.heads.sort_by(&:name), with: Entities::RepoObject, project: user_project + present user_project.repo.heads.sort_by(&:name), with: Entities::RepoObject end # Get a single branch # # Parameters: - # id (required) - The ID of a project + # id (required) - The ID or code name of a project # branch (required) - The name of the branch # Example Request: # GET /projects/:id/repository/branches/:branch get ":id/repository/branches/:branch" do @branch = user_project.repo.heads.find { |item| item.name == params[:branch] } - not_found!("Branch does not exist") if @branch.nil? - present @branch, with: Entities::RepoObject, project: user_project - end - - # Protect a single branch - # - # Parameters: - # id (required) - The ID of a project - # branch (required) - The name of the branch - # Example Request: - # PUT /projects/:id/repository/branches/:branch/protect - put ":id/repository/branches/:branch/protect" do - @branch = user_project.repo.heads.find { |item| item.name == params[:branch] } - not_found! unless @branch - protected = user_project.protected_branches.find_by_name(@branch.name) - - unless protected - user_project.protected_branches.create(name: @branch.name) - end - - present @branch, with: Entities::RepoObject, project: user_project - end - - # Unprotect a single branch - # - # Parameters: - # id (required) - The ID of a project - # branch (required) - The name of the branch - # Example Request: - # PUT /projects/:id/repository/branches/:branch/unprotect - put ":id/repository/branches/:branch/unprotect" do - @branch = user_project.repo.heads.find { |item| item.name == params[:branch] } - not_found! unless @branch - protected = user_project.protected_branches.find_by_name(@branch.name) - - if protected - protected.destroy - end - - present @branch, with: Entities::RepoObject, project: user_project + present @branch, with: Entities::RepoObject end # Get a project repository tags # # Parameters: - # id (required) - The ID of a project + # id (required) - The ID or code name of a project # Example Request: # GET /projects/:id/repository/tags get ":id/repository/tags" do @@ -360,25 +247,25 @@ module Gitlab # Get a project repository commits # # Parameters: - # id (required) - The ID of a project - # ref_name (optional) - The name of a repository branch or tag, if not given the default branch is used + # id (required) - The ID or code name of a project + # ref_name (optional) - The name of a repository branch or tag # Example Request: # GET /projects/:id/repository/commits get ":id/repository/commits" do authorize! :download_code, user_project page = params[:page] || 0 - per_page = (params[:per_page] || 20).to_i + per_page = params[:per_page] || 20 ref = params[:ref_name] || user_project.try(:default_branch) || 'master' - commits = user_project.repository.commits(ref, nil, per_page, page * per_page) - present commits, with: Entities::RepoCommit + commits = user_project.commits(ref, nil, per_page, page * per_page) + present CommitDecorator.decorate(commits), with: Entities::RepoCommit end # Get a project snippets # # Parameters: - # id (required) - The ID of a project + # id (required) - The ID or code name of a project # Example Request: # GET /projects/:id/snippets get ":id/snippets" do @@ -388,7 +275,7 @@ module Gitlab # Get a project snippet # # Parameters: - # id (required) - The ID of a project + # id (required) - The ID or code name of a project # snippet_id (required) - The ID of a project snippet # Example Request: # GET /projects/:id/snippets/:snippet_id @@ -400,7 +287,7 @@ module Gitlab # Create a new project snippet # # Parameters: - # id (required) - The ID of a project + # id (required) - The ID or code name of a project # title (required) - The title of a snippet # file_name (required) - The name of a snippet file # lifetime (optional) - The expiration date of a snippet @@ -409,7 +296,6 @@ module Gitlab # POST /projects/:id/snippets post ":id/snippets" do authorize! :write_snippet, user_project - required_attributes! [:title, :file_name, :code] attrs = attributes_for_keys [:title, :file_name] attrs[:expires_at] = params[:lifetime] if params[:lifetime].present? @@ -427,7 +313,7 @@ module Gitlab # Update an existing project snippet # # Parameters: - # id (required) - The ID of a project + # id (required) - The ID or code name of a project # snippet_id (required) - The ID of a project snippet # title (optional) - The title of a snippet # file_name (optional) - The name of a snippet file @@ -453,23 +339,21 @@ module Gitlab # Delete a project snippet # # Parameters: - # id (required) - The ID of a project + # id (required) - The ID or code name of a project # snippet_id (required) - The ID of a project snippet # Example Request: # DELETE /projects/:id/snippets/:snippet_id delete ":id/snippets/:snippet_id" do - begin - @snippet = user_project.snippets.find(params[:snippet_id]) - authorize! :modify_snippet, user_project - @snippet.destroy - rescue - end + @snippet = user_project.snippets.find(params[:snippet_id]) + authorize! :modify_snippet, @snippet + + @snippet.destroy end # Get a raw project snippet # # Parameters: - # id (required) - The ID of a project + # id (required) - The ID or code name of a project # snippet_id (required) - The ID of a project snippet # Example Request: # GET /projects/:id/snippets/:snippet_id/raw @@ -482,70 +366,26 @@ module Gitlab # Get a raw file contents # # Parameters: - # id (required) - The ID of a project + # id (required) - The ID or code name of a project # sha (required) - The commit or branch name # filepath (required) - The path to the file to display # Example Request: # GET /projects/:id/repository/commits/:sha/blob get ":id/repository/commits/:sha/blob" do authorize! :download_code, user_project - required_attributes! [:filepath] ref = params[:sha] - commit = user_project.repository.commit ref + commit = user_project.commit ref not_found! "Commit" unless commit - tree = Tree.new commit.tree, ref, params[:filepath] + tree = Tree.new commit.tree, user_project, ref, params[:filepath] not_found! "File" unless tree.try(:tree) content_type tree.mime_type present tree.data end - # Get a specific project's keys - # - # Example Request: - # GET /projects/:id/keys - get ":id/keys" do - present user_project.deploy_keys, with: Entities::SSHKey - end - - # Get single key owned by currently authenticated user - # - # Example Request: - # GET /projects/:id/keys/:id - get ":id/keys/:key_id" do - key = user_project.deploy_keys.find params[:key_id] - present key, with: Entities::SSHKey - end - - # Add new ssh key to currently authenticated user - # - # Parameters: - # key (required) - New SSH Key - # title (required) - New SSH Key's title - # Example Request: - # POST /projects/:id/keys - post ":id/keys" do - attrs = attributes_for_keys [:title, :key] - key = user_project.deploy_keys.new attrs - if key.save - present key, with: Entities::SSHKey - else - not_found! - end - end - - # Delete existed ssh key of currently authenticated user - # - # Example Request: - # DELETE /projects/:id/keys/:id - delete ":id/keys/:key_id" do - key = user_project.deploy_keys.find params[:key_id] - key.delete - end - end end end diff --git a/lib/api/system_hooks.rb b/lib/api/system_hooks.rb deleted file mode 100644 index da0b005d..00000000 --- a/lib/api/system_hooks.rb +++ /dev/null @@ -1,70 +0,0 @@ -module Gitlab - # Hooks API - class SystemHooks < Grape::API - before { - authenticate! - authenticated_as_admin! - } - - resource :hooks do - # Get the list of system hooks - # - # Example Request: - # GET /hooks - get do - @hooks = SystemHook.all - present @hooks, with: Entities::Hook - end - - # Create new system hook - # - # Parameters: - # url (required) - url for system hook - # Example Request - # POST /hooks - post do - attrs = attributes_for_keys [:url] - required_attributes! [:url] - @hook = SystemHook.new attrs - if @hook.save - present @hook, with: Entities::Hook - else - not_found! - end - end - - # Test a hook - # - # Example Request - # GET /hooks/:id - get ":id" do - @hook = SystemHook.find(params[:id]) - data = { - event_name: "project_create", - name: "Ruby", - path: "ruby", - project_id: 1, - owner_name: "Someone", - owner_email: "example@gitlabhq.com" - } - @hook.execute(data) - data - end - - # Delete a hook. This is an idempotent function. - # - # Parameters: - # id (required) - ID of the hook - # Example Request: - # DELETE /hooks/:id - delete ":id" do - begin - @hook = SystemHook.find(params[:id]) - @hook.destroy - rescue - # SystemHook raises an Error if no hook with id found - end - end - end - end -end \ No newline at end of file diff --git a/lib/api/users.rb b/lib/api/users.rb index 125a8624..57e0aa10 100644 --- a/lib/api/users.rb +++ b/lib/api/users.rb @@ -9,9 +9,7 @@ module Gitlab # Example Request: # GET /users get do - @users = User.scoped - @users = @users.active if params[:active].present? - @users = @users.search(params[:search]) if params[:search].present? + @users = paginate User present @users, with: Entities::User end @@ -36,16 +34,11 @@ module Gitlab # linkedin - Linkedin # twitter - Twitter account # projects_limit - Number of projects user can create - # extern_uid - External authentication provider UID - # provider - External provider - # bio - Bio # Example Request: # POST /users post do authenticated_as_admin! - required_attributes! [:email, :password, :name, :username] - - attrs = attributes_for_keys [:email, :name, :password, :skype, :linkedin, :twitter, :projects_limit, :username, :extern_uid, :provider, :bio] + attrs = attributes_for_keys [:email, :name, :password, :skype, :linkedin, :twitter, :projects_limit] user = User.new attrs, as: :admin if user.save present user, with: Entities::User @@ -53,70 +46,6 @@ module Gitlab not_found! end end - - # Update user. Available only for admin - # - # Parameters: - # email - Email - # name - Name - # password - Password - # skype - Skype ID - # linkedin - Linkedin - # twitter - Twitter account - # projects_limit - Limit projects each user can create - # extern_uid - External authentication provider UID - # provider - External provider - # bio - Bio - # Example Request: - # PUT /users/:id - put ":id" do - authenticated_as_admin! - - attrs = attributes_for_keys [:email, :name, :password, :skype, :linkedin, :twitter, :projects_limit, :username, :extern_uid, :provider, :bio] - user = User.find(params[:id]) - not_found!("User not found") unless user - - if user.update_attributes(attrs) - present user, with: Entities::User - else - not_found! - end - end - - # Add ssh key to a specified user. Only available to admin users. - # - # Parameters: - # id (required) - The ID of a user - # key (required) - New SSH Key - # title (required) - New SSH Key's title - # Example Request: - # POST /users/:id/keys - post ":id/keys" do - authenticated_as_admin! - user = User.find(params[:id]) - attrs = attributes_for_keys [:title, :key] - key = user.keys.new attrs - if key.save - present key, with: Entities::SSHKey - else - not_found! - end - end - - # Delete user. Available only for admin - # - # Example Request: - # DELETE /users/:id - delete ":id" do - authenticated_as_admin! - user = User.find_by_id(params[:id]) - - if user - user.destroy - else - not_found! - end - end end resource :user do @@ -125,7 +54,7 @@ module Gitlab # Example Request: # GET /user get do - present @current_user, with: Entities::UserLogin + present @current_user, with: Entities::User end # Get currently authenticated user's keys @@ -153,8 +82,6 @@ module Gitlab # Example Request: # POST /user/keys post "keys" do - required_attributes! [:title, :key] - attrs = attributes_for_keys [:title, :key] key = current_user.keys.new attrs if key.save @@ -164,19 +91,18 @@ module Gitlab end end - # Delete existing ssh key of currently authenticated user + # Delete existed ssh key of currently authenticated user # # Parameters: # id (required) - SSH Key ID # Example Request: # DELETE /user/keys/:id delete "keys/:id" do - begin - key = current_user.keys.find params[:id] - key.delete - rescue - end + key = current_user.keys.find params[:id] + key.delete end + + end end end diff --git a/lib/event_filter.rb b/lib/event_filter.rb index 9b4b8c38..14ab0193 100644 --- a/lib/event_filter.rb +++ b/lib/event_filter.rb @@ -37,15 +37,15 @@ class EventFilter filter = params.dup actions = [] - actions << Event::PUSHED if filter.include? 'push' - actions << Event::MERGED if filter.include? 'merged' + actions << Event::Pushed if filter.include? 'push' + actions << Event::Merged if filter.include? 'merged' if filter.include? 'team' - actions << Event::JOINED - actions << Event::LEFT + actions << Event::Joined + actions << Event::Left end - actions << Event::COMMENTED if filter.include? 'comments' + actions << Event::Commented if filter.include? 'comments' events = events.where(action: actions) end diff --git a/lib/extracts_path.rb b/lib/extracts_path.rb index 2e3ab1b2..b60dfd03 100644 --- a/lib/extracts_path.rb +++ b/lib/extracts_path.rb @@ -8,7 +8,7 @@ module ExtractsPath included do if respond_to?(:before_filter) - before_filter :assign_ref_vars + before_filter :assign_ref_vars, only: [:show] end end @@ -33,9 +33,6 @@ module ExtractsPath # extract_ref("v2.0.0/README.md") # # => ['v2.0.0', 'README.md'] # - # extract_ref('master/app/models/project.rb') - # # => ['master', 'app/models/project.rb'] - # # extract_ref('issues/1234/app/models/project.rb') # # => ['issues/1234', 'app/models/project.rb'] # @@ -45,12 +42,12 @@ module ExtractsPath # # Returns an Array where the first value is the tree-ish and the second is the # path - def extract_ref(id) + def extract_ref(input) pair = ['', ''] return pair unless @project - if id.match(/^([[:alnum:]]{40})(.+)/) + if input.match(/^([[:alnum:]]{40})(.+)/) # If the ref appears to be a SHA, we're done, just split the string pair = $~.captures else @@ -58,9 +55,10 @@ module ExtractsPath # branches and tags # Append a trailing slash if we only get a ref and no file path + id = input id += '/' unless id.ends_with?('/') - valid_refs = @project.repository.ref_names + valid_refs = @project.ref_names valid_refs.select! { |v| id.start_with?("#{v}/") } if valid_refs.length != 1 @@ -85,8 +83,8 @@ module ExtractsPath # - @id - A string representing the joined ref and path # - @ref - A string representing the ref (e.g., the branch, tag, or commit SHA) # - @path - A string representing the filesystem path - # - @commit - A Commit representing the commit from the given ref - # - @tree - A Tree representing the tree at the given ref/path + # - @commit - A CommitDecorator representing the commit from the given ref + # - @tree - A TreeDecorator representing the tree at the given ref/path # # If the :id parameter appears to be requesting a specific response format, # that will be handled as well. @@ -94,18 +92,23 @@ module ExtractsPath # Automatically renders `not_found!` if a valid tree path could not be # resolved (e.g., when a user inserts an invalid path or ref). def assign_ref_vars - @id = params[:id] + # Handle formats embedded in the id + if params[:id].ends_with?('.atom') + params[:id].gsub!(/\.atom$/, '') + request.format = :atom + end - @ref, @path = extract_ref(@id) + @ref, @path = extract_ref(params[:id]) - # It is used "@project.repository.commits(@ref, @path, 1, 0)", - # because "@project.repository.commit(@ref)" returns wrong commit when @ref is tag name. - @commit = @project.repository.commits(@ref, @path, 1, 0).first + @id = File.join(@ref, @path) - @tree = Tree.new(@commit.tree, @ref, @path) + @commit = CommitDecorator.decorate(@project.commit(@ref)) + + @tree = Tree.new(@commit.tree, @project, @ref, @path) + @tree = TreeDecorator.new(@tree) raise InvalidPathError if @tree.invalid? - rescue RuntimeError, NoMethodError, InvalidPathError + rescue NoMethodError, InvalidPathError not_found! end end diff --git a/lib/gitlab/auth.rb b/lib/gitlab/auth.rb index 0fee33db..5a24c5d0 100644 --- a/lib/gitlab/auth.rb +++ b/lib/gitlab/auth.rb @@ -20,9 +20,8 @@ module Gitlab def create_from_omniauth(auth, ldap = false) provider = auth.provider uid = auth.info.uid || auth.uid - uid = uid.to_s.force_encoding("utf-8") - name = auth.info.name.to_s.force_encoding("utf-8") - email = auth.info.email.to_s.downcase unless auth.info.email.nil? + name = auth.info.name.force_encoding("utf-8") + email = auth.info.email.downcase unless auth.info.email.nil? ldap_prefix = ldap ? '(LDAP) ' : '' raise OmniAuth::Error, "#{ldap_prefix}#{provider} does not provide an email"\ @@ -35,18 +34,15 @@ module Gitlab extern_uid: uid, provider: provider, name: name, - username: email.match(/^[^@]*/)[0], email: email, password: password, password_confirmation: password, - projects_limit: Gitlab.config.gitlab.default_projects_limit, + projects_limit: Gitlab.config.default_projects_limit, }, as: :admin) - @user.save! - if Gitlab.config.omniauth['block_auto_created_users'] && !ldap - @user.block + @user.blocked = true end - + @user.save! @user end diff --git a/lib/gitlab/backend/gitolite.rb b/lib/gitlab/backend/gitolite.rb new file mode 100644 index 00000000..fe5dcef4 --- /dev/null +++ b/lib/gitlab/backend/gitolite.rb @@ -0,0 +1,43 @@ +require_relative 'gitolite_config' + +module Gitlab + class Gitolite + class AccessDenied < StandardError; end + + def config + Gitlab::GitoliteConfig.new + end + + def set_key key_id, key_content, projects + config.apply do |config| + config.write_key(key_id, key_content) + config.update_projects(projects) + end + end + + def remove_key key_id, projects + config.apply do |config| + config.rm_key(key_id) + config.update_projects(projects) + end + end + + def update_repository project + config.update_project!(project.path, project) + end + + def remove_repository project + config.destroy_project!(project) + end + + def url_to_repo path + Gitlab.config.ssh_path + "#{path}.git" + end + + def enable_automerge + config.admin_all_repo! + end + + alias_method :create_repository, :update_repository + end +end diff --git a/lib/gitlab/backend/gitolite_config.rb b/lib/gitlab/backend/gitolite_config.rb new file mode 100644 index 00000000..7ae34de6 --- /dev/null +++ b/lib/gitlab/backend/gitolite_config.rb @@ -0,0 +1,209 @@ +require 'gitolite' +require 'timeout' +require 'fileutils' + +module Gitlab + class GitoliteConfig + class PullError < StandardError; end + class PushError < StandardError; end + + attr_reader :config_tmp_dir, :ga_repo, :conf + + def config_tmp_dir + @config_tmp_dir ||= Rails.root.join('tmp',"gitlabhq-gitolite-#{Time.now.to_i}") + end + + def ga_repo + @ga_repo ||= ::Gitolite::GitoliteAdmin.new( + File.join(config_tmp_dir,'gitolite'), + conf: Gitlab.config.gitolite_config_file + ) + end + + def apply + Timeout::timeout(30) do + File.open(Rails.root.join('tmp', "gitlabhq-gitolite.lock"), "w+") do |f| + begin + # Set exclusive lock + # to prevent race condition + f.flock(File::LOCK_EX) + + # Pull gitolite-admin repo + # in tmp dir before do any changes + pull(config_tmp_dir) + + # Build ga_repo object and @conf + # to access gitolite-admin configuration + @conf = ga_repo.config + + # Do any changes + # in gitolite-admin + # config here + yield(self) + + # Save changes in + # gitolite-admin repo + # before push it + ga_repo.save + + # Push gitolite-admin repo + # to apply all changes + push(config_tmp_dir) + ensure + # Remove tmp dir + # removing the gitolite folder first is important to avoid + # NFS issues. + FileUtils.rm_rf(File.join(config_tmp_dir, 'gitolite')) + + # Remove parent tmp dir + FileUtils.rm_rf(config_tmp_dir) + + # Unlock so other task can access + # gitolite configuration + f.flock(File::LOCK_UN) + end + end + end + rescue PullError => ex + log("Pull error -> " + ex.message) + raise Gitolite::AccessDenied, ex.message + + rescue PushError => ex + log("Push error -> " + " " + ex.message) + raise Gitolite::AccessDenied, ex.message + + rescue Exception => ex + log(ex.class.name + " " + ex.message) + raise Gitolite::AccessDenied.new("gitolite timeout") + end + + def log message + Gitlab::GitLogger.error(message) + end + + def destroy_project(project) + FileUtils.rm_rf(project.path_to_repo) + conf.rm_repo(project.path) + end + + def destroy_project!(project) + apply do |config| + config.destroy_project(project) + end + end + + def write_key(id, key) + File.open(File.join(config_tmp_dir, 'gitolite/keydir',"#{id}.pub"), 'w') do |f| + f.write(key.gsub(/\n/,'')) + end + end + + def rm_key(user) + key_path = File.join(config_tmp_dir, 'gitolite/keydir', "#{user}.pub") + ga_key = ::Gitolite::SSHKey.from_file(key_path) + ga_repo.rm_key(ga_key) + end + + # update or create + def update_project(repo_name, project) + repo = update_project_config(project, conf) + conf.add_repo(repo, true) + end + + def update_project!(repo_name, project) + apply do |config| + config.update_project(repo_name, project) + end + end + + # Updates many projects and uses project.path as the repo path + # An order of magnitude faster than update_project + def update_projects(projects) + projects.each do |project| + repo = update_project_config(project, conf) + conf.add_repo(repo, true) + end + end + + def update_project_config(project, conf) + repo_name = project.path + + repo = if conf.has_repo?(repo_name) + conf.get_repo(repo_name) + else + ::Gitolite::Config::Repo.new(repo_name) + end + + name_readers = project.repository_readers + name_writers = project.repository_writers + name_masters = project.repository_masters + + pr_br = project.protected_branches.map(&:name).join("$ ") + + repo.clean_permissions + + # Deny access to protected branches for writers + unless name_writers.blank? || pr_br.blank? + repo.add_permission("-", pr_br.strip + "$ ", name_writers) + end + + # Add read permissions + repo.add_permission("R", "", name_readers) unless name_readers.blank? + + # Add write permissions + repo.add_permission("RW+", "", name_writers) unless name_writers.blank? + repo.add_permission("RW+", "", name_masters) unless name_masters.blank? + + # Add sharedRepository config + repo.set_git_config("core.sharedRepository", "0660") + + repo + end + + # Enable access to all repos for gitolite admin. + # We use it for accept merge request feature + def admin_all_repo + owner_name = Gitlab.config.gitolite_admin_key + + # @ALL repos premission for gitolite owner + repo_name = "@all" + repo = if conf.has_repo?(repo_name) + conf.get_repo(repo_name) + else + ::Gitolite::Config::Repo.new(repo_name) + end + + repo.add_permission("RW+", "", owner_name) + conf.add_repo(repo, true) + end + + def admin_all_repo! + apply { |config| config.admin_all_repo } + end + + private + + def pull tmp_dir + Dir.mkdir tmp_dir + `git clone #{Gitlab.config.gitolite_admin_uri} #{tmp_dir}/gitolite` + + unless File.exists?(File.join(tmp_dir, 'gitolite', 'conf', 'gitolite.conf')) + raise PullError, "unable to clone gitolite-admin repo" + end + end + + def push tmp_dir + Dir.chdir(File.join(tmp_dir, "gitolite")) + raise "Git add failed." unless system('git add -A') + system('git commit -m "GitLab"') # git commit returns 0 on success, and 1 if there is nothing to commit + raise "Git commit failed." unless [0,1].include? $?.exitstatus + + if system('git push') + Dir.chdir(Rails.root) + else + raise PushError, "unable to push gitolite-admin repo" + end + end + end +end + diff --git a/lib/gitlab/backend/grack_auth.rb b/lib/gitlab/backend/grack_auth.rb index abbee613..dd5a9bec 100644 --- a/lib/gitlab/backend/grack_auth.rb +++ b/lib/gitlab/backend/grack_auth.rb @@ -1,40 +1,26 @@ -require_relative 'shell_env' - module Grack class Auth < Rack::Auth::Basic attr_accessor :user, :project - def call(env) - @env = env - @request = Rack::Request.new(env) - @auth = Request.new(env) + def valid? + # Authentication with username and password + email, password = @auth.credentials + self.user = User.find_by_email(email) + return false unless user.try(:valid_password?, password) + + # Set GL_USER env variable + ENV['GL_USER'] = email + # Pass Gitolite update hook + ENV['GL_BYPASS_UPDATE_HOOK'] = "true" # Need this patch due to the rails mount @env['PATH_INFO'] = @request.path @env['SCRIPT_NAME'] = "" - return render_not_found unless project - return unauthorized unless project.public || @auth.provided? - return bad_request if @auth.provided? && !@auth.basic? - - if valid? - if @auth.provided? - @env['REMOTE_USER'] = @auth.username - end - return @app.call(env) - else - unauthorized - end - end - - def valid? - if @auth.provided? - # Authentication with username and password - login, password = @auth.credentials - self.user = User.find_by_email(login) || User.find_by_username(login) - return false unless user.try(:valid_password?, password) - - Gitlab::ShellEnv.set_env(user) + # Find project by PATH_INFO from env + if m = /^\/([\w\.-]+)\.git/.match(@request.path_info).to_a + self.project = Project.find_by_path(m.last) + return false unless project end # Git upload and receive @@ -48,12 +34,12 @@ module Grack end def validate_get_request - project.public || can?(user, :download_code, project) + true end def validate_post_request if @request.path_info.end_with?('git-upload-pack') - project.public || can?(user, :download_code, project) + can?(user, :push_code, project) elsif @request.path_info.end_with?('git-receive-pack') action = if project.protected_branch?(current_ref) :push_code_to_protected_branches @@ -79,23 +65,7 @@ module Grack end # Need to reset seek point @request.body.rewind - /refs\/heads\/([\w\.-]+)/.match(input).to_a.last - end - - def project - unless instance_variable_defined? :@project - # Find project by PATH_INFO from env - if m = /^\/([\w\.\/-]+)\.git/.match(@request.path_info).to_a - @project = Project.find_with_namespace(m.last) - end - end - return @project - end - - PLAIN_TYPE = {"Content-Type" => "text/plain"} - - def render_not_found - [404, PLAIN_TYPE, ["Not Found"]] + /refs\/heads\/([\w\.-]+)/.match(input).to_a.first end protected diff --git a/lib/gitlab/backend/shell.rb b/lib/gitlab/backend/shell.rb deleted file mode 100644 index bae87977..00000000 --- a/lib/gitlab/backend/shell.rb +++ /dev/null @@ -1,136 +0,0 @@ -module Gitlab - class Shell - class AccessDenied < StandardError; end - - # Init new repository - # - # name - project path with namespace - # - # Ex. - # add_repository("gitlab/gitlab-ci") - # - def add_repository(name) - system("#{gitlab_shell_user_home}/gitlab-shell/bin/gitlab-projects add-project #{name}.git") - end - - # Import repository - # - # name - project path with namespace - # - # Ex. - # import_repository("gitlab/gitlab-ci", "https://github.com/randx/six.git") - # - def import_repository(name, url) - system("#{gitlab_shell_user_home}/gitlab-shell/bin/gitlab-projects import-project #{name}.git #{url}") - end - - # Move repository - # - # path - project path with namespace - # new_path - new project path with namespace - # - # Ex. - # mv_repository("gitlab/gitlab-ci", "randx/gitlab-ci-new.git") - # - def mv_repository(path, new_path) - system("#{gitlab_shell_user_home}/gitlab-shell/bin/gitlab-projects mv-project #{path}.git #{new_path}.git") - end - - # Remove repository from file system - # - # name - project path with namespace - # - # Ex. - # remove_repository("gitlab/gitlab-ci") - # - def remove_repository(name) - system("#{gitlab_shell_user_home}/gitlab-shell/bin/gitlab-projects rm-project #{name}.git") - end - - # Add new key to gitlab-shell - # - # Ex. - # add_key("key-42", "sha-rsa ...") - # - def add_key(key_id, key_content) - system("#{gitlab_shell_user_home}/gitlab-shell/bin/gitlab-keys add-key #{key_id} \"#{key_content}\"") - end - - # Remove ssh key from gitlab shell - # - # Ex. - # remove_key("key-342", "sha-rsa ...") - # - def remove_key(key_id, key_content) - system("#{gitlab_shell_user_home}/gitlab-shell/bin/gitlab-keys rm-key #{key_id} \"#{key_content}\"") - end - - # Add empty directory for storing repositories - # - # Ex. - # add_namespace("gitlab") - # - def add_namespace(name) - FileUtils.mkdir(full_path(name), mode: 0770) unless exists?(name) - end - - # Remove directory from repositories storage - # Every repository inside this directory will be removed too - # - # Ex. - # rm_namespace("gitlab") - # - def rm_namespace(name) - FileUtils.rm_r(full_path(name), force: true) - end - - # Move namespace directory inside repositories storage - # - # Ex. - # mv_namespace("gitlab", "gitlabhq") - # - def mv_namespace(old_name, new_name) - return false if exists?(new_name) || !exists?(old_name) - - FileUtils.mv(full_path(old_name), full_path(new_name)) - end - - # Remove GitLab Satellites for provided path (namespace or repo dir) - # - # Ex. - # rm_satellites("gitlab") - # - # rm_satellites("gitlab/gitlab-ci.git") - # - def rm_satellites(path) - raise ArgumentError.new("Path can't be blank") if path.blank? - - satellites_path = File.join(Gitlab.config.satellites.path, path) - FileUtils.rm_r(satellites_path, force: true) - end - - def url_to_repo path - Gitlab.config.gitlab_shell.ssh_path_prefix + "#{path}.git" - end - - protected - - def gitlab_shell_user_home - File.expand_path("~#{Gitlab.config.gitlab_shell.ssh_user}") - end - - def repos_path - Gitlab.config.gitlab_shell.repos_path - end - - def full_path(dir_name) - raise ArgumentError.new("Directory name can't be blank") if dir_name.blank? - - File.join(repos_path, dir_name) - end - - def exists?(dir_name) - File.exists?(full_path(dir_name)) - end - end -end diff --git a/lib/gitlab/backend/shell_adapter.rb b/lib/gitlab/backend/shell_adapter.rb deleted file mode 100644 index f247f459..00000000 --- a/lib/gitlab/backend/shell_adapter.rb +++ /dev/null @@ -1,12 +0,0 @@ -# == GitLab Shell mixin -# -# Provide a shortcut to Gitlab::Shell instance by gitlab_shell -# -module Gitlab - module ShellAdapter - def gitlab_shell - Gitlab::Shell.new - end - end -end - diff --git a/lib/gitlab/backend/shell_env.rb b/lib/gitlab/backend/shell_env.rb deleted file mode 100644 index 15721875..00000000 --- a/lib/gitlab/backend/shell_env.rb +++ /dev/null @@ -1,17 +0,0 @@ -module Gitlab - # This module provide 2 methods - # to set specific ENV variabled for GitLab Shell - module ShellEnv - extend self - - def set_env(user) - # Set GL_ID env variable - ENV['GL_ID'] = "user-#{user.id}" - end - - def reset_env - # Reset GL_ID env variable - ENV['GL_ID'] = nil - end - end -end diff --git a/lib/gitlab/git/blame.rb b/lib/gitlab/git/blame.rb deleted file mode 100644 index d6e988b6..00000000 --- a/lib/gitlab/git/blame.rb +++ /dev/null @@ -1,22 +0,0 @@ -module Gitlab - module Git - class Blame - - attr_accessor :repository, :sha, :path - - def initialize(repository, sha, path) - @repository, @sha, @path = repository, sha, path - - end - - def each - raw_blame = Grit::Blob.blame(repository.repo, sha, path) - - raw_blame.each do |commit, lines| - commit = Gitlab::Git::Commit.new(commit) - yield(commit, lines) - end - end - end - end -end diff --git a/lib/gitlab/git/commit.rb b/lib/gitlab/git/commit.rb deleted file mode 100644 index 35991a38..00000000 --- a/lib/gitlab/git/commit.rb +++ /dev/null @@ -1,153 +0,0 @@ -# Gitlab::Git::Commit is a wrapper around native Grit::Commit object -# We dont want to use grit objects inside app/ -# It helps us easily migrate to rugged in future -module Gitlab - module Git - class Commit - attr_accessor :raw_commit, :head, :refs - - delegate :message, :authored_date, :committed_date, :parents, :sha, - :date, :committer, :author, :diffs, :tree, :id, :stats, :to_patch, - to: :raw_commit - - class << self - def find_or_first(repo, commit_id = nil, root_ref) - commit = if commit_id - repo.commit(commit_id) - else - repo.commits(root_ref).first - end - - Commit.new(commit) if commit - end - - def fresh_commits(repo, n = 10) - commits = repo.heads.map do |h| - repo.commits(h.name, n).map { |c| Commit.new(c, h) } - end.flatten.uniq { |c| c.id } - - commits.sort! do |x, y| - y.committed_date <=> x.committed_date - end - - commits[0...n] - end - - def commits_with_refs(repo, n = 20) - commits = repo.branches.map { |ref| Commit.new(ref.commit, ref) } - - commits.sort! do |x, y| - y.committed_date <=> x.committed_date - end - - commits[0..n] - end - - def commits_since(repo, date) - commits = repo.heads.map do |h| - repo.log(h.name, nil, since: date).each { |c| Commit.new(c, h) } - end.flatten.uniq { |c| c.id } - - commits.sort! do |x, y| - y.committed_date <=> x.committed_date - end - - commits - end - - def commits(repo, ref, path = nil, limit = nil, offset = nil) - if path - repo.log(ref, path, max_count: limit, skip: offset) - elsif limit && offset - repo.commits(ref, limit, offset) - else - repo.commits(ref) - end.map{ |c| Commit.new(c) } - end - - def commits_between(repo, from, to) - repo.commits_between(from, to).map { |c| Commit.new(c) } - end - end - - def initialize(raw_commit, head = nil) - raise "Nil as raw commit passed" unless raw_commit - - @raw_commit = raw_commit - @head = head - end - - def short_id(length = 10) - id.to_s[0..length] - end - - def safe_message - @safe_message ||= message - end - - def created_at - committed_date - end - - def author_email - author.email - end - - def author_name - author.name - end - - # Was this commit committed by a different person than the original author? - def different_committer? - author_name != committer_name || author_email != committer_email - end - - def committer_name - committer.name - end - - def committer_email - committer.email - end - - def prev_commit - @prev_commit ||= if parents.present? - Commit.new(parents.first) - else - nil - end - end - - def prev_commit_id - prev_commit.try :id - end - - # Shows the diff between the commit's parent and the commit. - # - # Cuts out the header and stats from #to_patch and returns only the diff. - def to_diff - # see Grit::Commit#show - patch = to_patch - - # discard lines before the diff - lines = patch.split("\n") - while !lines.first.start_with?("diff --git") do - lines.shift - end - lines.pop if lines.last =~ /^[\d.]+$/ # Git version - lines.pop if lines.last == "-- " # end of diff - lines.join("\n") - end - - def has_zero_stats? - stats.total.zero? - rescue - true - end - - def no_commit_message - "--no commit message" - end - end - end -end diff --git a/lib/gitlab/git/compare.rb b/lib/gitlab/git/compare.rb deleted file mode 100644 index 1fa43062..00000000 --- a/lib/gitlab/git/compare.rb +++ /dev/null @@ -1,37 +0,0 @@ -module Gitlab - module Git - class Compare - attr_accessor :commits, :commit, :diffs, :same - - def initialize(repository, from, to) - @commits, @diffs = [], [] - @commit = nil - @same = false - - return unless from && to - - first = repository.commit(to.try(:strip)) - last = repository.commit(from.try(:strip)) - - return unless first && last - - if first.id == last.id - @same = true - return - end - - @commit = Commit.new(first) - - @commits = repository.commits_between(last.id, first.id) - @commits = @commits.map { |c| Commit.new(c) } - - @diffs = if @commits.size > 100 - [] - else - repository.repo.diff(last.id, first.id) rescue [] - end - end - end - end -end - diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb deleted file mode 100644 index 30344a3d..00000000 --- a/lib/gitlab/git/repository.rb +++ /dev/null @@ -1,185 +0,0 @@ -# Gitlab::Git::Gitlab::Git::Commit is a wrapper around native Grit::Repository object -# We dont want to use grit objects inside app/ -# It helps us easily migrate to rugged in future -module Gitlab - module Git - class Repository - include Gitlab::Popen - - class NoRepository < StandardError; end - - # Repository directory name with namespace direcotry - # Examples: - # gitlab/gitolite - # diaspora - # - attr_accessor :path_with_namespace - - # Grit repo object - attr_accessor :repo - - # Default branch in the repository - attr_accessor :root_ref - - def initialize(path_with_namespace, root_ref = 'master') - @root_ref = root_ref || "master" - @path_with_namespace = path_with_namespace - - # Init grit repo object - repo - end - - def raw - repo - end - - def path_to_repo - @path_to_repo ||= File.join(repos_path, "#{path_with_namespace}.git") - end - - def repos_path - Gitlab.config.gitlab_shell.repos_path - end - - def repo - @repo ||= Grit::Repo.new(path_to_repo) - rescue Grit::NoSuchPathError - raise NoRepository.new('no repository for such path') - end - - def commit(commit_id = nil) - Gitlab::Git::Commit.find_or_first(repo, commit_id, root_ref) - end - - def fresh_commits(n = 10) - Gitlab::Git::Commit.fresh_commits(repo, n) - end - - def commits_with_refs(n = 20) - Gitlab::Git::Commit.commits_with_refs(repo, n) - end - - def commits_since(date) - Gitlab::Git::Commit.commits_since(repo, date) - end - - def commits(ref, path = nil, limit = nil, offset = nil) - Gitlab::Git::Commit.commits(repo, ref, path, limit, offset) - end - - def last_commit_for(ref, path = nil) - commits(ref, path, 1).first - end - - def commits_between(from, to) - Gitlab::Git::Commit.commits_between(repo, from, to) - end - - # Returns an Array of branch names - # sorted by name ASC - def branch_names - branches.map(&:name) - end - - # Returns an Array of Branches - def branches - repo.branches.sort_by(&:name) - end - - # Returns an Array of tag names - def tag_names - repo.tags.collect(&:name).sort.reverse - end - - # Returns an Array of Tags - def tags - repo.tags.sort_by(&:name).reverse - end - - # Returns an Array of branch and tag names - def ref_names - [branch_names + tag_names].flatten - end - - def heads - @heads ||= repo.heads - end - - def tree(fcommit, path = nil) - fcommit = commit if fcommit == :head - tree = fcommit.tree - path ? (tree / path) : tree - end - - def has_commits? - !!commit - rescue Grit::NoSuchPathError - false - end - - def empty? - !has_commits? - end - - # Discovers the default branch based on the repository's available branches - # - # - If no branches are present, returns nil - # - If one branch is present, returns its name - # - If two or more branches are present, returns the one that has a name - # matching root_ref (default_branch or 'master' if default_branch is nil) - def discover_default_branch - if branch_names.length == 0 - nil - elsif branch_names.length == 1 - branch_names.first - else - branch_names.select { |v| v == root_ref }.first - end - end - - # Archive Project to .tar.gz - # - # Already packed repo archives stored at - # app_root/tmp/repositories/project_name/project_name-commit-id.tag.gz - # - def archive_repo(ref) - ref = ref || self.root_ref - commit = self.commit(ref) - return nil unless commit - - # Build file path - file_name = self.path_with_namespace.gsub("/","_") + "-" + commit.id.to_s + ".tar.gz" - storage_path = Rails.root.join("tmp", "repositories") - file_path = File.join(storage_path, self.path_with_namespace, file_name) - - # Put files into a directory before archiving - prefix = File.basename(self.path_with_namespace) + "/" - - # Create file if not exists - unless File.exists?(file_path) - FileUtils.mkdir_p File.dirname(file_path) - file = self.repo.archive_to_file(ref, prefix, file_path) - end - - file_path - end - - # Return repo size in megabytes - # Cached in redis - def size - Rails.cache.fetch(cache_key(:size)) do - size = popen('du -s', path_to_repo).first.strip.to_i - (size.to_f / 1024).round(2) - end - end - - def expire_cache - Rails.cache.delete(cache_key(:size)) - end - - def cache_key(type) - "#{type}:#{path_with_namespace}" - end - end - end -end diff --git a/lib/gitlab/graph/commit.rb b/lib/gitlab/graph/commit.rb new file mode 100644 index 00000000..af8d7828 --- /dev/null +++ b/lib/gitlab/graph/commit.rb @@ -0,0 +1,48 @@ +require "grit" + +module Gitlab + module Graph + class Commit + include ActionView::Helpers::TagHelper + + attr_accessor :time, :space, :refs + + def initialize(commit) + @_commit = commit + @time = -1 + @space = 0 + 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] = author.name + h[:time] = time + h[:space] = space + h[:refs] = refs.collect{|r|r.name}.join(" ") unless refs.nil? + h[:id] = sha + h[:date] = date + h[:message] = escape_once(message) + h[:login] = author.email + 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 new file mode 100644 index 00000000..c2c3fa66 --- /dev/null +++ b/lib/gitlab/graph/json_builder.rb @@ -0,0 +1,172 @@ +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 + @project = project + @repo = project.repo + @ref_cache = {} + + @commits = collect_commits + @days = index_commits + end + + def days_json + @days_json = @days.compact.map { |d| [d.day, d.strftime("%b")] }.to_json + end + + def commits_json + @commits_json = @commits.map(&:to_graph_hash).to_json + end + + protected + + # Get commits from repository + # + def collect_commits + @commits = Grit::Commit.find_all(repo, nil, {max_count: self.class.max_count}).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] comits to index + # + # @return [Array] list of commit dates corelated with time on commits + def index_commits + days, heads = [], [] + map = {} + + commits.reverse.each_with_index do |c,i| + c.time = i + days[i] = c.committed_date + map[c.id] = c + heads += c.refs unless c.refs.nil? + end + + heads.select!{|h| h.is_a? Grit::Head or h.is_a? Grit::Remote} + # sort heads so the master is top and current branches are closer + heads.sort! do |a,b| + if a.name == "master" + -1 + elsif b.name == "master" + 1 + else + b.commit.committed_date <=> a.commit.committed_date + end + end + + @_reserved = {} + days.each_index do |i| + @_reserved[i] = [] + end + + heads.each do |h| + if map.include? h.commit.id then + place_chain(map[h.commit.id], map) + end + end + + days + end + + # Add space mark on commit and its parents + # + # @param [Graph::Commit] the commit object. + # @param [Hash] map of commits + def place_chain(commit, map, parent_time = nil) + leaves = take_left_leaves(commit, map) + if leaves.empty? + return + end + space = find_free_space(leaves.last.time..leaves.first.time) + leaves.each{|l| l.space = space} + # 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 == 0} + 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) + reserved = [] + for day in time_range + reserved += @_reserved[day] + end + space = 1 + while reserved.include? space do + space += 1 + 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] map of commits + # + # @return [Array] list of branch commits + def take_left_leaves(commit, map) + leaves = [] + leaves.push(commit) if commit.space.zero? + + while true + parent = commit.parents.collect.select do |p| + map.include? p.id and map[p.id].space == 0 + end + + return leaves if parent.count.zero? + + commit = map[parent.first.id] + leaves.push(commit) + end + end + end + end +end diff --git a/lib/gitlab/logger.rb b/lib/gitlab/logger.rb index 389eef33..cf9a4c4a 100644 --- a/lib/gitlab/logger.rb +++ b/lib/gitlab/logger.rb @@ -11,12 +11,7 @@ module Gitlab def self.read_latest path = Rails.root.join("log", file_name) self.build unless File.exist?(path) - logs = `tail -n 2000 #{path}`.split("\n") - end - - def self.read_latest_for filename - path = Rails.root.join("log", filename) - logs = `tail -n 2000 #{path}`.split("\n") + logs = File.read(path).split("\n") end def self.build diff --git a/lib/gitlab/markdown.rb b/lib/gitlab/markdown.rb index ad6ba3e8..ee0ee05c 100644 --- a/lib/gitlab/markdown.rb +++ b/lib/gitlab/markdown.rb @@ -17,7 +17,7 @@ module Gitlab # Examples # # >> gfm("Hey @david, can you fix this?") - # => "Hey @david, can you fix this?" + # => "Hey @david, can you fix this?" # # >> gfm("Commit 35d5f7c closes #1234") # => "Commit 35d5f7c closes #1234" @@ -25,7 +25,17 @@ module Gitlab # >> gfm(":trollface:") # => "\":trollface:\" module Markdown - include IssuesHelper + REFERENCE_PATTERN = %r{ + (\W)? # Prefix (1) + ( # Reference (2) + @([\w\._]+) # User name (3) + |[#!$](\d+) # Issue/MR/Snippet ID (4) + |([\h]{6,40}) # Commit ID (5) + ) + (\W)? # Suffix (6) + }x.freeze + + EMOJI_PATTERN = %r{(:(\S+):)}.freeze attr_reader :html_options @@ -47,11 +57,12 @@ module Gitlab # Extract pre blocks so they are not altered # from http://github.github.com/github-flavored-markdown/ - text.gsub!(%r{
    .*?
    |.*?}m) { |match| extract_piece(match) } - # Extract links with probably parsable hrefs - text.gsub!(%r{.*?}m) { |match| extract_piece(match) } - # Extract images with probably parsable src - text.gsub!(%r{}m) { |match| extract_piece(match) } + extractions = {} + text.gsub!(%r{
    .*?
    |.*?}m) do |match| + md5 = Digest::MD5.hexdigest(match) + extractions[md5] = match + "{gfm-extraction-#{md5}}" + end # TODO: add popups with additional information @@ -59,7 +70,7 @@ module Gitlab # Insert pre block extractions text.gsub!(/\{gfm-extraction-(\h{32})\}/) do - insert_piece($1) + extractions[$1] end sanitize text.html_safe, attributes: ActionView::Base.sanitized_allowed_attributes + %w(id class) @@ -67,18 +78,6 @@ module Gitlab private - def extract_piece(text) - @extractions ||= {} - - md5 = Digest::MD5.hexdigest(text) - @extractions[md5] = text - "{gfm-extraction-#{md5}}" - end - - def insert_piece(id) - @extractions[id] - end - # Private: Parses text for references and emoji # # text - Text to parse @@ -93,46 +92,30 @@ module Gitlab text end - REFERENCE_PATTERN = %r{ - (?\W)? # Prefix - ( # Reference - @(?[a-zA-Z][a-zA-Z0-9_\-\.]*) # User name - |\#(?\d+) # Issue ID - |!(?\d+) # MR ID - |\$(?\d+) # Snippet ID - |(?[\h]{6,40}) # Commit ID - ) - (?\W)? # Suffix - }x.freeze - - TYPES = [:user, :issue, :merge_request, :snippet, :commit].freeze - def parse_references(text) # parse reference links text.gsub!(REFERENCE_PATTERN) do |match| - prefix = $~[:prefix] - suffix = $~[:suffix] - type = TYPES.select{|t| !$~[t].nil?}.first - identifier = $~[type] + prefix = $1 || '' + reference = $2 + identifier = $3 || $4 || $5 + suffix = $6 || '' # Avoid HTML entities - if prefix && suffix && prefix[0] == '&' && suffix[-1] == ';' + if prefix.ends_with?('&') || suffix.starts_with?(';') match - elsif ref_link = reference_link(type, identifier) - "#{prefix}#{ref_link}#{suffix}" + elsif ref_link = reference_link(reference, identifier) + prefix + ref_link + suffix else match end end end - EMOJI_PATTERN = %r{(:(\S+):)}.freeze - def parse_emoji(text) # parse emoji text.gsub!(EMOJI_PATTERN) do |match| if valid_emoji?($2) - image_tag(url_to_image("emoji/#{$2}.png"), class: 'emoji', title: $1, alt: $1, size: "20x20") + image_tag("emoji/#{$2}.png", size: "20x20", class: 'emoji', title: $1, alt: $1) else match end @@ -154,40 +137,44 @@ module Gitlab # identifier - Object identifier (Issue ID, SHA hash, etc.) # # Returns string rendered by the processing method - def reference_link(type, identifier) - send("reference_#{type}", identifier) + def reference_link(reference, identifier) + case reference + when /^@/ then reference_user(identifier) + when /^#/ then reference_issue(identifier) + when /^!/ then reference_merge_request(identifier) + when /^\$/ then reference_snippet(identifier) + when /^\h/ then reference_commit(identifier) + end end def reference_user(identifier) - if member = @project.users_projects.joins(:user).where(users: { username: identifier }).first - link_to("@#{identifier}", user_path(identifier), html_options.merge(class: "gfm gfm-team_member #{html_options[:class]}")) if member + if user = @project.users.where(name: identifier).first + member = @project.users_projects.where(user_id: user).first + link_to("@#{identifier}", project_team_member_path(@project, member), html_options.merge(class: "gfm gfm-team_member #{html_options[:class]}")) if member end end def reference_issue(identifier) - 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]}")) + if issue = @project.issues.where(id: identifier).first + link_to("##{identifier}", project_issue_path(@project, issue), html_options.merge(title: "Issue: #{issue.title}", class: "gfm gfm-issue #{html_options[:class]}")) end end def reference_merge_request(identifier) if merge_request = @project.merge_requests.where(id: identifier).first - link_to("!#{identifier}", project_merge_request_url(@project, merge_request), html_options.merge(title: "Merge Request: #{merge_request.title}", class: "gfm gfm-merge_request #{html_options[:class]}")) + link_to("!#{identifier}", project_merge_request_path(@project, merge_request), html_options.merge(title: "Merge Request: #{merge_request.title}", class: "gfm gfm-merge_request #{html_options[:class]}")) end end def reference_snippet(identifier) if snippet = @project.snippets.where(id: identifier).first - link_to("$#{identifier}", project_snippet_url(@project, snippet), html_options.merge(title: "Snippet: #{snippet.title}", class: "gfm gfm-snippet #{html_options[:class]}")) + link_to("$#{identifier}", project_snippet_path(@project, snippet), html_options.merge(title: "Snippet: #{snippet.title}", class: "gfm gfm-snippet #{html_options[:class]}")) end end def reference_commit(identifier) - if @project.valid_repo? && commit = @project.repository.commit(identifier) - link_to(identifier, project_commit_url(@project, commit), html_options.merge(title: commit.link_title, class: "gfm gfm-commit #{html_options[:class]}")) + if @project.valid_repo? && commit = @project.commit(identifier) + link_to(identifier, project_commit_path(@project, commit), html_options.merge(title: CommitDecorator.new(commit).link_title, class: "gfm gfm-commit #{html_options[:class]}")) end end end diff --git a/lib/gitlab/popen.rb b/lib/gitlab/popen.rb deleted file mode 100644 index f2cfd807..00000000 --- a/lib/gitlab/popen.rb +++ /dev/null @@ -1,18 +0,0 @@ -module Gitlab - module Popen - def popen(cmd, path) - vars = { "PWD" => path } - options = { :chdir => path } - - @cmd_output = "" - @cmd_status = 0 - Open3.popen3(vars, cmd, options) do |stdin, stdout, stderr, wait_thr| - @cmd_status = wait_thr.value.exitstatus - @cmd_output << stdout.read - @cmd_output << stderr.read - end - - return @cmd_output, @cmd_status - end - end -end diff --git a/lib/gitlab/regex.rb b/lib/gitlab/regex.rb deleted file mode 100644 index 5eeb7c80..00000000 --- a/lib/gitlab/regex.rb +++ /dev/null @@ -1,27 +0,0 @@ -module Gitlab - module Regex - extend self - - def username_regex - default_regex - end - - def project_name_regex - /\A[a-zA-Z][a-zA-Z0-9_\-\. ]*\z/ - end - - def name_regex - /\A[a-zA-Z0-9_\-\. ]*\z/ - end - - def path_regex - default_regex - end - - protected - - def default_regex - /\A[a-zA-Z][a-zA-Z0-9_\-\.]*\z/ - end - end -end diff --git a/lib/gitlab/satellite/action.rb b/lib/gitlab/satellite/action.rb index 63303ca3..ed2541f3 100644 --- a/lib/gitlab/satellite/action.rb +++ b/lib/gitlab/satellite/action.rb @@ -17,8 +17,6 @@ module Gitlab # * Locks the satellite repo # * Yields the prepared satellite repo def in_locked_and_timed_satellite - Gitlab::ShellEnv.set_env(user) - Grit::Git.with_timeout(options[:git_timeout]) do project.satellite.lock do return yield project.satellite.repo @@ -30,8 +28,6 @@ module Gitlab rescue Grit::Git::GitTimeout => ex Gitlab::GitLogger.error(ex.message) return false - ensure - Gitlab::ShellEnv.reset_env end # * Clears the satellite diff --git a/lib/gitlab/satellite/edit_file_action.rb b/lib/gitlab/satellite/edit_file_action.rb index 07570965..336afc88 100644 --- a/lib/gitlab/satellite/edit_file_action.rb +++ b/lib/gitlab/satellite/edit_file_action.rb @@ -13,7 +13,7 @@ module Gitlab # Updates the files content and creates a new commit for it # # Returns false if the ref has been updated while editing the file - # Returns false if committing the change fails + # Returns false if commiting the change fails # Returns false if pushing from the satellite to Gitolite failed or was rejected # Returns true otherwise def commit!(content, commit_message, last_commit) @@ -49,7 +49,7 @@ module Gitlab protected def can_edit?(last_commit) - current_last_commit = @project.repository.last_commit_for(ref, file_path).sha + current_last_commit = @project.last_commit_for(ref, file_path).sha last_commit == current_last_commit end end diff --git a/lib/gitlab/satellite/logger.rb b/lib/gitlab/satellite/logger.rb deleted file mode 100644 index 6f3f8255..00000000 --- a/lib/gitlab/satellite/logger.rb +++ /dev/null @@ -1,13 +0,0 @@ -module Gitlab - module Satellite - class Logger < Gitlab::Logger - def self.file_name - 'satellites.log' - end - - def format_message(severity, timestamp, progname, msg) - "#{timestamp.to_s(:long)}: #{msg}\n" - end - end - end -end diff --git a/lib/gitlab/satellite/satellite.rb b/lib/gitlab/satellite/satellite.rb index 668de75f..28b6f538 100644 --- a/lib/gitlab/satellite/satellite.rb +++ b/lib/gitlab/satellite/satellite.rb @@ -1,10 +1,6 @@ module Gitlab - class SatelliteNotExistError < StandardError; end - module Satellite class Satellite - include Gitlab::Popen - PARKING_BRANCH = "__parking_branch" attr_accessor :project @@ -13,35 +9,16 @@ module Gitlab @project = project end - def log message - Gitlab::Satellite::Logger.error(message) - end - - def raise_no_satellite - raise SatelliteNotExistError.new("Satellite doesn't exist") - end - def clear_and_update! - raise_no_satellite unless exists? + raise "Satellite doesn't exist" unless exists? - clear_working_dir! delete_heads! + clear_working_dir! update_from_source! end def create - output, status = popen("git clone #{project.repository.path_to_repo} #{path}", - Gitlab.config.satellites.path) - - log("PID: #{project.id}: git clone #{project.repository.path_to_repo} #{path}") - log("PID: #{project.id}: -> #{output}") - - if status.zero? - true - else - log("Failed to create satellite for #{project.name_with_namespace}") - false - end + `git clone #{project.url_to_repo} #{path}` end def exists? @@ -52,7 +29,7 @@ module Gitlab # * Changes the current directory to the satellite's working dir # * Yields def lock - raise_no_satellite unless exists? + raise "Satellite doesn't exist" unless exists? File.open(lock_file, "w+") do |f| f.flock(File::LOCK_EX) @@ -64,23 +41,19 @@ module Gitlab end def lock_file - Rails.root.join("tmp", "satellite_#{project.id}.lock") + Rails.root.join("tmp", "#{project.path}.lock") end def path - File.join(Gitlab.config.satellites.path, project.path_with_namespace) + Rails.root.join("tmp", "repo_satellites", project.path) end def repo - raise_no_satellite unless exists? + raise "Satellite doesn't exist" unless exists? @repo ||= Grit::Repo.new(path) end - def destroy - FileUtils.rm_rf(path) - end - private # Clear the working directory diff --git a/lib/gitlab/user_team_manager.rb b/lib/gitlab/user_team_manager.rb deleted file mode 100644 index 4127d5e1..00000000 --- a/lib/gitlab/user_team_manager.rb +++ /dev/null @@ -1,137 +0,0 @@ -# UserTeamManager class -# -# Used for manage User teams with project repositories -module Gitlab - class UserTeamManager - class << self - def assign(team, project, access) - project = Project.find(project) unless project.is_a? Project - searched_project = team.user_team_project_relationships.find_by_project_id(project.id) - - unless searched_project.present? - team.user_team_project_relationships.create(project_id: project.id, greatest_access: access) - update_team_users_access_in_project(team, project, :added) - end - end - - def resign(team, project) - project = Project.find(project) unless project.is_a? Project - - team.user_team_project_relationships.with_project(project).destroy_all - - update_team_users_access_in_project(team, project, :updated) - end - - def update_team_user_membership(team, member, options) - updates = {} - - if options[:default_projects_access] && options[:default_projects_access] != team.default_projects_access(member) - updates[:permission] = options[:default_projects_access] - end - - if options[:group_admin].to_s != team.admin?(member).to_s - updates[:group_admin] = options[:group_admin].present? - end - - unless updates.blank? - user_team_relationship = team.user_team_user_relationships.find_by_user_id(member) - if user_team_relationship.update_attributes(updates) - if updates[:permission] - rebuild_project_permissions_to_member(team, member) - end - true - else - false - end - else - true - end - end - - def update_project_greates_access(team, project, permission) - project_relation = team.user_team_project_relationships.find_by_project_id(project) - if permission != team.max_project_access(project) - if project_relation.update_attributes(greatest_access: permission) - update_team_users_access_in_project(team, project, :updated) - true - else - false - end - else - true - end - end - - def rebuild_project_permissions_to_member(team, member) - team.projects.each do |project| - update_team_user_access_in_project(team, member, project) - end - end - - def update_team_users_access_in_project(team, project, action) - members = team.members - members.each do |member| - update_team_user_access_in_project(team, member, project, action) - end - end - - def update_team_user_access_in_project(team, user, project, action) - granted_access = max_teams_member_permission_in_project(user, project, action) - - project_team_user = UsersProject.find_by_user_id_and_project_id(user.id, project.id) - project_team_user.destroy if project_team_user.present? - - # project_team_user.project_access != granted_access - project.team << [user, granted_access] if granted_access > 0 - end - - def max_teams_member_permission_in_project(user, project, action = nil, teams = nil) - result_access = 0 - - teams ||= project.user_teams.with_member(user) - - if action && (action == :added) && (teams.count == 1) - result_access ||= project.users_project.with_user(user).first.project_access - end - - if teams.any? - teams.each do |team| - granted_access = max_team_member_permission_in_project(team, user, project) - result_access = [granted_access, result_access].max - end - end - result_access - end - - def max_team_member_permission_in_project(team, user, project) - member_access = team.default_projects_access(user) - team_access = team.user_team_project_relationships.find_by_project_id(project.id).greatest_access - - [team_access, member_access].min - end - - def add_member_into_team(team, user, access, admin) - user = User.find(user) unless user.is_a? User - - team.user_team_user_relationships.create(user_id: user.id, permission: access, group_admin: admin) - team.projects.each do |project| - update_team_user_access_in_project(team, user, project, :added) - end - end - - def remove_member_from_team(team, user) - user = User.find(user) unless user.is_a? User - - team.user_team_user_relationships.with_user(user).destroy_all - other_teams = [] - team.projects.each do |project| - other_teams << project.user_teams.with_member(user) - end - other_teams.uniq - unless other_teams.any? - UsersProject.in_projects(team.projects).with_user(user).destroy_all - end - end - end - end -end diff --git a/lib/hooks/post-receive b/lib/hooks/post-receive new file mode 100755 index 00000000..4a3ce372 --- /dev/null +++ b/lib/hooks/post-receive @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +# This file was placed here by GitLab. It makes sure that your pushed commits +# will be processed properly. + +while read oldrev newrev ref +do + # For every branch or tag that was pushed, create a Resque job in redis. + pwd=`pwd` + reponame=`basename "$pwd" | sed s/\.git$//` + env -i redis-cli rpush "resque:gitlab:queue:post_receive" "{\"class\":\"PostReceive\",\"args\":[\"$reponame\",\"$oldrev\",\"$newrev\",\"$ref\",\"$GL_USER\"]}" > /dev/null 2>&1 +done diff --git a/lib/redcarpet/render/gitlab_html.rb b/lib/redcarpet/render/gitlab_html.rb index 4f2c86e2..48b4da9d 100644 --- a/lib/redcarpet/render/gitlab_html.rb +++ b/lib/redcarpet/render/gitlab_html.rb @@ -11,24 +11,12 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML def block_code(code, language) options = { options: {encoding: 'utf-8'} } - options.merge!(lexer: language.downcase) if Pygments::Lexer.find(language) - # New lines are placed to fix an rendering issue - # with code wrapped inside

    tag for next case: - # - # # Title kinda h1 - # - # ruby code here - # - <<-HTML - -
    #{Pygments.highlight(code, options)}
    - - HTML - end - - def link(link, title, content) - h.link_to_gfm(content, link, title: title) + if Pygments::Lexer.find(language) + Pygments.highlight(code, options.merge(lexer: language.downcase)) + else + Pygments.highlight(code, options) + end end def postprocess(full_document) diff --git a/lib/support/rewrite-hooks.sh b/lib/support/rewrite-hooks.sh new file mode 100755 index 00000000..6688785a --- /dev/null +++ b/lib/support/rewrite-hooks.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +src="/home/git/repositories" + +for dir in `ls "$src/"` +do + if [ -d "$src/$dir" ]; then + + if [ "$dir" = "gitolite-admin.git" ] + then + continue + fi + + project_hook="$src/$dir/hooks/post-receive" + gitolite_hook="/home/git/.gitolite/hooks/common/post-receive" + + ln -s -f $gitolite_hook $project_hook + fi +done diff --git a/lib/tasks/bulk_add_permission.rake b/lib/tasks/bulk_add_permission.rake new file mode 100644 index 00000000..bf08ace8 --- /dev/null +++ b/lib/tasks/bulk_add_permission.rake @@ -0,0 +1,20 @@ +desc "Add all users to all projects (admin users are added as masters)" +task :add_users_to_project_teams => :environment do |t, args| + user_ids = User.where(:admin => false).pluck(:id) + admin_ids = User.where(:admin => true).pluck(:id) + + Project.find_each do |project| + puts "Importing #{user_ids.size} users into #{project.code}" + UsersProject.bulk_import(project, user_ids, UsersProject::DEVELOPER) + puts "Importing #{admin_ids.size} admins into #{project.code}" + UsersProject.bulk_import(project, admin_ids, UsersProject::MASTER) + end +end + +desc "Add user to as a developer to all projects" +task :add_user_to_project_teams, [:email] => :environment do |t, args| + user = User.find_by_email args.email + project_ids = Project.pluck(:id) + + UsersProject.user_bulk_import(user, project_ids, UsersProject::DEVELOPER) +end diff --git a/lib/tasks/cache.rake b/lib/tasks/cache.rake deleted file mode 100644 index 8320b9b2..00000000 --- a/lib/tasks/cache.rake +++ /dev/null @@ -1,6 +0,0 @@ -namespace :cache do - desc "GITLAB | Clear redis cache" - task :clear => :environment do - Rails.cache.clear - end -end diff --git a/lib/tasks/gitlab/backup.rake b/lib/tasks/gitlab/backup.rake index 32acdcf5..17a0e336 100644 --- a/lib/tasks/gitlab/backup.rake +++ b/lib/tasks/gitlab/backup.rake @@ -1,16 +1,14 @@ require 'active_record/fixtures' namespace :gitlab do - namespace :backup do + namespace :app do # Create backup of GitLab system desc "GITLAB | Create a backup of the GitLab system" - task :create => :environment do - warn_user_is_not_gitlab + task :backup_create => :environment do + Rake::Task["gitlab:app:db_dump"].invoke + Rake::Task["gitlab:app:repo_dump"].invoke - Rake::Task["gitlab:backup:db:create"].invoke - Rake::Task["gitlab:backup:repo:create"].invoke - - Dir.chdir(Gitlab.config.backup.path) + Dir.chdir(Gitlab.config.backup_path) # saving additional informations s = {} @@ -19,93 +17,91 @@ namespace :gitlab do s[:gitlab_version] = %x{git rev-parse HEAD}.gsub(/\n/,"") s[:tar_version] = %x{tar --version | head -1}.gsub(/\n/,"") - File.open("#{Gitlab.config.backup.path}/backup_information.yml", "w+") do |file| + File.open("#{Gitlab.config.backup_path}/backup_information.yml", "w+") do |file| file << s.to_yaml.gsub(/^---\n/,'') end # create archive - print "Creating backup archive: #{Time.now.to_i}_gitlab_backup.tar ... " + print "Creating backup archive: #{Time.now.to_i}_gitlab_backup.tar " if Kernel.system("tar -cf #{Time.now.to_i}_gitlab_backup.tar repositories/ db/ backup_information.yml") - puts "done".green + puts "[DONE]".green else - puts "failed".red + puts "[FAILED]".red end # cleanup: remove tmp files - print "Deleting tmp directories ... " + print "Deleting tmp directories..." if Kernel.system("rm -rf repositories/ db/ backup_information.yml") - puts "done".green + puts "[DONE]".green else - puts "failed".red + puts "[FAILED]".red end # delete backups - print "Deleting old backups ... " - if Gitlab.config.backup.keep_time > 0 + print "Deleting old backups... " + if Gitlab.config.backup_keep_time > 0 file_list = Dir.glob("*_gitlab_backup.tar").map { |f| f.split(/_/).first.to_i } file_list.sort.each do |timestamp| - if Time.at(timestamp) < (Time.now - Gitlab.config.backup.keep_time) + if Time.at(timestamp) < (Time.now - Gitlab.config.backup_keep_time) %x{rm #{timestamp}_gitlab_backup.tar} end end - puts "done".green + puts "[DONE]".green else - puts "skipping".yellow + puts "[SKIPPING]".yellow end end # Restore backup of GitLab system desc "GITLAB | Restore a previously created backup" - task :restore => :environment do - warn_user_is_not_gitlab - - Dir.chdir(Gitlab.config.backup.path) + task :backup_restore => :environment do + Dir.chdir(Gitlab.config.backup_path) # check for existing backups in the backup dir file_list = Dir.glob("*_gitlab_backup.tar").each.map { |f| f.split(/_/).first.to_i } puts "no backups found" if file_list.count == 0 if file_list.count > 1 && ENV["BACKUP"].nil? puts "Found more than one backup, please specify which one you want to restore:" - puts "rake gitlab:backup:restore BACKUP=timestamp_of_backup" - exit 1 + puts "rake gitlab:app:backup_restore BACKUP=timestamp_of_backup" + exit 1; end tar_file = ENV["BACKUP"].nil? ? File.join("#{file_list.first}_gitlab_backup.tar") : File.join(ENV["BACKUP"] + "_gitlab_backup.tar") unless File.exists?(tar_file) puts "The specified backup doesn't exist!" - exit 1 + exit 1; end - print "Unpacking backup ... " + print "Unpacking backup... " unless Kernel.system("tar -xf #{tar_file}") - puts "failed".red + puts "[FAILED]".red exit 1 else - puts "done".green + puts "[DONE]".green end settings = YAML.load_file("backup_information.yml") - ENV["VERSION"] = "#{settings[:db_version]}" if settings[:db_version].to_i > 0 + ENV["VERSION"] = "#{settings["db_version"]}" if settings["db_version"].to_i > 0 # restoring mismatching backups can lead to unexpected problems if settings[:gitlab_version] != %x{git rev-parse HEAD}.gsub(/\n/,"") - puts "GitLab version mismatch:".red + puts "gitlab_version mismatch:".red puts " Your current HEAD differs from the HEAD in the backup!".red puts " Please switch to the following revision and try again:".red puts " revision: #{settings[:gitlab_version]}".red exit 1 end - Rake::Task["gitlab:backup:db:restore"].invoke - Rake::Task["gitlab:backup:repo:restore"].invoke + Rake::Task["gitlab:app:db_restore"].invoke + Rake::Task["gitlab:app:repo_restore"].invoke # cleanup: remove tmp files - print "Deleting tmp directories ... " + print "Deleting tmp directories..." if Kernel.system("rm -rf repositories/ db/ backup_information.yml") - puts "done".green + puts "[DONE]".green else - puts "failed".red + puts "[FAILED]".red end end @@ -114,101 +110,82 @@ namespace :gitlab do ################################# REPOSITORIES ################################# - namespace :repo do - task :create => :environment do - backup_path_repo = File.join(Gitlab.config.backup.path, "repositories") - FileUtils.mkdir_p(backup_path_repo) until Dir.exists?(backup_path_repo) - puts "Dumping repositories ...".blue - - Project.find_each(:batch_size => 1000) do |project| - print " * #{project.path_with_namespace} ... " - - if project.empty_repo? - puts "[SKIPPED]".cyan - next - end - - # Create namespace dir if missing - FileUtils.mkdir_p(File.join(backup_path_repo, project.namespace.path)) if project.namespace - - # Build a destination path for backup - path_to_bundle = File.join(backup_path_repo, project.path_with_namespace + ".bundle") - - if Kernel.system("cd #{project.repository.path_to_repo} > /dev/null 2>&1 && git bundle create #{path_to_bundle} --all > /dev/null 2>&1") - puts "[DONE]".green - else - puts "[FAILED]".red - end + task :repo_dump => :environment do + backup_path_repo = File.join(Gitlab.config.backup_path, "repositories") + FileUtils.mkdir_p(backup_path_repo) until Dir.exists?(backup_path_repo) + puts "Dumping repositories:" + project = Project.all.map { |n| [n.path, n.path_to_repo] } + project << ["gitolite-admin.git", File.join(File.dirname(project.first.second), "gitolite-admin.git")] + project.each do |project| + print "- Dumping repository #{project.first}... " + if Kernel.system("cd #{project.second} > /dev/null 2>&1 && git bundle create #{backup_path_repo}/#{project.first}.bundle --all > /dev/null 2>&1") + puts "[DONE]".green + else + puts "[FAILED]".red end end + end - task :restore => :environment do - backup_path_repo = File.join(Gitlab.config.backup.path, "repositories") - repos_path = Gitlab.config.gitlab_shell.repos_path - - puts "Restoring repositories ... " - - Project.find_each(:batch_size => 1000) do |project| - print "#{project.path_with_namespace} ... " - - if project.namespace - project.namespace.ensure_dir_exist - end - - # Build a backup path - path_to_bundle = File.join(backup_path_repo, project.path_with_namespace + ".bundle") - - if Kernel.system("git clone --bare #{path_to_bundle} #{project.repository.path_to_repo} > /dev/null 2>&1") - puts "[DONE]".green - else - puts "[FAILED]".red - end + task :repo_restore => :environment do + backup_path_repo = File.join(Gitlab.config.backup_path, "repositories") + puts "Restoring repositories:" + project = Project.all.map { |n| [n.path, n.path_to_repo] } + project << ["gitolite-admin.git", File.join(File.dirname(project.first.second), "gitolite-admin.git")] + project.each do |project| + print "- Restoring repository #{project.first}... " + FileUtils.rm_rf(project.second) if File.dirname(project.second) # delete old stuff + if Kernel.system("cd #{File.dirname(project.second)} > /dev/null 2>&1 && git clone --bare #{backup_path_repo}/#{project.first}.bundle #{project.first}.git > /dev/null 2>&1") + permission_commands = [ + "sudo chmod -R g+rwX #{Gitlab.config.git_base_path}", + "sudo chown -R #{Gitlab.config.ssh_user}:#{Gitlab.config.ssh_user} #{Gitlab.config.git_base_path}" + ] + permission_commands.each { |command| Kernel.system(command) } + puts "[DONE]".green + else + puts "[FAILED]".red end end end ###################################### DB ###################################### - namespace :db do - task :create => :environment do - backup_path_db = File.join(Gitlab.config.backup.path, "db") - FileUtils.mkdir_p(backup_path_db) unless Dir.exists?(backup_path_db) + task :db_dump => :environment do + backup_path_db = File.join(Gitlab.config.backup_path, "db") + FileUtils.mkdir_p(backup_path_db) unless Dir.exists?(backup_path_db) - puts "Dumping database tables ... ".blue - ActiveRecord::Base.connection.tables.each do |tbl| - print " * #{tbl.yellow} ... " - count = 1 - safe_tablename = ActiveRecord::Base.connection.quote_table_name(tbl) - File.open(File.join(backup_path_db, tbl + ".yml"), "w+") do |file| - ActiveRecord::Base.connection.select_all("SELECT * FROM #{safe_tablename}").each do |line| - line.delete_if{|k,v| v.blank?} - output = {tbl + '_' + count.to_s => line} - file << output.to_yaml.gsub(/^---\n/,'') + "\n" - count += 1 - end - puts "done".green - end - end - end - - task :restore => :environment do - backup_path_db = File.join(Gitlab.config.backup.path, "db") - - puts "Restoring database tables (loading fixtures) ... " - Rake::Task["db:reset"].invoke - - Dir.glob(File.join(backup_path_db, "*.yml") ).each do |dir| - fixture_file = File.basename(dir, ".*" ) - print "#{fixture_file.yellow} ... " - if File.size(dir) > 0 - ActiveRecord::Fixtures.create_fixtures(backup_path_db, fixture_file) - puts "done".green - else - puts "skipping".yellow + puts "Dumping database tables:" + ActiveRecord::Base.connection.tables.each do |tbl| + print "- Dumping table #{tbl}... " + count = 1 + File.open(File.join(backup_path_db, tbl + ".yml"), "w+") do |file| + ActiveRecord::Base.connection.select_all("SELECT * FROM `#{tbl}`").each do |line| + line.delete_if{|k,v| v.blank?} + output = {tbl + '_' + count.to_s => line} + file << output.to_yaml.gsub(/^---\n/,'') + "\n" + count += 1 end + puts "[DONE]".green end end end - end # namespace end: backup + task :db_restore=> :environment do + backup_path_db = File.join(Gitlab.config.backup_path, "db") + + puts "Restoring database tables:" + Rake::Task["db:reset"].invoke + + Dir.glob(File.join(backup_path_db, "*.yml") ).each do |dir| + fixture_file = File.basename(dir, ".*" ) + print "- Loading fixture #{fixture_file}..." + if File.size(dir) > 0 + ActiveRecord::Fixtures.create_fixtures(backup_path_db, fixture_file) + puts "[DONE]".green + else + puts "[SKIPPING]".yellow + end + end + end + + end # namespace end: app end # namespace end: gitlab diff --git a/lib/tasks/gitlab/bulk_add_permission.rake b/lib/tasks/gitlab/bulk_add_permission.rake deleted file mode 100644 index eb1a7559..00000000 --- a/lib/tasks/gitlab/bulk_add_permission.rake +++ /dev/null @@ -1,24 +0,0 @@ -namespace :gitlab do - namespace :import do - desc "GITLAB | Add all users to all projects (admin users are added as masters)" - task :all_users_to_all_projects => :environment do |t, args| - user_ids = User.where(:admin => false).pluck(:id) - admin_ids = User.where(:admin => true).pluck(:id) - projects_ids = Project.pluck(:id) - - puts "Importing #{user_ids.size} users into #{projects_ids.size} projects" - UsersProject.add_users_into_projects(projects_ids, user_ids, UsersProject::DEVELOPER) - - puts "Importing #{admin_ids.size} admins into #{projects_ids.size} projects" - UsersProject.add_users_into_projects(projects_ids, admin_ids, UsersProject::MASTER) - end - - desc "GITLAB | Add a specific user to all projects (as a developer)" - task :user_to_projects, [:email] => :environment do |t, args| - user = User.find_by_email args.email - project_ids = Project.pluck(:id) - puts "Importing #{user.email} users into #{project_ids.size} projects" - UsersProject.add_users_into_projects(project_ids, Array.wrap(user.id), UsersProject::DEVELOPER) - end - end -end \ No newline at end of file diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake deleted file mode 100644 index c8d8d531..00000000 --- a/lib/tasks/gitlab/check.rake +++ /dev/null @@ -1,646 +0,0 @@ -namespace :gitlab do - desc "GITLAB | Check the configuration of GitLab and its environment" - task check: %w{gitlab:env:check - gitlab:gitlab_shell:check - gitlab:sidekiq:check - gitlab:app:check} - - - - namespace :app do - desc "GITLAB | Check the configuration of the GitLab Rails app" - task check: :environment do - warn_user_is_not_gitlab - start_checking "GitLab" - - check_database_config_exists - check_database_is_not_sqlite - check_migrations_are_up - check_gitlab_config_exists - check_gitlab_config_not_outdated - check_log_writable - check_tmp_writable - check_init_script_exists - check_init_script_up_to_date - check_satellites_exist - - finished_checking "GitLab" - end - - - # Checks - ######################## - - def check_database_config_exists - print "Database config exists? ... " - - database_config_file = Rails.root.join("config", "database.yml") - - if File.exists?(database_config_file) - puts "yes".green - else - puts "no".red - try_fixing_it( - "Copy config/database.yml. to config/database.yml", - "Check that the information in config/database.yml is correct" - ) - for_more_information( - see_database_guide, - "http://guides.rubyonrails.org/getting_started.html#configuring-a-database" - ) - fix_and_rerun - end - end - - def check_database_is_not_sqlite - print "Database is SQLite ... " - - database_config_file = Rails.root.join("config", "database.yml") - - unless File.read(database_config_file) =~ /adapter:\s+sqlite/ - puts "no".green - else - puts "yes".red - for_more_information( - "https://github.com/gitlabhq/gitlabhq/wiki/Migrate-from-SQLite-to-MySQL", - see_database_guide - ) - fix_and_rerun - end - end - - def check_gitlab_config_exists - print "GitLab config exists? ... " - - gitlab_config_file = Rails.root.join("config", "gitlab.yml") - - if File.exists?(gitlab_config_file) - puts "yes".green - else - puts "no".red - try_fixing_it( - "Copy config/gitlab.yml.example to config/gitlab.yml", - "Update config/gitlab.yml to match your setup" - ) - for_more_information( - see_installation_guide_section "GitLab" - ) - fix_and_rerun - end - end - - def check_gitlab_config_not_outdated - print "GitLab config outdated? ... " - - gitlab_config_file = Rails.root.join("config", "gitlab.yml") - unless File.exists?(gitlab_config_file) - puts "can't check because of previous errors".magenta - end - - # omniauth or ldap could have been deleted from the file - unless Gitlab.config['git_host'] - puts "no".green - else - puts "yes".red - try_fixing_it( - "Backup your config/gitlab.yml", - "Copy config/gitlab.yml.example to config/gitlab.yml", - "Update config/gitlab.yml to match your setup" - ) - for_more_information( - see_installation_guide_section "GitLab" - ) - fix_and_rerun - end - end - - def check_init_script_exists - print "Init script exists? ... " - - script_path = "/etc/init.d/gitlab" - - if File.exists?(script_path) - puts "yes".green - else - puts "no".red - try_fixing_it( - "Install the init script" - ) - for_more_information( - see_installation_guide_section "Install Init Script" - ) - fix_and_rerun - end - end - - def check_init_script_up_to_date - print "Init script up-to-date? ... " - - script_path = "/etc/init.d/gitlab" - unless File.exists?(script_path) - puts "can't check because of previous errors".magenta - return - end - - recipe_content = `curl https://raw.github.com/gitlabhq/gitlab-recipes/master/init.d/gitlab 2>/dev/null` - script_content = File.read(script_path) - - if recipe_content == script_content - puts "yes".green - else - puts "no".red - try_fixing_it( - "Redownload the init script" - ) - for_more_information( - see_installation_guide_section "Install Init Script" - ) - fix_and_rerun - end - end - - def check_migrations_are_up - print "All migrations up? ... " - - migration_status = `bundle exec rake db:migrate:status` - - unless migration_status =~ /down\s+\d{14}/ - puts "yes".green - else - puts "no".red - try_fixing_it( - sudo_gitlab("bundle exec rake db:migrate RAILS_ENV=production") - ) - fix_and_rerun - end - end - - def check_satellites_exist - print "Projects have satellites? ... " - - unless Project.count > 0 - puts "can't check, you have no projects".magenta - return - end - puts "" - - Project.find_each(batch_size: 100) do |project| - print "#{project.name_with_namespace.yellow} ... " - - if project.satellite.exists? - puts "yes".green - elsif project.empty_repo? - puts "can't create, repository is empty".magenta - else - puts "no".red - try_fixing_it( - sudo_gitlab("bundle exec rake gitlab:satellites:create RAILS_ENV=production"), - "If necessary, remove the tmp/repo_satellites directory ...", - "... and rerun the above command" - ) - for_more_information( - "doc/raketasks/maintenance.md " - ) - fix_and_rerun - end - end - end - - def check_log_writable - print "Log directory writable? ... " - - log_path = Rails.root.join("log") - - if File.writable?(log_path) - puts "yes".green - else - puts "no".red - try_fixing_it( - "sudo chown -R gitlab #{log_path}", - "sudo chmod -R rwX #{log_path}" - ) - for_more_information( - see_installation_guide_section "GitLab" - ) - fix_and_rerun - end - end - - def check_tmp_writable - print "Tmp directory writable? ... " - - tmp_path = Rails.root.join("tmp") - - if File.writable?(tmp_path) - puts "yes".green - else - puts "no".red - try_fixing_it( - "sudo chown -R gitlab #{tmp_path}", - "sudo chmod -R rwX #{tmp_path}" - ) - for_more_information( - see_installation_guide_section "GitLab" - ) - fix_and_rerun - end - end - end - - - - namespace :env do - desc "GITLAB | Check the configuration of the environment" - task check: :environment do - warn_user_is_not_gitlab - start_checking "Environment" - - check_gitlab_git_config - check_python2_exists - check_python2_version - - finished_checking "Environment" - end - - - # Checks - ######################## - - def check_gitlab_git_config - gitlab_user = Gitlab.config.gitlab.user - print "Git configured for #{gitlab_user} user? ... " - - options = { - "user.name" => "GitLab", - "user.email" => Gitlab.config.gitlab.email_from - } - correct_options = options.map do |name, value| - run("git config --global --get #{name}").try(:squish) == value - end - - if correct_options.all? - puts "yes".green - else - puts "no".red - try_fixing_it( - sudo_gitlab("git config --global user.name \"#{options["user.name"]}\""), - sudo_gitlab("git config --global user.email \"#{options["user.email"]}\"") - ) - for_more_information( - see_installation_guide_section "GitLab" - ) - fix_and_rerun - end - end - - def check_python2_exists - print "Has python2? ... " - - # Python prints its version to STDERR - # so we can't just use run("python2 --version") - if run_and_match("which python2", /python2$/) - puts "yes".green - else - puts "no".red - try_fixing_it( - "Make sure you have Python 2.5+ installed", - "Link it to python2" - ) - for_more_information( - see_installation_guide_section "Packages / Dependencies" - ) - fix_and_rerun - end - end - - def check_python2_version - print "python2 is supported version? ... " - - # Python prints its version to STDERR - # so we can't just use run("python2 --version") - - unless run_and_match("which python2", /python2$/) - puts "can't check because of previous errors".magenta - return - end - - if `python2 --version 2>&1` =~ /2\.[567]\.\d/ - puts "yes".green - else - puts "no".red - try_fixing_it( - "Make sure you have Python 2.5+ installed", - "Link it to python2" - ) - for_more_information( - see_installation_guide_section "Packages / Dependencies" - ) - fix_and_rerun - end - end - end - - - - namespace :gitlab_shell do - desc "GITLAB | Check the configuration of Gitlab Shell" - task check: :environment do - warn_user_is_not_gitlab - start_checking "Gitlab Shell" - - check_gitlab_shell - check_repo_base_exists - check_repo_base_is_not_symlink - check_repo_base_user_and_group - check_repo_base_permissions - check_post_receive_hook_is_up_to_date - check_repos_post_receive_hooks_is_link - - finished_checking "Gitlab Shell" - end - - - # Checks - ######################## - - - def check_post_receive_hook_is_up_to_date - print "post-receive hook up-to-date? ... " - - hook_file = "post-receive" - gitlab_shell_hooks_path = Gitlab.config.gitlab_shell.hooks_path - gitlab_shell_hook_file = File.join(gitlab_shell_hooks_path, hook_file) - gitlab_shell_ssh_user = Gitlab.config.gitlab_shell.ssh_user - - unless File.exists?(gitlab_shell_hook_file) - puts "can't check because of previous errors".magenta - return - end - - puts "yes".green - end - - def check_repo_base_exists - print "Repo base directory exists? ... " - - repo_base_path = Gitlab.config.gitlab_shell.repos_path - - if File.exists?(repo_base_path) - puts "yes".green - else - puts "no".red - puts "#{repo_base_path} is missing".red - try_fixing_it( - "This should have been created when setting up Gitlab Shell.", - "Make sure it's set correctly in config/gitlab.yml", - "Make sure Gitlab Shell is installed correctly." - ) - for_more_information( - see_installation_guide_section "Gitlab Shell" - ) - fix_and_rerun - end - end - - def check_repo_base_is_not_symlink - print "Repo base directory is a symlink? ... " - - repo_base_path = Gitlab.config.gitlab_shell.repos_path - unless File.exists?(repo_base_path) - puts "can't check because of previous errors".magenta - return - end - - unless File.symlink?(repo_base_path) - puts "no".green - else - puts "yes".red - try_fixing_it( - "Make sure it's set to the real directory in config/gitlab.yml" - ) - fix_and_rerun - end - end - - def check_repo_base_permissions - print "Repo base access is drwxrws---? ... " - - repo_base_path = Gitlab.config.gitlab_shell.repos_path - unless File.exists?(repo_base_path) - puts "can't check because of previous errors".magenta - return - end - - if File.stat(repo_base_path).mode.to_s(8).ends_with?("2770") - puts "yes".green - else - puts "no".red - try_fixing_it( - "sudo chmod -R ug+rwX,o-rwx #{repo_base_path}", - "sudo chmod -R ug-s #{repo_base_path}", - "find #{repo_base_path} -type d -print0 | sudo xargs -0 chmod g+s" - ) - for_more_information( - see_installation_guide_section "Gitlab Shell" - ) - fix_and_rerun - end - end - - def check_repo_base_user_and_group - gitlab_shell_ssh_user = Gitlab.config.gitlab_shell.ssh_user - gitlab_shell_owner_group = Gitlab.config.gitlab_shell.owner_group - print "Repo base owned by #{gitlab_shell_ssh_user}:#{gitlab_shell_owner_group}? ... " - - repo_base_path = Gitlab.config.gitlab_shell.repos_path - unless File.exists?(repo_base_path) - puts "can't check because of previous errors".magenta - return - end - - if File.stat(repo_base_path).uid == uid_for(gitlab_shell_ssh_user) && - File.stat(repo_base_path).gid == gid_for(gitlab_shell_owner_group) - puts "yes".green - else - puts "no".red - try_fixing_it( - "sudo chown -R #{gitlab_shell_ssh_user}:#{gitlab_shell_owner_group} #{repo_base_path}" - ) - for_more_information( - see_installation_guide_section "Gitlab Shell" - ) - fix_and_rerun - end - end - - def check_repos_post_receive_hooks_is_link - print "post-receive hooks in repos are links: ... " - - hook_file = "post-receive" - gitlab_shell_hooks_path = Gitlab.config.gitlab_shell.hooks_path - gitlab_shell_hook_file = File.join(gitlab_shell_hooks_path, hook_file) - gitlab_shell_ssh_user = Gitlab.config.gitlab_shell.ssh_user - - unless File.exists?(gitlab_shell_hook_file) - puts "can't check because of previous errors".magenta - return - end - - unless Project.count > 0 - puts "can't check, you have no projects".magenta - return - end - puts "" - - Project.find_each(batch_size: 100) do |project| - print "#{project.name_with_namespace.yellow} ... " - - if project.empty_repo? - puts "repository is empty".magenta - else - project_hook_file = File.join(project.repository.path_to_repo, "hooks", hook_file) - - unless File.exists?(project_hook_file) - puts "missing".red - try_fixing_it( - "sudo -u #{gitlab_shell_ssh_user} ln -sf #{gitlab_shell_hook_file} #{project_hook_file}" - ) - for_more_information( - "#{gitlab_shell_user_home}/gitlab-shell/support/rewrite-hooks.sh" - ) - fix_and_rerun - next - end - - if File.lstat(project_hook_file).symlink? && - File.realpath(project_hook_file) == File.realpath(gitlab_shell_hook_file) - puts "ok".green - else - puts "not a link to Gitlab Shell's hook".red - try_fixing_it( - "sudo -u #{gitlab_shell_ssh_user} ln -sf #{gitlab_shell_hook_file} #{project_hook_file}" - ) - for_more_information( - "lib/support/rewrite-hooks.sh" - ) - fix_and_rerun - end - end - end - end - - - # Helper methods - ######################## - - def gitlab_shell_user_home - File.expand_path("~#{Gitlab.config.gitlab_shell.ssh_user}") - end - - def gitlab_shell_version - gitlab_shell_version_file = "#{gitlab_shell_user_home}/gitlab-shell/VERSION" - if File.readable?(gitlab_shell_version_file) - File.read(gitlab_shell_version_file) - end - end - - def has_gitlab_shell3? - gitlab_shell_version.try(:start_with?, "v3.") - end - end - - - - namespace :sidekiq do - desc "GITLAB | Check the configuration of Sidekiq" - task check: :environment do - warn_user_is_not_gitlab - start_checking "Sidekiq" - - check_sidekiq_running - - finished_checking "Sidekiq" - end - - - # Checks - ######################## - - def check_sidekiq_running - print "Running? ... " - - if run_and_match("ps aux | grep -i sidekiq", /sidekiq \d\.\d\.\d.+$/) - puts "yes".green - else - puts "no".red - try_fixing_it( - sudo_gitlab("bundle exec rake sidekiq:start RAILS_ENV=production") - ) - for_more_information( - see_installation_guide_section("Install Init Script"), - "see log/sidekiq.log for possible errors" - ) - fix_and_rerun - end - end - end - - - # Helper methods - ########################## - - def fix_and_rerun - puts " Please #{"fix the error above"} and rerun the checks.".red - end - - def for_more_information(*sources) - sources = sources.shift if sources.first.is_a?(Array) - - puts " For more information see:".blue - sources.each do |source| - puts " #{source}" - end - end - - def finished_checking(component) - puts "" - puts "Checking #{component.yellow} ... #{"Finished".green}" - puts "" - end - - def see_database_guide - "doc/install/databases.md" - end - - def see_installation_guide_section(section) - "doc/install/installation.md in section \"#{section}\"" - end - - def sudo_gitlab(command) - gitlab_user = Gitlab.config.gitlab.user - "sudo -u #{gitlab_user} -H #{command}" - end - - def start_checking(component) - puts "Checking #{component.yellow} ..." - puts "" - end - - def try_fixing_it(*steps) - steps = steps.shift if steps.first.is_a?(Array) - - puts " Try fixing it:".blue - steps.each do |step| - puts " #{step}" - end - end - - def check_gitlab_shell - print "GitLab Shell version? ... " - if gitlab_shell_version.strip == '1.2.0' - puts 'OK (1.2.0)'.green - else - puts 'FAIL. Please update gitlab-shell to v1.2.0'.red - end - end -end diff --git a/lib/tasks/gitlab/cleanup.rake b/lib/tasks/gitlab/cleanup.rake deleted file mode 100644 index d8ee56e5..00000000 --- a/lib/tasks/gitlab/cleanup.rake +++ /dev/null @@ -1,88 +0,0 @@ -namespace :gitlab do - namespace :cleanup do - desc "GITLAB | Cleanup | Clean namespaces" - task :dirs => :environment do - warn_user_is_not_gitlab - remove_flag = ENV['REMOVE'] - - - namespaces = Namespace.pluck(:path) - git_base_path = Gitlab.config.gitlab_shell.repos_path - all_dirs = Dir.glob(git_base_path + '/*') - - puts git_base_path.yellow - puts "Looking for directories to remove... " - - all_dirs.reject! do |dir| - # skip if git repo - dir =~ /.git$/ - end - - all_dirs.reject! do |dir| - dir_name = File.basename dir - - # skip if namespace present - namespaces.include?(dir_name) - end - - all_dirs.each do |dir_path| - - if remove_flag - if FileUtils.rm_rf dir_path - puts "Removed...#{dir_path}".red - else - puts "Cannot remove #{dir_path}".red - end - else - puts "Can be removed: #{dir_path}".red - end - end - - unless remove_flag - puts "To cleanup this directories run this command with REMOVE=true".yellow - end - end - - desc "GITLAB | Cleanup | Clean respositories" - task :repos => :environment do - warn_user_is_not_gitlab - remove_flag = ENV['REMOVE'] - - git_base_path = Gitlab.config.gitlab_shell.repos_path - all_dirs = Dir.glob(git_base_path + '/*') - - global_projects = Project.where(namespace_id: nil).pluck(:path) - - puts git_base_path.yellow - puts "Looking for global repos to remove... " - - # skip non git repo - all_dirs.select! do |dir| - dir =~ /.git$/ - end - - # skip existing repos - all_dirs.reject! do |dir| - repo_name = File.basename dir - path = repo_name.gsub(/\.git$/, "") - global_projects.include?(path) - end - - all_dirs.each do |dir_path| - if remove_flag - if FileUtils.rm_rf dir_path - puts "Removed...#{dir_path}".red - else - puts "Cannot remove #{dir_path}".red - end - else - puts "Can be removed: #{dir_path}".red - end - end - - unless remove_flag - puts "To cleanup this directories run this command with REMOVE=true".yellow - end - end - end -end diff --git a/lib/tasks/gitlab/enable_automerge.rake b/lib/tasks/gitlab/enable_automerge.rake index 6822e5cf..13b4bab6 100644 --- a/lib/tasks/gitlab/enable_automerge.rake +++ b/lib/tasks/gitlab/enable_automerge.rake @@ -1,42 +1,17 @@ namespace :gitlab do - namespace :satellites do - desc "GITLAB | Create satellite repos" - task create: :environment do - create_satellites - end - end + namespace :app do + desc "GITLAB | Enable auto merge" + task :enable_automerge => :environment do + Gitlab::Gitolite.new.enable_automerge - def create_satellites - warn_user_is_not_gitlab - - print "Creating satellites for ..." - unless Project.count > 0 - puts "skipping, because you have no projects".magenta - return - end - puts "" - - Project.find_each(batch_size: 100) do |project| - print "#{project.name_with_namespace.yellow} ... " - - unless project.repo_exists? - puts "skipping, because the repo is empty".magenta - next - end - - if project.satellite.exists? - puts "exists already".green - else - puts "" - project.satellite.create - - print "... " - if $?.success? - puts "created".green - else - puts "error".red + Project.find_each do |project| + if project.repo_exists? && !project.satellite.exists? + puts "Creating satellite for #{project.name}...".green + project.satellite.create end end + + puts "Done!".green end end end diff --git a/lib/tasks/gitlab/enable_namespaces.rake b/lib/tasks/gitlab/enable_namespaces.rake deleted file mode 100644 index 6b7b75fd..00000000 --- a/lib/tasks/gitlab/enable_namespaces.rake +++ /dev/null @@ -1,111 +0,0 @@ -namespace :gitlab do - desc "GITLAB | Enable usernames and namespaces for user projects" - task enable_namespaces: :environment do - warn_user_is_not_gitlab - - migrate_user_namespaces - migrate_groups - migrate_projects - end - - def migrate_user_namespaces - puts "\nGenerate usernames for users without one: ".blue - User.find_each(batch_size: 500) do |user| - if user.namespace - print '-'.cyan - next - end - - username = if user.username.present? - # if user already has username filled - user.username - else - build_username(user) - end - - begin - User.transaction do - user.update_attributes!(username: username) - print '.'.green - end - rescue - print 'F'.red - end - end - puts "\nDone" - end - - def build_username(user) - username = nil - - # generate username - username = user.email.match(/^[^@]*/)[0] - username.gsub!("+", ".") - - # return username if no mathes - return username unless User.find_by_username(username) - - # look for same username - (1..10).each do |i| - suffixed_username = "#{username}#{i}" - - return suffixed_username unless User.find_by_username(suffixed_username) - end - end - - def migrate_groups - puts "\nCreate directories for groups: ".blue - - Group.find_each(batch_size: 500) do |group| - begin - if group.dir_exists? - print '-'.cyan - else - if group.ensure_dir_exist - print '.'.green - else - print 'F'.red - end - end - rescue - print 'F'.red - end - end - puts "\nDone" - end - - def migrate_projects - git_path = Gitlab.config.gitlab_shell.repos_path - puts "\nMove projects in groups into respective directories ... ".blue - Project.where('namespace_id IS NOT NULL').find_each(batch_size: 500) do |project| - next unless project.group - - group = project.group - - print "#{project.name_with_namespace.yellow} ... " - - new_path = File.join(git_path, project.path_with_namespace + '.git') - - if File.exists?(new_path) - puts "already at #{new_path}".green - next - end - - old_path = File.join(git_path, project.path + '.git') - - unless File.exists?(old_path) - puts "couldn't find it at #{old_path}".red - next - end - - begin - project.transfer(group.path) - puts "moved to #{new_path}".green - rescue - puts "failed moving to #{new_path}".red - end - end - - puts "\nDone" - end -end diff --git a/lib/tasks/gitlab/generate_docs.rake b/lib/tasks/gitlab/generate_docs.rake deleted file mode 100644 index 58795fac..00000000 --- a/lib/tasks/gitlab/generate_docs.rake +++ /dev/null @@ -1,7 +0,0 @@ -namespace :gitlab do - desc "GITLAB | Generate sdocs for project" - task generate_docs: :environment do - system("bundle exec sdoc -o doc/code app lib") - end -end - diff --git a/lib/tasks/gitlab/gitolite_rebuild.rake b/lib/tasks/gitlab/gitolite_rebuild.rake new file mode 100644 index 00000000..fce10eb5 --- /dev/null +++ b/lib/tasks/gitlab/gitolite_rebuild.rake @@ -0,0 +1,24 @@ +namespace :gitlab do + namespace :gitolite do + desc "GITLAB | Rebuild each project at gitolite config" + task :update_repos => :environment do + puts "Starting Projects" + Project.find_each(:batch_size => 100) do |project| + puts "\n=== #{project.name}" + project.update_repository + puts + end + puts "Done with projects" + end + + desc "GITLAB | Rebuild each key at gitolite config" + task :update_keys => :environment do + puts "Starting Key" + Key.find_each(:batch_size => 100) do |key| + Gitlab::Gitolite.new.set_key(key.identifier, key.key, key.projects) + print '.' + end + puts "Done with keys" + end + end +end diff --git a/lib/tasks/gitlab/import.rake b/lib/tasks/gitlab/import.rake index bddbd7ef..09f0dc9e 100644 --- a/lib/tasks/gitlab/import.rake +++ b/lib/tasks/gitlab/import.rake @@ -12,25 +12,18 @@ namespace :gitlab do desc "GITLAB | Import bare repositories from git_host -> base_path into GitLab project instance" task :repos => :environment do - git_base_path = Gitlab.config.gitlab_shell.repos_path + git_base_path = Gitlab.config.git_base_path repos_to_import = Dir.glob(git_base_path + '/*') - namespaces = Namespace.pluck(:path) - repos_to_import.each do |repo_path| repo_name = File.basename repo_path - # Skip if group or user - next if namespaces.include?(repo_name) - - # skip if not git repo - next unless repo_name =~ /.git$/ - + # skip gitolite admin next if repo_name == 'gitolite-admin.git' path = repo_name.sub(/\.git$/, '') - project = Project.find_with_namespace(path) + project = Project.find_by_path(path) puts "Processing #{repo_name}".yellow @@ -41,9 +34,11 @@ namespace :gitlab do project_params = { :name => path, + :code => path, + :path => path, } - project = Projects::CreateContext.new(user, project_params).execute + project = Project.create_by_user(project_params, user) if project.valid? puts " * Created #{project.name} (#{repo_name})".green diff --git a/lib/tasks/gitlab/info.rake b/lib/tasks/gitlab/info.rake deleted file mode 100644 index 091de6ee..00000000 --- a/lib/tasks/gitlab/info.rake +++ /dev/null @@ -1,71 +0,0 @@ -namespace :gitlab do - namespace :env do - desc "GITLAB | Show information about GitLab and its environment" - task info: :environment do - - # check if there is an RVM environment - rvm_version = run_and_match("rvm --version", /[\d\.]+/).try(:to_s) - # check Ruby version - ruby_version = run_and_match("ruby --version", /[\d\.p]+/).try(:to_s) - # check Gem version - gem_version = run("gem --version") - # check Bundler version - bunder_version = run_and_match("bundle --version", /[\d\.]+/).try(:to_s) - # check Bundler version - rake_version = run_and_match("rake --version", /[\d\.]+/).try(:to_s) - - puts "" - puts "System information".yellow - puts "System:\t\t#{os_name || "unknown".red}" - puts "Current User:\t#{`whoami`}" - puts "Using RVM:\t#{rvm_version.present? ? "yes".green : "no"}" - puts "RVM Version:\t#{rvm_version}" if rvm_version.present? - puts "Ruby Version:\t#{ruby_version || "unknown".red}" - puts "Gem Version:\t#{gem_version || "unknown".red}" - puts "Bundler Version:#{bunder_version || "unknown".red}" - puts "Rake Version:\t#{rake_version || "unknown".red}" - - - # check database adapter - database_adapter = ActiveRecord::Base.connection.adapter_name.downcase - - project = Project.new(path: "some-project") - project.path = "some-project" - # construct clone URLs - http_clone_url = project.http_url_to_repo - ssh_clone_url = project.ssh_url_to_repo - - omniauth_providers = Gitlab.config.omniauth.providers - omniauth_providers.map! { |provider| provider['name'] } - - puts "" - puts "GitLab information".yellow - puts "Version:\t#{Gitlab::VERSION}" - puts "Revision:\t#{Gitlab::REVISION}" - puts "Directory:\t#{Rails.root}" - puts "DB Adapter:\t#{database_adapter}" - puts "URL:\t\t#{Gitlab.config.gitlab.url}" - puts "HTTP Clone URL:\t#{http_clone_url}" - puts "SSH Clone URL:\t#{ssh_clone_url}" - puts "Using LDAP:\t#{Gitlab.config.ldap.enabled ? "yes".green : "no"}" - puts "Using Omniauth:\t#{Gitlab.config.omniauth.enabled ? "yes".green : "no"}" - puts "Omniauth Providers: #{omniauth_providers.map(&:magenta).join(', ')}" if Gitlab.config.omniauth.enabled - - - - # check Gitolite version - gitlab_shell_version_file = "#{Gitlab.config.gitlab_shell.repos_path}/../gitlab-shell/VERSION" - if File.readable?(gitlab_shell_version_file) - gitlab_shell_version = File.read(gitlab_shell_version_file) - end - - puts "" - puts "GitLab Shell".yellow - puts "Version:\t#{gitlab_shell_version || "unknown".red}" - puts "Repositories:\t#{Gitlab.config.gitlab_shell.repos_path}" - puts "Hooks:\t\t#{Gitlab.config.gitlab_shell.hooks_path}" - puts "Git:\t\t#{Gitlab.config.git.bin_path}" - - end - end -end diff --git a/lib/tasks/gitlab/migrate_wiki.rake b/lib/tasks/gitlab/migrate_wiki.rake deleted file mode 100644 index 5d9881e4..00000000 --- a/lib/tasks/gitlab/migrate_wiki.rake +++ /dev/null @@ -1,42 +0,0 @@ -namespace :gitlab do - namespace :wiki do - - # This task will migrate all of the existing Wiki - # content stored in your database into the new - # Gollum Wiki system. A new repository named - # namespace/project.wiki.git will be created for - # each project that currently has Wiki pages in - # the database. - # - # Notes: - # * The existing Wiki content will remain in your - # database in-tact. - desc "GITLAB | Migrate Wiki content from database to Gollum repositories." - task :migrate => :environment do - wiki_migrator = WikiToGollumMigrator.new - wiki_migrator.migrate! - end - - # This task will destroy all of the Wiki repos - # that the Wiki migration task created. Run this - # to clean up your environment if you experienced - # problems during the original migration. After - # executing this task, you can attempt the original - # migration again. - # - # Notes: - # * This will not affect Wikis that have been created - # as Gollum Wikis only. It will only remove the wikis - # for the repositories that have old Wiki data in the - # dataabase. - # * If you have any repositories already named - # namespace/project.wiki that you do not wish - # to be removed you may want to perform a manual - # cleanup instead. - desc "GITLAB | Remove the Wiki repositories created by the `gitlab:wiki:migrate` task." - task :rollback => :environment do - wiki_migrator = WikiToGollumMigrator.new - wiki_migrator.rollback! - end - end -end diff --git a/lib/tasks/gitlab/setup.rake b/lib/tasks/gitlab/setup.rake index 5b74daf9..08f35c7e 100644 --- a/lib/tasks/gitlab/setup.rake +++ b/lib/tasks/gitlab/setup.rake @@ -1,23 +1,10 @@ namespace :gitlab do - desc "GITLAB | Setup production application" - task :setup => :environment do - setup_db - end - - def setup_db - warn_user_is_not_gitlab - - unless ENV['force'] == 'yes' - puts "This will create the necessary database tables and seed the database." - puts "You will lose any previous data stored in the database." - ask_to_continue - puts "" - end - - Rake::Task["db:setup"].invoke - Rake::Task["db:seed_fu"].invoke - rescue Gitlab::TaskAbortedByUserError - puts "Quitting...".red - exit 1 + namespace :app do + desc "GITLAB | Setup production application" + task :setup => [ + 'db:setup', + 'db:seed_fu', + 'gitlab:app:enable_automerge' + ] end end diff --git a/lib/tasks/gitlab/shell.rake b/lib/tasks/gitlab/shell.rake deleted file mode 100644 index ec5451dd..00000000 --- a/lib/tasks/gitlab/shell.rake +++ /dev/null @@ -1,49 +0,0 @@ -namespace :gitlab do - namespace :shell do - desc "GITLAB | Setup gitlab-shell" - task setup: :environment do - setup - end - - desc "GITLAB | Build missing projects" - task build_missing_projects: :environment do - Project.find_each(batch_size: 1000) do |project| - path_to_repo = File.join(Gitlab.config.gitlab_shell.repos_path, "#{project.path_with_namespace}.git") - if File.exists?(path_to_repo) - print '-' - else - if Gitlab::Shell.new.add_repository(project.path_with_namespace) - print '.' - else - print 'F' - end - end - end - end - end - - def setup - warn_user_is_not_gitlab - - gitlab_shell_authorized_keys = File.join(File.expand_path("~#{Gitlab.config.gitlab_shell.ssh_user}"),'.ssh/authorized_keys') - puts "This will rebuild an authorized_keys file." - puts "You will lose any data stored in #{gitlab_shell_authorized_keys}." - ask_to_continue - puts "" - - system("echo '# Managed by gitlab-shell' > #{gitlab_shell_authorized_keys}") - - Key.find_each(batch_size: 1000) do |key| - if Gitlab::Shell.new.add_key(key.shell_id, key.key) - print '.' - else - print 'F' - end - end - - rescue Gitlab::TaskAbortedByUserError - puts "Quitting...".red - exit 1 - end -end - diff --git a/lib/tasks/gitlab/status.rake b/lib/tasks/gitlab/status.rake new file mode 100644 index 00000000..cbc77abb --- /dev/null +++ b/lib/tasks/gitlab/status.rake @@ -0,0 +1,113 @@ +namespace :gitlab do + namespace :app do + desc "GITLAB | Check GitLab installation status" + task :status => :environment do + puts "\nStarting diagnostics".yellow + git_base_path = Gitlab.config.git_base_path + + print "config/database.yml............" + if File.exists?(Rails.root.join "config", "database.yml") + puts "exists".green + else + puts "missing".red + return + end + + print "config/gitlab.yml............" + if File.exists?(Rails.root.join "config", "gitlab.yml") + puts "exists".green + else + puts "missing".red + return + end + + print "#{git_base_path}............" + if File.exists?(git_base_path) + puts "exists".green + else + puts "missing".red + return + end + + print "#{git_base_path} is writable?............" + if File.stat(git_base_path).writable? + puts "YES".green + else + puts "NO".red + return + end + + FileUtils.rm_rf("/tmp/gitolite_gitlab_test") + begin + `git clone -q #{Gitlab.config.gitolite_admin_uri} /tmp/gitolite_gitlab_test` + raise unless $?.success? + print "Can clone gitolite-admin?............" + puts "YES".green + rescue + print "Can clone gitolite-admin?............" + puts "NO".red + return + end + + begin + Dir.chdir("/tmp/gitolite_gitlab_test") do + `touch blah && git add blah && git commit -qm blah -- blah` + raise unless $?.success? + end + print "Can git commit?............" + puts "YES".green + rescue + print "Can git commit?............" + puts "NO".red + return + ensure + FileUtils.rm_rf("/tmp/gitolite_gitlab_test") + end + + print "UMASK for .gitolite.rc is 0007? ............" + if open(File.absolute_path("#{git_base_path}/../.gitolite.rc")).grep(/UMASK([ \t]*)=([ \t>]*)0007/).any? + puts "YES".green + else + puts "NO".red + return + end + + gitolite_hooks_path = File.join(Gitlab.config.git_hooks_path, "common") + gitlab_hook_files = ['post-receive'] + gitlab_hook_files.each do |file_name| + dest = File.join(gitolite_hooks_path, file_name) + print "#{dest} exists? ............" + if File.exists?(dest) + puts "YES".green + else + puts "NO".red + return + end + end + + if Project.count > 0 + puts "\nValidating projects repositories:".yellow + Project.find_each(:batch_size => 100) do |project| + print "* #{project.name}....." + hook_file = File.join(project.path_to_repo, 'hooks', 'post-receive') + + unless File.exists?(hook_file) + puts "post-receive file missing".red + next + end + + original_content = File.read(Rails.root.join('lib', 'hooks', 'post-receive')) + new_content = File.read(hook_file) + + if original_content == new_content + puts "post-receive file ok".green + else + puts "post-receive file content does not match".red + end + end + end + + puts "\nFinished".blue + end + end +end diff --git a/lib/tasks/gitlab/task_helpers.rake b/lib/tasks/gitlab/task_helpers.rake deleted file mode 100644 index cfab3670..00000000 --- a/lib/tasks/gitlab/task_helpers.rake +++ /dev/null @@ -1,97 +0,0 @@ -module Gitlab - class TaskAbortedByUserError < StandardError; end -end - -namespace :gitlab do - - # Ask if the user wants to continue - # - # Returns "yes" the user chose to continue - # Raises Gitlab::TaskAbortedByUserError if the user chose *not* to continue - def ask_to_continue - answer = prompt("Do you want to continue (yes/no)? ".blue, %w{yes no}) - raise Gitlab::TaskAbortedByUserError unless answer == "yes" - end - - # Check which OS is running - # - # It will primarily use lsb_relase to determine the OS. - # It has fallbacks to Debian, SuSE and OS X. - def os_name - os_name = run("lsb_release -irs") - os_name ||= if File.readable?('/etc/system-release') - File.read('/etc/system-release') - end - os_name ||= if File.readable?('/etc/debian_version') - debian_version = File.read('/etc/debian_version') - "Debian #{debian_version}" - end - os_name ||= if File.readable?('/etc/SuSE-release') - File.read('/etc/SuSE-release') - end - os_name ||= if os_x_version = run("sw_vers -productVersion") - "Mac OS X #{os_x_version}" - end - os_name.try(:squish!) - end - - # Prompt the user to input something - # - # message - the message to display before input - # choices - array of strings of acceptable answers or nil for any answer - # - # Returns the user's answer - def prompt(message, choices = nil) - begin - print(message) - answer = STDIN.gets.chomp - end while choices.present? && !choices.include?(answer) - answer - end - - # Runs the given command and matches the output against the given pattern - # - # Returns nil if nothing matched - # Retunrs the MatchData if the pattern matched - # - # see also #run - # see also String#match - def run_and_match(command, regexp) - run(command).try(:match, regexp) - end - - # Runs the given command - # - # Returns nil if the command was not found - # Returns the output of the command otherwise - # - # see also #run_and_match - def run(command) - unless `#{command} 2>/dev/null`.blank? - `#{command}` - end - end - - def uid_for(user_name) - run("id -u #{user_name}").chomp.to_i - end - - def gid_for(group_name) - Etc.getgrnam(group_name).gid - end - - def warn_user_is_not_gitlab - unless @warned_user_not_gitlab - gitlab_user = Gitlab.config.gitlab.user - current_user = run("whoami").chomp - unless current_user == gitlab_user - puts "#{Colored.color(:black)+Colored.color(:on_yellow)} Warning #{Colored.extra(:clear)}" - puts " You are running as user #{current_user.magenta}, we hope you know what you are doing." - puts " Things may work\/fail for the wrong reasons." - puts " For correct results you should run this as user #{gitlab_user.magenta}." - puts "" - end - @warned_user_not_gitlab = true - end - end -end diff --git a/lib/tasks/resque.rake b/lib/tasks/resque.rake new file mode 100644 index 00000000..9b30bb0a --- /dev/null +++ b/lib/tasks/resque.rake @@ -0,0 +1 @@ +require 'resque/tasks' diff --git a/lib/tasks/sidekiq.rake b/lib/tasks/sidekiq.rake deleted file mode 100644 index d0e9dfe4..00000000 --- a/lib/tasks/sidekiq.rake +++ /dev/null @@ -1,20 +0,0 @@ -namespace :sidekiq do - desc "GITLAB | Stop sidekiq" - task :stop do - system "bundle exec sidekiqctl stop #{pidfile}" - end - - desc "GITLAB | Start sidekiq" - task :start do - 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 - 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 -end diff --git a/lib/tasks/travis.rake b/lib/tasks/travis.rake index 6b434830..13e32135 100644 --- a/lib/tasks/travis.rake +++ b/lib/tasks/travis.rake @@ -1,5 +1,7 @@ -desc "Travis run tests" -task :travis => [ - :spinach, - :spec -] +task :travis do + ["spinach", "rspec spec"].each do |cmd| + puts "Starting to run #{cmd}..." + system("export DISPLAY=:99.0 && bundle exec #{cmd}") + raise "#{cmd} failed!" unless $?.exitstatus == 0 + end +end diff --git a/lib/wiki_to_gollum_migrator.rb b/lib/wiki_to_gollum_migrator.rb deleted file mode 100644 index ed6a1700..00000000 --- a/lib/wiki_to_gollum_migrator.rb +++ /dev/null @@ -1,124 +0,0 @@ -class WikiToGollumMigrator - - attr_reader :projects - - def initialize - @projects = [] - - Project.find_in_batches(batch_size: 50) do |batch| - batch.each { |p| @projects << p if p.wikis.any? } - end - end - - def migrate! - projects.each do |project| - log "\nMigrating Wiki for '#{project.path_with_namespace}'" - wiki = create_gollum_repo(project) - create_pages project, wiki - log "Project '#{project.path_with_namespace}' migrated. " + "[OK]".green - end - end - - def rollback! - log "\nBeginning Wiki Migration Rollback..." - projects.each do |project| - destroy_gollum_repo project - end - log "\nWiki Rollback Complete." - end - - private - - def create_gollum_repo(project) - GollumWiki.new(project, nil).wiki - end - - def destroy_gollum_repo(project) - log " Removing Wiki repo for project: #{project.path_with_namespace}" - path = GollumWiki.new(project, nil).path_with_namespace - if Gitlab::Shell.new.remove_repository(path) - log " Wiki destroyed successfully. " + "[OK}".green - else - log " Problem destroying wiki. Please remove it manually. " + "[FAILED]".red - end - end - - def create_pages(project, wiki) - pages = project.wikis.group(:slug).all - - pages.each do |page| - create_page_and_revisions(project, page) - end - end - - def create_page_and_revisions(project, page) - # Grab all revisions of the page - revisions = project.wikis.where(slug: page.slug).ordered.all - - # Remove the first revision created from the array - # and use it to create the Gollum page. Each successive revision - # will then be applied to the new Gollum page as an update. - first_rev = revisions.pop - - wiki = GollumWiki.new(project, page.user) - wiki_page = WikiPage.new(wiki) - - attributes = extract_attributes_from_page(first_rev, project) - - log " Creating page '#{first_rev.title}'..." - if wiki_page.create(attributes) - log " Created page '#{wiki_page.title}' " + "[OK]".green - - # Reverse the revisions to create them in the correct - # chronological order. - create_revisions(project, wiki_page, revisions.reverse) - else - log " Failed to create page '#{wiki_page.title}' " + "[FAILED]".red - end - end - - def create_revisions(project, page, revisions) - log " Creating revisions..." - revisions.each do |revision| - # Reinitialize a new GollumWiki instance for each page - # and revision created so the correct User is shown in - # the commit message. - wiki = GollumWiki.new(project, revision.user) - wiki_page = wiki.find_page(page.slug) - - attributes = extract_attributes_from_page(revision, project) - - content = attributes[:content] - - if wiki_page.update(content) - log " Created revision " + "[OK]".green - else - log " Failed to create revision " + "[FAILED]".red - end - end - end - - def extract_attributes_from_page(page, project) - attributes = page.attributes - .with_indifferent_access - .slice(:title, :content) - - slug = page.slug - - # Change 'index' pages to 'home' pages to match Gollum standards - if slug.downcase == "index" - attributes[:title] = "home" unless home_already_exists?(project) - end - - attributes - end - - def home_already_exists?(project) - project.wikis.where(slug: 'home').any? || project.wikis.where(slug: 'Home').any? - end - - def log(message) - puts message - end - -end diff --git a/public/404.html b/public/404.html index 867f193a..3e56e52c 100644 --- a/public/404.html +++ b/public/404.html @@ -7,8 +7,9 @@

    404

    -

    The page you were looking for doesn't exist.

    -
    -

    You may have mistyped the address or the page may have moved.

    +
    +

    The page you were looking for doesn't exist.

    +

    You may have mistyped the address or the page may have moved.

    +
    diff --git a/public/500.html b/public/500.html index 5b78e3e3..3be1cc25 100644 --- a/public/500.html +++ b/public/500.html @@ -4,10 +4,13 @@ We're sorry, but something went wrong (500) + +

    500

    -

    We're sorry, but something went wrong.

    -
    -

    We've been notified about this issue and we'll take a look at it shortly.

    +
    +

    We're sorry, but something went wrong.

    +

    We've been notified about this issue and we'll take a look at it shortly.

    +
    diff --git a/public/deploy.html b/public/deploy.html deleted file mode 100644 index d9c4bb5c..00000000 --- a/public/deploy.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - Deploy in progress. Please try again in few minutes - - - -

    Deploy in progress

    -

    Please try again in few minutes or contact your administrator.

    - - diff --git a/public/githost_error.html b/public/githost_error.html new file mode 100644 index 00000000..b5258ce1 --- /dev/null +++ b/public/githost_error.html @@ -0,0 +1,36 @@ + + + + We're sorry, but we can't get access to your gitolite + + + + + +
    +

    Gitolite Error

    +

    Application can't get access to your gitolite system.

    +
    +

    1. Check 'config/gitlab.yml' for correct settings.

    +

    2. Make sure web server user has access to gitolite. Setup tutorial

    +

    3. Try:

    + +
    +sudo chmod -R 770 /home/git/repositories/
    +sudo chown -R git:git /home/git/repositories/
    +        
    +
    +
    + + diff --git a/public/gitlab_logo.png b/public/gitlab_logo.png deleted file mode 100644 index e3cda597..00000000 Binary files a/public/gitlab_logo.png and /dev/null differ diff --git a/public/static.css b/public/static.css index aa834553..6090d7b2 100644 --- a/public/static.css +++ b/public/static.css @@ -1,31 +1,57 @@ -body { - color: #666; - text-align: center; - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - sans-serif; - margin:0; - width: 800px; - margin: auto; - font-size: 14px; -} -h1 { - font-size: 56px; - line-height: 100px; - font-weight: normal; - color: #456; -} +body { color: #666; text-align: center; font-family: arial, sans-serif; margin:0; padding:0; } +h1 { font-size: 48px; color: #444; line-height: 1.5em; } h2 { font-size: 24px; color: #666; line-height: 1.5em; } -h3 { - color: #456; - font-size: 20px; - font-weight: normal; +.alert-message { + position: relative; + padding: 7px 15px; + margin-bottom: 18px; + color: #404040; + background-color: #eedc94; + background-repeat: repeat-x; + background-image: -khtml-gradient(linear, left top, left bottom, from(#fceec1), to(#eedc94)); + background-image: -moz-linear-gradient(top, #fceec1, #eedc94); + background-image: -ms-linear-gradient(top, #fceec1, #eedc94); + background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #fceec1), color-stop(100%, #eedc94)); + background-image: -webkit-linear-gradient(top, #fceec1, #eedc94); + background-image: -o-linear-gradient(top, #fceec1, #eedc94); + background-image: linear-gradient(top, #fceec1, #eedc94); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFCEEC1', endColorstr='#FFEEDC94', GradientType=0); + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + border-color: #eedc94 #eedc94 #e4c652; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) fadein(rgba(0, 0, 0, 0.1), 15%); + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); + border-width: 1px; + border-style: solid; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25); + -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25); +} +.alert-message .close { + margin-top: 1px; + *margin-top: 0; +} +.alert-message a { + font-weight: bold; + color: #404040; +} +.alert-message.danger p a, .alert-message.error p a, .alert-message.success p a, .alert-message.info p a { + color: #404040; +} +.alert-message h5 { + line-height: 18px; +} +.alert-message p { + margin-bottom: 0; +} +.alert-message div { + margin-top: 5px; + margin-bottom: 2px; line-height: 28px; } -hr { - margin: 18px 0; - border: 0; - border-top: 1px solid #EEE; - border-bottom: 1px solid white; +.alert-message.block-message.error { + background: #FDDFDE; + border-color: #FBC7C6; } + diff --git a/resque.sh b/resque.sh new file mode 100755 index 00000000..0ebf319e --- /dev/null +++ b/resque.sh @@ -0,0 +1,2 @@ +mkdir -p tmp/pids +nohup bundle exec rake environment resque:work QUEUE=post_receive,mailer,system_hook RAILS_ENV=production PIDFILE=tmp/pids/resque_worker.pid > ./log/resque.log & diff --git a/resque_dev.sh b/resque_dev.sh new file mode 100755 index 00000000..b250caa9 --- /dev/null +++ b/resque_dev.sh @@ -0,0 +1,2 @@ +mkdir -p tmp/pids +nohup bundle exec rake environment resque:work QUEUE=post_receive,mailer,system_hook VVERBOSE=1 RAILS_ENV=development PIDFILE=tmp/pids/resque_worker.pid > ./log/resque.log & diff --git a/script/check b/script/check deleted file mode 100755 index d2eb4a2f..00000000 --- a/script/check +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -sudo -u gitlab -H bundle exec rake gitlab:check RAILS_ENV=production diff --git a/spec/contexts/projects_create_context_spec.rb b/spec/contexts/projects_create_context_spec.rb deleted file mode 100644 index dd10dd3e..00000000 --- a/spec/contexts/projects_create_context_spec.rb +++ /dev/null @@ -1,38 +0,0 @@ -require 'spec_helper' - -describe Projects::CreateContext do - describe :create_by_user do - before do - @user = create :user - @opts = { - name: "GitLab" - } - end - - context 'user namespace' do - before do - @project = create_project(@user, @opts) - end - - it { @project.should be_valid } - it { @project.owner.should == @user } - it { @project.namespace.should == @user.namespace } - end - - context 'group namespace' do - before do - @group = create :group, owner: @user - @opts.merge!(namespace_id: @group.id) - @project = create_project(@user, @opts) - end - - it { @project.should be_valid } - it { @project.owner.should == @user } - it { @project.namespace.should == @group } - end - end - - def create_project(user, opts) - Projects::CreateContext.new(user, opts).execute - end -end diff --git a/spec/controllers/commit_controller_spec.rb b/spec/controllers/commit_controller_spec.rb deleted file mode 100644 index 5fffbf0e..00000000 --- a/spec/controllers/commit_controller_spec.rb +++ /dev/null @@ -1,74 +0,0 @@ -require 'spec_helper' - -describe CommitController do - let(:project) { create(:project_with_code) } - let(:user) { create(:user) } - let(:commit) { project.repository.last_commit_for("master") } - - before do - sign_in(user) - - project.team << [user, :master] - end - - describe "#show" do - shared_examples "export as" do |format| - it "should generally work" do - get :show, project_id: project.code, id: commit.id, format: format - - expect(response).to be_success - end - - it "should generate it" do - Commit.any_instance.should_receive(:"to_#{format}") - - get :show, project_id: project.code, id: commit.id, format: format - end - - it "should render it" do - get :show, project_id: project.code, id: commit.id, format: format - - expect(response.body).to eq(commit.send(:"to_#{format}")) - end - - it "should not escape Html" do - Commit.any_instance.stub(:"to_#{format}").and_return('HTML entities &<>" ') - - get :show, project_id: project.code, id: commit.id, format: format - - expect(response.body).to_not include('&') - expect(response.body).to_not include('>') - expect(response.body).to_not include('<') - expect(response.body).to_not include('"') - end - end - - describe "as diff" do - include_examples "export as", :diff - let(:format) { :diff } - - it "should really only be a git diff" do - get :show, project_id: project.code, id: commit.id, format: format - - expect(response.body).to start_with("diff --git") - end - end - - describe "as patch" do - include_examples "export as", :patch - let(:format) { :patch } - - it "should really be a git email patch" do - get :show, project_id: project.code, id: commit.id, format: format - - expect(response.body).to start_with("From #{commit.id}") - end - - it "should contain a git diff" do - get :show, project_id: project.code, id: commit.id, format: format - - expect(response.body).to match(/^diff --git/) - end - end - end -end diff --git a/spec/controllers/commits_controller_spec.rb b/spec/controllers/commits_controller_spec.rb index ce402917..bf335634 100644 --- a/spec/controllers/commits_controller_spec.rb +++ b/spec/controllers/commits_controller_spec.rb @@ -1,19 +1,19 @@ require 'spec_helper' describe CommitsController do - let(:project) { create(:project_with_code) } + let(:project) { create(:project) } let(:user) { create(:user) } before do sign_in(user) - project.team << [user, :master] + project.add_access(user, :read, :admin) end describe "GET show" do context "as atom feed" do it "should render as atom" do - get :show, project_id: project.path, id: "master", format: "atom" + get :show, project_id: project.code, id: "master.atom" response.should be_success response.content_type.should == 'application/atom+xml' end diff --git a/spec/controllers/merge_requests_controller_spec.rb b/spec/controllers/merge_requests_controller_spec.rb deleted file mode 100644 index e8dd9bf9..00000000 --- a/spec/controllers/merge_requests_controller_spec.rb +++ /dev/null @@ -1,85 +0,0 @@ -require 'spec_helper' - -describe MergeRequestsController do - let(:project) { create(:project_with_code) } - let(:user) { create(:user) } - let(:merge_request) { create(:merge_request_with_diffs, project: project, target_branch: "bcf03b5d~3", source_branch: "bcf03b5d") } - - before do - sign_in(user) - project.team << [user, :master] - MergeRequestsController.any_instance.stub(validates_merge_request: true) - end - - describe "#show" do - shared_examples "export as" do |format| - it "should generally work" do - get :show, project_id: project.code, id: merge_request.id, format: format - - expect(response).to be_success - end - - it "should generate it" do - MergeRequest.any_instance.should_receive(:"to_#{format}") - - get :show, project_id: project.code, id: merge_request.id, format: format - end - - it "should render it" do - get :show, project_id: project.code, id: merge_request.id, format: format - - expect(response.body).to eq(merge_request.send(:"to_#{format}")) - end - - it "should not escape Html" do - MergeRequest.any_instance.stub(:"to_#{format}").and_return('HTML entities &<>" ') - - get :show, project_id: project.code, id: merge_request.id, format: format - - expect(response.body).to_not include('&') - expect(response.body).to_not include('>') - expect(response.body).to_not include('<') - expect(response.body).to_not include('"') - end - end - - describe "as diff" do - include_examples "export as", :diff - let(:format) { :diff } - - it "should really only be a git diff" do - get :show, project_id: project.code, id: merge_request.id, format: format - - expect(response.body).to start_with("diff --git") - end - end - - describe "as patch" do - include_examples "export as", :patch - let(:format) { :patch } - - it "should really be a git email patch with commit" do - get :show, project_id: project.code, id: merge_request.id, format: format - - expect(response.body[0..100]).to start_with("From #{merge_request.commits.last.id}") - end - - # TODO: fix or remove - #it "should contain as many patches as there are commits" do - #get :show, project_id: project.code, id: merge_request.id, format: format - - #patch_count = merge_request.commits.count - #merge_request.commits.each_with_index do |commit, patch_num| - #expect(response.body).to match(/^From #{commit.id}/) - #expect(response.body).to match(/^Subject: \[PATCH #{patch_num}\/#{patch_count}\]/) - #end - #end - - it "should contain git diffs" do - get :show, project_id: project.code, id: merge_request.id, format: format - - expect(response.body).to match(/^diff --git/) - end - end - end -end diff --git a/spec/controllers/tree_controller_spec.rb b/spec/controllers/tree_controller_spec.rb index 8232f147..b9295537 100644 --- a/spec/controllers/tree_controller_spec.rb +++ b/spec/controllers/tree_controller_spec.rb @@ -1,13 +1,13 @@ require 'spec_helper' describe TreeController do - let(:project) { create(:project_with_code) } + let(:project) { create(:project) } let(:user) { create(:user) } before do sign_in(user) - project.team << [user, :master] + project.add_access(user, :read, :admin) project.stub(:branches).and_return(['master', 'foo/bar/baz']) project.stub(:tags).and_return(['v1.0.0', 'v2.0.0']) diff --git a/spec/factories.rb b/spec/factories.rb index 3205cabd..7c33f0ec 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -9,10 +9,9 @@ FactoryGirl.define do sequence(:url) { Faker::Internet.uri('http') } - factory :user, aliases: [:author, :assignee, :owner, :creator] do + factory :user, aliases: [:author, :assignee, :owner] do email { Faker::Internet.email } name - sequence(:username) { |n| "#{Faker::Internet.user_name}#{n}" } password "123456" password_confirmation { password } @@ -26,35 +25,19 @@ FactoryGirl.define do factory :project do sequence(:name) { |n| "project#{n}" } path { name.downcase.gsub(/\s/, '_') } - creator - end - - factory :redmine_project, parent: :project do - issues_tracker { "redmine" } - issues_tracker_id { "project_name_in_redmine" } - end - - factory :project_with_code, parent: :project do - path { 'gitlabhq' } + code { name.downcase.gsub(/\s/, '_') } + owner end factory :group do sequence(:name) { |n| "group#{n}" } - path { name.downcase.gsub(/\s/, '_') } - owner - type 'Group' - end - - factory :namespace do - sequence(:name) { |n| "namespace#{n}" } - path { name.downcase.gsub(/\s/, '_') } + code { name.downcase.gsub(/\s/, '_') } owner end factory :users_project do user project - project_access { UsersProject::MASTER } end factory :issue do @@ -63,88 +46,35 @@ FactoryGirl.define do project trait :closed do - state :closed - end - - trait :reopened do - state :reopened + closed true end factory :closed_issue, traits: [:closed] - factory :reopened_issue, traits: [:reopened] end factory :merge_request do title author - project factory: :project_with_code + project source_branch "master" target_branch "stable" - # pick 3 commits "at random" (from bcf03b5d~3 to bcf03b5d) - trait :with_diffs do - target_branch "master" # pretend bcf03b5d~3 - source_branch "stable" # pretend bcf03b5d - st_commits do - [Commit.new(project.repository.commit('bcf03b5d')), - Commit.new(project.repository.commit('bcf03b5d~1')), - Commit.new(project.repository.commit('bcf03b5d~2'))] - end - st_diffs do - project.repo.diff("bcf03b5d~3", "bcf03b5d") - end - end - trait :closed do - state :closed - end - - trait :reopened do - state :reopened + closed true end factory :closed_merge_request, traits: [:closed] - factory :reopened_merge_request, traits: [:reopened] - factory :merge_request_with_diffs, traits: [:with_diffs] end factory :note do project note "Note" - author - - factory :note_on_commit, traits: [:on_commit] - factory :note_on_commit_diff, traits: [:on_commit, :on_diff] - factory :note_on_issue, traits: [:on_issue], aliases: [:votable_note] - factory :note_on_merge_request, traits: [:on_merge_request] - factory :note_on_merge_request_diff, traits: [:on_merge_request, :on_diff] - - trait :on_commit do - project factory: :project_with_code - commit_id "bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a" - noteable_type "Commit" - end - - trait :on_diff do - line_code "0_184_184" - end - - trait :on_merge_request do - project factory: :project_with_code - noteable_id 1 - noteable_type "MergeRequest" - end - - trait :on_issue do - noteable_id 1 - noteable_type "Issue" - end end factory :event do factory :closed_issue_event do project - action { Event::CLOSED } + action Event::Closed target factory: :closed_issue author factory: :user end @@ -169,23 +99,11 @@ FactoryGirl.define do "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa ++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=" end end - - factory :invalid_key do - key do - "ssh-rsa this_is_invalid_key==" - end - end end factory :milestone do title project - - trait :closed do - state :closed - end - - factory :closed_milestone, traits: [:closed] end factory :system_hook do diff --git a/spec/factories/user_team_project_relationships.rb b/spec/factories/user_team_project_relationships.rb deleted file mode 100644 index e900d86c..00000000 --- a/spec/factories/user_team_project_relationships.rb +++ /dev/null @@ -1,21 +0,0 @@ -# == Schema Information -# -# Table name: user_team_project_relationships -# -# id :integer not null, primary key -# project_id :integer -# user_team_id :integer -# greatest_access :integer -# created_at :datetime not null -# updated_at :datetime not null -# - -# Read about factories at https://github.com/thoughtbot/factory_girl - -FactoryGirl.define do - factory :user_team_project_relationship do - project - user_team - greatest_access { UsersProject::MASTER } - end -end diff --git a/spec/factories/user_team_user_relationships.rb b/spec/factories/user_team_user_relationships.rb deleted file mode 100644 index 8c729dd8..00000000 --- a/spec/factories/user_team_user_relationships.rb +++ /dev/null @@ -1,23 +0,0 @@ -# == Schema Information -# -# Table name: user_team_user_relationships -# -# id :integer not null, primary key -# user_id :integer -# user_team_id :integer -# group_admin :boolean -# permission :integer -# created_at :datetime not null -# updated_at :datetime not null -# - -# Read about factories at https://github.com/thoughtbot/factory_girl - -FactoryGirl.define do - factory :user_team_user_relationship do - user - user_team - group_admin false - permission { UsersProject::MASTER } - end -end diff --git a/spec/factories/user_teams.rb b/spec/factories/user_teams.rb deleted file mode 100644 index 3aeea40a..00000000 --- a/spec/factories/user_teams.rb +++ /dev/null @@ -1,23 +0,0 @@ -# == Schema Information -# -# Table name: user_teams -# -# id :integer not null, primary key -# name :string(255) -# path :string(255) -# owner_id :integer -# created_at :datetime not null -# updated_at :datetime not null -# description :string(255) default(""), not null -# - -# Read about factories at https://github.com/thoughtbot/factory_girl - -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 -end diff --git a/spec/factories_spec.rb b/spec/factories_spec.rb index 8360477d..5ee73546 100644 --- a/spec/factories_spec.rb +++ b/spec/factories_spec.rb @@ -1,9 +1,6 @@ require 'spec_helper' -INVALID_FACTORIES = [ - :key_with_a_space_in_the_middle, - :invalid_key, -] +INVALID_FACTORIES = [:key_with_a_space_in_the_middle] FactoryGirl.factories.map(&:name).each do |factory_name| next if INVALID_FACTORIES.include?(factory_name) diff --git a/spec/features/admin/admin_projects_spec.rb b/spec/features/admin/admin_projects_spec.rb deleted file mode 100644 index 23370891..00000000 --- a/spec/features/admin/admin_projects_spec.rb +++ /dev/null @@ -1,34 +0,0 @@ -require 'spec_helper' - -describe "Admin::Projects" do - before do - @project = create(:project) - login_as :admin - end - - describe "GET /admin/projects" do - before do - visit admin_projects_path - end - - it "should be ok" do - current_path.should == admin_projects_path - end - - it "should have projects list" do - page.should have_content(@project.name) - end - end - - describe "GET /admin/projects/:id" do - before do - visit admin_projects_path - click_link "#{@project.name}" - end - - it "should have project info" do - page.should have_content(@project.path) - page.should have_content(@project.name) - end - end -end diff --git a/spec/features/notes_on_merge_requests_spec.rb b/spec/features/notes_on_merge_requests_spec.rb deleted file mode 100644 index 82f44279..00000000 --- a/spec/features/notes_on_merge_requests_spec.rb +++ /dev/null @@ -1,228 +0,0 @@ -require 'spec_helper' - -describe "On a merge request", js: true do - let!(:project) { create(:project_with_code) } - let!(:merge_request) { create(:merge_request, project: project) } - - before do - login_as :user - project.team << [@user, :master] - - visit project_merge_request_path(project, merge_request) - end - - subject { page } - - describe "the note form" do - # main target form creation - it { should have_css(".js-main-target-form", visible: true, count: 1) } - - # button initalization - it { find(".js-main-target-form input[type=submit]").value.should == "Add Comment" } - it { within(".js-main-target-form") { should_not have_link("Cancel") } } - - describe "without text" do - it { within(".js-main-target-form") { should have_css(".js-note-preview-button", visible: false) } } - end - - describe "with text" do - before do - within(".js-main-target-form") do - fill_in "note[note]", with: "This is awesome" - end - end - - it { within(".js-main-target-form") { should_not have_css(".js-comment-button[disabled]") } } - - it { within(".js-main-target-form") { should have_css(".js-note-preview-button", visible: true) } } - end - - describe "with preview" do - before do - within(".js-main-target-form") do - fill_in "note[note]", with: "This is awesome" - find(".js-note-preview-button").trigger("click") - end - end - - it { within(".js-main-target-form") { should have_css(".js-note-preview", text: "This is awesome", visible: true) } } - - it { within(".js-main-target-form") { should have_css(".js-note-preview-button", visible: false) } } - it { within(".js-main-target-form") { should have_css(".js-note-edit-button", visible: true) } } - end - end - - describe "when posting a note" do - before do - within(".js-main-target-form") do - fill_in "note[note]", with: "This is awsome!" - find(".js-note-preview-button").trigger("click") - click_button "Add Comment" - end - end - - # note added - it { should have_content("This is awsome!") } - - # reset form - it { within(".js-main-target-form") { should have_no_field("note[note]", with: "This is awesome!") } } - - # return from preview - it { within(".js-main-target-form") { should have_css(".js-note-preview", visible: false) } } - it { within(".js-main-target-form") { should have_css(".js-note-text", visible: true) } } - - - it "should be removable" do - find(".js-note-delete").trigger("click") - - should_not have_css(".note") - end - end -end - - - -describe "On a merge request diff", js: true, focus: true do - let!(:project) { create(:project_with_code) } - let!(:merge_request) { create(:merge_request_with_diffs, project: project) } - - before do - login_as :user - project.team << [@user, :master] - - visit diffs_project_merge_request_path(project, merge_request) - - within '.diffs-tab' do - click_link("Diff") - end - end - - subject { page } - - describe "when adding a note" do - before do - find("#4735dfc552ad7bf15ca468adc3cad9d05b624490_185_185.line_holder .js-add-diff-note-button").trigger("click") - end - - describe "the notes holder" do - it { should have_css("#4735dfc552ad7bf15ca468adc3cad9d05b624490_185_185.line_holder + .js-temp-notes-holder") } - - it { within(".js-temp-notes-holder") { should have_css(".new_note") } } - end - - describe "the note form" do - # set up hidden fields correctly - it { within(".js-temp-notes-holder") { find("#note_noteable_type").value.should == "MergeRequest" } } - it { within(".js-temp-notes-holder") { find("#note_noteable_id").value.should == merge_request.id.to_s } } - it { within(".js-temp-notes-holder") { find("#note_commit_id").value.should == "" } } - it { within(".js-temp-notes-holder") { find("#note_line_code").value.should == "4735dfc552ad7bf15ca468adc3cad9d05b624490_185_185" } } - - # buttons - it { should have_button("Add Comment") } - it { should have_css(".js-close-discussion-note-form", text: "Cancel") } - - it "shouldn't add a second form for same row" do - find("#4735dfc552ad7bf15ca468adc3cad9d05b624490_185_185.line_holder .js-add-diff-note-button").trigger("click") - - should have_css("#4735dfc552ad7bf15ca468adc3cad9d05b624490_185_185.line_holder + .js-temp-notes-holder form", count: 1) - end - - it "should be removed when canceled" do - within(".file form[rel$='4735dfc552ad7bf15ca468adc3cad9d05b624490_185_185']") do - find(".js-close-discussion-note-form").trigger("click") - end - - should have_no_css(".js-temp-notes-holder") - end - end - end - - describe "with muliple note forms" do - before do - find("#4735dfc552ad7bf15ca468adc3cad9d05b624490_185_185.line_holder .js-add-diff-note-button").trigger("click") - find("#342e16cbbd482ac2047dc679b2749d248cc1428f_18_17.line_holder .js-add-diff-note-button").trigger("click") - end - - # has two line forms - it { should have_css(".js-temp-notes-holder", count: 2) } - - describe "previewing them separately" do - before do - # add two separate texts and trigger previews on both - within("#4735dfc552ad7bf15ca468adc3cad9d05b624490_185_185.line_holder + .js-temp-notes-holder") do - fill_in "note[note]", with: "One comment on line 185" - find(".js-note-preview-button").trigger("click") - end - within("#342e16cbbd482ac2047dc679b2749d248cc1428f_18_17.line_holder + .js-temp-notes-holder") do - fill_in "note[note]", with: "Another comment on line 17" - find(".js-note-preview-button").trigger("click") - end - end - - # check if previews were rendered separately - it { within("#4735dfc552ad7bf15ca468adc3cad9d05b624490_185_185.line_holder + .js-temp-notes-holder") { should have_css(".js-note-preview", text: "One comment on line 185") } } - it { within("#342e16cbbd482ac2047dc679b2749d248cc1428f_18_17.line_holder + .js-temp-notes-holder") { should have_css(".js-note-preview", text: "Another comment on line 17") } } - end - - describe "posting a note" do - before do - within("#342e16cbbd482ac2047dc679b2749d248cc1428f_18_17.line_holder + .js-temp-notes-holder") do - fill_in "note[note]", with: "Another comment on line 17" - click_button("Add Comment") - end - end - - # removed form after submit - it { should have_no_css("#342e16cbbd482ac2047dc679b2749d248cc1428f_18_17.line_holder + .js-temp-notes-holder") } - - # added discussion - it { should have_content("Another comment on line 17") } - it { should have_css("#342e16cbbd482ac2047dc679b2749d248cc1428f_18_17.line_holder + .notes_holder") } - it { should have_css("#342e16cbbd482ac2047dc679b2749d248cc1428f_18_17.line_holder + .notes_holder .note", count: 1) } - it { should have_link("Reply") } - - it "should remove last note of a discussion" do - within("#342e16cbbd482ac2047dc679b2749d248cc1428f_18_17.line_holder + .notes_holder") do - find(".js-note-delete").trigger("click") - end - - # removed whole discussion - should_not have_css(".note_holder") - should have_css("#342e16cbbd482ac2047dc679b2749d248cc1428f_18_17.line_holder + #342e16cbbd482ac2047dc679b2749d248cc1428f_18_18.line_holder") - end - end - end - - describe "when replying to a note" do - before do - # create first note - find("#4735dfc552ad7bf15ca468adc3cad9d05b624490_184_184.line_holder .js-add-diff-note-button").trigger("click") - within("#4735dfc552ad7bf15ca468adc3cad9d05b624490_184_184.line_holder + .js-temp-notes-holder") do - fill_in "note[note]", with: "One comment on line 184" - click_button("Add Comment") - end - # create second note - within("#4735dfc552ad7bf15ca468adc3cad9d05b624490_184_184.line_holder + .notes_holder") do - find(".js-discussion-reply-button").trigger("click") - fill_in "note[note]", with: "An additional comment in reply" - click_button("Add Comment") - end - end - - # inserted note - it { should have_content("An additional comment in reply") } - it { within("#4735dfc552ad7bf15ca468adc3cad9d05b624490_184_184.line_holder + .notes_holder") { should have_css(".note", count: 2) } } - - # removed form after reply - it { within("#4735dfc552ad7bf15ca468adc3cad9d05b624490_184_184.line_holder + .notes_holder") { should have_no_css("form") } } - it { within("#4735dfc552ad7bf15ca468adc3cad9d05b624490_184_184.line_holder + .notes_holder") { should have_link("Reply") } } - end -end - - - -describe "On merge request discussion", js: true do - describe "with merge request diff note" - describe "with commit note" - describe "with commit diff note" -end diff --git a/spec/features/profile_spec.rb b/spec/features/profile_spec.rb deleted file mode 100644 index 51b95c25..00000000 --- a/spec/features/profile_spec.rb +++ /dev/null @@ -1,49 +0,0 @@ -require 'spec_helper' - -describe "Profile account page" do - let(:user) { create(:user) } - - before do - login_as :user - end - - describe "when signup is enabled" do - before do - Gitlab.config.gitlab.stub(:signup_enabled).and_return(true) - visit account_profile_path - end - - it { page.should have_content("Remove account") } - - it "should delete the account", js: true do - expect { click_link "Delete account" }.to change {User.count}.by(-1) - current_path.should == new_user_session_path - end - end - - describe "when signup is enabled and user has a project" do - before do - Gitlab.config.gitlab.stub(:signup_enabled).and_return(true) - @project = create(:project, namespace: @user.namespace) - @project.team << [@user, :master] - visit account_profile_path - end - it { page.should have_content("Remove account") } - - it "should not allow user to delete the account" do - expect { click_link "Delete account" }.not_to change {User.count}.by(-1) - end - end - - describe "when signup is disabled" do - before do - Gitlab.config.gitlab.stub(:signup_enabled).and_return(false) - visit account_profile_path - end - - it "should not have option to remove account" do - page.should_not have_content("Remove account") - current_path.should == account_profile_path - end - end -end diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb deleted file mode 100644 index 1ffc28bf..00000000 --- a/spec/features/projects_spec.rb +++ /dev/null @@ -1,17 +0,0 @@ -require 'spec_helper' - -describe "Projects" do - before { login_as :user } - - describe "DELETE /projects/:id" do - before do - @project = create(:project, namespace: @user.namespace) - @project.team << [@user, :master] - visit edit_project_path(@project) - end - - it "should be correct path" do - expect { click_link "Remove project" }.to change {Project.count}.by(-1) - end - end -end diff --git a/spec/features/users_spec.rb b/spec/features/users_spec.rb deleted file mode 100644 index ed9e44fb..00000000 --- a/spec/features/users_spec.rb +++ /dev/null @@ -1,19 +0,0 @@ -require 'spec_helper' - -describe 'Users' do - describe "GET /users/sign_up" do - before do - Gitlab.config.gitlab.stub(:signup_enabled).and_return(true) - end - - it "should create a new user account" do - visit new_user_registration_path - fill_in "user_name", with: "Name Surname" - fill_in "user_username", with: "Great" - fill_in "user_email", with: "name@mail.com" - fill_in "user_password", with: "password1234" - fill_in "user_password_confirmation", with: "password1234" - expect { click_button "Sign up" }.to change {User.count}.by(1) - end - end -end diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb index ba1af084..a94d5505 100644 --- a/spec/helpers/application_helper_spec.rb +++ b/spec/helpers/application_helper_spec.rb @@ -43,7 +43,7 @@ describe ApplicationHelper do let(:user_email) { 'user@email.com' } it "should return a generic avatar path when Gravatar is disabled" do - Gitlab.config.gravatar.stub(:enabled).and_return(false) + Gitlab.config.stub(:disable_gravatar?).and_return(true) gravatar_icon(user_email).should == 'no_avatar.png' end @@ -51,36 +51,14 @@ describe ApplicationHelper do gravatar_icon('').should == 'no_avatar.png' end - it "should return default gravatar url" do - stub!(:request).and_return(double(:ssl? => false)) - gravatar_icon(user_email).should match('http://www.gravatar.com/avatar/b58c6f14d292556214bd64909bcdb118') - end - it "should use SSL when appropriate" do stub!(:request).and_return(double(:ssl? => true)) gravatar_icon(user_email).should match('https://secure.gravatar.com') end - it "should return custom gravatar path when gravatar_url is set" do - stub!(:request).and_return(double(:ssl? => false)) - Gitlab.config.gravatar.stub(:plain_url).and_return('http://example.local/?s=%{size}&hash=%{hash}') - gravatar_icon(user_email, 20).should == 'http://example.local/?s=20&hash=b58c6f14d292556214bd64909bcdb118' - end - it "should accept a custom size" do stub!(:request).and_return(double(:ssl? => false)) gravatar_icon(user_email, 64).should match(/\?s=64/) end - - it "should use default size when size is wrong" do - stub!(:request).and_return(double(:ssl? => false)) - gravatar_icon(user_email, nil).should match(/\?s=40/) - end - - it "should be case insensitive" do - stub!(:request).and_return(double(:ssl? => false)) - gravatar_icon(user_email).should == gravatar_icon(user_email.upcase + " ") - end - end end diff --git a/spec/helpers/gitlab_markdown_helper_spec.rb b/spec/helpers/gitlab_markdown_helper_spec.rb index 4140ba48..ec830e40 100644 --- a/spec/helpers/gitlab_markdown_helper_spec.rb +++ b/spec/helpers/gitlab_markdown_helper_spec.rb @@ -1,13 +1,10 @@ require "spec_helper" describe GitlabMarkdownHelper do - include ApplicationHelper - include IssuesHelper + let!(:project) { create(:project) } - let!(:project) { create(:project_with_code) } - - let(:user) { create(:user, username: 'gfm') } - let(:commit) { project.repository.commit } + let(:user) { create(:user, name: 'gfm') } + let(:commit) { CommitDecorator.decorate(project.commit) } let(:issue) { create(:issue, project: project) } let(:merge_request) { create(:merge_request, project: project) } let(:snippet) { create(:snippet, project: project) } @@ -84,11 +81,11 @@ describe GitlabMarkdownHelper do end describe "referencing a team member" do - let(:actual) { "@#{user.username} you are right." } - let(:expected) { user_path(user) } + let(:actual) { "@#{user.name} you are right." } + let(:expected) { project_team_member_path(project, member) } before do - project.team << [user, :master] + project.users << user end it "should link using a simple name" do @@ -106,18 +103,18 @@ describe GitlabMarkdownHelper do end it "should link with adjacent text" do - actual = "Mail the admin (@#{user.username})" + actual = "Mail the admin (@gfm)" gfm(actual).should match(expected) end it "should keep whitespace intact" do - actual = "Yes, @#{user.username} is right." - expected = /Yes, @#{user.username}<\/a> is right/ + actual = "Yes, @#{user.name} is right." + expected = /Yes, @#{user.name}<\/a> is right/ gfm(actual).should match(expected) end it "should not link with an invalid id" do - actual = expected = "@#{user.username.reverse} you are right." + actual = expected = "@#{user.name.reverse} you are right." gfm(actual).should == expected end @@ -275,7 +272,7 @@ describe GitlabMarkdownHelper do groups[0].should match(/This should finally fix $/) # First issue link - groups[1].should match(/href="#{project_issue_url(project, issues[0])}"/) + groups[1].should match(/href="#{project_issue_path(project, issues[0])}"/) groups[1].should match(/##{issues[0].id}$/) # Internal commit link @@ -283,7 +280,7 @@ describe GitlabMarkdownHelper do groups[2].should match(/ and /) # Second issue link - groups[3].should match(/href="#{project_issue_url(project, issues[1])}"/) + groups[3].should match(/href="#{project_issue_path(project, issues[1])}"/) groups[3].should match(/##{issues[1].id}$/) # Trailing commit link @@ -317,12 +314,12 @@ describe GitlabMarkdownHelper do end it "should handle references in lists" do - project.team << [user, :master] + project.users << user - actual = "\n* dark: ##{issue.id}\n* light by @#{member.user.username}" + actual = "\n* dark: ##{issue.id}\n* light by @#{member.user_name}" markdown(actual).should match(%r{
  • dark: ##{issue.id}
  • }) - markdown(actual).should match(%r{
  • light by @#{member.user.username}
  • }) + markdown(actual).should match(%r{
  • light by @#{member.user_name}
  • }) end it "should handle references in " do @@ -332,59 +329,13 @@ describe GitlabMarkdownHelper do end it "should leave code blocks untouched" do - helper.stub(:user_color_scheme_class).and_return(:white) + markdown("\n some code from $#{snippet.id}\n here too\n").should == "
    some code from $#{snippet.id}\nhere too\n
    " - helper.markdown("\n some code from $#{snippet.id}\n here too\n").should include("
    some code from $#{snippet.id}\nhere too\n
    ") - - helper.markdown("\n```\nsome code from $#{snippet.id}\nhere too\n```\n").should include("
    some code from $#{snippet.id}\nhere too\n
    ") + markdown("\n```\nsome code from $#{snippet.id}\nhere too\n```\n").should == "
    some code from $#{snippet.id}\nhere too\n
    " end it "should leave inline code untouched" do markdown("\nDon't use `$#{snippet.id}` here.\n").should == "

    Don't use $#{snippet.id} here.

    \n" end - - it "should leave ref-like autolinks untouched" do - markdown("look at http://example.tld/#!#{merge_request.id}").should == "

    look at http://example.tld/#!#{merge_request.id}

    \n" - end - - it "should leave ref-like href of 'manual' links untouched" do - markdown("why not [inspect !#{merge_request.id}](http://example.tld/#!#{merge_request.id})").should == "

    why not inspect !#{merge_request.id}

    \n" - end - - it "should leave ref-like src of images untouched" do - markdown("screen shot: ![some image](http://example.tld/#!#{merge_request.id})").should == "

    screen shot: \"some

    \n" - end - - it "should generate absolute urls for refs" do - markdown("##{issue.id}").should include(project_issue_url(project, issue)) - end - - it "should generate absolute urls for emoji" do - markdown(":smile:").should include("src=\"#{url_to_image("emoji/smile")}") - end - end - - describe "#render_wiki_content" do - before do - @wiki = stub('WikiPage') - @wiki.stub(:content).and_return('wiki content') - end - - it "should use Gitlab Flavored Markdown for markdown files" do - @wiki.stub(:format).and_return(:markdown) - - helper.should_receive(:markdown).with('wiki content') - - helper.render_wiki_content(@wiki) - end - - it "should use the Gollum renderer for all other file types" do - @wiki.stub(:format).and_return(:rdoc) - formatted_content_stub = stub('formatted_content') - formatted_content_stub.should_receive(:html_safe) - @wiki.stub(:formatted_content).and_return(formatted_content_stub) - - helper.render_wiki_content(@wiki) - end end end diff --git a/spec/helpers/issues_helper_spec.rb b/spec/helpers/issues_helper_spec.rb deleted file mode 100644 index a1f23073..00000000 --- a/spec/helpers/issues_helper_spec.rb +++ /dev/null @@ -1,106 +0,0 @@ -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 - - describe :url_for_new_issue do - let(:issues_url) { Gitlab.config.issues_tracker.redmine.new_issue_url} - let(:ext_expected) do - issues_url.gsub(':project_id', ext_project.id.to_s) - .gsub(':issues_tracker_id', ext_project.issues_tracker_id.to_s) - end - let(:int_expected) { new_project_issue_path(project) } - - it "should return internal path if used internal tracker" do - @project = project - url_for_new_issue.should match(int_expected) - end - - it "should return path to external tracker" do - @project = ext_project - - url_for_new_issue.should match(ext_expected) - end - - it "should return empty string if project nil" do - @project = nil - - url_for_new_issue.should eq "" - end - end - -end diff --git a/spec/helpers/notifications_helper_spec.rb b/spec/helpers/notifications_helper_spec.rb deleted file mode 100644 index f97959ee..00000000 --- a/spec/helpers/notifications_helper_spec.rb +++ /dev/null @@ -1,15 +0,0 @@ -require 'spec_helper' - -# Specs in this file have access to a helper object that includes -# the NotificationsHelper. For example: -# -# describe NotificationsHelper do -# describe "string concat" do -# it "concats two strings with spaces" do -# helper.concat_strings("this","that").should == "this that" -# end -# end -# end -describe NotificationsHelper do - pending "add some examples to (or delete) #{__FILE__}" -end diff --git a/spec/lib/extracts_path_spec.rb b/spec/lib/extracts_path_spec.rb index aac72c63..cf422017 100644 --- a/spec/lib/extracts_path_spec.rb +++ b/spec/lib/extracts_path_spec.rb @@ -7,8 +7,7 @@ describe ExtractsPath do before do @project = project - project.stub(repository: stub(ref_names: ['master', 'foo/bar/baz', 'v1.0.0', 'v2.0.0'])) - project.stub(path_with_namespace: 'gitlab/gitlab-ci') + project.stub(:ref_names).and_return(['master', 'foo/bar/baz', 'v1.0.0', 'v2.0.0']) end describe '#extract_ref' do diff --git a/spec/lib/git/commit_spec.rb b/spec/lib/git/commit_spec.rb deleted file mode 100644 index 475bc359..00000000 --- a/spec/lib/git/commit_spec.rb +++ /dev/null @@ -1,49 +0,0 @@ -require "spec_helper" - -describe Gitlab::Git::Commit do - let(:commit) { create(:project_with_code).repository.commit } - - describe "Commit info" do - before do - @committer = double( - email: 'mike@smith.com', - name: 'Mike Smith' - ) - - @author = double( - email: 'john@smith.com', - name: 'John Smith' - ) - - @raw_commit = double( - id: "bcf03b5de6abcf03b5de6c", - author: @author, - committer: @committer, - committed_date: Date.yesterday, - message: 'Refactoring specs' - ) - - @commit = Gitlab::Git::Commit.new(@raw_commit) - end - - it { @commit.short_id.should == "bcf03b5de6a" } - it { @commit.safe_message.should == @raw_commit.message } - it { @commit.created_at.should == @raw_commit.committed_date } - it { @commit.author_email.should == @author.email } - it { @commit.author_name.should == @author.name } - it { @commit.committer_name.should == @committer.name } - it { @commit.committer_email.should == @committer.email } - it { @commit.different_committer?.should be_true } - end - - describe "Class methods" do - subject { Gitlab::Git::Commit } - - it { should respond_to(:find_or_first) } - it { should respond_to(:fresh_commits) } - it { should respond_to(:commits_with_refs) } - it { should respond_to(:commits_since) } - it { should respond_to(:commits_between) } - it { should respond_to(:commits) } - end -end diff --git a/spec/lib/git/repository_spec.rb b/spec/lib/git/repository_spec.rb deleted file mode 100644 index b2b6f196..00000000 --- a/spec/lib/git/repository_spec.rb +++ /dev/null @@ -1,104 +0,0 @@ -require "spec_helper" - -describe Gitlab::Git::Repository do - let(:repository) { Gitlab::Git::Repository.new('gitlabhq', 'master') } - - describe "Respond to" do - subject { repository } - - it { should respond_to(:repo) } - it { should respond_to(:tree) } - it { should respond_to(:root_ref) } - it { should respond_to(:tags) } - it { should respond_to(:commit) } - it { should respond_to(:commits) } - it { should respond_to(:commits_between) } - it { should respond_to(:commits_with_refs) } - it { should respond_to(:commits_since) } - it { should respond_to(:commits_between) } - end - - - describe "#discover_default_branch" do - let(:master) { 'master' } - let(:stable) { 'stable' } - - it "returns 'master' when master exists" do - repository.should_receive(:branch_names).at_least(:once).and_return([stable, master]) - repository.discover_default_branch.should == 'master' - end - - it "returns non-master when master exists but default branch is set to something else" do - repository.root_ref = 'stable' - repository.should_receive(:branch_names).at_least(:once).and_return([stable, master]) - repository.discover_default_branch.should == 'stable' - end - - it "returns a non-master branch when only one exists" do - repository.should_receive(:branch_names).at_least(:once).and_return([stable]) - repository.discover_default_branch.should == 'stable' - end - - it "returns nil when no branch exists" do - repository.should_receive(:branch_names).at_least(:once).and_return([]) - repository.discover_default_branch.should be_nil - end - end - - describe :commit do - it "should return first head commit if without params" do - repository.commit.id.should == repository.repo.commits.first.id - end - - it "should return valid commit" do - repository.commit(ValidCommit::ID).should be_valid_commit - end - - it "should return nil" do - repository.commit("+123_4532530XYZ").should be_nil - end - end - - describe :tree do - before do - @commit = repository.commit(ValidCommit::ID) - end - - it "should raise error w/o arguments" do - lambda { repository.tree }.should raise_error - end - - it "should return root tree for commit" do - tree = repository.tree(@commit) - tree.contents.size.should == ValidCommit::FILES_COUNT - tree.contents.map(&:name).should == ValidCommit::FILES - end - - it "should return root tree for commit with correct path" do - tree = repository.tree(@commit, ValidCommit::C_FILE_PATH) - tree.contents.map(&:name).should == ValidCommit::C_FILES - end - - it "should return root tree for commit with incorrect path" do - repository.tree(@commit, "invalid_path").should be_nil - end - end - - describe "fresh commits" do - it { repository.fresh_commits(3).count.should == 3 } - it { repository.fresh_commits.first.id.should == "bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a" } - it { repository.fresh_commits.last.id.should == "f403da73f5e62794a0447aca879360494b08f678" } - end - - describe "commits_between" do - subject do - commits = repository.commits_between("3a4b4fb4cde7809f033822a171b9feae19d41fff", - "8470d70da67355c9c009e4401746b1d5410af2e3") - commits.map { |c| c.id } - end - - it { should have(3).elements } - it { should include("f0f14c8eaba69ebddd766498a9d0b0e79becd633") } - it { should_not include("bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a") } - end -end diff --git a/spec/lib/gitolite_config_spec.rb b/spec/lib/gitolite_config_spec.rb new file mode 100644 index 00000000..c3ce0db5 --- /dev/null +++ b/spec/lib/gitolite_config_spec.rb @@ -0,0 +1,16 @@ +require 'spec_helper' + +describe Gitlab::GitoliteConfig do + let(:gitolite) { Gitlab::GitoliteConfig.new } + + it { should respond_to :write_key } + it { should respond_to :rm_key } + it { should respond_to :update_project } + it { should respond_to :update_project! } + it { should respond_to :update_projects } + it { should respond_to :destroy_project } + it { should respond_to :destroy_project! } + it { should respond_to :apply } + it { should respond_to :admin_all_repo } + it { should respond_to :admin_all_repo! } +end diff --git a/spec/lib/gitolite_spec.rb b/spec/lib/gitolite_spec.rb new file mode 100644 index 00000000..cc8ce8b2 --- /dev/null +++ b/spec/lib/gitolite_spec.rb @@ -0,0 +1,25 @@ +require 'spec_helper' + +describe Gitlab::Gitolite do + let(:project) { double('Project', path: 'diaspora') } + let(:gitolite_config) { double('Gitlab::GitoliteConfig') } + let(:gitolite) { Gitlab::Gitolite.new } + + before do + gitolite.stub(config: gitolite_config) + end + + it { should respond_to :set_key } + it { should respond_to :remove_key } + + it { should respond_to :update_repository } + it { should respond_to :create_repository } + it { should respond_to :remove_repository } + + it { gitolite.url_to_repo('diaspora').should == Gitlab.config.ssh_path + "diaspora.git" } + + it "should call config update" do + gitolite_config.should_receive(:update_project!) + gitolite.update_repository project + end +end diff --git a/spec/lib/popen_spec.rb b/spec/lib/popen_spec.rb deleted file mode 100644 index 4791be41..00000000 --- a/spec/lib/popen_spec.rb +++ /dev/null @@ -1,29 +0,0 @@ -require 'spec_helper' - -describe 'Gitlab::Popen', no_db: true do - let (:path) { Rails.root.join('tmp').to_s } - - before do - @klass = Class.new(Object) - @klass.send(:include, Gitlab::Popen) - end - - context 'zero status' do - before do - @output, @status = @klass.new.popen('ls', path) - end - - it { @status.should be_zero } - it { @output.should include('cache') } - end - - context 'non-zero status' do - before do - @output, @status = @klass.new.popen('cat NOTHING', path) - end - - it { @status.should == 1 } - it { @output.should include('No such file or directory') } - end -end - diff --git a/spec/lib/shell_spec.rb b/spec/lib/shell_spec.rb deleted file mode 100644 index 3c04f4bb..00000000 --- a/spec/lib/shell_spec.rb +++ /dev/null @@ -1,17 +0,0 @@ -require 'spec_helper' - -describe Gitlab::Shell do - let(:project) { double('Project', id: 7, path: 'diaspora') } - let(:gitlab_shell) { Gitlab::Shell.new } - - before do - Project.stub(find: project) - end - - it { should respond_to :add_key } - it { should respond_to :remove_key } - it { should respond_to :add_repository } - it { should respond_to :remove_repository } - - it { gitlab_shell.url_to_repo('diaspora').should == Gitlab.config.gitlab_shell.ssh_path_prefix + "diaspora.git" } -end diff --git a/spec/lib/votes_spec.rb b/spec/lib/votes_spec.rb deleted file mode 100644 index b49ed15b..00000000 --- a/spec/lib/votes_spec.rb +++ /dev/null @@ -1,132 +0,0 @@ -require 'spec_helper' - -describe MergeRequest do - let(:merge_request) { FactoryGirl.create(:merge_request_with_diffs) } - - describe "#upvotes" do - it "with no notes has a 0/0 score" do - merge_request.upvotes.should == 0 - end - - it "should recognize non-+1 notes" do - merge_request.notes << create(:note, note: "No +1 here") - merge_request.should have(1).note - merge_request.notes.first.upvote?.should be_false - merge_request.upvotes.should == 0 - end - - it "should recognize a single +1 note" do - merge_request.notes << create(:note, note: "+1 This is awesome") - merge_request.upvotes.should == 1 - end - - it "should recognize multiple +1 notes" do - merge_request.notes << create(:note, note: "+1 This is awesome") - merge_request.notes << create(:note, note: "+1 I want this") - merge_request.upvotes.should == 2 - end - end - - describe "#downvotes" do - it "with no notes has a 0/0 score" do - merge_request.downvotes.should == 0 - end - - it "should recognize non--1 notes" do - merge_request.notes << create(:note, note: "Almost got a -1") - merge_request.should have(1).note - merge_request.notes.first.downvote?.should be_false - merge_request.downvotes.should == 0 - end - - it "should recognize a single -1 note" do - merge_request.notes << create(:note, note: "-1 This is bad") - merge_request.downvotes.should == 1 - end - - it "should recognize multiple -1 notes" do - merge_request.notes << create(:note, note: "-1 This is bad") - merge_request.notes << create(:note, note: "-1 Away with this") - merge_request.downvotes.should == 2 - end - end - - describe "#votes_count" do - it "with no notes has a 0/0 score" do - merge_request.votes_count.should == 0 - end - - it "should recognize non notes" do - merge_request.notes << create(:note, note: "No +1 here") - merge_request.should have(1).note - merge_request.votes_count.should == 0 - end - - it "should recognize a single +1 note" do - merge_request.notes << create(:note, note: "+1 This is awesome") - merge_request.votes_count.should == 1 - end - - it "should recognize a single -1 note" do - merge_request.notes << create(:note, note: "-1 This is bad") - merge_request.votes_count.should == 1 - end - - it "should recognize multiple notes" do - merge_request.notes << create(:note, note: "+1 This is awesome") - merge_request.notes << create(:note, note: "-1 This is bad") - merge_request.notes << create(:note, note: "+1 I want this") - merge_request.votes_count.should == 3 - end - end - - describe "#upvotes_in_percent" do - it "with no notes has a 0% score" do - merge_request.upvotes_in_percent.should == 0 - end - - it "should count a single 1 note as 100%" do - merge_request.notes << create(:note, note: "+1 This is awesome") - merge_request.upvotes_in_percent.should == 100 - end - - it "should count multiple +1 notes as 100%" do - merge_request.notes << create(:note, note: "+1 This is awesome") - merge_request.notes << create(:note, note: "+1 I want this") - merge_request.upvotes_in_percent.should == 100 - end - - it "should count fractions for multiple +1 and -1 notes correctly" do - merge_request.notes << create(:note, note: "+1 This is awesome") - merge_request.notes << create(:note, note: "+1 I want this") - merge_request.notes << create(:note, note: "-1 This is bad") - merge_request.notes << create(:note, note: "+1 me too") - merge_request.upvotes_in_percent.should == 75 - end - end - - describe "#downvotes_in_percent" do - it "with no notes has a 0% score" do - merge_request.downvotes_in_percent.should == 0 - end - - it "should count a single -1 note as 100%" do - merge_request.notes << create(:note, note: "-1 This is bad") - merge_request.downvotes_in_percent.should == 100 - end - - it "should count multiple -1 notes as 100%" do - merge_request.notes << create(:note, note: "-1 This is bad") - merge_request.notes << create(:note, note: "-1 Away with this") - merge_request.downvotes_in_percent.should == 100 - end - - it "should count fractions for multiple +1 and -1 notes correctly" do - merge_request.notes << create(:note, note: "+1 This is awesome") - merge_request.notes << create(:note, note: "+1 I want this") - merge_request.notes << create(:note, note: "-1 This is bad") - merge_request.notes << create(:note, note: "+1 me too") - merge_request.downvotes_in_percent.should == 25 - end - end -end diff --git a/spec/lib/wiki_to_gollum_migrator_spec.rb b/spec/lib/wiki_to_gollum_migrator_spec.rb deleted file mode 100644 index f9b9c78e..00000000 --- a/spec/lib/wiki_to_gollum_migrator_spec.rb +++ /dev/null @@ -1,219 +0,0 @@ -require "spec_helper" - -describe WikiToGollumMigrator do - - def create_wiki_for(project) - 3.times { @pages[project.id] << create_page(project) } - end - - def create_revisions_for(project) - @pages[project.id].each do |page| - create_revision(page) - end - end - - def create_page(project) - page = project.wikis.new(title: "Page #{rand(1000)}", content: "Content") - page.user = project.owner - page.slug = page.title.parameterize - page.save! - page - end - - def create_revision(page) - revision = page.dup - revision.content = "Updated Content" - revision.save! - end - - def create_temp_repo(path) - FileUtils.mkdir_p path - command = "git init --quiet --bare #{path};" - system(command) - end - - before do - @repo_path = "#{Rails.root}/tmp/test-git-base-path" - @projects = [] - @pages = Hash.new {|h,k| h[k] = Array.new } - - @projects << create(:project) - @projects << create(:project) - - @projects.each do |project| - create_wiki_for project - create_revisions_for project - end - - @project_without_wiki = create(:project) - end - - context "Before the migration" do - it "has two projects with valid wikis" do - @projects.each do |project| - pages = project.wikis.group(:slug).all - pages.count.should == 3 - end - end - - it "has two revision for each page" do - @projects.each do |project| - @pages[project.id].each do |page| - revisions = project.wikis.where(slug: page.slug) - revisions.count.should == 2 - end - end - end - end - - describe "#initialize" do - it "finds all projects that have existing wiki pages" do - Project.count.should == 3 - subject.projects.count.should == 2 - end - end - - context "#migrate!" do - before do - Gitlab::Shell.any_instance.stub(:add_repository) do |path| - create_temp_repo("#{@repo_path}/#{path}.git") - end - - subject.stub(:log).as_null_object - - subject.migrate! - end - - it "creates a new Gollum Wiki for each project" do - @projects.each do |project| - wiki_path = project.path_with_namespace + ".wiki.git" - full_path = @repo_path + "/" + wiki_path - File.exist?(full_path).should be_true - File.directory?(full_path).should be_true - end - end - - it "creates a gollum page for each unique Wiki page" do - @projects.each do |project| - wiki = GollumWiki.new(project, nil) - wiki.pages.count.should == 3 - end - end - - it "creates a new revision for each old revision of the page" do - @projects.each do |project| - wiki = GollumWiki.new(project, nil) - wiki.pages.each do |page| - page.versions.count.should == 2 - end - end - end - - context "wikis with pages that have titles that do not match the slugs" do - before do - project = @projects.last - @page = project.wikis.new(title: "test page", content: "Invalid Page") - @page.slug = "totally-incorrect-slug" - @page.user = project.owner - @page.save! - - create_revision(@page) - - subject.rollback! - subject.migrate! - end - - it "has a page with a title differing the slug" do - @page.slug.should_not == @page.title.parameterize - end - - it "creates a new revision for each old revision of the page" do - @projects.each do |project| - wiki = GollumWiki.new(project, nil) - wiki.pages.each do |page| - page.versions.count.should == 2 - end - end - end - end - - context "changing wiki title from index to home" do - before do - @project = @projects.last - @page = @project.wikis.new(title: "Index", content: "Home Page") - @page.slug = "index" - @page.user = @project.owner - @page.save! - - create_revision(@page) - - subject.rollback! - end - - it "creates a page called Home" do - subject.migrate! - wiki = GollumWiki.new(@project, nil) - page = wiki.find_page("home") - page.should be_present - end - - context "when a page called Home already exists" do - before do - @index_page = @project.wikis.new(title: "Index", content: "Index Page") - @index_page.slug = "index" - @index_page.user = @project.owner - @index_page.save! - - create_revision(@index_page) - - @home_page = @project.wikis.new(title: "Home", content: "Home Page") - @home_page.slug = "home" - @home_page.user = @project.owner - @home_page.save! - - create_revision(@home_page) - subject.migrate! - end - - it "creates the index page" do - wiki = GollumWiki.new(@project, nil) - page = wiki.find_page("index") - page.should be_present - end - - it "creates the home page" do - wiki = GollumWiki.new(@project, nil) - page = wiki.find_page("home") - page.should be_present - end - end - end - end - - context "#rollback!" do - before do - Gitlab::Shell.any_instance.stub(:add_repository) do |path| - create_temp_repo("#{@repo_path}/#{path}.git") - end - - Gitlab::Shell.any_instance.stub(:remove_repository) do |path| - FileUtils.rm_rf "#{@repo_path}/#{path}.git" - end - - subject.stub(:log).as_null_object - - subject.migrate! - subject.rollback! - end - - it "destroys all of the wiki repositories that were created during migrate!" do - @projects.each do |project| - wiki_path = project.path_with_namespace + ".wiki.git" - full_path = @repo_path + "/" + wiki_path - File.exist?(full_path).should be_false - end - end - end - - -end diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb index 84ce7e86..b6b1769f 100644 --- a/spec/mailers/notify_spec.rb +++ b/spec/mailers/notify_spec.rb @@ -5,7 +5,7 @@ describe Notify do include EmailSpec::Matchers let(:recipient) { create(:user, email: 'recipient@example.com') } - let(:project) { create(:project_with_code) } + let(:project) { create(:project) } shared_examples 'a multiple recipients email' do it 'is sent to the given recipient' do @@ -32,7 +32,6 @@ describe Notify do end it 'contains the new user\'s password' do - Gitlab.config.gitlab.stub(:signup_enabled).and_return(false) should have_body_text /#{new_user.password}/ end @@ -41,57 +40,6 @@ describe Notify do end end - - describe 'for users that signed up, the email' do - let(:example_site_path) { root_path } - let(:new_user) { create(:user, email: 'newguy@example.com', password: "securePassword") } - - subject { Notify.new_user_email(new_user.id, new_user.password) } - - it 'is sent to the new user' do - should deliver_to new_user.email - end - - it 'has the correct subject' do - should have_subject /^gitlab \| Account was created for you$/i - end - - it 'contains the new user\'s login name' do - should have_body_text /#{new_user.email}/ - end - - it 'should not contain the new user\'s password' do - Gitlab.config.gitlab.stub(:signup_enabled).and_return(true) - should_not have_body_text /#{new_user.password}/ - end - - it 'includes a link to the site' do - should have_body_text /#{example_site_path}/ - end - end - - describe 'user added ssh key' do - let(:key) { create(:personal_key) } - - subject { Notify.new_ssh_key_email(key.id) } - - it 'is sent to the new user' do - should deliver_to key.user.email - end - - it 'has the correct subject' do - should have_subject /^gitlab \| SSH key was added to your account$/i - end - - it 'contains the new ssh key title' do - should have_body_text /#{key.title}/ - end - - it 'includes a link to ssh keys page' do - should have_body_text /#{keys_path}/ - end - end - context 'for a project' do describe 'items that are assignable, the email' do let(:assignee) { create(:user, email: 'assignee@example.com') } @@ -107,12 +55,12 @@ describe Notify do let(:issue) { create(:issue, assignee: assignee, project: project ) } describe 'that are new' do - subject { Notify.new_issue_email(issue.assignee_id, issue.id) } + subject { Notify.new_issue_email(issue.id) } it_behaves_like 'an assignee email' it 'has the correct subject' do - should have_subject /#{project.name} \| new issue ##{issue.id} \| #{issue.title}/ + should have_subject /new issue ##{issue.id} \| #{issue.title} \| #{project.name}/ end it 'contains a link to the new issue' do @@ -172,7 +120,7 @@ describe Notify do let(:merge_request) { create(:merge_request, assignee: assignee, project: project) } describe 'that are new' do - subject { Notify.new_merge_request_email(merge_request.assignee_id, merge_request.id) } + subject { Notify.new_merge_request_email(merge_request.id) } it_behaves_like 'an assignee email' @@ -221,7 +169,9 @@ describe Notify do end describe 'project access changed' do - let(:project) { create(:project) } + let(:project) { create(:project, + path: "Fuu", + code: "Fuu") } let(:user) { create(:user) } let(:users_project) { create(:users_project, project: project, @@ -243,7 +193,7 @@ describe Notify do let(:note) { create(:note, project: project, author: note_author) } before :each do - Note.stub(:find).with(note.id).and_return(note) + Note.stub(:find).with(note.id).and_return(note) end shared_examples 'a note email' do @@ -261,7 +211,7 @@ describe Notify do end describe 'on a project wall' do - let(:note_on_the_wall_path) { project_wall_path(project, anchor: "note_#{note.id}") } + let(:note_on_the_wall_path) { wall_project_path(project, anchor: "note_#{note.id}") } subject { Notify.note_wall_email(recipient.id, note.id) } @@ -277,8 +227,14 @@ describe Notify do end describe 'on a commit' do - let(:commit) { project.repository.commit } - + let(:commit) do + mock(:commit).tap do |commit| + commit.stub(:id).and_return('fauxsha1') + commit.stub(:project).and_return(project) + commit.stub(:short_id).and_return('fauxsha1') + commit.stub(:safe_message).and_return('some message') + end + end before(:each) { note.stub(:noteable).and_return(commit) } subject { Notify.note_commit_email(recipient.id, note.id) } @@ -290,7 +246,7 @@ describe Notify do end it 'contains a link to the commit' do - should have_body_text commit.short_id + should have_body_text /fauxsha1/ end end diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb index 6cf777be..e4bc1936 100644 --- a/spec/models/commit_spec.rb +++ b/spec/models/commit_spec.rb @@ -1,50 +1,37 @@ require 'spec_helper' describe Commit do - let(:commit) { create(:project_with_code).repository.commit } + let(:commit) { create(:project).commit } + describe CommitDecorator do + let(:decorator) { CommitDecorator.new(commit) } - describe '#title' do - it "returns no_commit_message when safe_message is blank" do - commit.stub(:safe_message).and_return('') - commit.title.should == "--no commit message" + describe '#title' do + it "returns no_commit_message when safe_message is blank" do + decorator.stub(:safe_message).and_return('') + decorator.title.should == "--no commit message" + end + + it "truncates a message without a newline at 70 characters" do + message = commit.safe_message * 10 + + decorator.stub(:safe_message).and_return(message) + decorator.title.should == "#{message[0..69]}…" + end + + it "truncates a message with a newline before 80 characters at the newline" do + message = commit.safe_message.split(" ").first + + decorator.stub(:safe_message).and_return(message + "\n" + message) + decorator.title.should == message + end + + it "truncates a message with a newline after 80 characters at 70 characters" do + message = (commit.safe_message * 10) + "\n" + + decorator.stub(:safe_message).and_return(message) + decorator.title.should == "#{message[0..69]}…" + end end - - it "truncates a message without a newline at 70 characters" do - message = commit.safe_message * 10 - - commit.stub(:safe_message).and_return(message) - commit.title.should == "#{message[0..69]}…" - end - - it "truncates a message with a newline before 80 characters at the newline" do - message = commit.safe_message.split(" ").first - - commit.stub(:safe_message).and_return(message + "\n" + message) - commit.title.should == message - end - - it "truncates a message with a newline after 80 characters at 70 characters" do - message = (commit.safe_message * 10) + "\n" - - commit.stub(:safe_message).and_return(message) - commit.title.should == "#{message[0..69]}…" - end - end - - describe "delegation" do - subject { commit } - - it { should respond_to(:message) } - it { should respond_to(:authored_date) } - it { should respond_to(:committed_date) } - it { should respond_to(:parents) } - it { should respond_to(:date) } - it { should respond_to(:committer) } - it { should respond_to(:author) } - it { should respond_to(:diffs) } - it { should respond_to(:tree) } - it { should respond_to(:id) } - it { should respond_to(:to_patch) } end end diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb index 85bdf08a..49cb49db 100644 --- a/spec/models/event_spec.rb +++ b/spec/models/event_spec.rb @@ -52,14 +52,14 @@ describe Event do @event = Event.create( project: project, - action: Event::PUSHED, + action: Event::Pushed, data: data, author_id: @user.id ) end it { @event.push?.should be_true } - it { @event.proper?.should be_true } + it { @event.allowed?.should be_true } it { @event.new_branch?.should be_true } it { @event.tag?.should be_false } it { @event.branch_name.should == "master" } @@ -72,7 +72,6 @@ describe Event do before { Event.should_receive :create - observer.stub(notification: stub.as_null_object) } describe "Joined project team" do diff --git a/spec/models/gitlab_ci_service_spec.rb b/spec/models/gitlab_ci_service_spec.rb deleted file mode 100644 index b86588af..00000000 --- a/spec/models/gitlab_ci_service_spec.rb +++ /dev/null @@ -1,46 +0,0 @@ -# == Schema Information -# -# Table name: services -# -# id :integer not null, primary key -# type :string(255) -# title :string(255) -# token :string(255) -# project_id :integer not null -# created_at :datetime not null -# updated_at :datetime not null -# active :boolean default(FALSE), not null -# project_url :string(255) -# - -require 'spec_helper' - -describe GitlabCiService do - describe "Associations" do - it { should belong_to :project } - it { should have_one :service_hook } - end - - describe "Mass assignment" do - it { should_not allow_mass_assignment_of(:project_id) } - end - - describe 'commits methods' do - before do - @service = GitlabCiService.new - @service.stub( - service_hook: true, - project_url: 'http://ci.gitlab.org/projects/2', - token: 'verySecret' - ) - end - - describe :commit_status_path do - it { @service.commit_status_path("2ab7834c").should == "http://ci.gitlab.org/projects/2/builds/2ab7834c/status.json?token=verySecret"} - end - - describe :build_page do - it { @service.build_page("2ab7834c").should == "http://ci.gitlab.org/projects/2/builds/2ab7834c"} - end - end -end diff --git a/spec/models/gollum_wiki_spec.rb b/spec/models/gollum_wiki_spec.rb deleted file mode 100644 index aa850dfd..00000000 --- a/spec/models/gollum_wiki_spec.rb +++ /dev/null @@ -1,196 +0,0 @@ -require "spec_helper" - -describe GollumWiki do - - def create_temp_repo(path) - FileUtils.mkdir_p path - command = "git init --quiet #{path};" - system(command) - end - - def remove_temp_repo(path) - FileUtils.rm_rf path - end - - def commit_details - commit = {name: user.name, email: user.email, message: "test commit"} - end - - def create_page(name, content) - subject.wiki.write_page(name, :markdown, content, commit_details) - end - - def destroy_page(page) - subject.wiki.delete_page(page, commit_details) - end - - let(:project) { create(:project) } - let(:repository) { project.repository } - let(:user) { project.owner } - let(:gitlab_shell) { Gitlab::Shell.new } - - subject { GollumWiki.new(project, user) } - - before do - create_temp_repo(subject.send(:path_to_repo)) - end - - describe "#path_with_namespace" do - it "returns the project path with namespace with the .wiki extension" do - subject.path_with_namespace.should == project.path_with_namespace + ".wiki" - end - end - - describe "#url_to_repo" do - it "returns the correct ssh url to the repo" do - subject.url_to_repo.should == gitlab_shell.url_to_repo(subject.path_with_namespace) - end - end - - describe "#ssh_url_to_repo" do - it "equals #url_to_repo" do - subject.ssh_url_to_repo.should == subject.url_to_repo - end - end - - describe "#http_url_to_repo" do - it "provides the full http url to the repo" do - gitlab_url = Gitlab.config.gitlab.url - repo_http_url = "#{gitlab_url}/#{subject.path_with_namespace}.git" - subject.http_url_to_repo.should == repo_http_url - end - end - - describe "#wiki" do - it "contains a Gollum::Wiki instance" do - subject.wiki.should be_a Gollum::Wiki - end - - before do - Gitlab::Shell.any_instance.stub(:add_repository) do - create_temp_repo("#{Rails.root}/tmp/test-git-base-path/non-existant.wiki.git") - end - project.stub(:path_with_namespace).and_return("non-existant") - end - - it "creates a new wiki repo if one does not yet exist" do - wiki = GollumWiki.new(project, user) - wiki.create_page("index", "test content").should_not == false - - FileUtils.rm_rf wiki.send(:path_to_repo) - end - - it "raises CouldNotCreateWikiError if it can't create the wiki repository" do - GollumWiki.any_instance.stub(:init_repo).and_return(false) - expect { GollumWiki.new(project, user).wiki }.to raise_exception(GollumWiki::CouldNotCreateWikiError) - end - end - - describe "#pages" do - before do - create_page("index", "This is an awesome new Gollum Wiki") - @pages = subject.pages - end - - after do - destroy_page(@pages.first.page) - end - - it "returns an array of WikiPage instances" do - @pages.first.should be_a WikiPage - end - - it "returns the correct number of pages" do - @pages.count.should == 1 - end - end - - describe "#find_page" do - before do - create_page("index page", "This is an awesome Gollum Wiki") - end - - after do - destroy_page(subject.pages.first.page) - end - - it "returns the latest version of the page if it exists" do - page = subject.find_page("index page") - page.title.should == "index page" - end - - it "returns nil if the page does not exist" do - subject.find_page("non-existant").should == nil - end - - it "can find a page by slug" do - page = subject.find_page("index-page") - page.title.should == "index page" - end - - it "returns a WikiPage instance" do - page = subject.find_page("index page") - page.should be_a WikiPage - end - end - - describe "#create_page" do - after do - destroy_page(subject.pages.first.page) - end - - it "creates a new wiki page" do - subject.create_page("test page", "this is content").should_not == false - subject.pages.count.should == 1 - end - - it "returns false when a duplicate page exists" do - subject.create_page("test page", "content") - subject.create_page("test page", "content").should == false - end - - it "stores an error message when a duplicate page exists" do - 2.times { subject.create_page("test page", "content") } - subject.error_message.should =~ /Duplicate page:/ - end - - it "sets the correct commit message" do - subject.create_page("test page", "some content", :markdown, "commit message") - subject.pages.first.page.version.message.should == "commit message" - end - end - - describe "#update_page" do - before do - create_page("update-page", "some content") - @gollum_page = subject.wiki.paged("update-page") - subject.update_page(@gollum_page, "some other content", :markdown, "updated page") - @page = subject.pages.first.page - end - - after do - destroy_page(@page) - end - - it "updates the content of the page" do - @page.raw_data.should == "some other content" - end - - it "sets the correct commit message" do - @page.version.message.should == "updated page" - end - end - - describe "#delete_page" do - before do - create_page("index", "some content") - @page = subject.wiki.paged("index") - end - - it "deletes the page" do - subject.delete_page(@page) - subject.pages.count.should == 0 - end - end - -end diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 5d4674e3..6ae2cb20 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -1,15 +1,13 @@ # == Schema Information # -# Table name: namespaces +# Table name: groups # -# 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) -# description :string(255) default(""), not null +# id :integer not null, primary key +# name :string(255) not null +# code :string(255) not null +# owner_id :integer not null +# created_at :datetime not null +# updated_at :datetime not null # require 'spec_helper' @@ -20,15 +18,7 @@ describe Group do it { should have_many :projects } it { should validate_presence_of :name } it { should validate_uniqueness_of(:name) } - it { should validate_presence_of :path } - it { should validate_uniqueness_of(:path) } + it { should validate_presence_of :code } + it { should validate_uniqueness_of(:code) } it { should validate_presence_of :owner } - - describe :users do - it { group.users.should == [group.owner] } - end - - describe :human_name do - it { group.human_name.should == group.name } - end end diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb index 1bc794dc..4c52a094 100644 --- a/spec/models/issue_spec.rb +++ b/spec/models/issue_spec.rb @@ -9,11 +9,11 @@ # project_id :integer # created_at :datetime not null # updated_at :datetime not null +# closed :boolean default(FALSE), not null # position :integer default(0) # branch_name :string(255) # description :text # milestone_id :integer -# state :string(255) # require 'spec_helper' @@ -28,8 +28,13 @@ describe Issue do it { should_not allow_mass_assignment_of(:project_id) } end + describe "Validation" do + it { should ensure_length_of(:description).is_within(0..2000) } + end + describe 'modules' do - it { should include_module(Issuable) } + it { should include_module(IssueCommonality) } + it { should include_module(Votes) } end subject { create(:issue) } @@ -44,15 +49,34 @@ describe Issue do end end - describe '#is_being_reassigned?' do - it 'returnes issues assigned to user' do - user = create :user + describe '#is_being_closed?' do + it 'returns true if the closed attribute has changed and is now true' do + subject.closed = true + subject.is_being_closed?.should be_true + end + it 'returns false if the closed attribute has changed and is now false' do + issue = create(:closed_issue) + issue.closed = false + issue.is_being_closed?.should be_false + end + it 'returns false if the closed attribute has not changed' do + subject.is_being_closed?.should be_false + end + end - 2.times do - issue = create :issue, assignee: user - end - Issue.open_for(user).count.should eq 2 + describe '#is_being_reopened?' do + it 'returns true if the closed attribute has changed and is now false' do + issue = create(:closed_issue) + issue.closed = false + issue.is_being_reopened?.should be_true + end + it 'returns false if the closed attribute has changed and is now true' do + subject.closed = true + subject.is_being_reopened?.should be_false + end + it 'returns false if the closed attribute has not changed' do + subject.is_being_reopened?.should be_false end end end diff --git a/spec/models/key_spec.rb b/spec/models/key_spec.rb index a9ab2f05..6d2310df 100644 --- a/spec/models/key_spec.rb +++ b/spec/models/key_spec.rb @@ -46,9 +46,9 @@ describe Key do key.should_not be_valid end - it "does not accept the same key for another project" do + it "does accept the same key for another project" do key = build(:key, project_id: 0) - key.should_not be_valid + key.should be_valid end end @@ -73,12 +73,8 @@ describe Key do build(:key, user: user).should be_valid end - it "rejects the unfingerprintable key (contains space in middle)" do + it "rejects the unfingerprintable key" do build(:key_with_a_space_in_the_middle).should_not be_valid end - - it "rejects the unfingerprintable key (not a key)" do - build(:invalid_key).should_not be_valid - end end end diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index c64c053b..d70647f6 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -9,13 +9,14 @@ # author_id :integer # assignee_id :integer # title :string(255) +# closed :boolean default(FALSE), not null # created_at :datetime not null # updated_at :datetime not null # st_commits :text(2147483647) # st_diffs :text(2147483647) +# merged :boolean default(FALSE), not null +# state :integer default(1), not null # milestone_id :integer -# state :string(255) -# merge_status :string(255) # require 'spec_helper' @@ -31,26 +32,17 @@ describe MergeRequest do it { should_not allow_mass_assignment_of(:project_id) } end - describe "Respond to" do - it { should respond_to(:unchecked?) } - it { should respond_to(:can_be_merged?) } - it { should respond_to(:cannot_be_merged?) } - end - describe 'modules' do - it { should include_module(Issuable) } - end - - describe "#mr_and_commit_notes" do - + it { should include_module(IssueCommonality) } + it { should include_module(Votes) } end describe "#mr_and_commit_notes" do let!(:merge_request) { create(:merge_request) } before do - merge_request.stub(:commits) { [merge_request.project.repository.commit] } - create(:note, commit_id: merge_request.commits.first.id, noteable_type: 'Commit') + merge_request.stub(:commits) { [merge_request.project.commit] } + create(:note, noteable: merge_request.commits.first) create(:note, noteable: merge_request) end @@ -71,4 +63,35 @@ describe MergeRequest do subject.is_being_reassigned?.should be_false end end + + describe '#is_being_closed?' do + it 'returns true if the closed attribute has changed and is now true' do + subject.closed = true + subject.is_being_closed?.should be_true + end + it 'returns false if the closed attribute has changed and is now false' do + merge_request = create(:closed_merge_request) + merge_request.closed = false + merge_request.is_being_closed?.should be_false + end + it 'returns false if the closed attribute has not changed' do + subject.is_being_closed?.should be_false + end + end + + + describe '#is_being_reopened?' do + it 'returns true if the closed attribute has changed and is now false' do + merge_request = create(:closed_merge_request) + merge_request.closed = false + merge_request.is_being_reopened?.should be_true + end + it 'returns false if the closed attribute has changed and is now true' do + subject.closed = true + subject.is_being_reopened?.should be_false + end + it 'returns false if the closed attribute has not changed' do + subject.is_being_reopened?.should be_false + end + end end diff --git a/spec/models/milestone_spec.rb b/spec/models/milestone_spec.rb index f74fea01..431985d0 100644 --- a/spec/models/milestone_spec.rb +++ b/spec/models/milestone_spec.rb @@ -7,9 +7,9 @@ # project_id :integer not null # description :text # due_date :date +# closed :boolean default(FALSE), not null # created_at :datetime not null # updated_at :datetime not null -# state :string(255) # require 'spec_helper' @@ -27,6 +27,7 @@ describe Milestone do describe "Validation" do it { should validate_presence_of(:title) } it { should validate_presence_of(:project) } + it { should ensure_inclusion_of(:closed).in_array([true, false]) } end let(:milestone) { create(:milestone) } @@ -39,8 +40,7 @@ describe Milestone do end it "should count closed issues" do - IssueObserver.current_user = issue.author - issue.close + issue.update_attributes(closed: true) milestone.issues << issue milestone.percent_complete.should == 100 end @@ -62,82 +62,4 @@ describe Milestone do milestone.expires_at.should be_present end end - - describe :expired? do - context "expired" do - before do - milestone.stub(due_date: Date.today.prev_year) - end - - it { milestone.expired?.should be_true } - end - - context "not expired" do - before do - milestone.stub(due_date: Date.today.next_year) - end - - it { milestone.expired?.should be_false } - end - end - - describe :percent_complete do - before do - milestone.stub( - closed_items_count: 3, - total_items_count: 4 - ) - end - - it { milestone.percent_complete.should == 75 } - end - - describe :items_count do - before do - milestone.issues << create(:issue) - milestone.issues << create(:closed_issue) - milestone.merge_requests << create(:merge_request) - end - - it { milestone.closed_items_count.should == 1 } - it { milestone.open_items_count.should == 2 } - it { milestone.total_items_count.should == 3 } - it { milestone.is_empty?.should be_false } - end - - describe :can_be_closed? do - it { milestone.can_be_closed?.should be_true } - end - - describe :is_empty? do - before do - issue = create :closed_issue, milestone: milestone - merge_request = create :merge_request, milestone: milestone - end - - it 'Should return total count of issues and merge requests assigned to milestone' do - milestone.total_items_count.should eq 2 - end - end - - describe :can_be_closed? do - before do - milestone = create :milestone - create :closed_issue, milestone: milestone - - issue = create :issue - end - - it 'should be true if milestone active and all nestied issues closed' do - milestone.can_be_closed?.should be_true - end - - it 'should be false if milestone active and not all nestied issues closed' do - issue.milestone = milestone - issue.save - - milestone.can_be_closed?.should be_false - end - end - end diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb deleted file mode 100644 index b40a0795..00000000 --- a/spec/models/namespace_spec.rb +++ /dev/null @@ -1,79 +0,0 @@ -# == Schema Information -# -# 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) -# description :string(255) default(""), not null -# - -require 'spec_helper' - -describe Namespace do - let!(:namespace) { create(:namespace) } - - it { should have_many :projects } - it { should validate_presence_of :name } - it { should validate_uniqueness_of(:name) } - it { should validate_presence_of :path } - it { should validate_uniqueness_of(:path) } - it { should validate_presence_of :owner } - - describe "Mass assignment" do - it { should allow_mass_assignment_of(:name) } - it { should allow_mass_assignment_of(:path) } - end - - describe "Respond to" do - it { should respond_to(:human_name) } - it { should respond_to(:to_param) } - end - - it { Namespace.global_id.should == 'GLN' } - - describe :to_param do - it { namespace.to_param.should == namespace.path } - end - - describe :human_name do - it { namespace.human_name.should == namespace.owner_name } - end - - describe :search do - before do - @namespace = create :namespace - end - - it { Namespace.search(@namespace.path).should == [@namespace] } - it { Namespace.search('unknown').should == [] } - end - - describe :move_dir do - before do - @namespace = create :namespace - @namespace.stub(path_changed?: true) - end - - it "should raise error when dirtory exists" do - expect { @namespace.move_dir }.to raise_error("namespace directory cannot be moved") - end - - it "should move dir if path changed" do - new_path = @namespace.path + "_new" - @namespace.stub(path_was: @namespace.path) - @namespace.stub(path: new_path) - @namespace.move_dir.should be_true - end - end - - describe :rm_dir do - it "should remove dir" do - namespace.rm_dir.should be_true - end - end -end diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb index 52e24a78..4f9352b9 100644 --- a/spec/models/note_spec.rb +++ b/spec/models/note_spec.rb @@ -4,6 +4,7 @@ # # id :integer not null, primary key # note :text +# noteable_id :string(255) # noteable_type :string(255) # author_id :integer # created_at :datetime not null @@ -11,8 +12,6 @@ # project_id :integer # attachment :string(255) # line_code :string(255) -# commit_id :string(255) -# noteable_id :integer # require 'spec_helper' @@ -34,112 +33,89 @@ describe Note do it { should validate_presence_of(:project) } end + describe "Scopes" do + it "should have a today named scope that returns ..." do + Note.today.where_values.should == ["created_at >= '#{Date.today}'"] + end + end + describe "Voting score" do let(:project) { create(:project) } it "recognizes a neutral note" do - note = create(:votable_note, note: "This is not a +1 note") + note = create(:note, note: "This is not a +1 note") note.should_not be_upvote note.should_not be_downvote end it "recognizes a neutral emoji note" do - note = build(:votable_note, note: "I would :+1: this, but I don't want to") + note = build(:note, note: "I would :+1: this, but I don't want to") note.should_not be_upvote note.should_not be_downvote end it "recognizes a +1 note" do - note = create(:votable_note, note: "+1 for this") + note = create(:note, note: "+1 for this") note.should be_upvote end it "recognizes a +1 emoji as a vote" do - note = build(:votable_note, note: ":+1: for this") + note = build(:note, note: ":+1: for this") note.should be_upvote end it "recognizes a -1 note" do - note = create(:votable_note, note: "-1 for this") + note = create(:note, note: "-1 for this") note.should be_downvote end it "recognizes a -1 emoji as a vote" do - note = build(:votable_note, note: ":-1: for this") + note = build(:note, note: ":-1: for this") note.should be_downvote end end let(:project) { create(:project) } - let(:commit) { project.repository.commit } + let(:commit) { project.commit } describe "Commit notes" do - let!(:note) { create(:note_on_commit, note: "+1 from me") } - let!(:commit) { note.noteable } + before do + @note = create(:note, + noteable_id: commit.id, + noteable_type: "Commit") + end it "should be accessible through #noteable" do - note.commit_id.should == commit.id - note.noteable.should be_a(Commit) - note.noteable.should == commit + @note.noteable_id.should == commit.id + @note.noteable.should be_a(Commit) + @note.noteable.should == commit end it "should save a valid note" do - note.commit_id.should == commit.id - note.noteable == commit + @note.noteable_id.should == commit.id + @note.noteable == commit end it "should be recognized by #for_commit?" do - note.should be_for_commit - end - - it "should not be votable" do - note.should_not be_votable + @note.should be_for_commit end end - describe "Commit diff line notes" do - let!(:note) { create(:note_on_commit_diff, note: "+1 from me") } - let!(:commit) { note.noteable } + describe "Pre-line commit notes" do + before do + @note = create(:note, + noteable_id: commit.id, + noteable_type: "Commit", + line_code: "0_16_1") + end it "should save a valid note" do - note.commit_id.should == commit.id - note.noteable.id.should == commit.id + @note.noteable_id.should == commit.id + @note.noteable.id.should == commit.id end it "should be recognized by #for_diff_line?" do - note.should be_for_diff_line - end - - it "should be recognized by #for_commit_diff_line?" do - note.should be_for_commit_diff_line - end - - it "should not be votable" do - note.should_not be_votable - end - end - - describe "Issue notes" do - let!(:note) { create(:note_on_issue, note: "+1 from me") } - - it "should not be votable" do - note.should be_votable - end - end - - describe "Merge request notes" do - let!(:note) { create(:note_on_merge_request, note: "+1 from me") } - - it "should not be votable" do - note.should be_votable - end - end - - describe "Merge request diff line notes" do - let!(:note) { create(:note_on_merge_request_diff, note: "+1 from me") } - - it "should not be votable" do - note.should_not be_votable + @note.should be_for_diff_line end end diff --git a/spec/models/project_hooks_spec.rb b/spec/models/project_hooks_spec.rb new file mode 100644 index 00000000..7c8f05b1 --- /dev/null +++ b/spec/models/project_hooks_spec.rb @@ -0,0 +1,123 @@ +require 'spec_helper' + +describe Project, "Hooks" do + let(:project) { create(:project) } + before do + @key = create(:key, user: project.owner) + @user = @key.user + @key_id = @key.identifier + end + + describe "Post Receive Event" do + it "should create push event" do + oldrev, newrev, ref = '00000000000000000000000000000000', 'newrev', 'refs/heads/master' + data = project.post_receive_data(oldrev, newrev, ref, @user) + + project.observe_push(data) + event = Event.last + + event.should_not be_nil + event.project.should == project + event.action.should == Event::Pushed + event.data.should == data + end + end + + describe "Project hooks" do + context "with no web hooks" do + it "raises no errors" do + lambda { + project.execute_hooks({}) + }.should_not raise_error + end + end + + context "with web hooks" do + before do + @project_hook = create(:project_hook) + @project_hook_2 = create(:project_hook) + project.hooks << [@project_hook, @project_hook_2] + end + + it "executes multiple web hook" do + @project_hook.should_receive(:execute).once + @project_hook_2.should_receive(:execute).once + + project.trigger_post_receive('oldrev', 'newrev', 'refs/heads/master', @user) + end + end + + context "does not execute web hooks" do + before do + @project_hook = create(:project_hook) + project.hooks << [@project_hook] + end + + it "when pushing a branch for the first time" do + @project_hook.should_not_receive(:execute) + project.trigger_post_receive('00000000000000000000000000000000', 'newrev', 'refs/heads/master', @user) + end + + it "when pushing tags" do + @project_hook.should_not_receive(:execute) + project.trigger_post_receive('oldrev', 'newrev', 'refs/tags/v1.0.0', @user) + end + end + + context "when pushing new branches" do + + end + + context "when gathering commit data" do + before do + @oldrev, @newrev, @ref = project.fresh_commits(2).last.sha, project.fresh_commits(2).first.sha, 'refs/heads/master' + @commit = project.fresh_commits(2).first + + # Fill nil/empty attributes + project.description = "This is a description" + + @data = project.post_receive_data(@oldrev, @newrev, @ref, @user) + end + + subject { @data } + + it { should include(before: @oldrev) } + it { should include(after: @newrev) } + it { should include(ref: @ref) } + it { should include(user_id: project.owner.id) } + it { should include(user_name: project.owner.name) } + + context "with repository data" do + subject { @data[:repository] } + + it { should include(name: project.name) } + it { should include(url: project.web_url) } + it { should include(description: project.description) } + it { should include(homepage: project.web_url) } + end + + context "with commits" do + subject { @data[:commits] } + + it { should be_an(Array) } + it { should have(1).element } + + context "the commit" do + subject { @data[:commits].first } + + it { should include(id: @commit.id) } + it { should include(message: @commit.safe_message) } + it { should include(timestamp: @commit.date.xmlschema) } + it { should include(url: "#{Gitlab.config.url}/#{project.code}/commits/#{@commit.id}") } + + context "with a author" do + subject { @data[:commits].first[:author] } + + it { should include(name: @commit.author_name) } + it { should include(email: @commit.author_email) } + end + end + end + end + end +end diff --git a/spec/models/project_security_spec.rb b/spec/models/project_security_spec.rb index 1f2bd7a5..60f8d45c 100644 --- a/spec/models/project_security_spec.rb +++ b/spec/models/project_security_spec.rb @@ -4,109 +4,38 @@ describe Project do describe :authorization do before do @p1 = create(:project) - @u1 = create(:user) @u2 = create(:user) - @u3 = create(:user) - @u4 = @p1.owner - @abilities = Six.new @abilities << Ability end - let(:guest_actions) { Ability.project_guest_rules } - let(:report_actions) { Ability.project_report_rules } - let(:dev_actions) { Ability.project_dev_rules } - let(:master_actions) { Ability.project_master_rules } - let(:admin_actions) { Ability.project_admin_rules } - - describe "Non member rules" do - it "should deny for non-project users any actions" do - admin_actions.each do |action| - @abilities.allowed?(@u1, action, @p1).should be_false - end - end - end - - describe "Guest Rules" do - before do - @p1.users_projects.create(project: @p1, user: @u2, project_access: UsersProject::GUEST) - end - - it "should allow for project user any guest actions" do - guest_actions.each do |action| - @abilities.allowed?(@u2, action, @p1).should be_true - end - end - end - - describe "Report Rules" do + describe "read access" do before do @p1.users_projects.create(project: @p1, user: @u2, project_access: UsersProject::REPORTER) end - it "should allow for project user any report actions" do - report_actions.each do |action| - @abilities.allowed?(@u2, action, @p1).should be_true - end - end + it { @abilities.allowed?(@u1, :read_project, @p1).should be_false } + it { @abilities.allowed?(@u2, :read_project, @p1).should be_true } end - describe "Developer Rules" do - before do - @p1.users_projects.create(project: @p1, user: @u2, project_access: UsersProject::REPORTER) - @p1.users_projects.create(project: @p1, user: @u3, project_access: UsersProject::DEVELOPER) - end - - it "should deny for developer master-specific actions" do - [dev_actions - report_actions].each do |action| - @abilities.allowed?(@u2, action, @p1).should be_false - end - end - - it "should allow for project user any dev actions" do - dev_actions.each do |action| - @abilities.allowed?(@u3, action, @p1).should be_true - end - end - end - - describe "Master Rules" do + describe "write access" do before do @p1.users_projects.create(project: @p1, user: @u2, project_access: UsersProject::DEVELOPER) - @p1.users_projects.create(project: @p1, user: @u3, project_access: UsersProject::MASTER) end - it "should deny for developer master-specific actions" do - [master_actions - dev_actions].each do |action| - @abilities.allowed?(@u2, action, @p1).should be_false - end - end - - it "should allow for project user any master actions" do - master_actions.each do |action| - @abilities.allowed?(@u3, action, @p1).should be_true - end - end + it { @abilities.allowed?(@u1, :write_project, @p1).should be_false } + it { @abilities.allowed?(@u2, :write_project, @p1).should be_true } end - describe "Admin Rules" do + describe "admin access" do before do - @p1.users_projects.create(project: @p1, user: @u2, project_access: UsersProject::DEVELOPER) - @p1.users_projects.create(project: @p1, user: @u3, project_access: UsersProject::MASTER) + @p1.users_projects.create(project: @p1, user: @u1, project_access: UsersProject::DEVELOPER) + @p1.users_projects.create(project: @p1, user: @u2, project_access: UsersProject::MASTER) end - it "should deny for masters admin-specific actions" do - [admin_actions - master_actions].each do |action| - @abilities.allowed?(@u2, action, @p1).should be_false - end - end - - it "should allow for project owner any admin actions" do - admin_actions.each do |action| - @abilities.allowed?(@u4, action, @p1).should be_true - end - end + it { @abilities.allowed?(@u1, :admin_project, @p1).should be_false } + it { @abilities.allowed?(@u2, :admin_project, @p1).should be_true } end end end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index cbc7f278..5bcab924 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -8,17 +8,15 @@ # description :text # created_at :datetime not null # updated_at :datetime not null -# creator_id :integer +# private_flag :boolean default(TRUE), not null +# code :string(255) +# owner_id :integer # default_branch :string(255) # issues_enabled :boolean default(TRUE), not null # wall_enabled :boolean default(TRUE), not null # merge_requests_enabled :boolean default(TRUE), not null # wiki_enabled :boolean default(TRUE), not null -# namespace_id :integer -# public :boolean default(FALSE), not null -# issues_tracker :string(255) default("gitlab"), not null -# issues_tracker_id :string(255) -# snippets_enabled :boolean default(TRUE), not null +# group_id :integer # require 'spec_helper' @@ -26,8 +24,7 @@ require 'spec_helper' describe Project do describe "Associations" do it { should belong_to(:group) } - it { should belong_to(:namespace) } - it { should belong_to(:creator).class_name('User') } + it { should belong_to(:owner).class_name('User') } it { should have_many(:users) } it { should have_many(:events).dependent(:destroy) } it { should have_many(:merge_requests).dependent(:destroy) } @@ -43,8 +40,8 @@ describe Project do end describe "Mass assignment" do - it { should_not allow_mass_assignment_of(:namespace_id) } - it { should_not allow_mass_assignment_of(:creator_id) } + it { should_not allow_mass_assignment_of(:owner_id) } + it { should_not allow_mass_assignment_of(:private_flag) } end describe "Validation" do @@ -57,42 +54,117 @@ describe Project do it { should validate_presence_of(:path) } it { should validate_uniqueness_of(:path) } it { should ensure_length_of(:path).is_within(0..255) } + # TODO: Formats + it { should ensure_length_of(:description).is_within(0..2000) } - it { should validate_presence_of(:creator) } + + it { should validate_presence_of(:code) } + it { should validate_uniqueness_of(:code) } + it { should ensure_length_of(:code).is_within(1..255) } + # TODO: Formats + + it { should validate_presence_of(:owner) } it { should ensure_inclusion_of(:issues_enabled).in_array([true, false]) } 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)) + project.stub(:owner).and_return(double(can_create_project?: false, projects_limit: 1)) project.should_not be_valid - project.errors[:limit_reached].first.should match(/Your own projects limit is 1/) + project.errors[:base].first.should match(/Your own projects limit is 1/) + end + + it "should not allow 'gitolite-admin' as repo name" do + should allow_value("blah").for(:path) + should_not allow_value("gitolite-admin").for(:path) end end describe "Respond to" do + it { should respond_to(:public?) } + it { should respond_to(:private?) } it { should respond_to(:url_to_repo) } + it { should respond_to(:path_to_repo) } + it { should respond_to(:valid_repo?) } it { should respond_to(:repo_exists?) } + + # Repository Role + it { should respond_to(:tree) } + it { should respond_to(:root_ref) } + it { should respond_to(:repo) } + it { should respond_to(:tags) } + it { should respond_to(:commit) } + it { should respond_to(:commits) } + it { should respond_to(:commits_between) } + it { should respond_to(:commits_with_refs) } + it { should respond_to(:commits_since) } + it { should respond_to(:commits_between) } it { should respond_to(:satellite) } + it { should respond_to(:update_repository) } + it { should respond_to(:destroy_repository) } + it { should respond_to(:archive_repo) } + + # Authority Role + it { should respond_to(:add_access) } + it { should respond_to(:reset_access) } + it { should respond_to(:repository_writers) } + it { should respond_to(:repository_masters) } + it { should respond_to(:repository_readers) } + it { should respond_to(:allow_read_for?) } + it { should respond_to(:guest_access_for?) } + it { should respond_to(:report_access_for?) } + it { should respond_to(:dev_access_for?) } + it { should respond_to(:master_access_for?) } + + # Team Role + it { should respond_to(:team_member_by_name_or_email) } + it { should respond_to(:team_member_by_id) } + it { should respond_to(:add_user_to_team) } + it { should respond_to(:add_users_to_team) } + it { should respond_to(:add_user_id_to_team) } + it { should respond_to(:add_users_ids_to_team) } + + # Project Push Role + it { should respond_to(:observe_push) } it { should respond_to(:update_merge_requests) } it { should respond_to(:execute_hooks) } - it { should respond_to(:transfer) } - it { should respond_to(:name_with_namespace) } - it { should respond_to(:namespace_owner) } - it { should respond_to(:owner) } - it { should respond_to(:path_with_namespace) } + it { should respond_to(:post_receive_data) } + it { should respond_to(:trigger_post_receive) } + end + + describe 'modules' do + it { should include_module(Repository) } + it { should include_module(PushObserver) } + it { should include_module(Authority) } + it { should include_module(Team) } end it "should return valid url to repo" do project = Project.new(path: "somewhere") - project.url_to_repo.should == Gitlab.config.gitlab_shell.ssh_path_prefix + "somewhere.git" + project.url_to_repo.should == Gitlab.config.ssh_path + "somewhere.git" + end + + it "should return path to repo" do + project = Project.new(path: "somewhere") + project.path_to_repo.should == Rails.root.join("tmp", "repositories", "somewhere") end it "returns the full web URL for this repo" do - project = Project.new(path: "somewhere") - project.web_url.should == "#{Gitlab.config.gitlab.url}/somewhere" + project = Project.new(code: "somewhere") + project.web_url.should == "#{Gitlab.config.url}/somewhere" + end + + describe :valid_repo? do + it "should be valid repo" do + project = create(:project) + project.valid_repo?.should be_true + end + + it "should be invalid repo" do + project = Project.new(name: "ok_name", path: "/INVALID_PATH/", code: "NEOK") + project.valid_repo?.should be_false + end end describe "last_activity methods" do @@ -118,11 +190,93 @@ describe Project do end end + describe "fresh commits" do + let(:project) { create(:project) } + + it { project.fresh_commits(3).count.should == 3 } + it { project.fresh_commits.first.id.should == "bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a" } + it { project.fresh_commits.last.id.should == "f403da73f5e62794a0447aca879360494b08f678" } + end + + describe "commits_between" do + let(:project) { create(:project) } + + subject do + commits = project.commits_between("3a4b4fb4cde7809f033822a171b9feae19d41fff", + "8470d70da67355c9c009e4401746b1d5410af2e3") + commits.map { |c| c.id } + end + + it { should have(3).elements } + it { should include("f0f14c8eaba69ebddd766498a9d0b0e79becd633") } + it { should_not include("bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a") } + end + + describe "Git methods" do + let(:project) { create(:project) } + + describe :repo do + it "should return valid repo" do + project.repo.should be_kind_of(Grit::Repo) + end + + it "should return nil" do + lambda { Project.new(path: "invalid").repo }.should raise_error(Grit::NoSuchPathError) + end + + it "should return nil" do + lambda { Project.new.repo }.should raise_error(TypeError) + end + end + + describe :commit do + it "should return first head commit if without params" do + project.commit.id.should == project.repo.commits.first.id + end + + it "should return valid commit" do + project.commit(ValidCommit::ID).should be_valid_commit + end + + it "should return nil" do + project.commit("+123_4532530XYZ").should be_nil + end + end + + describe :tree do + before do + @commit = project.commit(ValidCommit::ID) + end + + it "should raise error w/o arguments" do + lambda { project.tree }.should raise_error + end + + it "should return root tree for commit" do + tree = project.tree(@commit) + tree.contents.size.should == ValidCommit::FILES_COUNT + tree.contents.map(&:name).should == ValidCommit::FILES + end + + it "should return root tree for commit with correct path" do + tree = project.tree(@commit, ValidCommit::C_FILE_PATH) + tree.contents.map(&:name).should == ValidCommit::C_FILES + end + + it "should return root tree for commit with incorrect path" do + project.tree(@commit, "invalid_path").should be_nil + end + end + end + describe :update_merge_requests do - let(:project) { create(:project_with_code) } + let(:project) { create(:project) } before do - @merge_request = create(:merge_request, project: project) + @merge_request = create(:merge_request, + project: project, + merged: false, + closed: false) @key = create(:key, user_id: project.owner.id) end @@ -131,7 +285,8 @@ describe Project do @merge_request.last_commit.id.should == "bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a" project.update_merge_requests("8716fc78f3c65bbf7bcf7b574febd583bc5d2812", "bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a", "refs/heads/stable", @key.user) @merge_request.reload - @merge_request.merged?.should be_true + @merge_request.merged.should be_true + @merge_request.closed.should be_true end it "should update merge request commits with new one if pushed to source branch" do @@ -141,117 +296,4 @@ describe Project do @merge_request.last_commit.id.should == "bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a" end end - - - describe :find_with_namespace do - context 'with namespace' do - before do - @group = create :group, name: 'gitlab' - @project = create(:project, name: 'gitlab-ci', namespace: @group) - end - - it { Project.find_with_namespace('gitlab/gitlab-ci').should == @project } - it { Project.find_with_namespace('gitlab-ci').should be_nil } - end - - context 'w/o namespace' do - before do - @project = create(:project, name: 'gitlab-ci') - end - - it { Project.find_with_namespace('gitlab-ci').should == @project } - it { Project.find_with_namespace('gitlab/gitlab-ci').should be_nil } - end - end - - describe :to_param do - context 'with namespace' do - before do - @group = create :group, name: 'gitlab' - @project = create(:project, name: 'gitlab-ci', namespace: @group) - end - - it { @project.to_param.should == "gitlab/gitlab-ci" } - end - - context 'w/o namespace' do - before do - @project = create(:project, name: 'gitlab-ci') - end - - it { @project.to_param.should == "gitlab-ci" } - end - end - - describe :repository do - let(:project) { create(:project) } - - it "should return valid repo" do - project.repository.should be_kind_of(Repository) - 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 - - describe :open_branches do - let(:project) { create(:project_with_code) } - - before do - project.protected_branches.create(name: 'master') - end - - it { project.open_branches.map(&:name).should include('bootstrap') } - it { project.open_branches.map(&:name).should_not include('master') } - end end diff --git a/spec/models/project_team_spec.rb b/spec/models/project_team_spec.rb deleted file mode 100644 index 3e3543e8..00000000 --- a/spec/models/project_team_spec.rb +++ /dev/null @@ -1,15 +0,0 @@ -require "spec_helper" - -describe ProjectTeam do - let(:team) { create(:project).team } - - describe "Respond to" do - subject { team } - - it { should respond_to(:developers) } - it { should respond_to(:masters) } - it { should respond_to(:reporters) } - it { should respond_to(:guests) } - end -end - diff --git a/spec/models/protected_branch_spec.rb b/spec/models/protected_branch_spec.rb index 6e830393..7340ce50 100644 --- a/spec/models/protected_branch_spec.rb +++ b/spec/models/protected_branch_spec.rb @@ -24,4 +24,28 @@ describe ProtectedBranch do it { should validate_presence_of(:project) } it { should validate_presence_of(:name) } end + + describe 'Callbacks' do + let(:branch) { build(:protected_branch) } + + it 'call update_repository after save' do + branch.should_receive(:update_repository) + branch.save + end + + it 'call update_repository after destroy' do + branch.save + branch.should_receive(:update_repository) + branch.destroy + end + end + + describe '#commit' do + let(:branch) { create(:protected_branch) } + + it 'commits itself to its project' do + branch.project.should_receive(:commit).with(branch.name) + branch.commit + end + end end diff --git a/spec/models/snippet_spec.rb b/spec/models/snippet_spec.rb index e4d19348..b474d88c 100644 --- a/spec/models/snippet_spec.rb +++ b/spec/models/snippet_spec.rb @@ -38,5 +38,6 @@ describe Snippet do it { should ensure_length_of(:title).is_within(0..255) } it { should validate_presence_of(:content) } + it { should ensure_length_of(:content).is_within(0..10_000) } end end diff --git a/spec/models/system_hook_spec.rb b/spec/models/system_hook_spec.rb index a9ed6a5f..9d03b56c 100644 --- a/spec/models/system_hook_spec.rb +++ b/spec/models/system_hook_spec.rb @@ -23,40 +23,53 @@ describe SystemHook do end it "project_create hook" do - project = create(:project) + with_resque do + project = create(:project) + end WebMock.should have_requested(:post, @system_hook.url).with(body: /project_create/).once end it "project_destroy hook" do project = create(:project) - project.destroy + with_resque do + project.destroy + end WebMock.should have_requested(:post, @system_hook.url).with(body: /project_destroy/).once end it "user_create hook" do - create(:user) + with_resque do + create(:user) + end WebMock.should have_requested(:post, @system_hook.url).with(body: /user_create/).once end it "user_destroy hook" do user = create(:user) - user.destroy + with_resque do + user.destroy + end WebMock.should have_requested(:post, @system_hook.url).with(body: /user_destroy/).once end it "project_create hook" do user = create(:user) project = create(:project) - project.team << [user, :master] + with_resque do + project.users << user + end WebMock.should have_requested(:post, @system_hook.url).with(body: /user_add_to_team/).once end it "project_destroy hook" do user = create(:user) project = create(:project) - project.team << [user, :master] - project.users_projects.clear + project.users << user + with_resque do + project.users_projects.clear + end WebMock.should have_requested(:post, @system_hook.url).with(body: /user_remove_from_team/).once end end + end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 4e276dea..4ac699b1 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -22,27 +22,23 @@ # linkedin :string(255) default(""), not null # twitter :string(255) default(""), not null # authentication_token :string(255) +# dark_scheme :boolean default(FALSE), not null # theme_id :integer default(1), not null # bio :string(255) +# blocked :boolean default(FALSE), not null # failed_attempts :integer default(0) # locked_at :datetime # extern_uid :string(255) # provider :string(255) -# username :string(255) -# can_create_group :boolean default(TRUE), not null -# can_create_team :boolean default(TRUE), not null -# state :string(255) -# color_scheme_id :integer default(1), not null -# notification_level :integer default(1), not null # require 'spec_helper' describe User do describe "Associations" do - it { should have_one(:namespace) } it { should have_many(:users_projects).dependent(:destroy) } - it { should have_many(:groups) } + it { should have_many(:projects) } + it { should have_many(:my_own_projects).class_name('Project') } it { should have_many(:keys).dependent(:destroy) } it { should have_many(:events).class_name('Event').dependent(:destroy) } it { should have_many(:recent_events).class_name('Event') } @@ -59,7 +55,6 @@ describe User do end describe 'validations' do - it { should validate_presence_of(:username) } it { should validate_presence_of(:projects_limit) } it { should validate_numericality_of(:projects_limit) } it { should allow_value(0).for(:projects_limit) } @@ -70,10 +65,28 @@ describe User do describe "Respond to" do it { should respond_to(:is_admin?) } + it { should respond_to(:identifier) } it { should respond_to(:name) } it { should respond_to(:private_token) } end + describe '#identifier' do + it "should return valid identifier" do + user = build(:user, email: "test@mail.com") + user.identifier.should == "test_mail_com" + end + + it "should return identifier without + sign" do + user = build(:user, email: "test+foo@mail.com") + user.identifier.should == "test_foo_mail_com" + end + + it "should conform to Gitolite's required identifier pattern" do + user = build(:user, email: "_test@example.com") + user.identifier.should == 'test_example_com' + end + end + describe '#generate_password' do it "should execute callback when force_random_password specified" do user = build(:user, force_random_password: true) @@ -99,83 +112,4 @@ describe User do user.authentication_token.should_not be_blank end end - - describe 'projects' do - before do - ActiveRecord::Base.observers.enable(:user_observer) - @user = create :user - @project = create :project, namespace: @user.namespace - end - - it { @user.authorized_projects.should include(@project) } - it { @user.owned_projects.should include(@project) } - it { @user.personal_projects.should include(@project) } - end - - describe 'groups' do - before do - ActiveRecord::Base.observers.enable(:user_observer) - @user = create :user - @group = create :group, owner: @user - end - - it { @user.several_namespaces?.should be_true } - it { @user.namespaces.should == [@user.namespace, @group] } - it { @user.authorized_groups.should == [@group] } - it { @user.owned_groups.should == [@group] } - end - - describe 'namespaced' do - before do - ActiveRecord::Base.observers.enable(:user_observer) - @user = create :user - @project = create :project, namespace: @user.namespace - end - - it { @user.several_namespaces?.should be_false } - it { @user.namespaces.should == [@user.namespace] } - end - - describe 'blocking user' do - let(:user) { create(:user, name: 'John Smith') } - - it "should block user" do - user.block - user.blocked?.should be_true - end - end - - describe 'filter' do - before do - User.delete_all - @user = create :user - @admin = create :user, admin: true - @blocked = create :user, state: :blocked - end - - it { User.filter("admins").should == [@admin] } - it { User.filter("blocked").should == [@blocked] } - it { User.filter("wop").should == [@user, @admin, @blocked] } - it { User.filter(nil).should == [@user, @admin] } - end - - describe :not_in_project do - before do - User.delete_all - @user = create :user - @project = create :project - end - - it { User.not_in_project(@project).should == [@user, @project.owner] } - end - - describe 'normal user' do - let(:user) { create(:user, name: 'John Smith') } - - it { user.is_admin?.should be_false } - it { user.require_ssh_key?.should be_true } - it { user.can_create_group?.should be_true } - it { user.can_create_project?.should be_true } - it { user.first_name.should == 'John' } - end end diff --git a/spec/models/user_team_project_relationship_spec.rb b/spec/models/user_team_project_relationship_spec.rb deleted file mode 100644 index 86150cf3..00000000 --- a/spec/models/user_team_project_relationship_spec.rb +++ /dev/null @@ -1,17 +0,0 @@ -# == Schema Information -# -# Table name: user_team_project_relationships -# -# id :integer not null, primary key -# project_id :integer -# user_team_id :integer -# greatest_access :integer -# created_at :datetime not null -# updated_at :datetime not null -# - -require 'spec_helper' - -describe UserTeamProjectRelationship do - pending "add some examples to (or delete) #{__FILE__}" -end diff --git a/spec/models/user_team_spec.rb b/spec/models/user_team_spec.rb deleted file mode 100644 index d330bbf3..00000000 --- a/spec/models/user_team_spec.rb +++ /dev/null @@ -1,37 +0,0 @@ -# == Schema Information -# -# Table name: user_teams -# -# id :integer not null, primary key -# name :string(255) -# path :string(255) -# owner_id :integer -# created_at :datetime not null -# updated_at :datetime not null -# description :string(255) default(""), not null -# - -require 'spec_helper' - -describe UserTeam do - let(:team) { FactoryGirl.create :user_team } - - context ".add_member" do - let(:user) { FactoryGirl.create :user } - - it "should work" do - team.add_member(user, UsersProject::DEVELOPER, false) - team.members.should include(user) - end - end - - context ".remove_member" do - let(:user) { FactoryGirl.create :user } - before { team.add_member(user, UsersProject::DEVELOPER, false) } - - it "should work" do - team.remove_member(user) - team.members.should_not include(user) - end - end -end diff --git a/spec/models/user_team_user_relationship_spec.rb b/spec/models/user_team_user_relationship_spec.rb deleted file mode 100644 index 981ad1e8..00000000 --- a/spec/models/user_team_user_relationship_spec.rb +++ /dev/null @@ -1,18 +0,0 @@ -# == Schema Information -# -# Table name: user_team_user_relationships -# -# id :integer not null, primary key -# user_id :integer -# user_team_id :integer -# group_admin :boolean -# permission :integer -# created_at :datetime not null -# updated_at :datetime not null -# - -require 'spec_helper' - -describe UserTeamUserRelationship do - pending "add some examples to (or delete) #{__FILE__}" -end diff --git a/spec/models/users_project_spec.rb b/spec/models/users_project_spec.rb index e8f5b647..1f896324 100644 --- a/spec/models/users_project_spec.rb +++ b/spec/models/users_project_spec.rb @@ -29,7 +29,6 @@ describe UsersProject do it { should validate_uniqueness_of(:user_id).scoped_to(:project_id).with_message(/already exists/) } it { should validate_presence_of(:project) } - it { should ensure_inclusion_of(:project_access).in_array(UsersProject.access_roles.values) } end describe "Delegate methods" do @@ -48,10 +47,10 @@ describe UsersProject do @user_1 = create :user @user_2 = create :user - @project_1.team << [ @user_1, :developer ] - @project_2.team << [ @user_2, :reporter ] + @project_1.add_access @user_1, :write + @project_2.add_access @user_2, :read - @status = @project_2.team.import(@project_1) + @status = UsersProject.import_team(@project_1, @project_2) end it { @status.should be_true } @@ -69,45 +68,4 @@ describe UsersProject do it { @project_1.users.should_not include(@user_2) } end end - - describe :add_users_into_projects do - before do - @project_1 = create :project - @project_2 = create :project - - @user_1 = create :user - @user_2 = create :user - - UsersProject.add_users_into_projects( - [@project_1.id, @project_2.id], - [@user_1.id, @user_2.id], - UsersProject::MASTER - ) - end - - it { @project_1.users.should include(@user_1) } - it { @project_1.users.should include(@user_2) } - - - it { @project_2.users.should include(@user_1) } - it { @project_2.users.should include(@user_2) } - end - - describe :truncate_teams do - before do - @project_1 = create :project - @project_2 = create :project - - @user_1 = create :user - @user_2 = create :user - - @project_1.team << [ @user_1, :developer] - @project_2.team << [ @user_2, :reporter] - - UsersProject.truncate_teams([@project_1.id, @project_2.id]) - end - - it { @project_1.users.should be_empty } - it { @project_2.users.should be_empty } - end end diff --git a/spec/models/wiki_page_spec.rb b/spec/models/wiki_page_spec.rb deleted file mode 100644 index 67f2a6da..00000000 --- a/spec/models/wiki_page_spec.rb +++ /dev/null @@ -1,164 +0,0 @@ -require "spec_helper" - -describe WikiPage do - - def create_temp_repo(path) - FileUtils.mkdir_p path - command = "git init --quiet #{path};" - system(command) - end - - def remove_temp_repo(path) - FileUtils.rm_rf path - end - - def commit_details - commit = {name: user.name, email: user.email, message: "test commit"} - end - - def create_page(name, content) - wiki.wiki.write_page(name, :markdown, content, commit_details) - end - - def destroy_page(title) - page = wiki.wiki.paged(title) - wiki.wiki.delete_page(page, commit_details) - end - - let(:project) { create(:project) } - let(:repository) { project.repository } - let(:user) { project.owner } - let(:wiki) { GollumWiki.new(project, user) } - - subject { WikiPage.new(wiki) } - - before do - create_temp_repo(wiki.send(:path_to_repo)) - end - - describe "#initialize" do - context "when initialized with an existing gollum page" do - before do - create_page("test page", "test content") - @page = wiki.wiki.paged("test page") - @wiki_page = WikiPage.new(wiki, @page, true) - end - - it "sets the slug attribute" do - @wiki_page.slug.should == "test-page" - end - - it "sets the title attribute" do - @wiki_page.title.should == "test page" - end - - it "sets the formatted content attribute" do - @wiki_page.content.should == "test content" - end - - it "sets the format attribute" do - @wiki_page.format.should == :markdown - end - - it "sets the message attribute" do - @wiki_page.message.should == "test commit" - end - - it "sets the version attribute" do - @wiki_page.version.should be_a Commit - end - end - end - - describe "validations" do - before do - subject.attributes = {title: 'title', content: 'content'} - end - - it "validates presence of title" do - subject.attributes.delete(:title) - subject.valid?.should be_false - end - - it "validates presence of content" do - subject.attributes.delete(:content) - subject.valid?.should be_false - end - end - - before do - @wiki_attr = {title: "Index", content: "Home Page", format: "markdown"} - end - - describe "#create" do - after do - destroy_page("Index") - end - - context "with valid attributes" do - it "saves the wiki page" do - subject.create(@wiki_attr) - wiki.find_page("Index").should_not be_nil - end - - it "returns true" do - subject.create(@wiki_attr).should == true - end - end - end - - describe "#update" do - before do - create_page("Update", "content") - @page = wiki.find_page("Update") - end - - after do - destroy_page("Update") - end - - context "with valid attributes" do - it "updates the content of the page" do - @page.update("new content") - @page = wiki.find_page("Update") - end - - it "returns true" do - @page.update("more content").should be_true - end - end - end - - describe "#destroy" do - before do - create_page("Delete Page", "content") - @page = wiki.find_page("Delete Page") - end - - it "should delete the page" do - @page.delete - wiki.pages.should be_empty - end - - it "should return true" do - @page.delete.should == true - end - end - - describe "#versions" do - before do - create_page("Update", "content") - @page = wiki.find_page("Update") - end - - after do - destroy_page("Update") - end - - it "returns an array of all commits for the page" do - 3.times { |i| @page.update("content #{i}") } - @page.versions.count.should == 4 - end - end - -end diff --git a/spec/observers/activity_observer_spec.rb b/spec/observers/activity_observer_spec.rb index 3d503027..0eec41f4 100644 --- a/spec/observers/activity_observer_spec.rb +++ b/spec/observers/activity_observer_spec.rb @@ -17,7 +17,7 @@ describe ActivityObserver do end it_should_be_valid_event - it { @event.action.should == Event::CREATED } + it { @event.action.should == Event::Created } it { @event.target.should == @merge_request } end @@ -30,21 +30,19 @@ describe ActivityObserver do end it_should_be_valid_event - it { @event.action.should == Event::CREATED } + it { @event.action.should == Event::Created } it { @event.target.should == @issue } end - describe "Issue commented" do - before do - Note.observers.enable :activity_observer do - @issue = create(:issue, project: project) - @note = create(:note, noteable: @issue, project: project, author: @issue.author) - @event = Event.last - end - end + #describe "Issue commented" do + #before do + #@issue = create(:issue, project: project) + #@note = create(:note, noteable: @issue, project: project) + #@event = Event.last + #end - it_should_be_valid_event - it { @event.action.should == Event::COMMENTED } - it { @event.target.should == @note } - end + #it_should_be_valid_event + #it { @event.action.should == Event::Commented } + #it { @event.target.should == @note } + #end end diff --git a/spec/observers/issue_observer_spec.rb b/spec/observers/issue_observer_spec.rb index 539c6550..509c1d02 100644 --- a/spec/observers/issue_observer_spec.rb +++ b/spec/observers/issue_observer_spec.rb @@ -1,19 +1,12 @@ require 'spec_helper' describe IssueObserver do - let(:some_user) { create :user } - let(:assignee) { create :user } - let(:author) { create :user } - let(:mock_issue) { double(:issue, id: 42, assignee: assignee, author: author) } - let(:assigned_issue) { create(:issue, assignee: assignee, author: author) } - let(:unassigned_issue) { create(:issue, author: author) } - let(:closed_assigned_issue) { create(:closed_issue, assignee: assignee, author: author) } - let(:closed_unassigned_issue) { create(:closed_issue, author: author) } - - - before { subject.stub(:current_user).and_return(some_user) } - before { subject.stub(notification: mock('NotificationService').as_null_object) } + let(:some_user) { double(:user, id: 1) } + let(:assignee) { double(:user, id: 2) } + let(:author) { double(:user, id: 3) } + let(:issue) { double(:issue, id: 42, assignee: assignee, author: author) } + before(:each) { subject.stub(:current_user).and_return(some_user) } subject { IssueObserver.instance } @@ -27,57 +20,26 @@ describe IssueObserver do end end - it 'trigger notification to send emails' do - subject.should_receive(:notification) + it 'sends an email to the assignee' do + Notify.should_receive(:new_issue_email).with(issue.id). + and_return(double(deliver: true)) - subject.after_create(mock_issue) - end - end - - context '#after_close' do - context 'a status "closed"' do - it 'note is created if the issue is being closed' do - Note.should_receive(:create_status_change_note).with(assigned_issue, some_user, 'closed') - - assigned_issue.close - end - - it 'trigger notification to send emails' do - subject.should_receive(:notification) - - assigned_issue.close - end - - it 'creates a note' do - Note.should_receive(:create_status_change_note).with(unassigned_issue, some_user, 'closed') - unassigned_issue.close - end + subject.after_create(issue) end - context 'a status "reopened"' do - it 'note is created if the issue is being reopened' do - Note.should_receive(:create_status_change_note).with(closed_assigned_issue, some_user, 'reopened') + it 'does not send an email to the assignee if assignee created the issue' do + subject.stub(:current_user).and_return(assignee) + Notify.should_not_receive(:new_issue_email) - closed_assigned_issue.reopen - end - - it 'trigger notification to send emails' do - subject.should_receive(:notification) - - closed_assigned_issue.reopen - end - - it 'create a note' do - Note.should_receive(:create_status_change_note).with(closed_unassigned_issue, some_user, 'reopened') - - closed_unassigned_issue.reopen - end + subject.after_create(issue) end end context '#after_update' do before(:each) do - mock_issue.stub(:is_being_reassigned?).and_return(false) + issue.stub(:is_being_reassigned?).and_return(false) + issue.stub(:is_being_closed?).and_return(false) + issue.stub(:is_being_reopened?).and_return(false) end it 'is called when an issue is changed' do @@ -90,19 +52,147 @@ describe IssueObserver do end end - context 'notification' do - it 'triggered if the issue is being reassigned' do - mock_issue.should_receive(:is_being_reassigned?).and_return(true) - subject.should_receive(:notification) + context 'a reassigned email' do + it 'is sent if the issue is being reassigned' do + issue.should_receive(:is_being_reassigned?).and_return(true) + subject.should_receive(:send_reassigned_email).with(issue) - subject.after_update(mock_issue) + subject.after_update(issue) end - it 'is not triggered if the issue is not being reassigned' do - mock_issue.should_receive(:is_being_reassigned?).and_return(false) - subject.should_not_receive(:notification) + it 'is not sent if the issue is not being reassigned' do + issue.should_receive(:is_being_reassigned?).and_return(false) + subject.should_not_receive(:send_reassigned_email) - subject.after_update(mock_issue) + subject.after_update(issue) + end + end + + context 'a status "closed"' do + it 'note is created if the issue is being closed' do + issue.should_receive(:is_being_closed?).and_return(true) + Note.should_receive(:create_status_change_note).with(issue, some_user, 'closed') + + subject.after_update(issue) + end + + it 'note is not created if the issue is not being closed' do + issue.should_receive(:is_being_closed?).and_return(false) + Note.should_not_receive(:create_status_change_note).with(issue, some_user, 'closed') + + subject.after_update(issue) + end + + it 'notification is delivered if the issue being closed' do + issue.stub(:is_being_closed?).and_return(true) + Notify.should_receive(:issue_status_changed_email).twice + Note.should_receive(:create_status_change_note).with(issue, some_user, 'closed') + + subject.after_update(issue) + end + + it 'notification is not delivered if the issue not being closed' do + issue.stub(:is_being_closed?).and_return(false) + Notify.should_not_receive(:issue_status_changed_email) + Note.should_not_receive(:create_status_change_note).with(issue, some_user, 'closed') + + subject.after_update(issue) + end + + it 'notification is delivered only to author if the issue being closed' do + issue_without_assignee = double(:issue, id: 42, author: author, assignee: nil) + issue_without_assignee.stub(:is_being_reassigned?).and_return(false) + issue_without_assignee.stub(:is_being_closed?).and_return(true) + issue_without_assignee.stub(:is_being_reopened?).and_return(false) + Notify.should_receive(:issue_status_changed_email).once + Note.should_receive(:create_status_change_note).with(issue_without_assignee, some_user, 'closed') + + subject.after_update(issue_without_assignee) + end + end + + context 'a status "reopened"' do + it 'note is created if the issue is being reopened' do + issue.should_receive(:is_being_reopened?).and_return(true) + Note.should_receive(:create_status_change_note).with(issue, some_user, 'reopened') + + subject.after_update(issue) + end + + it 'note is not created if the issue is not being reopened' do + issue.should_receive(:is_being_reopened?).and_return(false) + Note.should_not_receive(:create_status_change_note).with(issue, some_user, 'reopened') + + subject.after_update(issue) + end + + it 'notification is delivered if the issue being reopened' do + issue.stub(:is_being_reopened?).and_return(true) + Notify.should_receive(:issue_status_changed_email).twice + Note.should_receive(:create_status_change_note).with(issue, some_user, 'reopened') + + subject.after_update(issue) + end + + it 'notification is not delivered if the issue not being reopened' do + issue.stub(:is_being_reopened?).and_return(false) + Notify.should_not_receive(:issue_status_changed_email) + Note.should_not_receive(:create_status_change_note).with(issue, some_user, 'reopened') + + subject.after_update(issue) + end + + it 'notification is delivered only to author if the issue being reopened' do + issue_without_assignee = double(:issue, id: 42, author: author, assignee: nil) + issue_without_assignee.stub(:is_being_reassigned?).and_return(false) + issue_without_assignee.stub(:is_being_closed?).and_return(false) + issue_without_assignee.stub(:is_being_reopened?).and_return(true) + Notify.should_receive(:issue_status_changed_email).once + Note.should_receive(:create_status_change_note).with(issue_without_assignee, some_user, 'reopened') + + subject.after_update(issue_without_assignee) + end + end + end + + describe '#send_reassigned_email' do + let(:previous_assignee) { double(:user, id: 3) } + + before(:each) do + issue.stub(:assignee_id).and_return(assignee.id) + issue.stub(:assignee_id_was).and_return(previous_assignee.id) + end + + def it_sends_a_reassigned_email_to(recipient) + Notify.should_receive(:reassigned_issue_email).with(recipient, issue.id, previous_assignee.id). + and_return(double(deliver: true)) + end + + def it_does_not_send_a_reassigned_email_to(recipient) + Notify.should_not_receive(:reassigned_issue_email).with(recipient, issue.id, previous_assignee.id) + end + + it 'sends a reassigned email to the previous and current assignees' do + it_sends_a_reassigned_email_to assignee.id + it_sends_a_reassigned_email_to previous_assignee.id + + subject.send(:send_reassigned_email, issue) + end + + context 'does not send an email to the user who made the reassignment' do + it 'if the user is the assignee' do + subject.stub(:current_user).and_return(assignee) + it_sends_a_reassigned_email_to previous_assignee.id + it_does_not_send_a_reassigned_email_to assignee.id + + subject.send(:send_reassigned_email, issue) + end + it 'if the user is the previous assignee' do + subject.stub(:current_user).and_return(previous_assignee) + it_sends_a_reassigned_email_to assignee.id + it_does_not_send_a_reassigned_email_to previous_assignee.id + + subject.send(:send_reassigned_email, issue) end end end diff --git a/spec/observers/key_observer_spec.rb b/spec/observers/key_observer_spec.rb index 452844cb..7f2a76a3 100644 --- a/spec/observers/key_observer_spec.rb +++ b/spec/observers/key_observer_spec.rb @@ -1,22 +1,33 @@ require 'spec_helper' describe KeyObserver do - before do - @key = create(:personal_key) + before do + @key = double('Key', + identifier: 'admin_654654', + key: '== a vaild ssh key', + projects: [], + is_deploy_key: false + ) + + @gitolite = double('Gitlab::Gitolite', + set_key: true, + remove_key: true + ) @observer = KeyObserver.instance + @observer.stub(:git_host => @gitolite) end context :after_save do it do - GitlabShellWorker.should_receive(:perform_async).with(:add_key, @key.shell_id, @key.key) + @gitolite.should_receive(:set_key).with(@key.identifier, @key.key, @key.projects) @observer.after_save(@key) end end - context :after_destroy do + context :after_destroy do it do - GitlabShellWorker.should_receive(:perform_async).with(:remove_key, @key.shell_id, @key.key) + @gitolite.should_receive(:remove_key).with(@key.identifier, @key.projects) @observer.after_destroy(@key) end end diff --git a/spec/observers/merge_request_observer_spec.rb b/spec/observers/merge_request_observer_spec.rb index 2593da72..6b15be96 100644 --- a/spec/observers/merge_request_observer_spec.rb +++ b/spec/observers/merge_request_observer_spec.rb @@ -1,17 +1,12 @@ require 'spec_helper' describe MergeRequestObserver do - let(:some_user) { create :user } - let(:assignee) { create :user } - let(:author) { create :user } - let(:mr_mock) { double(:merge_request, id: 42, assignee: assignee, author: author) } - let(:assigned_mr) { create(:merge_request, assignee: assignee, author: author) } - let(:unassigned_mr) { create(:merge_request, author: author) } - let(:closed_assigned_mr) { create(:closed_merge_request, assignee: assignee, author: author) } - let(:closed_unassigned_mr) { create(:closed_merge_request, author: author) } + let(:some_user) { double(:user, id: 1) } + let(:assignee) { double(:user, id: 2) } + let(:author) { double(:user, id: 3) } + let(:mr) { double(:merge_request, id: 42, assignee: assignee, author: author) } - before { subject.stub(:current_user).and_return(some_user) } - before { subject.stub(notification: mock('NotificationService').as_null_object) } + before(:each) { subject.stub(:current_user).and_return(some_user) } subject { MergeRequestObserver.instance } @@ -19,18 +14,32 @@ describe MergeRequestObserver do it 'is called when a merge request is created' do subject.should_receive(:after_create) - create(:merge_request, project: create(:project)) + + MergeRequest.observers.enable :merge_request_observer do + create(:merge_request, project: create(:project)) + end end - it 'trigger notification service' do - subject.should_receive(:notification) - subject.after_create(mr_mock) + it 'sends an email to the assignee' do + Notify.should_receive(:new_merge_request_email).with(mr.id). + and_return(double(deliver: true)) + + subject.after_create(mr) + end + + it 'does not send an email to the assignee if assignee created the merge request' do + subject.stub(:current_user).and_return(assignee) + Notify.should_not_receive(:new_merge_request_email) + + subject.after_create(mr) end end context '#after_update' do before(:each) do - mr_mock.stub(:is_being_reassigned?).and_return(false) + mr.stub(:is_being_reassigned?).and_return(false) + mr.stub(:is_being_closed?).and_return(false) + mr.stub(:is_being_reopened?).and_return(false) end it 'is called when a merge request is changed' do @@ -43,51 +52,141 @@ describe MergeRequestObserver do end end - context 'a notification' do + context 'a reassigned email' do it 'is sent if the merge request is being reassigned' do - mr_mock.should_receive(:is_being_reassigned?).and_return(true) - subject.should_receive(:notification) + mr.should_receive(:is_being_reassigned?).and_return(true) + subject.should_receive(:send_reassigned_email).with(mr) - subject.after_update(mr_mock) + subject.after_update(mr) end it 'is not sent if the merge request is not being reassigned' do - mr_mock.should_receive(:is_being_reassigned?).and_return(false) - subject.should_not_receive(:notification) + mr.should_receive(:is_being_reassigned?).and_return(false) + subject.should_not_receive(:send_reassigned_email) - subject.after_update(mr_mock) + subject.after_update(mr) end end - end - context '#after_close' do context 'a status "closed"' do it 'note is created if the merge request is being closed' do - Note.should_receive(:create_status_change_note).with(assigned_mr, some_user, 'closed') + mr.should_receive(:is_being_closed?).and_return(true) + Note.should_receive(:create_status_change_note).with(mr, some_user, 'closed') - assigned_mr.close + subject.after_update(mr) + end + + it 'note is not created if the merge request is not being closed' do + mr.should_receive(:is_being_closed?).and_return(false) + Note.should_not_receive(:create_status_change_note).with(mr, some_user, 'closed') + + subject.after_update(mr) + end + + it 'notification is delivered if the merge request being closed' do + mr.stub(:is_being_closed?).and_return(true) + Note.should_receive(:create_status_change_note).with(mr, some_user, 'closed') + + subject.after_update(mr) + end + + it 'notification is not delivered if the merge request not being closed' do + mr.stub(:is_being_closed?).and_return(false) + Note.should_not_receive(:create_status_change_note).with(mr, some_user, 'closed') + + subject.after_update(mr) end it 'notification is delivered only to author if the merge request is being closed' do - Note.should_receive(:create_status_change_note).with(unassigned_mr, some_user, 'closed') + mr_without_assignee = double(:merge_request, id: 42, author: author, assignee: nil) + mr_without_assignee.stub(:is_being_reassigned?).and_return(false) + mr_without_assignee.stub(:is_being_closed?).and_return(true) + mr_without_assignee.stub(:is_being_reopened?).and_return(false) + Note.should_receive(:create_status_change_note).with(mr_without_assignee, some_user, 'closed') - unassigned_mr.close + subject.after_update(mr_without_assignee) + end + end + + context 'a status "reopened"' do + it 'note is created if the merge request is being reopened' do + mr.should_receive(:is_being_reopened?).and_return(true) + Note.should_receive(:create_status_change_note).with(mr, some_user, 'reopened') + + subject.after_update(mr) + end + + it 'note is not created if the merge request is not being reopened' do + mr.should_receive(:is_being_reopened?).and_return(false) + Note.should_not_receive(:create_status_change_note).with(mr, some_user, 'reopened') + + subject.after_update(mr) + end + + it 'notification is delivered if the merge request being reopened' do + mr.stub(:is_being_reopened?).and_return(true) + Note.should_receive(:create_status_change_note).with(mr, some_user, 'reopened') + + subject.after_update(mr) + end + + it 'notification is not delivered if the merge request is not being reopened' do + mr.stub(:is_being_reopened?).and_return(false) + Note.should_not_receive(:create_status_change_note).with(mr, some_user, 'reopened') + + subject.after_update(mr) + end + + it 'notification is delivered only to author if the merge request is being reopened' do + mr_without_assignee = double(:merge_request, id: 42, author: author, assignee: nil) + mr_without_assignee.stub(:is_being_reassigned?).and_return(false) + mr_without_assignee.stub(:is_being_closed?).and_return(false) + mr_without_assignee.stub(:is_being_reopened?).and_return(true) + Note.should_receive(:create_status_change_note).with(mr_without_assignee, some_user, 'reopened') + + subject.after_update(mr_without_assignee) end end end - context '#after_reopen' do - context 'a status "reopened"' do - it 'note is created if the merge request is being reopened' do - Note.should_receive(:create_status_change_note).with(closed_assigned_mr, some_user, 'reopened') + describe '#send_reassigned_email' do + let(:previous_assignee) { double(:user, id: 3) } - closed_assigned_mr.reopen + before(:each) do + mr.stub(:assignee_id).and_return(assignee.id) + mr.stub(:assignee_id_was).and_return(previous_assignee.id) + end + + def it_sends_a_reassigned_email_to(recipient) + Notify.should_receive(:reassigned_merge_request_email).with(recipient, mr.id, previous_assignee.id). + and_return(double(deliver: true)) + end + + def it_does_not_send_a_reassigned_email_to(recipient) + Notify.should_not_receive(:reassigned_merge_request_email).with(recipient, mr.id, previous_assignee.id) + end + + it 'sends a reassigned email to the previous and current assignees' do + it_sends_a_reassigned_email_to assignee.id + it_sends_a_reassigned_email_to previous_assignee.id + + subject.send(:send_reassigned_email, mr) + end + + context 'does not send an email to the user who made the reassignment' do + it 'if the user is the assignee' do + subject.stub(:current_user).and_return(assignee) + it_sends_a_reassigned_email_to previous_assignee.id + it_does_not_send_a_reassigned_email_to assignee.id + + subject.send(:send_reassigned_email, mr) end + it 'if the user is the previous assignee' do + subject.stub(:current_user).and_return(previous_assignee) + it_sends_a_reassigned_email_to assignee.id + it_does_not_send_a_reassigned_email_to previous_assignee.id - it 'notification is delivered only to author if the merge request is being reopened' do - Note.should_receive(:create_status_change_note).with(closed_unassigned_mr, some_user, 'reopened') - - closed_unassigned_mr.reopen + subject.send(:send_reassigned_email, mr) end end end diff --git a/spec/observers/note_observer_spec.rb b/spec/observers/note_observer_spec.rb index 9ada9270..7dfa9f77 100644 --- a/spec/observers/note_observer_spec.rb +++ b/spec/observers/note_observer_spec.rb @@ -2,9 +2,9 @@ require 'spec_helper' describe NoteObserver do subject { NoteObserver.instance } - before { subject.stub(notification: mock('NotificationService').as_null_object) } let(:team_without_author) { (1..2).map { |n| double :user, id: n } } + let(:delivery_success) { double deliver: true } describe '#after_create' do let(:note) { double :note } @@ -18,9 +18,111 @@ describe NoteObserver do end it 'sends out notifications' do - subject.should_receive(:notification) + subject.should_receive(:send_notify_mails).with(note) subject.after_create(note) end end + + describe "#send_notify_mails" do + let(:note) { double :note, notify: false, notify_author: false } + + it 'notifies team of new note when flagged to notify' do + note.stub(:notify).and_return(true) + subject.should_receive(:notify_team).with(note) + + subject.after_create(note) + end + + it 'does not notify team of new note when not flagged to notify' do + subject.should_not_receive(:notify_team).with(note) + + subject.after_create(note) + end + + it 'notifies the author of a commit when flagged to notify the author' do + note.stub(:notify_author).and_return(true) + note.stub(:id).and_return(42) + author = double :user, id: 1 + note.stub(:commit_author).and_return(author) + Notify.should_receive(:note_commit_email).and_return(delivery_success) + + subject.after_create(note) + end + + it 'does not notify the author of a commit when not flagged to notify the author' do + Notify.should_not_receive(:note_commit_email) + + subject.after_create(note) + end + + it 'does nothing if no notify flags are set' do + subject.after_create(note).should be_nil + end + end + + describe '#notify_team' do + let(:note) { double :note, id: 1 } + + before :each do + subject.stub(:team_without_note_author).with(note).and_return(team_without_author) + end + + context 'notifies team of a new note on' do + it 'a commit' do + note.stub(:noteable_type).and_return('Commit') + Notify.should_receive(:note_commit_email).twice.and_return(delivery_success) + + subject.send(:notify_team, note) + end + + it 'an issue' do + note.stub(:noteable_type).and_return('Issue') + Notify.should_receive(:note_issue_email).twice.and_return(delivery_success) + + subject.send(:notify_team, note) + end + + it 'a wiki page' do + note.stub(:noteable_type).and_return('Wiki') + Notify.should_receive(:note_wiki_email).twice.and_return(delivery_success) + + subject.send(:notify_team, note) + end + + it 'a merge request' do + note.stub(:noteable_type).and_return('MergeRequest') + Notify.should_receive(:note_merge_request_email).twice.and_return(delivery_success) + + subject.send(:notify_team, note) + end + + it 'a wall' do + # Note: wall posts have #noteable_type of nil + note.stub(:noteable_type).and_return(nil) + Notify.should_receive(:note_wall_email).twice.and_return(delivery_success) + + subject.send(:notify_team, note) + end + end + + it 'does nothing for a new note on a snippet' do + note.stub(:noteable_type).and_return('Snippet') + + subject.send(:notify_team, note).should be_nil + end + end + + + describe '#team_without_note_author' do + let(:author) { double :user, id: 4 } + + let(:users) { team_without_author + [author] } + let(:project) { double :project, users: users } + let(:note) { double :note, project: project, author: author } + + it 'returns the projects user without the note author included' do + subject.send(:team_without_note_author, note).should == team_without_author + end + end end diff --git a/spec/observers/user_observer_spec.rb b/spec/observers/user_observer_spec.rb index 5b735a8f..08254f44 100644 --- a/spec/observers/user_observer_spec.rb +++ b/spec/observers/user_observer_spec.rb @@ -2,24 +2,30 @@ require 'spec_helper' describe UserObserver do subject { UserObserver.instance } - before { subject.stub(notification: mock('NotificationService').as_null_object) } it 'calls #after_create when new users are created' do new_user = build(:user) subject.should_receive(:after_create).with(new_user) - new_user.save + + User.observers.enable :user_observer do + new_user.save + end end context 'when a new user is created' do + let(:user) { double(:user, id: 42, password: 'P@ssword!', name: 'John', email: 'u@mail.local') } + let(:notification) { double :notification } + it 'sends an email' do - subject.should_receive(:notification) - create(:user) + notification.should_receive(:deliver) + Notify.should_receive(:new_user_email).with(user.id, user.password).and_return(notification) + + subject.after_create(user) 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) + subject.after_create(user) end end end diff --git a/spec/observers/users_project_observer_spec.rb b/spec/observers/users_project_observer_spec.rb index c034501e..cbe42248 100644 --- a/spec/observers/users_project_observer_spec.rb +++ b/spec/observers/users_project_observer_spec.rb @@ -2,64 +2,75 @@ require 'spec_helper' describe UsersProjectObserver do let(:user) { create(:user) } - let(:project) { create(:project) } + let(:project) { create(:project, + code: "Fuu", + path: "Fuu" ) } + let(:users_project) { create(:users_project, + project: project, + user: user )} subject { UsersProjectObserver.instance } - before { subject.stub(notification: mock('NotificationService').as_null_object) } describe "#after_commit" do it "should called when UsersProject created" do - subject.should_receive(:after_commit) - create(:users_project) + subject.should_receive(:after_commit).once + UsersProject.observers.enable :users_project_observer do + create(:users_project) + end end it "should send email to user" do - subject.should_receive(:notification) + Notify.should_receive(:project_access_granted_email).with(users_project.id).and_return(double(deliver: true)) + subject.after_commit(users_project) Event.stub(:create => true) - - create(:users_project) end it "should create new event" do - Event.should_receive(:create) + Event.should_receive(:create).with( + project_id: users_project.project.id, + action: Event::Joined, + author_id: users_project.user.id + ) - create(:users_project) + subject.after_create(users_project) end end describe "#after_update" do - before do - @users_project = create :users_project - end - it "should called when UsersProject updated" do - subject.should_receive(:after_commit) - @users_project.update_attribute(:project_access, UsersProject::MASTER) + subject.should_receive(:after_commit).once + UsersProject.observers.enable :users_project_observer do + create(:users_project).update_attribute(:project_access, UsersProject::MASTER) + end end it "should send email to user" do - subject.should_receive(:notification) - @users_project.update_attribute(:project_access, UsersProject::MASTER) + Notify.should_receive(:project_access_granted_email).with(users_project.id).and_return(double(deliver: true)) + subject.after_commit(users_project) end - it "should not called after UsersProject destroyed" do subject.should_not_receive(:after_commit) - @users_project.destroy + UsersProject.observers.enable :users_project_observer do + users_project.destroy + end end end describe "#after_destroy" do - before do - @users_project = create :users_project - end - it "should called when UsersProject destroyed" do subject.should_receive(:after_destroy) - @users_project.destroy + + UsersProject.observers.enable :users_project_observer do + create(:users_project).destroy + end end it "should create new event" do - Event.should_receive(:create) - @users_project.destroy + Event.should_receive(:create).with( + project_id: users_project.project.id, + action: Event::Left, + author_id: users_project.user.id + ) + subject.after_destroy(users_project) end end end diff --git a/spec/features/admin/admin_hooks_spec.rb b/spec/requests/admin/admin_hooks_spec.rb similarity index 88% rename from spec/features/admin/admin_hooks_spec.rb rename to spec/requests/admin/admin_hooks_spec.rb index 102a1b92..3f35b2fd 100644 --- a/spec/features/admin/admin_hooks_spec.rb +++ b/spec/requests/admin/admin_hooks_spec.rb @@ -2,7 +2,9 @@ require 'spec_helper' describe "Admin::Hooks" do before do - @project = create(:project) + @project = create(:project, + name: "LeGiT", + code: "LGT") login_as :admin @system_hook = create(:system_hook) @@ -12,7 +14,7 @@ describe "Admin::Hooks" do describe "GET /admin/hooks" do it "should be ok" do visit admin_root_path - within ".main-nav" do + within ".main_menu" do click_on "Hooks" end current_path.should == admin_hooks_path diff --git a/spec/requests/admin/admin_projects_spec.rb b/spec/requests/admin/admin_projects_spec.rb new file mode 100644 index 00000000..43e39d7c --- /dev/null +++ b/spec/requests/admin/admin_projects_spec.rb @@ -0,0 +1,118 @@ +require 'spec_helper' + +describe "Admin::Projects" do + before do + @project = create(:project, + name: "LeGiT", + code: "LGT") + login_as :admin + end + + describe "GET /admin/projects" do + before do + visit admin_projects_path + end + + it "should be ok" do + current_path.should == admin_projects_path + end + + it "should have projects list" do + page.should have_content(@project.name) + end + end + + describe "GET /admin/projects/:id" do + before do + visit admin_projects_path + click_link "#{@project.name}" + end + + it "should have project info" do + page.should have_content(@project.code) + page.should have_content(@project.name) + end + end + + describe "GET /admin/projects/:id/edit" do + before do + visit admin_projects_path + click_link "edit_project_#{@project.id}" + end + + it "should have project edit page" do + page.should have_content("Project name") + page.should have_content("URL") + end + + describe "Update project" do + before do + fill_in "project_name", with: "Big Bang" + fill_in "project_code", with: "BB1" + click_button "Save Project" + @project.reload + end + + it "should show page with new data" do + page.should have_content("BB1") + page.should have_content("Big Bang") + end + + it "should change project entry" do + @project.name.should == "Big Bang" + @project.code.should == "BB1" + end + end + end + + describe "GET /admin/projects/new" do + before do + visit admin_projects_path + click_link "New Project" + end + + it "should be correct path" do + current_path.should == new_admin_project_path + end + + it "should have labels for new project" do + page.should have_content("Project name is") + page.should have_content("Git Clone") + page.should have_content("URL") + end + end + + describe "POST /admin/projects" do + before do + visit new_admin_project_path + fill_in 'project_name', with: 'NewProject' + fill_in 'project_code', with: 'NPR' + fill_in 'project_path', with: 'gitlabhq_1' + expect { click_button "Create project" }.to change { Project.count }.by(1) + @project = Project.last + end + + it "should be correct path" do + current_path.should == admin_project_path(@project) + end + + it "should show project" do + page.should have_content(@project.name) + page.should have_content(@project.path) + end + end + + describe "Add new team member" do + before do + @new_user = create(:user) + visit admin_project_path(@project) + end + + it "should create new user" do + select @new_user.name, from: "user_ids" + expect { click_button "Add" }.to change { UsersProject.count }.by(1) + page.should have_content @new_user.name + current_path.should == admin_project_path(@project) + end + end +end diff --git a/spec/features/admin/admin_users_spec.rb b/spec/requests/admin/admin_users_spec.rb similarity index 78% rename from spec/features/admin/admin_users_spec.rb rename to spec/requests/admin/admin_users_spec.rb index 22d1ee91..9f43f07a 100644 --- a/spec/features/admin/admin_users_spec.rb +++ b/spec/requests/admin/admin_users_spec.rb @@ -23,7 +23,6 @@ describe "Admin::Users" do @password = "123ABC" visit new_admin_user_path fill_in "user_name", with: "Big Bang" - fill_in "user_username", with: "bang" fill_in "user_email", with: "bigbang@mail.com" fill_in "user_password", with: @password fill_in "user_password_confirmation", with: @password @@ -41,7 +40,7 @@ describe "Admin::Users" do end it "should call send mail" do - Notify.should_receive(:new_user_email) + Notify.should_receive(:new_user_email).and_return(stub(deliver: true)) User.observers.enable :user_observer do click_button "Save" @@ -49,26 +48,15 @@ describe "Admin::Users" do end it "should send valid email to user with email & password" do - Gitlab.config.gitlab.stub(:signup_enabled).and_return(false) User.observers.enable :user_observer do - click_button "Save" + with_resque do + click_button "Save" + end user = User.last email = ActionMailer::Base.deliveries.last email.subject.should have_content("Account was created") - email.text_part.body.should have_content(user.email) - email.text_part.body.should have_content(@password) - end - end - - it "should send valid email to user with email without password when signup is enabled" do - Gitlab.config.gitlab.stub(:signup_enabled).and_return(true) - User.observers.enable :user_observer do - click_button "Save" - user = User.last - email = ActionMailer::Base.deliveries.last - email.subject.should have_content("Account was created") - email.text_part.body.should have_content(user.email) - email.text_part.body.should_not have_content(@password) + email.body.should have_content(user.email) + email.body.should have_content(@password) end end end @@ -82,6 +70,7 @@ describe "Admin::Users" do it "should have user info" do page.should have_content(@user.email) page.should have_content(@user.name) + page.should have_content(@user.projects_limit) end end diff --git a/spec/features/admin/security_spec.rb b/spec/requests/admin/security_spec.rb similarity index 100% rename from spec/features/admin/security_spec.rb rename to spec/requests/admin/security_spec.rb diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb deleted file mode 100644 index e97ceb2c..00000000 --- a/spec/requests/api/groups_spec.rb +++ /dev/null @@ -1,126 +0,0 @@ -require 'spec_helper' - -describe Gitlab::API do - include ApiHelpers - - let(:user1) { create(:user) } - let(:user2) { create(:user) } - let(:admin) { create(:admin) } - let!(:group1) { create(:group, owner: user1) } - let!(:group2) { create(:group, owner: user2) } - - describe "GET /groups" do - context "when unauthenticated" do - it "should return authentication error" do - get api("/groups") - response.status.should == 401 - end - end - - context "when authenticated as user" do - it "normal user: should return an array of groups of user1" do - get api("/groups", user1) - response.status.should == 200 - json_response.should be_an Array - json_response.length.should == 1 - json_response.first['name'].should == group1.name - end - end - - context "when authenticated as admin" do - it "admin: should return an array of all groups" do - get api("/groups", admin) - response.status.should == 200 - json_response.should be_an Array - json_response.length.should == 2 - end - end - end - - describe "GET /groups/:id" do - context "when authenticated as user" do - it "should return one of user1's groups" do - get api("/groups/#{group1.id}", user1) - response.status.should == 200 - json_response['name'] == group1.name - end - - it "should not return a non existing group" do - get api("/groups/1328", user1) - response.status.should == 404 - end - - it "should not return a group not attached to user1" do - get api("/groups/#{group2.id}", user1) - response.status.should == 404 - end - end - - context "when authenticated as admin" do - it "should return any existing group" do - get api("/groups/#{group2.id}", admin) - response.status.should == 200 - json_response['name'] == group2.name - end - - it "should not return a non existing group" do - get api("/groups/1328", admin) - response.status.should == 404 - end - end - end - - describe "POST /groups" do - context "when authenticated as user" do - it "should not create group" do - post api("/groups", user1), attributes_for(:group) - response.status.should == 403 - end - end - - context "when authenticated as admin" do - it "should create group" do - post api("/groups", admin), attributes_for(:group) - response.status.should == 201 - end - - it "should not create group, duplicate" do - post api("/groups", admin), {:name => "Duplicate Test", :path => group2.path} - response.status.should == 404 - end - - it "should return 400 bad request error if name not given" do - post api("/groups", admin), { :path => group2.path } - response.status.should == 400 - end - - it "should return 400 bad request error if path not given" do - post api("/groups", admin), { :name => 'test' } - response.status.should == 400 - end - end - end - - describe "POST /groups/:id/projects/:project_id" do - let(:project) { create(:project) } - before(:each) do - project.stub!(:transfer).and_return(true) - Project.stub(:find).and_return(project) - end - - - context "when authenticated as user" do - it "should not transfer project to group" do - post api("/groups/#{group1.id}/projects/#{project.id}", user2) - response.status.should == 403 - end - end - - context "when authenticated as admin" do - it "should transfer project to group" do - project.should_receive(:transfer) - post api("/groups/#{group1.id}/projects/#{project.id}", admin) - end - end - end -end diff --git a/spec/requests/api/internal_spec.rb b/spec/requests/api/internal_spec.rb deleted file mode 100644 index ba11a41d..00000000 --- a/spec/requests/api/internal_spec.rb +++ /dev/null @@ -1,124 +0,0 @@ -require 'spec_helper' - -describe Gitlab::API do - include ApiHelpers - - let(:user) { create(:user) } - let(:key) { create(:key, user: user) } - let(:project) { create(:project) } - - describe "GET /internal/check", no_db: true do - it do - get api("/internal/check") - - response.status.should == 200 - json_response['api_version'].should == Gitlab::API.version - end - end - - describe "GET /internal/discover" do - it do - get(api("/internal/discover"), key_id: key.id) - - response.status.should == 200 - - json_response['name'].should == user.name - end - end - - describe "GET /internal/allowed" do - context "access granted" do - before do - project.team << [user, :developer] - end - - context "git pull" do - it do - pull(key, project) - - response.status.should == 200 - response.body.should == 'true' - end - end - - context "git push" do - it do - push(key, project) - - response.status.should == 200 - response.body.should == 'true' - end - end - end - - context "access denied" do - before do - project.team << [user, :guest] - end - - context "git pull" do - it do - pull(key, project) - - response.status.should == 200 - response.body.should == 'false' - end - end - - context "git push" do - it do - push(key, project) - - response.status.should == 200 - response.body.should == 'false' - end - end - end - - context "blocked user" do - let(:personal_project) { create(:project, namespace: user.namespace) } - - before do - user.block - end - - context "git pull" do - it do - pull(key, personal_project) - - response.status.should == 200 - response.body.should == 'false' - end - end - - context "git push" do - it do - push(key, personal_project) - - response.status.should == 200 - response.body.should == 'false' - end - end - end - end - - def pull(key, project) - get( - api("/internal/allowed"), - ref: 'master', - key_id: key.id, - project: project.path_with_namespace, - action: 'git-upload-pack' - ) - end - - def push(key, project) - get( - api("/internal/allowed"), - ref: 'master', - key_id: key.id, - project: project.path_with_namespace, - action: 'git-receive-pack' - ) - end -end diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb index ecf0bdb7..6ea7e9b5 100644 --- a/spec/requests/api/issues_spec.rb +++ b/spec/requests/api/issues_spec.rb @@ -4,9 +4,9 @@ describe Gitlab::API do include ApiHelpers let(:user) { create(:user) } - let!(:project) { create(:project, namespace: user.namespace ) } + let!(:project) { create(:project, owner: user) } let!(:issue) { create(:issue, author: user, assignee: user, project: project) } - before { project.team << [user, :reporter] } + before { project.add_access(user, :read) } describe "GET /issues" do context "when unauthenticated" do @@ -28,7 +28,7 @@ describe Gitlab::API do describe "GET /projects/:id/issues" do it "should return project issues" do - get api("/projects/#{project.id}/issues", user) + get api("/projects/#{project.code}/issues", user) response.status.should == 200 json_response.should be_an Array json_response.first['title'].should == issue.title @@ -37,63 +37,37 @@ describe Gitlab::API do describe "GET /projects/:id/issues/:issue_id" do it "should return a project issue by id" do - get api("/projects/#{project.id}/issues/#{issue.id}", user) + get api("/projects/#{project.code}/issues/#{issue.id}", user) response.status.should == 200 json_response['title'].should == issue.title end - - it "should return 404 if issue id not found" do - get api("/projects/#{project.id}/issues/54321", user) - response.status.should == 404 - end end describe "POST /projects/:id/issues" do it "should create a new project issue" do - post api("/projects/#{project.id}/issues", user), + post api("/projects/#{project.code}/issues", user), title: 'new issue', labels: 'label, label2' response.status.should == 201 json_response['title'].should == 'new issue' json_response['description'].should be_nil json_response['labels'].should == ['label', 'label2'] end - - it "should return a 400 bad request if title not given" do - post api("/projects/#{project.id}/issues", user), labels: 'label, label2' - response.status.should == 400 - end end - describe "PUT /projects/:id/issues/:issue_id to update only title" do + describe "PUT /projects/:id/issues/:issue_id" do it "should update a project issue" do - put api("/projects/#{project.id}/issues/#{issue.id}", user), - title: 'updated title' + put api("/projects/#{project.code}/issues/#{issue.id}", user), + title: 'updated title', labels: 'label2', closed: 1 response.status.should == 200 - json_response['title'].should == 'updated title' - end - - it "should return 404 error if issue id not found" do - put api("/projects/#{project.id}/issues/44444", user), - title: 'updated title' - response.status.should == 404 - end - end - - describe "PUT /projects/:id/issues/:issue_id to update state and label" do - it "should update a project issue" do - put api("/projects/#{project.id}/issues/#{issue.id}", user), - labels: 'label2', state_event: "close" - response.status.should == 200 - json_response['labels'].should == ['label2'] - json_response['state'].should eq "closed" + json_response['closed'].should be_true end end describe "DELETE /projects/:id/issues/:issue_id" do it "should delete a project issue" do - delete api("/projects/#{project.id}/issues/#{issue.id}", user) + delete api("/projects/#{project.code}/issues/#{issue.id}", user) response.status.should == 405 end end diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index 25bbd986..e83f2467 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -4,21 +4,21 @@ describe Gitlab::API do include ApiHelpers let(:user) { create(:user ) } - let!(:project) { create(:project_with_code, creator_id: user.id) } + let!(:project) { create(:project, owner: user) } let!(:merge_request) { create(:merge_request, author: user, assignee: user, project: project, title: "Test") } - before { project.team << [user, :reporters] } + before { project.add_access(user, :read) } describe "GET /projects/:id/merge_requests" do context "when unauthenticated" do it "should return authentication error" do - get api("/projects/#{project.id}/merge_requests") + get api("/projects/#{project.code}/merge_requests") response.status.should == 401 end end context "when authenticated" do it "should return an array of merge_requests" do - get api("/projects/#{project.id}/merge_requests", user) + get api("/projects/#{project.code}/merge_requests", user) response.status.should == 200 json_response.should be_an Array json_response.first['title'].should == merge_request.title @@ -28,102 +28,35 @@ describe Gitlab::API do describe "GET /projects/:id/merge_request/:merge_request_id" do it "should return merge_request" do - get api("/projects/#{project.id}/merge_request/#{merge_request.id}", user) + get api("/projects/#{project.code}/merge_request/#{merge_request.id}", user) response.status.should == 200 json_response['title'].should == merge_request.title end - - it "should return a 404 error if merge_request_id not found" do - get api("/projects/#{project.id}/merge_request/999", user) - response.status.should == 404 - end end describe "POST /projects/:id/merge_requests" do it "should return merge_request" do - post api("/projects/#{project.id}/merge_requests", user), + post api("/projects/#{project.code}/merge_requests", user), title: 'Test merge_request', source_branch: "stable", target_branch: "master", author: user response.status.should == 201 json_response['title'].should == 'Test merge_request' end - - it "should return 422 when source_branch equals target_branch" do - post api("/projects/#{project.id}/merge_requests", user), - title: "Test merge_request", source_branch: "master", target_branch: "master", author: user - response.status.should == 422 - end - - it "should return 400 when source_branch is missing" do - post api("/projects/#{project.id}/merge_requests", user), - title: "Test merge_request", target_branch: "master", author: user - response.status.should == 400 - end - - it "should return 400 when target_branch is missing" do - post api("/projects/#{project.id}/merge_requests", user), - title: "Test merge_request", source_branch: "stable", author: user - response.status.should == 400 - end - - it "should return 400 when title is missing" do - post api("/projects/#{project.id}/merge_requests", user), - target_branch: 'master', source_branch: 'stable' - response.status.should == 400 - end - end - - describe "PUT /projects/:id/merge_request/:merge_request_id to close MR" do - it "should return merge_request" do - put api("/projects/#{project.id}/merge_request/#{merge_request.id}", user), state_event: "close" - response.status.should == 200 - json_response['state'].should == 'closed' - end - end - - describe "PUT /projects/:id/merge_request/:merge_request_id to merge MR" do - it "should return merge_request" do - put api("/projects/#{project.id}/merge_request/#{merge_request.id}", user), state_event: "merge" - response.status.should == 200 - json_response['state'].should == 'merged' - end end describe "PUT /projects/:id/merge_request/:merge_request_id" do it "should return merge_request" do - put api("/projects/#{project.id}/merge_request/#{merge_request.id}", user), title: "New title" + put api("/projects/#{project.code}/merge_request/#{merge_request.id}", user), title: "New title" response.status.should == 200 json_response['title'].should == 'New title' end - - it "should return 422 when source_branch and target_branch are renamed the same" do - put api("/projects/#{project.id}/merge_request/#{merge_request.id}", user), - source_branch: "master", target_branch: "master" - response.status.should == 422 - end - - it "should return merge_request with renamed target_branch" do - put api("/projects/#{project.id}/merge_request/#{merge_request.id}", user), target_branch: "test" - response.status.should == 200 - json_response['target_branch'].should == 'test' - end end describe "POST /projects/:id/merge_request/:merge_request_id/comments" do it "should return comment" do - post api("/projects/#{project.id}/merge_request/#{merge_request.id}/comments", user), note: "My comment" + post api("/projects/#{project.code}/merge_request/#{merge_request.id}/comments", user), note: "My comment" response.status.should == 201 json_response['note'].should == 'My comment' end - - it "should return 400 if note is missing" do - post api("/projects/#{project.id}/merge_request/#{merge_request.id}/comments", user) - response.status.should == 400 - end - - it "should return 404 if note is attached to non existent merge request" do - post api("/projects/#{project.id}/merge_request/111/comments", user), note: "My comment" - response.status.should == 404 - end end end diff --git a/spec/requests/api/milestones_spec.rb b/spec/requests/api/milestones_spec.rb index c379e8a5..860825ab 100644 --- a/spec/requests/api/milestones_spec.rb +++ b/spec/requests/api/milestones_spec.rb @@ -4,87 +4,44 @@ describe Gitlab::API do include ApiHelpers let(:user) { create(:user) } - let!(:project) { create(:project, namespace: user.namespace ) } + let!(:project) { create(:project, owner: user) } let!(:milestone) { create(:milestone, project: project) } - before { project.team << [user, :developer] } + before { project.add_access(user, :read) } describe "GET /projects/:id/milestones" do it "should return project milestones" do - get api("/projects/#{project.id}/milestones", user) + get api("/projects/#{project.code}/milestones", user) response.status.should == 200 json_response.should be_an Array json_response.first['title'].should == milestone.title end - - it "should return a 401 error if user not authenticated" do - get api("/projects/#{project.id}/milestones") - response.status.should == 401 - end end describe "GET /projects/:id/milestones/:milestone_id" do it "should return a project milestone by id" do - get api("/projects/#{project.id}/milestones/#{milestone.id}", user) + get api("/projects/#{project.code}/milestones/#{milestone.id}", user) response.status.should == 200 json_response['title'].should == milestone.title end - - it "should return 401 error if user not authenticated" do - get api("/projects/#{project.id}/milestones/#{milestone.id}") - response.status.should == 401 - end - - it "should return a 404 error if milestone id not found" do - get api("/projects/#{project.id}/milestones/1234", user) - response.status.should == 404 - end end describe "POST /projects/:id/milestones" do it "should create a new project milestone" do - post api("/projects/#{project.id}/milestones", user), title: 'new milestone' + post api("/projects/#{project.code}/milestones", user), + title: 'new milestone' response.status.should == 201 json_response['title'].should == 'new milestone' json_response['description'].should be_nil end - - it "should create a new project milestone with description and due date" do - post api("/projects/#{project.id}/milestones", user), - title: 'new milestone', description: 'release', due_date: '2013-03-02' - response.status.should == 201 - json_response['description'].should == 'release' - json_response['due_date'].should == '2013-03-02' - end - - it "should return a 400 error if title is missing" do - post api("/projects/#{project.id}/milestones", user) - response.status.should == 400 - end end describe "PUT /projects/:id/milestones/:milestone_id" do it "should update a project milestone" do - put api("/projects/#{project.id}/milestones/#{milestone.id}", user), + put api("/projects/#{project.code}/milestones/#{milestone.id}", user), title: 'updated title' response.status.should == 200 json_response['title'].should == 'updated title' end - - it "should return a 404 error if milestone id not found" do - put api("/projects/#{project.id}/milestones/1234", user), - title: 'updated title' - response.status.should == 404 - end - end - - describe "PUT /projects/:id/milestones/:milestone_id to close milestone" do - it "should update a project milestone" do - put api("/projects/#{project.id}/milestones/#{milestone.id}", user), - state_event: 'close' - response.status.should == 200 - - json_response['state'].should == 'closed' - end end end diff --git a/spec/requests/api/notes_spec.rb b/spec/requests/api/notes_spec.rb deleted file mode 100644 index 90164083..00000000 --- a/spec/requests/api/notes_spec.rb +++ /dev/null @@ -1,177 +0,0 @@ -require 'spec_helper' - -describe Gitlab::API do - include ApiHelpers - - let(:user) { create(:user) } - let!(:project) { create(:project, namespace: user.namespace ) } - let!(:issue) { create(:issue, project: project, author: user) } - let!(:merge_request) { create(:merge_request, project: project, author: user) } - let!(:snippet) { create(:snippet, project: project, author: user) } - let!(:issue_note) { create(:note, noteable: issue, project: project, author: user) } - let!(:merge_request_note) { create(:note, noteable: merge_request, project: project, author: user) } - let!(:snippet_note) { create(:note, noteable: snippet, project: project, author: user) } - let!(:wall_note) { create(:note, project: project, author: user) } - before { project.team << [user, :reporter] } - - describe "GET /projects/:id/notes" do - context "when unauthenticated" do - it "should return authentication error" do - get api("/projects/#{project.id}/notes") - response.status.should == 401 - end - end - - context "when authenticated" do - it "should return project wall notes" do - get api("/projects/#{project.id}/notes", user) - response.status.should == 200 - json_response.should be_an Array - json_response.first['body'].should == wall_note.note - end - end - end - - describe "GET /projects/:id/notes/:note_id" do - it "should return a wall note by id" do - get api("/projects/#{project.id}/notes/#{wall_note.id}", user) - response.status.should == 200 - json_response['body'].should == wall_note.note - end - - it "should return a 404 error if note not found" do - get api("/projects/#{project.id}/notes/123", user) - response.status.should == 404 - end - end - - describe "POST /projects/:id/notes" do - it "should create a new wall note" do - post api("/projects/#{project.id}/notes", user), body: 'hi!' - response.status.should == 201 - json_response['body'].should == 'hi!' - end - - it "should return 401 unauthorized error" do - post api("/projects/#{project.id}/notes") - response.status.should == 401 - end - - it "should return a 400 bad request if body is missing" do - post api("/projects/#{project.id}/notes", user) - response.status.should == 400 - end - end - - describe "GET /projects/:id/noteable/:noteable_id/notes" do - context "when noteable is an Issue" do - it "should return an array of issue notes" do - get api("/projects/#{project.id}/issues/#{issue.id}/notes", user) - response.status.should == 200 - json_response.should be_an Array - json_response.first['body'].should == issue_note.note - end - - it "should return a 404 error when issue id not found" do - get api("/projects/#{project.id}/issues/123/notes", user) - response.status.should == 404 - end - end - - context "when noteable is a Snippet" do - it "should return an array of snippet notes" do - get api("/projects/#{project.id}/snippets/#{snippet.id}/notes", user) - response.status.should == 200 - json_response.should be_an Array - json_response.first['body'].should == snippet_note.note - end - - it "should return a 404 error when snippet id not found" do - get api("/projects/#{project.id}/snippets/42/notes", user) - response.status.should == 404 - end - end - - context "when noteable is a Merge Request" do - it "should return an array of merge_requests notes" do - get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/notes", user) - response.status.should == 200 - json_response.should be_an Array - json_response.first['body'].should == merge_request_note.note - end - - it "should return a 404 error if merge request id not found" do - get api("/projects/#{project.id}/merge_requests/4444/notes", user) - response.status.should == 404 - end - end - end - - describe "GET /projects/:id/noteable/:noteable_id/notes/:note_id" do - context "when noteable is an Issue" do - it "should return an issue note by id" do - get api("/projects/#{project.id}/issues/#{issue.id}/notes/#{issue_note.id}", user) - response.status.should == 200 - json_response['body'].should == issue_note.note - end - - it "should return a 404 error if issue note not found" do - get api("/projects/#{project.id}/issues/#{issue.id}/notes/123", user) - response.status.should == 404 - end - end - - context "when noteable is a Snippet" do - it "should return a snippet note by id" do - get api("/projects/#{project.id}/snippets/#{snippet.id}/notes/#{snippet_note.id}", user) - response.status.should == 200 - json_response['body'].should == snippet_note.note - end - - it "should return a 404 error if snippet note not found" do - get api("/projects/#{project.id}/snippets/#{snippet.id}/notes/123", user) - response.status.should == 404 - end - end - end - - describe "POST /projects/:id/noteable/:noteable_id/notes" do - context "when noteable is an Issue" do - it "should create a new issue note" do - post api("/projects/#{project.id}/issues/#{issue.id}/notes", user), body: 'hi!' - response.status.should == 201 - json_response['body'].should == 'hi!' - json_response['author']['email'].should == user.email - end - - it "should return a 400 bad request error if body not given" do - post api("/projects/#{project.id}/issues/#{issue.id}/notes", user) - response.status.should == 400 - end - - it "should return a 401 unauthorized error if user not authenticated" do - post api("/projects/#{project.id}/issues/#{issue.id}/notes"), body: 'hi!' - response.status.should == 401 - end - end - - context "when noteable is a Snippet" do - it "should create a new snippet note" do - post api("/projects/#{project.id}/snippets/#{snippet.id}/notes", user), body: 'hi!' - response.status.should == 201 - json_response['body'].should == 'hi!' - json_response['author']['email'].should == user.email - end - - it "should return a 400 bad request error if body not given" do - post api("/projects/#{project.id}/snippets/#{snippet.id}/notes", user) - response.status.should == 400 - end - - it "should return a 401 unauthorized error if user not authenticated" do - post api("/projects/#{project.id}/snippets/#{snippet.id}/notes"), body: 'hi!' - response.status.should == 401 - end - end - end -end diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index aca3919a..d24ce43d 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -6,15 +6,12 @@ describe Gitlab::API do let(:user) { create(:user) } let(:user2) { create(:user) } let(:user3) { create(:user) } - let(:admin) { create(:admin) } - let!(:project) { create(:project_with_code, creator_id: user.id) } let!(:hook) { create(:project_hook, project: project, url: "http://example.com") } + let!(:project) { create(:project, owner: user ) } let!(:snippet) { create(:snippet, author: user, project: project, title: 'example') } let!(:users_project) { create(:users_project, user: user, project: project, project_access: UsersProject::MASTER) } let!(:users_project2) { create(:users_project, user: user3, project: project, project_access: UsersProject::DEVELOPER) } - let(:key) { create(:key, project: project) } - - before { project.team << [user, :reporter] } + before { project.add_access(user, :read) } describe "GET /projects" do context "when unauthenticated" do @@ -36,21 +33,7 @@ describe Gitlab::API do end describe "POST /projects" do - context "maximum number of projects reached" do - before do - (1..user2.projects_limit).each do |project| - post api("/projects", user2), name: "foo#{project}" - end - end - - it "should not create new project" do - expect { - post api("/projects", user2), name: 'foo' - }.to change {Project.count}.by(0) - end - end - - it "should create new project without path" do + it "should create new project without code and path" do expect { post api("/projects", user), name: 'foo' }.to change {Project.count}.by(1) end @@ -58,37 +41,20 @@ describe Gitlab::API do expect { post api("/projects", user) }.to_not change {Project.count} end - it "should return a 400 error if name not given" do - post api("/projects", user) - response.status.should == 400 - end - - it "should create last project before reaching project limit" do - (1..user2.projects_limit-1).each { |p| post api("/projects", user2), name: "foo#{p}" } - post api("/projects", user2), name: "foo" - response.status.should == 201 - end - it "should respond with 201 on success" do post api("/projects", user), name: 'foo' response.status.should == 201 end - it "should respond with 400 if name is not given" do + it "should respond with 404 on failure" do post api("/projects", user) - response.status.should == 400 - end - - it "should return a 403 error if project limit reached" do - (1..user.projects_limit).each do |p| - post api("/projects", user), name: "foo#{p}" - end - post api("/projects", user), name: 'bar' - response.status.should == 403 + response.status.should == 404 end it "should assign attributes to project" do project = attributes_for(:project, { + path: 'path', + code: 'code', description: Faker::Lorem.sentence, default_branch: 'stable', issues_enabled: false, @@ -100,47 +66,6 @@ describe Gitlab::API do post api("/projects", user), project project.each_pair do |k,v| - next if k == :path - json_response[k.to_s].should == v - end - end - end - - describe "POST /projects/user/:id" do - before { admin } - - it "should create new project without path" do - expect { post api("/projects/user/#{user.id}", admin), name: 'foo' }.to change {Project.count}.by(1) - end - - it "should not create new project without name" do - expect { post api("/projects/user/#{user.id}", admin) }.to_not change {Project.count} - end - - it "should respond with 201 on success" do - post api("/projects/user/#{user.id}", admin), name: 'foo' - response.status.should == 201 - end - - it "should respond with 404 on failure" do - post api("/projects/user/#{user.id}", admin) - response.status.should == 404 - end - - it "should assign attributes to project" do - project = attributes_for(:project, { - description: Faker::Lorem.sentence, - default_branch: 'stable', - issues_enabled: false, - wall_enabled: false, - merge_requests_enabled: false, - wiki_enabled: false - }) - - post api("/projects/user/#{user.id}", admin), project - - project.each_pair do |k,v| - next if k == :path json_response[k.to_s].should == v end end @@ -154,8 +79,8 @@ describe Gitlab::API do json_response['owner']['email'].should == user.email end - it "should return a project by path name" do - get api("/projects/#{project.id}", user) + it "should return a project by code name" do + get api("/projects/#{project.code}", user) response.status.should == 200 json_response['name'].should == project.name end @@ -165,17 +90,11 @@ describe Gitlab::API do response.status.should == 404 json_response['message'].should == '404 Not Found' end - - it "should return a 404 error if user is not a member" do - other_user = create(:user) - get api("/projects/#{project.id}", other_user) - response.status.should == 404 - end end describe "GET /projects/:id/repository/branches" do it "should return an array of project branches" do - get api("/projects/#{project.id}/repository/branches", user) + get api("/projects/#{project.code}/repository/branches", user) response.status.should == 200 json_response.should be_an Array json_response.first['name'].should == project.repo.heads.sort_by(&:name).first.name @@ -184,105 +103,37 @@ describe Gitlab::API do describe "GET /projects/:id/repository/branches/:branch" do it "should return the branch information for a single branch" do - get api("/projects/#{project.id}/repository/branches/new_design", user) + get api("/projects/#{project.code}/repository/branches/new_design", user) response.status.should == 200 json_response['name'].should == 'new_design' json_response['commit']['id'].should == '621491c677087aa243f165eab467bfdfbee00be1' - json_response['protected'].should == false - end - - it "should return a 404 error if branch is not available" do - get api("/projects/#{project.id}/repository/branches/unknown", user) - response.status.should == 404 - end - end - - describe "PUT /projects/:id/repository/branches/:branch/protect" do - it "should protect a single branch" do - put api("/projects/#{project.id}/repository/branches/new_design/protect", user) - response.status.should == 200 - - json_response['name'].should == 'new_design' - json_response['commit']['id'].should == '621491c677087aa243f165eab467bfdfbee00be1' - json_response['protected'].should == true - end - - it "should return a 404 error if branch not found" do - put api("/projects/#{project.id}/repository/branches/unknown/protect", user) - response.status.should == 404 - end - - it "should return success when protect branch again" do - put api("/projects/#{project.id}/repository/branches/new_design/protect", user) - put api("/projects/#{project.id}/repository/branches/new_design/protect", user) - response.status.should == 200 - end - end - - describe "PUT /projects/:id/repository/branches/:branch/unprotect" do - it "should unprotect a single branch" do - put api("/projects/#{project.id}/repository/branches/new_design/unprotect", user) - response.status.should == 200 - - json_response['name'].should == 'new_design' - json_response['commit']['id'].should == '621491c677087aa243f165eab467bfdfbee00be1' - json_response['protected'].should == false - end - - it "should return success when unprotect branch" do - put api("/projects/#{project.id}/repository/branches/unknown/unprotect", user) - response.status.should == 404 - end - - it "should return success when unprotect branch again" do - put api("/projects/#{project.id}/repository/branches/new_design/unprotect", user) - put api("/projects/#{project.id}/repository/branches/new_design/unprotect", user) - response.status.should == 200 end end describe "GET /projects/:id/members" do it "should return project team members" do - get api("/projects/#{project.id}/members", user) + get api("/projects/#{project.code}/members", user) response.status.should == 200 json_response.should be_an Array json_response.count.should == 2 json_response.first['email'].should == user.email end - - it "finds team members with query string" do - get api("/projects/#{project.id}/members", user), query: user.username - response.status.should == 200 - json_response.should be_an Array - json_response.count.should == 1 - json_response.first['email'].should == user.email - end - - it "should return a 404 error if id not found" do - get api("/projects/9999/members", user) - response.status.should == 404 - end end describe "GET /projects/:id/members/:user_id" do it "should return project team member" do - get api("/projects/#{project.id}/members/#{user.id}", user) + get api("/projects/#{project.code}/members/#{user.id}", user) response.status.should == 200 json_response['email'].should == user.email json_response['access_level'].should == UsersProject::MASTER end - - it "should return a 404 error if user id not found" do - get api("/projects/#{project.id}/members/1234", user) - response.status.should == 404 - end end describe "POST /projects/:id/members" do it "should add user to project team" do expect { - post api("/projects/#{project.id}/members", user), user_id: user2.id, + post api("/projects/#{project.code}/members", user), user_id: user2.id, access_level: UsersProject::DEVELOPER }.to change { UsersProject.count }.by(1) @@ -290,210 +141,76 @@ describe Gitlab::API do json_response['email'].should == user2.email json_response['access_level'].should == UsersProject::DEVELOPER end - - it "should return a 201 status if user is already project member" do - post api("/projects/#{project.id}/members", user), user_id: user2.id, - access_level: UsersProject::DEVELOPER - expect { - post api("/projects/#{project.id}/members", user), user_id: user2.id, - access_level: UsersProject::DEVELOPER - }.not_to change { UsersProject.count }.by(1) - - response.status.should == 201 - json_response['email'].should == user2.email - json_response['access_level'].should == UsersProject::DEVELOPER - end - - it "should return a 400 error when user id is not given" do - post api("/projects/#{project.id}/members", user), access_level: UsersProject::MASTER - response.status.should == 400 - end - - it "should return a 400 error when access level is not given" do - post api("/projects/#{project.id}/members", user), user_id: user2.id - response.status.should == 400 - end - - it "should return a 422 error when access level is not known" do - post api("/projects/#{project.id}/members", user), user_id: user2.id, access_level: 1234 - response.status.should == 422 - end end describe "PUT /projects/:id/members/:user_id" do it "should update project team member" do - put api("/projects/#{project.id}/members/#{user3.id}", user), access_level: UsersProject::MASTER + put api("/projects/#{project.code}/members/#{user3.id}", user), access_level: UsersProject::MASTER response.status.should == 200 json_response['email'].should == user3.email json_response['access_level'].should == UsersProject::MASTER end - - it "should return a 404 error if user_id is not found" do - put api("/projects/#{project.id}/members/1234", user), access_level: UsersProject::MASTER - response.status.should == 404 - end - - it "should return a 400 error when access level is not given" do - put api("/projects/#{project.id}/members/#{user3.id}", user) - response.status.should == 400 - end - - it "should return a 422 error when access level is not known" do - put api("/projects/#{project.id}/members/#{user3.id}", user), access_level: 123 - response.status.should == 422 - end end describe "DELETE /projects/:id/members/:user_id" do it "should remove user from project team" do expect { - delete api("/projects/#{project.id}/members/#{user3.id}", user) + delete api("/projects/#{project.code}/members/#{user3.id}", user) }.to change { UsersProject.count }.by(-1) end - - it "should return 200 if team member is not part of a project" do - delete api("/projects/#{project.id}/members/#{user3.id}", user) - expect { - delete api("/projects/#{project.id}/members/#{user3.id}", user) - }.to_not change { UsersProject.count }.by(1) - end - - it "should return 200 if team member already removed" do - delete api("/projects/#{project.id}/members/#{user3.id}", user) - delete api("/projects/#{project.id}/members/#{user3.id}", user) - response.status.should == 200 - end - end - - describe "DELETE /projects/:id/members/:user_id" do - it "should return 200 OK when the user was not member" do - expect { - delete api("/projects/#{project.id}/members/1000000", user) - }.to change { UsersProject.count }.by(0) - response.status.should == 200 - json_response['message'].should == "Access revoked" - json_response['id'].should == 1000000 - end end describe "GET /projects/:id/hooks" do - context "authorized user" do - it "should return project hooks" do - get api("/projects/#{project.id}/hooks", user) - response.status.should == 200 + it "should return project hooks" do + get api("/projects/#{project.code}/hooks", user) - json_response.should be_an Array - json_response.count.should == 1 - json_response.first['url'].should == "http://example.com" - end - end + response.status.should == 200 - context "unauthorized user" do - it "should not access project hooks" do - get api("/projects/#{project.id}/hooks", user3) - response.status.should == 403 - end + json_response.should be_an Array + json_response.count.should == 1 + json_response.first['url'].should == "http://example.com" end end describe "GET /projects/:id/hooks/:hook_id" do - context "authorized user" do - it "should return a project hook" do - get api("/projects/#{project.id}/hooks/#{hook.id}", user) - response.status.should == 200 - json_response['url'].should == hook.url - end - - it "should return a 404 error if hook id is not available" do - get api("/projects/#{project.id}/hooks/1234", user) - response.status.should == 404 - end - end - - context "unauthorized user" do - it "should not access an existing hook" do - get api("/projects/#{project.id}/hooks/#{hook.id}", user3) - response.status.should == 403 - end - end - - it "should return a 404 error if hook id is not available" do - get api("/projects/#{project.id}/hooks/1234", user) - response.status.should == 404 + it "should return a project hook" do + get api("/projects/#{project.code}/hooks/#{hook.id}", user) + response.status.should == 200 + json_response['url'].should == hook.url end end describe "POST /projects/:id/hooks" do it "should add hook to project" do expect { - post api("/projects/#{project.id}/hooks", user), - url: "http://example.com" + post api("/projects/#{project.code}/hooks", user), + "url" => "http://example.com" }.to change {project.hooks.count}.by(1) - response.status.should == 201 - end - - it "should return a 400 error if url not given" do - post api("/projects/#{project.id}/hooks", user) - response.status.should == 400 - end - - it "should return a 422 error if url not valid" do - post api("/projects/#{project.id}/hooks", user), "url" => "ftp://example.com" - response.status.should == 422 end end describe "PUT /projects/:id/hooks/:hook_id" do it "should update an existing project hook" do - put api("/projects/#{project.id}/hooks/#{hook.id}", user), + put api("/projects/#{project.code}/hooks/#{hook.id}", user), url: 'http://example.org' response.status.should == 200 json_response['url'].should == 'http://example.org' end - - it "should return 404 error if hook id not found" do - put api("/projects/#{project.id}/hooks/1234", user), url: 'http://example.org' - response.status.should == 404 - end - - it "should return 400 error if url is not given" do - put api("/projects/#{project.id}/hooks/#{hook.id}", user) - response.status.should == 400 - end - - it "should return a 422 error if url is not valid" do - put api("/projects/#{project.id}/hooks/#{hook.id}", user), url: 'ftp://example.com' - response.status.should == 422 - end end + describe "DELETE /projects/:id/hooks" do it "should delete hook from project" do expect { - delete api("/projects/#{project.id}/hooks", user), hook_id: hook.id + delete api("/projects/#{project.code}/hooks", user), + hook_id: hook.id }.to change {project.hooks.count}.by(-1) - response.status.should == 200 - end - - it "should return success when deleting hook" do - delete api("/projects/#{project.id}/hooks", user), hook_id: hook.id - response.status.should == 200 - end - - it "should return success when deleting non existent hook" do - delete api("/projects/#{project.id}/hooks", user), hook_id: 42 - response.status.should == 200 - end - - it "should return a 400 error if hook id not given" do - delete api("/projects/#{project.id}/hooks", user) - response.status.should == 400 end end describe "GET /projects/:id/repository/tags" do it "should return an array of project tags" do - get api("/projects/#{project.id}/repository/tags", user) + get api("/projects/#{project.code}/repository/tags", user) response.status.should == 200 json_response.should be_an Array json_response.first['name'].should == project.repo.tags.sort_by(&:name).reverse.first.name @@ -502,20 +219,20 @@ describe Gitlab::API do describe "GET /projects/:id/repository/commits" do context "authorized user" do - before { project.team << [user2, :reporter] } + before { project.add_access(user2, :read) } it "should return project commits" do - get api("/projects/#{project.id}/repository/commits", user) + get api("/projects/#{project.code}/repository/commits", user) response.status.should == 200 json_response.should be_an Array - json_response.first['id'].should == project.repository.commit.id + json_response.first['id'].should == project.commit.id end end context "unauthorized user" do it "should not return project commits" do - get api("/projects/#{project.id}/repository/commits") + get api("/projects/#{project.code}/repository/commits") response.status.should == 401 end end @@ -523,7 +240,7 @@ describe Gitlab::API do describe "GET /projects/:id/snippets" do it "should return an array of project snippets" do - get api("/projects/#{project.id}/snippets", user) + get api("/projects/#{project.code}/snippets", user) response.status.should == 200 json_response.should be_an Array json_response.first['title'].should == snippet.title @@ -532,160 +249,59 @@ describe Gitlab::API do describe "GET /projects/:id/snippets/:snippet_id" do it "should return a project snippet" do - get api("/projects/#{project.id}/snippets/#{snippet.id}", user) + get api("/projects/#{project.code}/snippets/#{snippet.id}", user) response.status.should == 200 json_response['title'].should == snippet.title end - - it "should return a 404 error if snippet id not found" do - get api("/projects/#{project.id}/snippets/1234", user) - response.status.should == 404 - end end describe "POST /projects/:id/snippets" do it "should create a new project snippet" do - post api("/projects/#{project.id}/snippets", user), + post api("/projects/#{project.code}/snippets", user), title: 'api test', file_name: 'sample.rb', code: 'test' response.status.should == 201 json_response['title'].should == 'api test' end - - it "should return a 400 error if title is not given" do - post api("/projects/#{project.id}/snippets", user), - file_name: 'sample.rb', code: 'test' - response.status.should == 400 - end - - it "should return a 400 error if file_name not given" do - post api("/projects/#{project.id}/snippets", user), - title: 'api test', code: 'test' - response.status.should == 400 - end - - it "should return a 400 error if code not given" do - post api("/projects/#{project.id}/snippets", user), - title: 'api test', file_name: 'sample.rb' - response.status.should == 400 - end end describe "PUT /projects/:id/snippets/:shippet_id" do it "should update an existing project snippet" do - put api("/projects/#{project.id}/snippets/#{snippet.id}", user), + put api("/projects/#{project.code}/snippets/#{snippet.id}", user), code: 'updated code' response.status.should == 200 json_response['title'].should == 'example' snippet.reload.content.should == 'updated code' end - - it "should update an existing project snippet with new title" do - put api("/projects/#{project.id}/snippets/#{snippet.id}", user), - title: 'other api test' - response.status.should == 200 - json_response['title'].should == 'other api test' - end end describe "DELETE /projects/:id/snippets/:snippet_id" do it "should delete existing project snippet" do expect { - delete api("/projects/#{project.id}/snippets/#{snippet.id}", user) + delete api("/projects/#{project.code}/snippets/#{snippet.id}", user) }.to change { Snippet.count }.by(-1) - response.status.should == 200 - end - - it "should return success when deleting unknown snippet id" do - delete api("/projects/#{project.id}/snippets/1234", user) - response.status.should == 200 end end describe "GET /projects/:id/snippets/:snippet_id/raw" do it "should get a raw project snippet" do - get api("/projects/#{project.id}/snippets/#{snippet.id}/raw", user) + get api("/projects/#{project.code}/snippets/#{snippet.id}/raw", user) response.status.should == 200 end - - it "should return a 404 error if raw project snippet not found" do - get api("/projects/#{project.id}/snippets/5555/raw", user) - response.status.should == 404 - end end - describe "GET /projects/:id/repository/commits/:sha/blob" do + describe "GET /projects/:id/:sha/blob" do it "should get the raw file contents" do - get api("/projects/#{project.id}/repository/commits/master/blob?filepath=README.md", user) + get api("/projects/#{project.code}/repository/commits/master/blob?filepath=README.md", user) response.status.should == 200 end it "should return 404 for invalid branch_name" do - get api("/projects/#{project.id}/repository/commits/invalid_branch_name/blob?filepath=README.md", user) + get api("/projects/#{project.code}/repository/commits/invalid_branch_name/blob?filepath=README.md", user) response.status.should == 404 end it "should return 404 for invalid file" do - get api("/projects/#{project.id}/repository/commits/master/blob?filepath=README.invalid", user) - response.status.should == 404 - end - - it "should return a 400 error if filepath is missing" do - get api("/projects/#{project.id}/repository/commits/master/blob", user) - response.status.should == 400 - end - end - - describe "GET /projects/:id/keys" do - it "should return array of ssh keys" do - project.deploy_keys << key - project.save - get api("/projects/#{project.id}/keys", user) - response.status.should == 200 - json_response.should be_an Array - json_response.first['title'].should == key.title - end - end - - describe "GET /projects/:id/keys/:key_id" do - it "should return a single key" do - project.deploy_keys << key - project.save - get api("/projects/#{project.id}/keys/#{key.id}", user) - response.status.should == 200 - json_response['title'].should == key.title - end - - it "should return 404 Not Found with invalid ID" do - get api("/projects/#{project.id}/keys/404", user) - response.status.should == 404 - end - end - - describe "POST /projects/:id/keys" do - it "should not create an invalid ssh key" do - post api("/projects/#{project.id}/keys", user), { title: "invalid key" } - response.status.should == 404 - end - - it "should create new ssh key" do - key_attrs = attributes_for :key - expect { - post api("/projects/#{project.id}/keys", user), key_attrs - }.to change{ project.deploy_keys.count }.by(1) - end - end - - describe "DELETE /projects/:id/keys/:key_id" do - it "should delete existing key" do - project.deploy_keys << key - project.save - expect { - delete api("/projects/#{project.id}/keys/#{key.id}", user) - }.to change{ project.deploy_keys.count }.by(-1) - end - - it "should return 404 Not Found with invalid ID" do - delete api("/projects/#{project.id}/keys/404", user) + get api("/projects/#{project.code}/repository/commits/master/blob?filepath=README.invalid", user) response.status.should == 404 end end diff --git a/spec/requests/api/session_spec.rb b/spec/requests/api/session_spec.rb index 4a37312b..afae8be8 100644 --- a/spec/requests/api/session_spec.rb +++ b/spec/requests/api/session_spec.rb @@ -13,10 +13,6 @@ describe Gitlab::API do json_response['email'].should == user.email json_response['private_token'].should == user.private_token - json_response['is_admin'].should == user.is_admin? - json_response['can_create_team'].should == user.can_create_team? - json_response['can_create_project'].should == user.can_create_project? - json_response['can_create_group'].should == user.can_create_group? end end @@ -39,15 +35,5 @@ describe Gitlab::API do json_response['private_token'].should be_nil end end - - context "when empty name" do - it "should return authentication error" do - post api("/session"), password: user.password - response.status.should == 401 - - json_response['email'].should be_nil - json_response['private_token'].should be_nil - end - end end end diff --git a/spec/requests/api/system_hooks_spec.rb b/spec/requests/api/system_hooks_spec.rb deleted file mode 100644 index fe1b324c..00000000 --- a/spec/requests/api/system_hooks_spec.rb +++ /dev/null @@ -1,81 +0,0 @@ -require 'spec_helper' - -describe Gitlab::API do - include ApiHelpers - - let(:user) { create(:user) } - let(:admin) { create(:admin) } - let!(:hook) { create(:system_hook, url: "http://example.com") } - - before { stub_request(:post, hook.url) } - - describe "GET /hooks" do - context "when no user" do - it "should return authentication error" do - get api("/hooks") - response.status.should == 401 - end - end - - context "when not an admin" do - it "should return forbidden error" do - get api("/hooks", user) - response.status.should == 403 - end - end - - context "when authenticated as admin" do - it "should return an array of hooks" do - get api("/hooks", admin) - response.status.should == 200 - json_response.should be_an Array - json_response.first['url'].should == hook.url - end - end - end - - describe "POST /hooks" do - it "should create new hook" do - expect { - post api("/hooks", admin), url: 'http://example.com' - }.to change { SystemHook.count }.by(1) - end - - it "should respond with 400 if url not given" do - post api("/hooks", admin) - response.status.should == 400 - end - - it "should not create new hook without url" do - expect { - post api("/hooks", admin) - }.to_not change { SystemHook.count } - end - end - - describe "GET /hooks/:id" do - it "should return hook by id" do - get api("/hooks/#{hook.id}", admin) - response.status.should == 200 - json_response['event_name'].should == 'project_create' - end - - it "should return 404 on failure" do - get api("/hooks/404", admin) - response.status.should == 404 - end - end - - describe "DELETE /hooks/:id" do - it "should delete a hook" do - expect { - delete api("/hooks/#{hook.id}", admin) - }.to change { SystemHook.count }.by(-1) - end - - it "should return success if hook id not found" do - delete api("/hooks/12345", admin) - response.status.should == 200 - end - end -end \ No newline at end of file diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb index b4688dd2..4cfb4884 100644 --- a/spec/requests/api/users_spec.rb +++ b/spec/requests/api/users_spec.rb @@ -31,182 +31,26 @@ describe Gitlab::API do response.status.should == 200 json_response['email'].should == user.email end - - it "should return a 401 if unauthenticated" do - get api("/users/9998") - response.status.should == 401 - end - - it "should return a 404 error if user id not found" do - get api("/users/9999", user) - response.status.should == 404 - end end describe "POST /users" do before{ admin } + it "should not create invalid user" do + post api("/users", admin), { email: "invalid email" } + response.status.should == 404 + end + it "should create user" do expect { post api("/users", admin), attributes_for(:user, projects_limit: 3) }.to change { User.count }.by(1) end - it "should return 201 Created on success" do - post api("/users", admin), attributes_for(:user, projects_limit: 3) - response.status.should == 201 - end - - it "should not create user with invalid email" do - post api("/users", admin), { email: "invalid email", password: 'password' } - response.status.should == 400 - end - - it "should return 400 error if password not given" do - post api("/users", admin), { email: 'test@example.com' } - response.status.should == 400 - end - - it "should return 400 error if email not given" do - post api("/users", admin), { password: 'pass1234' } - response.status.should == 400 - end - it "shouldn't available for non admin users" do post api("/users", user), attributes_for(:user) response.status.should == 403 end - - context "with existing user" do - before { post api("/users", admin), { email: 'test@example.com', password: 'password', username: 'test' } } - - it "should not create user with same email" do - expect { - post api("/users", admin), { email: 'test@example.com', password: 'password' } - }.to change { User.count }.by(0) - end - - it "should return 409 conflict error if user with email exists" do - post api("/users", admin), { email: 'test@example.com', password: 'password' } - end - - it "should return 409 conflict error if same username exists" do - post api("/users", admin), { email: 'foo@example.com', password: 'pass', username: 'test' } - end - end - end - - describe "GET /users/sign_up" do - context 'enabled' do - before do - Gitlab.config.gitlab.stub(:signup_enabled).and_return(true) - end - - it "should return sign up page if signup is enabled" do - get "/users/sign_up" - response.status.should == 200 - end - end - - context 'disabled' do - before do - Gitlab.config.gitlab.stub(:signup_enabled).and_return(false) - end - - it "should redirect to sign in page if signup is disabled" do - get "/users/sign_up" - response.status.should == 302 - response.should redirect_to(new_user_session_path) - end - end - end - - describe "PUT /users/:id" do - before { admin } - - it "should update user with new bio" do - put api("/users/#{user.id}", admin), {bio: 'new test bio'} - response.status.should == 200 - json_response['bio'].should == 'new test bio' - user.reload.bio.should == 'new test bio' - end - - it "should not allow invalid update" do - put api("/users/#{user.id}", admin), {email: 'invalid email'} - response.status.should == 404 - user.reload.email.should_not == 'invalid email' - end - - it "shouldn't available for non admin users" do - put api("/users/#{user.id}", user), attributes_for(:user) - response.status.should == 403 - end - - it "should return 404 for non-existing user" do - put api("/users/999999", admin), {bio: 'update should fail'} - response.status.should == 404 - end - - context "with existing user" do - before { - post api("/users", admin), { email: 'test@example.com', password: 'password', username: 'test', name: 'test' } - post api("/users", admin), { email: 'foo@bar.com', password: 'password', username: 'john', name: 'john' } - @user_id = User.all.last.id - } - -# it "should return 409 conflict error if email address exists" do -# put api("/users/#{@user_id}", admin), { email: 'test@example.com' } -# response.status.should == 409 -# end -# -# it "should return 409 conflict error if username taken" do -# @user_id = User.all.last.id -# put api("/users/#{@user_id}", admin), { username: 'test' } -# response.status.should == 409 -# end - end - end - - describe "POST /users/:id/keys" do - before { admin } - - it "should not create invalid ssh key" do - post api("/users/#{user.id}/keys", admin), { title: "invalid key" } - response.status.should == 404 - end - - it "should create ssh key" do - key_attrs = attributes_for :key - expect { - post api("/users/#{user.id}/keys", admin), key_attrs - }.to change{ user.keys.count }.by(1) - end - end - - describe "DELETE /users/:id" do - before { admin } - - it "should delete user" do - delete api("/users/#{user.id}", admin) - response.status.should == 200 - expect { User.find(user.id) }.to raise_error ActiveRecord::RecordNotFound - json_response['email'].should == user.email - end - - it "should not delete for unauthenticated user" do - delete api("/users/#{user.id}") - response.status.should == 401 - end - - it "shouldn't available for non admin users" do - delete api("/users/#{user.id}", user) - response.status.should == 403 - end - - it "should return 404 for non-existing user" do - delete api("/users/999999", admin) - response.status.should == 404 - end end describe "GET /user" do @@ -214,15 +58,6 @@ describe Gitlab::API do get api("/user", user) response.status.should == 200 json_response['email'].should == user.email - json_response['is_admin'].should == user.is_admin? - json_response['can_create_team'].should == user.can_create_team? - json_response['can_create_project'].should == user.can_create_project? - json_response['can_create_group'].should == user.can_create_group? - end - - it "should return 401 error if user is unauthenticated" do - get api("/user") - response.status.should == 401 end end @@ -259,38 +94,19 @@ describe Gitlab::API do get api("/user/keys/42", user) response.status.should == 404 end - - it "should return 404 error if admin accesses user's ssh key" do - user.keys << key - user.save - admin - get api("/user/keys/#{key.id}", admin) - response.status.should == 404 - end end describe "POST /user/keys" do + it "should not create invalid ssh key" do + post api("/user/keys", user), { title: "invalid key" } + response.status.should == 404 + end + it "should create ssh key" do key_attrs = attributes_for :key expect { post api("/user/keys", user), key_attrs }.to change{ user.keys.count }.by(1) - response.status.should == 201 - end - - it "should return a 401 error if unauthorized" do - post api("/user/keys"), title: 'some title', key: 'some key' - response.status.should == 401 - end - - it "should not create ssh key without key" do - post api("/user/keys", user), title: 'title' - response.status.should == 400 - end - - it "should not create ssh key without title" do - post api("/user/keys", user), key: "somekey" - response.status.should == 400 end end @@ -301,19 +117,11 @@ describe Gitlab::API do expect { delete api("/user/keys/#{key.id}", user) }.to change{user.keys.count}.by(-1) - response.status.should == 200 end - it "should return success if key ID not found" do + it "should return 404 Not Found within invalid ID" do delete api("/user/keys/42", user) - response.status.should == 200 - end - - it "should return 401 error if unauthorized" do - user.keys << key - user.save - delete api("/user/keys/#{key.id}") - response.status.should == 401 + response.status.should == 404 end end end diff --git a/spec/features/atom/dashboard_issues_spec.rb b/spec/requests/atom/dashboard_issues_spec.rb similarity index 93% rename from spec/features/atom/dashboard_issues_spec.rb rename to spec/requests/atom/dashboard_issues_spec.rb index 6f5d51d1..8ce64cd4 100644 --- a/spec/features/atom/dashboard_issues_spec.rb +++ b/spec/requests/atom/dashboard_issues_spec.rb @@ -10,7 +10,7 @@ describe "Dashboard Issues Feed" do describe "atom feed" do it "should render atom feed via private token" do - visit issues_dashboard_path(:atom, private_token: user.private_token) + visit dashboard_issues_path(:atom, private_token: user.private_token) page.response_headers['Content-Type'].should have_content("application/atom+xml") page.body.should have_selector("title", text: "#{user.name} issues") diff --git a/spec/features/atom/dashboard_spec.rb b/spec/requests/atom/dashboard_spec.rb similarity index 100% rename from spec/features/atom/dashboard_spec.rb rename to spec/requests/atom/dashboard_spec.rb diff --git a/spec/features/atom/issues_spec.rb b/spec/requests/atom/issues_spec.rb similarity index 91% rename from spec/features/atom/issues_spec.rb rename to spec/requests/atom/issues_spec.rb index 0488c1f2..29f88f3f 100644 --- a/spec/features/atom/issues_spec.rb +++ b/spec/requests/atom/issues_spec.rb @@ -3,10 +3,10 @@ require 'spec_helper' describe "Issues Feed" do describe "GET /issues" do let!(:user) { create(:user) } - let!(:project) { create(:project, namespace: user.namespace) } + let!(:project) { create(:project, owner: user) } let!(:issue) { create(:issue, author: user, project: project) } - before { project.team << [user, :developer] } + before { project.add_access(user, :read, :write) } context "when authenticated" do it "should render atom feed" do diff --git a/spec/features/gitlab_flavored_markdown_spec.rb b/spec/requests/gitlab_flavored_markdown_spec.rb similarity index 66% rename from spec/features/gitlab_flavored_markdown_spec.rb rename to spec/requests/gitlab_flavored_markdown_spec.rb index 653ff865..aedd435e 100644 --- a/spec/features/gitlab_flavored_markdown_spec.rb +++ b/spec/requests/gitlab_flavored_markdown_spec.rb @@ -1,12 +1,12 @@ require 'spec_helper' describe "Gitlab Flavored Markdown" do - let(:project) { create(:project_with_code) } + let(:project) { create(:project) } let(:issue) { create(:issue, project: project) } let(:merge_request) { create(:merge_request, project: project) } let(:fred) do u = create(:user, name: "fred") - project.team << [u, :master] + project.users << u u end @@ -19,7 +19,7 @@ describe "Gitlab Flavored Markdown" do @test_file = "gfm_test_file" i.add(@test_file, "foo\nbar\n") # add commit with gfm - i.commit("fix ##{issue.id}\n\nask @#{fred.username} for details", head: @branch_name) + i.commit("fix ##{issue.id}\n\nask @#{fred.name} for details", head: @branch_name) # add test tag @tag_name = "gfm-test-tag" @@ -33,11 +33,11 @@ describe "Gitlab Flavored Markdown" do project.repo.gc_auto end - let(:commit) { project.repository.commits(@branch_name).first } + let(:commit) { project.commits(@branch_name).first } before do login_as :user - project.team << [@user, :developer] + project.add_access(@user, :read, :write) end describe "for commits" do @@ -56,7 +56,7 @@ describe "Gitlab Flavored Markdown" do it "should render description in commits#show" do visit project_commit_path(project, commit) - page.should have_link("@#{fred.username}") + page.should have_link("@#{fred.name}") end it "should render title in refs#tree", js: true do @@ -67,14 +67,13 @@ describe "Gitlab Flavored Markdown" do end end - # @wip - #it "should render title in refs#blame" do - #visit project_blame_path(project, File.join(@branch_name, @test_file)) + it "should render title in refs#blame" do + visit project_blame_path(project, File.join(@branch_name, @test_file)) - #within(".blame_commit") do - #page.should have_link("##{issue.id}") - #end - #end + within(".blame_commit") do + page.should have_link("##{issue.id}") + end + end it "should render title in repositories#branches" do visit branches_project_repository_path(project) @@ -94,7 +93,7 @@ describe "Gitlab Flavored Markdown" do assignee: @user, project: project, title: "fix ##{@other_issue.id}", - description: "ask @#{fred.username} for details") + description: "ask @#{fred.name} for details") end it "should render subject in issues#index" do @@ -112,7 +111,7 @@ describe "Gitlab Flavored Markdown" do it "should render details in issues#show" do visit project_issue_path(project, @issue) - page.should have_link("@#{fred.username}") + page.should have_link("@#{fred.name}") end end @@ -143,7 +142,7 @@ describe "Gitlab Flavored Markdown" do @milestone = create(:milestone, project: project, title: "fix ##{issue.id}", - description: "ask @#{fred.username} for details") + description: "ask @#{fred.name} for details") end it "should render title in milestones#index" do @@ -161,7 +160,7 @@ describe "Gitlab Flavored Markdown" do it "should render description in milestones#show" do visit project_milestone_path(project, @milestone) - page.should have_link("@#{fred.username}") + page.should have_link("@#{fred.name}") end end @@ -169,32 +168,67 @@ describe "Gitlab Flavored Markdown" do describe "for notes" do it "should render in commits#show", js: true do visit project_commit_path(project, commit) - within ".new_note.js-main-target-form" do - fill_in "note_note", with: "see ##{issue.id}" - click_button "Add Comment" - end + fill_in "note_note", with: "see ##{issue.id}" + click_button "Add Comment" page.should have_link("##{issue.id}") end it "should render in issue#show", js: true do visit project_issue_path(project, issue) - within ".new_note.js-main-target-form" do - fill_in "note_note", with: "see ##{issue.id}" - click_button "Add Comment" - end + fill_in "note_note", with: "see ##{issue.id}" + click_button "Add Comment" page.should have_link("##{issue.id}") end it "should render in merge_request#show", js: true do visit project_merge_request_path(project, merge_request) - within ".new_note.js-main-target-form" do - fill_in "note_note", with: "see ##{issue.id}" - click_button "Add Comment" - end + fill_in "note_note", with: "see ##{issue.id}" + click_button "Add Comment" + + page.should have_link("##{issue.id}") + end + + it "should render in projects#wall", js: true do + visit wall_project_path(project) + fill_in "note_note", with: "see ##{issue.id}" + click_button "Add Comment" + + page.should have_link("##{issue.id}") + end + + it "should render in wikis#index", js: true do + visit project_wiki_path(project, :index) + fill_in "Title", with: 'Test title' + fill_in "Content", with: '[link test](test)' + click_on "Save" + + fill_in "note_note", with: "see ##{issue.id}" + click_button "Add Comment" page.should have_link("##{issue.id}") end end + + + describe "for wikis" do + before do + visit project_wiki_path(project, :index) + fill_in "Title", with: "Circumvent ##{issue.id}" + fill_in "Content", with: "# Other pages\n\n* [Foo](foo)\n* [Bar](bar)\n\nAlso look at ##{issue.id} :-)" + click_on "Save" + end + + it "should NOT render title in wikis#show" do + within(".content h3") do # page title + page.should have_content("Circumvent ##{issue.id}") + page.should_not have_link("##{issue.id}") + end + end + + it "should render content in wikis#show" do + page.should have_link("##{issue.id}") + end + end end diff --git a/spec/features/issues_spec.rb b/spec/requests/issues_spec.rb similarity index 80% rename from spec/features/issues_spec.rb rename to spec/requests/issues_spec.rb index 6fff59f0..ff4d4c8b 100644 --- a/spec/features/issues_spec.rb +++ b/spec/requests/issues_spec.rb @@ -7,10 +7,11 @@ describe "Issues" do login_as :user user2 = create(:user) - project.team << [[@user, user2], :developer] + project.add_access(@user, :read, :write) + project.add_access(user2, :read, :write) end - describe "Edit issue" do + describe "Edit issue", js: true do let!(:issue) do create(:issue, author: @user, @@ -58,7 +59,8 @@ describe "Issues" do it "should be able to search on different statuses" do issue = Issue.first # with title 'foobar' - issue.close + issue.closed = true + issue.save visit project_issues_path(project) click_link 'Closed' @@ -77,6 +79,18 @@ describe "Issues" do page.should have_content 'foobar2' page.should_not have_content 'gitlab' end + + it "should return all results if term has been cleared" do + visit project_issues_path(project) + fill_in "issue_search", with: "foobar" + # Reset the search field and trigger loading the issues + fill_in "issue_search", with: "" + page.execute_script("$('#issue_search').keyup();"); + + page.should have_content 'foobar' + page.should have_content 'foobar2' + page.should have_content 'gitlab' + end end describe "Filter issue" do @@ -89,13 +103,13 @@ describe "Issues" do title: title) end - @issue = Issue.first # with title 'foobar' - @issue.milestone = create(:milestone, project: project) - @issue.assignee = nil - @issue.save + issue = Issue.first # with title 'foobar' + issue.milestone = create(:milestone, project: project) + issue.assignee = nil + issue.save end - let(:issue) { @issue } + let(:issue) { Issue.first } it "should allow filtering by issues with no specified milestone" do visit project_issues_path(project, milestone_id: '0') diff --git a/spec/features/projects_deploy_keys_spec.rb b/spec/requests/projects_deploy_keys_spec.rb similarity index 97% rename from spec/features/projects_deploy_keys_spec.rb rename to spec/requests/projects_deploy_keys_spec.rb index 25b1da9e..35323f55 100644 --- a/spec/features/projects_deploy_keys_spec.rb +++ b/spec/requests/projects_deploy_keys_spec.rb @@ -5,7 +5,7 @@ describe "Projects", "DeployKeys" do before do login_as :user - project.team << [@user, :master] + project.add_access(@user, :read, :write, :admin) end describe "GET /keys" do diff --git a/spec/requests/projects_spec.rb b/spec/requests/projects_spec.rb new file mode 100644 index 00000000..c44bea89 --- /dev/null +++ b/spec/requests/projects_spec.rb @@ -0,0 +1,81 @@ +require 'spec_helper' + +describe "Projects" do + before { login_as :user } + + describe 'GET /project/new' do + it "should work autocomplete", :js => true do + visit new_project_path + + fill_in 'project_name', with: 'Awesome' + find("#project_path").value.should == 'awesome' + find("#project_code").value.should == 'awesome' + end + end + + describe "GET /projects/show" do + before do + @project = create(:project, owner: @user) + @project.add_access(@user, :read) + + visit project_path(@project) + end + + it "should be correct path" do + current_path.should == project_path(@project) + end + end + + describe "GET /projects/:id/edit" do + before do + @project = create(:project) + @project.add_access(@user, :admin, :read) + + visit edit_project_path(@project) + end + + it "should be correct path" do + current_path.should == edit_project_path(@project) + end + + it "should have labels for new project" do + page.should have_content("Project name is") + page.should have_content("Advanced settings:") + page.should have_content("Features:") + end + end + + describe "PUT /projects/:id" do + before do + @project = create(:project, owner: @user) + @project.add_access(@user, :admin, :read) + + visit edit_project_path(@project) + + fill_in 'project_name', with: 'Awesome' + fill_in 'project_code', with: 'gitlabhq' + click_button "Save" + @project = @project.reload + end + + it "should be correct path" do + current_path.should == edit_project_path(@project) + end + + it "should show project" do + page.should have_content("Awesome") + end + end + + describe "DELETE /projects/:id" do + before do + @project = create(:project) + @project.add_access(@user, :read, :admin) + visit edit_project_path(@project) + end + + it "should be correct path" do + expect { click_link "Remove" }.to change {Project.count}.by(-1) + end + end +end diff --git a/spec/features/search_spec.rb b/spec/requests/search_spec.rb similarity index 60% rename from spec/features/search_spec.rb rename to spec/requests/search_spec.rb index 5ce4cae3..17cc0d39 100644 --- a/spec/features/search_spec.rb +++ b/spec/requests/search_spec.rb @@ -4,13 +4,10 @@ describe "Search" do before do login_as :user @project = create(:project) - @project.team << [@user, :reporter] + @project.add_access(@user, :read) visit search_path - - within '.search-holder' do - fill_in "search", with: @project.name[0..3] - click_button "Search" - end + fill_in "search", with: @project.name[0..3] + click_button "Search" end it "should show project in search results" do diff --git a/spec/features/security/profile_access_spec.rb b/spec/requests/security/profile_access_spec.rb similarity index 76% rename from spec/features/security/profile_access_spec.rb rename to spec/requests/security/profile_access_spec.rb index f854f3fb..8562b8e7 100644 --- a/spec/features/security/profile_access_spec.rb +++ b/spec/requests/security/profile_access_spec.rb @@ -29,16 +29,7 @@ describe "Users Security" do end describe "GET /profile/account" do - subject { account_profile_path } - - it { should be_allowed_for @u1 } - it { should be_allowed_for :admin } - it { should be_allowed_for :user } - it { should be_denied_for :visitor } - end - - describe "GET /profile/design" do - subject { design_profile_path } + subject { profile_account_path } it { should be_allowed_for @u1 } it { should be_allowed_for :admin } diff --git a/spec/features/security/project_access_spec.rb b/spec/requests/security/project_access_spec.rb similarity index 79% rename from spec/features/security/project_access_spec.rb rename to spec/requests/security/project_access_spec.rb index cfbb8f13..060a276b 100644 --- a/spec/features/security/project_access_spec.rb +++ b/spec/requests/security/project_access_spec.rb @@ -14,7 +14,7 @@ describe "Application access" do end describe "Project" do - let(:project) { create(:project_with_code) } + let(:project) { create(:project) } let(:master) { create(:user) } let(:guest) { create(:user) } @@ -22,10 +22,10 @@ describe "Application access" do before do # full access - project.team << [master, :master] + project.users_projects.create(user: master, project_access: UsersProject::MASTER) # readonly - project.team << [reporter, :reporter] + project.users_projects.create(user: reporter, project_access: UsersProject::REPORTER) end describe "GET /project_code" do @@ -33,40 +33,40 @@ describe "Application access" do it { should be_allowed_for master } it { should be_allowed_for reporter } - it { should be_allowed_for :admin } + it { should be_denied_for :admin } it { should be_denied_for guest } it { should be_denied_for :user } it { should be_denied_for :visitor } end describe "GET /project_code/tree/master" do - subject { project_tree_path(project, project.repository.root_ref) } + subject { project_tree_path(project, project.root_ref) } it { should be_allowed_for master } it { should be_allowed_for reporter } - it { should be_allowed_for :admin } + it { should be_denied_for :admin } it { should be_denied_for guest } it { should be_denied_for :user } it { should be_denied_for :visitor } end describe "GET /project_code/commits/master" do - subject { project_commits_path(project, project.repository.root_ref, limit: 1) } + subject { project_commits_path(project, project.root_ref, limit: 1) } it { should be_allowed_for master } it { should be_allowed_for reporter } - it { should be_allowed_for :admin } + it { should be_denied_for :admin } it { should be_denied_for guest } it { should be_denied_for :user } it { should be_denied_for :visitor } end describe "GET /project_code/commit/:sha" do - subject { project_commit_path(project, project.repository.commit) } + subject { project_commit_path(project, project.commit) } it { should be_allowed_for master } it { should be_allowed_for reporter } - it { should be_allowed_for :admin } + it { should be_denied_for :admin } it { should be_denied_for guest } it { should be_denied_for :user } it { should be_denied_for :visitor } @@ -77,7 +77,7 @@ describe "Application access" do it { should be_allowed_for master } it { should be_allowed_for reporter } - it { should be_allowed_for :admin } + it { should be_denied_for :admin } it { should be_denied_for guest } it { should be_denied_for :user } it { should be_denied_for :visitor } @@ -88,18 +88,18 @@ describe "Application access" do it { should be_allowed_for master } it { should be_allowed_for reporter } - it { should be_allowed_for :admin } + it { should be_denied_for :admin } it { should be_denied_for guest } it { should be_denied_for :user } it { should be_denied_for :visitor } end describe "GET /project_code/wall" do - subject { project_wall_path(project) } + subject { wall_project_path(project) } it { should be_allowed_for master } it { should be_allowed_for reporter } - it { should be_allowed_for :admin } + it { should be_denied_for :admin } it { should be_denied_for guest } it { should be_denied_for :user } it { should be_denied_for :visitor } @@ -107,14 +107,14 @@ describe "Application access" do describe "GET /project_code/blob" do before do - commit = project.repository.commit + commit = project.commit path = commit.tree.contents.select { |i| i.is_a?(Grit::Blob)}.first.name @blob_path = project_blob_path(project, File.join(commit.id, path)) end it { @blob_path.should be_allowed_for master } it { @blob_path.should be_allowed_for reporter } - it { @blob_path.should be_allowed_for :admin } + it { @blob_path.should be_denied_for :admin } it { @blob_path.should be_denied_for guest } it { @blob_path.should be_denied_for :user } it { @blob_path.should be_denied_for :visitor } @@ -125,7 +125,7 @@ describe "Application access" do it { should be_allowed_for master } it { should be_denied_for reporter } - it { should be_allowed_for :admin } + it { should be_denied_for :admin } it { should be_denied_for guest } it { should be_denied_for :user } it { should be_denied_for :visitor } @@ -136,7 +136,7 @@ describe "Application access" do it { should be_allowed_for master } it { should be_denied_for reporter } - it { should be_allowed_for :admin } + it { should be_denied_for :admin } it { should be_denied_for guest } it { should be_denied_for :user } it { should be_denied_for :visitor } @@ -147,7 +147,7 @@ describe "Application access" do it { should be_allowed_for master } it { should be_allowed_for reporter } - it { should be_allowed_for :admin } + it { should be_denied_for :admin } it { should be_denied_for guest } it { should be_denied_for :user } it { should be_denied_for :visitor } @@ -158,7 +158,7 @@ describe "Application access" do it { should be_allowed_for master } it { should be_allowed_for reporter } - it { should be_allowed_for :admin } + it { should be_denied_for :admin } it { should be_denied_for guest } it { should be_denied_for :user } it { should be_denied_for :visitor } @@ -169,7 +169,7 @@ describe "Application access" do it { should be_allowed_for master } it { should be_allowed_for reporter } - it { should be_allowed_for :admin } + it { should be_denied_for :admin } it { should be_denied_for guest } it { should be_denied_for :user } it { should be_denied_for :visitor } @@ -180,7 +180,7 @@ describe "Application access" do it { should be_allowed_for master } it { should be_allowed_for reporter } - it { should be_allowed_for :admin } + it { should be_denied_for :admin } it { should be_denied_for guest } it { should be_denied_for :user } it { should be_denied_for :visitor } @@ -196,7 +196,7 @@ describe "Application access" do it { should be_allowed_for master } it { should be_allowed_for reporter } - it { should be_allowed_for :admin } + it { should be_denied_for :admin } it { should be_denied_for guest } it { should be_denied_for :user } it { should be_denied_for :visitor } @@ -212,7 +212,7 @@ describe "Application access" do it { should be_allowed_for master } it { should be_allowed_for reporter } - it { should be_allowed_for :admin } + it { should be_denied_for :admin } it { should be_denied_for guest } it { should be_denied_for :user } it { should be_denied_for :visitor } @@ -223,7 +223,18 @@ describe "Application access" do it { should be_allowed_for master } it { should be_allowed_for reporter } - it { should be_allowed_for :admin } + it { should be_denied_for :admin } + it { should be_denied_for guest } + it { should be_denied_for :user } + it { should be_denied_for :visitor } + end + + describe "GET /project_code/files" do + subject { files_project_path(project) } + + it { should be_allowed_for master } + it { should be_allowed_for reporter } + it { should be_denied_for :admin } it { should be_denied_for guest } it { should be_denied_for :user } it { should be_denied_for :visitor } diff --git a/spec/features/snippets_spec.rb b/spec/requests/snippets_spec.rb similarity index 89% rename from spec/features/snippets_spec.rb rename to spec/requests/snippets_spec.rb index 1a0f6eae..9ef217ba 100644 --- a/spec/features/snippets_spec.rb +++ b/spec/requests/snippets_spec.rb @@ -5,7 +5,7 @@ describe "Snippets" do before do login_as :user - project.team << [@user, :developer] + project.add_access(@user, :read, :write) end describe "GET /snippets" do @@ -26,7 +26,7 @@ describe "Snippets" do before do # admin access to remove snippet @user.users_projects.destroy_all - project.team << [@user, :master] + project.add_access(@user, :read, :write, :admin) visit edit_project_snippet_path(project, @snippet) end @@ -48,11 +48,11 @@ describe "Snippets" do page.current_path.should == new_project_snippet_path(project) end - describe "fill in", js: true do + describe "fill in" do before do fill_in "snippet_title", with: "login function" fill_in "snippet_file_name", with: "test.rb" - page.execute_script("editor.insert('def login; end');") + fill_in "snippet_content", with: "def login; end" end it { expect { click_button "Save" }.to change {Snippet.count}.by(1) } @@ -72,7 +72,7 @@ describe "Snippets" do author: @user, project: project) visit project_snippet_path(project, @snippet) - click_link "Edit Snippet" + click_link "Edit" end it "should open edit page" do @@ -83,6 +83,7 @@ describe "Snippets" do before do fill_in "snippet_title", with: "login function" fill_in "snippet_file_name", with: "test.rb" + fill_in "snippet_content", with: "def login; end" end it { expect { click_button "Save" }.to_not change {Snippet.count} } diff --git a/spec/models/concerns/issuable_spec.rb b/spec/roles/issue_commonality_spec.rb similarity index 94% rename from spec/models/concerns/issuable_spec.rb rename to spec/roles/issue_commonality_spec.rb index 551e1753..11f278de 100644 --- a/spec/models/concerns/issuable_spec.rb +++ b/spec/roles/issue_commonality_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Issue, "Issuable" do +describe Issue, "IssueCommonality" do let(:issue) { create(:issue) } describe "Associations" do @@ -15,6 +15,7 @@ describe Issue, "Issuable" do it { should validate_presence_of(:author) } it { should validate_presence_of(:title) } it { should ensure_length_of(:title).is_at_least(0).is_at_most(255) } + it { should ensure_inclusion_of(:closed).in_array([true, false]) } end describe "Scope" do diff --git a/spec/roles/repository_spec.rb b/spec/roles/repository_spec.rb new file mode 100644 index 00000000..3507585a --- /dev/null +++ b/spec/roles/repository_spec.rb @@ -0,0 +1,72 @@ +require 'spec_helper' + +describe Project, "Repository" do + let(:project) { build(:project) } + + describe "#empty_repo?" do + it "should return true if the repo doesn't exist" do + project.stub(repo_exists?: false, has_commits?: true) + project.should be_empty_repo + end + + it "should return true if the repo has commits" do + project.stub(repo_exists?: true, has_commits?: false) + project.should be_empty_repo + end + + it "should return false if the repo exists and has commits" do + project.stub(repo_exists?: true, has_commits?: true) + project.should_not be_empty_repo + end + end + + describe "#discover_default_branch" do + let(:master) { 'master' } + let(:stable) { 'stable' } + + it "returns 'master' when master exists" do + project.should_receive(:branch_names).at_least(:once).and_return([stable, master]) + project.discover_default_branch.should == 'master' + end + + it "returns non-master when master exists but default branch is set to something else" do + project.default_branch = 'stable' + project.should_receive(:branch_names).at_least(:once).and_return([stable, master]) + project.discover_default_branch.should == 'stable' + end + + it "returns a non-master branch when only one exists" do + project.should_receive(:branch_names).at_least(:once).and_return([stable]) + project.discover_default_branch.should == 'stable' + end + + it "returns nil when no branch exists" do + project.should_receive(:branch_names).at_least(:once).and_return([]) + project.discover_default_branch.should be_nil + end + end + + describe "#root_ref" do + it "returns default_branch when set" do + project.default_branch = 'stable' + project.root_ref.should == 'stable' + end + + it "returns 'master' when default_branch is nil" do + project.default_branch = nil + project.root_ref.should == 'master' + end + end + + describe "#root_ref?" do + it "returns true when branch is root_ref" do + project.default_branch = 'stable' + project.root_ref?('stable').should be_true + end + + it "returns false when branch is not root_ref" do + project.default_branch = nil + project.root_ref?('stable').should be_false + end + end +end diff --git a/spec/roles/votes_spec.rb b/spec/roles/votes_spec.rb new file mode 100644 index 00000000..98666022 --- /dev/null +++ b/spec/roles/votes_spec.rb @@ -0,0 +1,132 @@ +require 'spec_helper' + +describe Issue do + let(:issue) { create(:issue) } + + describe "#upvotes" do + it "with no notes has a 0/0 score" do + issue.upvotes.should == 0 + end + + it "should recognize non-+1 notes" do + issue.notes << create(:note, note: "No +1 here") + issue.should have(1).note + issue.notes.first.upvote?.should be_false + issue.upvotes.should == 0 + end + + it "should recognize a single +1 note" do + issue.notes << create(:note, note: "+1 This is awesome") + issue.upvotes.should == 1 + end + + it "should recognize multiple +1 notes" do + issue.notes << create(:note, note: "+1 This is awesome") + issue.notes << create(:note, note: "+1 I want this") + issue.upvotes.should == 2 + end + end + + describe "#downvotes" do + it "with no notes has a 0/0 score" do + issue.downvotes.should == 0 + end + + it "should recognize non--1 notes" do + issue.notes << create(:note, note: "Almost got a -1") + issue.should have(1).note + issue.notes.first.downvote?.should be_false + issue.downvotes.should == 0 + end + + it "should recognize a single -1 note" do + issue.notes << create(:note, note: "-1 This is bad") + issue.downvotes.should == 1 + end + + it "should recognize multiple -1 notes" do + issue.notes << create(:note, note: "-1 This is bad") + issue.notes << create(:note, note: "-1 Away with this") + issue.downvotes.should == 2 + end + end + + describe "#votes_count" do + it "with no notes has a 0/0 score" do + issue.votes_count.should == 0 + end + + it "should recognize non notes" do + issue.notes << create(:note, note: "No +1 here") + issue.should have(1).note + issue.votes_count.should == 0 + end + + it "should recognize a single +1 note" do + issue.notes << create(:note, note: "+1 This is awesome") + issue.votes_count.should == 1 + end + + it "should recognize a single -1 note" do + issue.notes << create(:note, note: "-1 This is bad") + issue.votes_count.should == 1 + end + + it "should recognize multiple notes" do + issue.notes << create(:note, note: "+1 This is awesome") + issue.notes << create(:note, note: "-1 This is bad") + issue.notes << create(:note, note: "+1 I want this") + issue.votes_count.should == 3 + end + end + + describe "#upvotes_in_percent" do + it "with no notes has a 0% score" do + issue.upvotes_in_percent.should == 0 + end + + it "should count a single 1 note as 100%" do + issue.notes << create(:note, note: "+1 This is awesome") + issue.upvotes_in_percent.should == 100 + end + + it "should count multiple +1 notes as 100%" do + issue.notes << create(:note, note: "+1 This is awesome") + issue.notes << create(:note, note: "+1 I want this") + issue.upvotes_in_percent.should == 100 + end + + it "should count fractions for multiple +1 and -1 notes correctly" do + issue.notes << create(:note, note: "+1 This is awesome") + issue.notes << create(:note, note: "+1 I want this") + issue.notes << create(:note, note: "-1 This is bad") + issue.notes << create(:note, note: "+1 me too") + issue.upvotes_in_percent.should == 75 + end + end + + describe "#downvotes_in_percent" do + it "with no notes has a 0% score" do + issue.downvotes_in_percent.should == 0 + end + + it "should count a single -1 note as 100%" do + issue.notes << create(:note, note: "-1 This is bad") + issue.downvotes_in_percent.should == 100 + end + + it "should count multiple -1 notes as 100%" do + issue.notes << create(:note, note: "-1 This is bad") + issue.notes << create(:note, note: "-1 Away with this") + issue.downvotes_in_percent.should == 100 + end + + it "should count fractions for multiple +1 and -1 notes correctly" do + issue.notes << create(:note, note: "+1 This is awesome") + issue.notes << create(:note, note: "+1 I want this") + issue.notes << create(:note, note: "-1 This is bad") + issue.notes << create(:note, note: "+1 me too") + issue.downvotes_in_percent.should == 25 + end + end +end diff --git a/spec/routing/admin_routing_spec.rb b/spec/routing/admin_routing_spec.rb index b6509fcb..60261c7a 100644 --- a/spec/routing/admin_routing_spec.rb +++ b/spec/routing/admin_routing_spec.rb @@ -66,29 +66,57 @@ end # PUT /admin/projects/:id(.:format) admin/projects#update {:id=>/[^\/]+/} # DELETE /admin/projects/:id(.:format) admin/projects#destroy {:id=>/[^\/]+/} describe Admin::ProjectsController, "routing" do + it "to #team" do + get("/admin/projects/gitlab/team").should route_to('admin/projects#team', id: 'gitlab') + end + + it "to #team_update" do + put("/admin/projects/gitlab/team_update").should route_to('admin/projects#team_update', id: 'gitlab') + end + it "to #index" do get("/admin/projects").should route_to('admin/projects#index') end + it "to #create" do + post("/admin/projects").should route_to('admin/projects#create') + end + + it "to #new" do + get("/admin/projects/new").should route_to('admin/projects#new') + end + + it "to #edit" do + get("/admin/projects/gitlab/edit").should route_to('admin/projects#edit', id: 'gitlab') + end + it "to #show" do get("/admin/projects/gitlab").should route_to('admin/projects#show', id: 'gitlab') end -end - -# edit_admin_project_member GET /admin/projects/:project_id/members/:id/edit(.:format) admin/projects/members#edit {:id=>/[^\/]+/, :project_id=>/[^\/]+/} -# admin_project_member PUT /admin/projects/:project_id/members/:id(.:format) admin/projects/members#update {:id=>/[^\/]+/, :project_id=>/[^\/]+/} -# DELETE /admin/projects/:project_id/members/:id(.:format) admin/projects/members#destroy {:id=>/[^\/]+/, :project_id=>/[^\/]+/} -describe Admin::Projects::MembersController, "routing" do - it "to #edit" do - get("/admin/projects/test/members/1/edit").should route_to('admin/projects/members#edit', project_id: 'test', id: '1') - end it "to #update" do - put("/admin/projects/test/members/1").should route_to('admin/projects/members#update', project_id: 'test', id: '1') + put("/admin/projects/gitlab").should route_to('admin/projects#update', id: 'gitlab') end it "to #destroy" do - delete("/admin/projects/test/members/1").should route_to('admin/projects/members#destroy', project_id: 'test', id: '1') + delete("/admin/projects/gitlab").should route_to('admin/projects#destroy', id: 'gitlab') + end +end + +# edit_admin_team_member GET /admin/team_members/:id/edit(.:format) admin/team_members#edit +# admin_team_member PUT /admin/team_members/:id(.:format) admin/team_members#update +# DELETE /admin/team_members/:id(.:format) admin/team_members#destroy +describe Admin::TeamMembersController, "routing" do + it "to #edit" do + get("/admin/team_members/1/edit").should route_to('admin/team_members#edit', id: '1') + end + + it "to #update" do + put("/admin/team_members/1").should route_to('admin/team_members#update', id: '1') + end + + it "to #destroy" do + delete("/admin/team_members/1").should route_to('admin/team_members#destroy', id: '1') end end diff --git a/spec/routing/notifications_routing_spec.rb b/spec/routing/notifications_routing_spec.rb deleted file mode 100644 index 6880d281..00000000 --- a/spec/routing/notifications_routing_spec.rb +++ /dev/null @@ -1,13 +0,0 @@ -require "spec_helper" - -describe NotificationsController do - describe "routing" do - it "routes to #show" do - get("/profile/notifications").should route_to("notifications#show") - end - - it "routes to #update" do - put("/profile/notifications").should route_to("notifications#update") - end - end -end diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb index 41533f8b..dc687d2a 100644 --- a/spec/routing/project_routing_spec.rb +++ b/spec/routing/project_routing_spec.rb @@ -56,6 +56,7 @@ end # projects POST /projects(.:format) projects#create # new_project GET /projects/new(.:format) projects#new # wall_project GET /:id/wall(.:format) projects#wall +# graph_project GET /:id/graph(.:format) projects#graph # files_project GET /:id/files(.:format) projects#files # edit_project GET /:id/edit(.:format) projects#edit # project GET /:id(.:format) projects#show @@ -71,7 +72,15 @@ describe ProjectsController, "routing" do end it "to #wall" do - get("/gitlabhq/wall").should route_to('walls#show', project_id: 'gitlabhq') + get("/gitlabhq/wall").should route_to('projects#wall', id: 'gitlabhq') + end + + it "to #graph" do + get("/gitlabhq/graph").should route_to('projects#graph', id: 'gitlabhq') + end + + it "to #files" do + get("/gitlabhq/files").should route_to('projects#files', id: 'gitlabhq') end it "to #edit" do @@ -182,24 +191,24 @@ describe ProtectedBranchesController, "routing" do end end -# switch_project_refs GET /:project_id/refs/switch(.:format) refs#switch -# logs_tree_project_ref GET /:project_id/refs/:id/logs_tree(.:format) refs#logs_tree -# logs_file_project_ref GET /:project_id/refs/:id/logs_tree/:path(.:format) refs#logs_tree +# switch_project_refs GET /:project_id/switch(.:format) refs#switch +# logs_tree_project_ref GET /:project_id/:id/logs_tree(.:format) refs#logs_tree +# logs_file_project_ref GET /:project_id/:id/logs_tree/:path(.:format) refs#logs_tree describe RefsController, "routing" do it "to #switch" do - get("/gitlabhq/refs/switch").should route_to('refs#switch', project_id: 'gitlabhq') + get("/gitlabhq/switch").should route_to('refs#switch', project_id: 'gitlabhq') end it "to #logs_tree" do - get("/gitlabhq/refs/stable/logs_tree").should route_to('refs#logs_tree', project_id: 'gitlabhq', id: 'stable') - get("/gitlabhq/refs/stable/logs_tree/foo/bar/baz").should route_to('refs#logs_tree', project_id: 'gitlabhq', id: 'stable', path: 'foo/bar/baz') - get("/gitlab/gitlabhq/refs/stable/logs_tree/files.scss").should route_to('refs#logs_tree', project_id: 'gitlab/gitlabhq', id: 'stable', path: 'files.scss') + get("/gitlabhq/stable/logs_tree").should route_to('refs#logs_tree', project_id: 'gitlabhq', id: 'stable') + get("/gitlabhq/stable/logs_tree/foo/bar/baz").should route_to('refs#logs_tree', project_id: 'gitlabhq', id: 'stable', path: 'foo/bar/baz') end end # diffs_project_merge_request GET /:project_id/merge_requests/:id/diffs(.:format) merge_requests#diffs # automerge_project_merge_request GET /:project_id/merge_requests/:id/automerge(.:format) merge_requests#automerge # automerge_check_project_merge_request GET /:project_id/merge_requests/:id/automerge_check(.:format) merge_requests#automerge_check +# raw_project_merge_request GET /:project_id/merge_requests/:id/raw(.:format) merge_requests#raw # branch_from_project_merge_requests GET /:project_id/merge_requests/branch_from(.:format) merge_requests#branch_from # branch_to_project_merge_requests GET /:project_id/merge_requests/branch_to(.:format) merge_requests#branch_to # project_merge_requests GET /:project_id/merge_requests(.:format) merge_requests#index @@ -222,6 +231,10 @@ describe MergeRequestsController, "routing" do get("/gitlabhq/merge_requests/1/automerge_check").should route_to('merge_requests#automerge_check', project_id: 'gitlabhq', id: '1') end + it "to #raw" do + get("/gitlabhq/merge_requests/1/raw").should route_to('merge_requests#raw', project_id: 'gitlabhq', id: '1') + end + it "to #branch_from" do get("/gitlabhq/merge_requests/branch_from").should route_to('merge_requests#branch_from', project_id: 'gitlabhq') end @@ -230,14 +243,8 @@ describe MergeRequestsController, "routing" do get("/gitlabhq/merge_requests/branch_to").should route_to('merge_requests#branch_to', project_id: 'gitlabhq') end - it "to #show" do - get("/gitlabhq/merge_requests/1.diff").should route_to('merge_requests#show', project_id: 'gitlabhq', id: '1', format: 'diff') - get("/gitlabhq/merge_requests/1.patch").should route_to('merge_requests#show', project_id: 'gitlabhq', id: '1', format: 'patch') - end - it_behaves_like "RESTful project resources" do let(:controller) { 'merge_requests' } - let(:actions) { [:index, :create, :new, :edit, :show, :update] } end end @@ -278,7 +285,6 @@ end describe CommitController, "routing" do it "to #show" do get("/gitlabhq/commit/4246fb").should route_to('commit#show', project_id: 'gitlabhq', id: '4246fb') - get("/gitlabhq/commit/4246fb.diff").should route_to('commit#show', project_id: 'gitlabhq', id: '4246fb', format: 'diff') get("/gitlabhq/commit/4246fb.patch").should route_to('commit#show', project_id: 'gitlabhq', id: '4246fb', format: 'patch') get("/gitlabhq/commit/4246fbd13872934f72a8fd0d6fb1317b47b59cb5").should route_to('commit#show', project_id: 'gitlabhq', id: '4246fbd13872934f72a8fd0d6fb1317b47b59cb5') end @@ -293,10 +299,6 @@ describe CommitsController, "routing" do let(:actions) { [:show] } let(:controller) { 'commits' } end - - it "to #show" do - get("/gitlab/gitlabhq/commits/master.atom").should route_to('commits#show', project_id: 'gitlab/gitlabhq', id: "master", format: "atom") - end end # project_team_members GET /:project_id/team_members(.:format) team_members#index @@ -322,7 +324,6 @@ end describe MilestonesController, "routing" do it_behaves_like "RESTful project resources" do let(:controller) { 'milestones' } - let(:actions) { [:index, :create, :new, :edit, :show, :update] } end end @@ -358,7 +359,6 @@ describe IssuesController, "routing" do it_behaves_like "RESTful project resources" do let(:controller) { 'issues' } - let(:actions) { [:index, :create, :new, :edit, :show, :update] } end end @@ -381,7 +381,6 @@ end describe BlameController, "routing" do it "to #show" do get("/gitlabhq/blame/master/app/models/project.rb").should route_to('blame#show', project_id: 'gitlabhq', id: 'master/app/models/project.rb') - get("/gitlab/gitlabhq/blame/master/files.scss").should route_to('blame#show', project_id: 'gitlab/gitlabhq', id: 'master/files.scss') end end @@ -389,8 +388,6 @@ end describe BlobController, "routing" do it "to #show" do get("/gitlabhq/blob/master/app/models/project.rb").should route_to('blob#show', project_id: 'gitlabhq', id: 'master/app/models/project.rb') - get("/gitlabhq/blob/master/app/models/compare.rb").should route_to('blob#show', project_id: 'gitlabhq', id: 'master/app/models/compare.rb') - get("/gitlab/gitlabhq/blob/master/files.scss").should route_to('blob#show', project_id: 'gitlab/gitlabhq', id: 'master/files.scss') end end @@ -398,7 +395,6 @@ end describe TreeController, "routing" do it "to #show" do get("/gitlabhq/tree/master/app/models/project.rb").should route_to('tree#show', project_id: 'gitlabhq', id: 'master/app/models/project.rb') - get("/gitlab/gitlabhq/tree/master/files.scss").should route_to('tree#show', project_id: 'gitlab/gitlabhq', id: 'master/files.scss') end end @@ -419,10 +415,3 @@ describe CompareController, "routing" do get("/gitlabhq/compare/issue/1234...stable").should route_to('compare#show', project_id: 'gitlabhq', from: 'issue/1234', to: 'stable') end end - -describe GraphController, "routing" do - it "to #show" do - get("/gitlabhq/graph/master").should route_to('graph#show', project_id: 'gitlabhq', id: 'master') - get("/gitlabhq/graph/master.json").should route_to('graph#show', project_id: 'gitlabhq', id: 'master', format: "json") - end -end diff --git a/spec/routing/routing_spec.rb b/spec/routing/routing_spec.rb index 5ad8165e..cb8dbf37 100644 --- a/spec/routing/routing_spec.rb +++ b/spec/routing/routing_spec.rb @@ -33,7 +33,6 @@ end # help_system_hooks GET /help/system_hooks(.:format) help#system_hooks # help_markdown GET /help/markdown(.:format) help#markdown # help_ssh GET /help/ssh(.:format) help#ssh -# help_raketasks GET /help/raketasks(.:format) help#raketasks describe HelpController, "routing" do it "to #index" do get("/help").should route_to('help#index') @@ -66,10 +65,6 @@ describe HelpController, "routing" do it "to #ssh" do get("/help/ssh").should route_to('help#ssh') end - - it "to #raketasks" do - get("/help/raketasks").should route_to('help#raketasks') - end end # errors_githost GET /errors/githost(.:format) errors#githost @@ -87,25 +82,37 @@ end # profile GET /profile(.:format) profile#show # profile_design GET /profile/design(.:format) profile#design # profile_update PUT /profile/update(.:format) profile#update -describe ProfilesController, "routing" do +describe ProfileController, "routing" do it "to #account" do - get("/profile/account").should route_to('profiles#account') + get("/profile/account").should route_to('profile#account') end it "to #history" do - get("/profile/history").should route_to('profiles#history') + get("/profile/history").should route_to('profile#history') + end + + it "to #password_update" do + put("/profile/password").should route_to('profile#password_update') + end + + it "to #token" do + get("/profile/token").should route_to('profile#token') end it "to #reset_private_token" do - put("/profile/reset_private_token").should route_to('profiles#reset_private_token') + put("/profile/reset_private_token").should route_to('profile#reset_private_token') end it "to #show" do - get("/profile").should route_to('profiles#show') + get("/profile").should route_to('profile#show') end it "to #design" do - get("/profile/design").should route_to('profiles#design') + get("/profile/design").should route_to('profile#design') + end + + it "to #update" do + put("/profile/update").should route_to('profile#update') end end @@ -146,14 +153,14 @@ describe KeysController, "routing" do end end -# dashboard GET /dashboard(.:format) dashboard#show +# dashboard GET /dashboard(.:format) dashboard#index # dashboard_issues GET /dashboard/issues(.:format) dashboard#issues # dashboard_merge_requests GET /dashboard/merge_requests(.:format) dashboard#merge_requests -# root / dashboard#show +# root / dashboard#index describe DashboardController, "routing" do it "to #index" do - get("/dashboard").should route_to('dashboard#show') - get("/").should route_to('dashboard#show') + get("/dashboard").should route_to('dashboard#index') + get("/").should route_to('dashboard#index') end it "to #issues" do diff --git a/spec/services/git_push_service_spec.rb b/spec/services/git_push_service_spec.rb deleted file mode 100644 index 286a8cda..00000000 --- a/spec/services/git_push_service_spec.rb +++ /dev/null @@ -1,111 +0,0 @@ -require 'spec_helper' - -describe GitPushService do - let (:user) { create :user } - let (:project) { create :project_with_code } - let (:service) { GitPushService.new } - - before do - @oldrev = 'b98a310def241a6fd9c9a9a3e7934c48e498fe81' - @newrev = 'b19a04f53caeebf4fe5ec2327cb83e9253dc91bb' - @ref = 'refs/heads/master' - end - - describe "Git Push Data" do - before do - service.execute(project, user, @oldrev, @newrev, @ref) - @push_data = service.push_data - @commit = project.repository.commit(@newrev) - end - - subject { @push_data } - - it { should include(before: @oldrev) } - it { should include(after: @newrev) } - it { should include(ref: @ref) } - it { should include(user_id: user.id) } - it { should include(user_name: user.name) } - - context "with repository data" do - subject { @push_data[:repository] } - - it { should include(name: project.name) } - it { should include(url: project.url_to_repo) } - it { should include(description: project.description) } - it { should include(homepage: project.web_url) } - end - - context "with commits" do - subject { @push_data[:commits] } - - it { should be_an(Array) } - it { should have(1).element } - - context "the commit" do - subject { @push_data[:commits].first } - - it { should include(id: @commit.id) } - it { should include(message: @commit.safe_message) } - it { should include(timestamp: @commit.date.xmlschema) } - it { should include(url: "#{Gitlab.config.gitlab.url}/#{project.code}/commit/#{@commit.id}") } - - context "with a author" do - subject { @push_data[:commits].first[:author] } - - it { should include(name: @commit.author_name) } - it { should include(email: @commit.author_email) } - end - end - end - end - - describe "Push Event" do - before do - service.execute(project, user, @oldrev, @newrev, @ref) - @event = Event.last - end - - it { @event.should_not be_nil } - it { @event.project.should == project } - it { @event.action.should == Event::PUSHED } - it { @event.data.should == service.push_data } - end - - describe "Web Hooks" do - context "with web hooks" do - before do - @project_hook = create(:project_hook) - @project_hook_2 = create(:project_hook) - project.hooks << [@project_hook, @project_hook_2] - - stub_request(:post, @project_hook.url) - stub_request(:post, @project_hook_2.url) - end - - it "executes multiple web hook" do - @project_hook.should_receive(:async_execute).once - @project_hook_2.should_receive(:async_execute).once - - service.execute(project, user, @oldrev, @newrev, @ref) - end - end - - context "does not execute web hooks" do - before do - @project_hook = create(:project_hook) - project.hooks << [@project_hook] - end - - it "when pushing a branch for the first time" do - @project_hook.should_not_receive(:execute) - service.execute(project, user, '00000000000000000000000000000000', 'newrev', 'refs/heads/master') - end - - it "when pushing tags" do - @project_hook.should_not_receive(:execute) - service.execute(project, user, 'newrev', 'newrev', 'refs/tags/v1.0.0') - end - end - end -end - diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb deleted file mode 100644 index fa47a635..00000000 --- a/spec/services/notification_service_spec.rb +++ /dev/null @@ -1,247 +0,0 @@ -require 'spec_helper' - -describe NotificationService do - # Disable observers to prevent factory trigger notification service - before(:all) { ActiveRecord::Base.observers.disable :all } - after(:all) { ActiveRecord::Base.observers.enable :all } - - let(:notification) { NotificationService.new } - - describe 'Keys' do - describe :new_key do - let(:key) { create(:personal_key) } - - it { notification.new_key(key).should be_true } - - it 'should sent email to key owner' do - Notify.should_receive(:new_ssh_key_email).with(key.id) - notification.new_key(key) - end - end - end - - describe 'Notes' do - context 'issue note' do - let(:issue) { create(:issue, assignee: create(:user)) } - let(:note) { create(:note_on_issue, noteable: issue, project_id: issue.project_id) } - - before do - build_team(note.project) - end - - describe :new_note do - it do - should_email(@u_watcher.id) - should_email(note.noteable.author_id) - should_email(note.noteable.assignee_id) - should_not_email(note.author_id) - should_not_email(@u_participating.id) - should_not_email(@u_disabled.id) - notification.new_note(note) - end - - def should_email(user_id) - Notify.should_receive(:note_issue_email).with(user_id, note.id) - end - - def should_not_email(user_id) - Notify.should_not_receive(:note_issue_email).with(user_id, note.id) - end - end - end - - context 'commit note' do - let(:note) { create :note_on_commit } - - before do - build_team(note.project) - end - - describe :new_note do - it do - should_email(@u_watcher.id) - should_not_email(note.author_id) - should_not_email(@u_participating.id) - should_not_email(@u_disabled.id) - notification.new_note(note) - end - - it do - create(:note_on_commit, - author: @u_participating, - project_id: note.project_id, - commit_id: note.commit_id) - - should_email(@u_watcher.id) - should_email(@u_participating.id) - should_not_email(note.author_id) - should_not_email(@u_disabled.id) - notification.new_note(note) - end - - def should_email(user_id) - Notify.should_receive(:note_commit_email).with(user_id, note.id) - end - - def should_not_email(user_id) - Notify.should_not_receive(:note_commit_email).with(user_id, note.id) - end - end - end - end - - describe 'Issues' do - let(:issue) { create :issue, assignee: create(:user) } - - before do - build_team(issue.project) - end - - describe :new_issue do - it do - should_email(issue.assignee_id) - should_email(@u_watcher.id) - should_not_email(@u_participating.id) - should_not_email(@u_disabled.id) - notification.new_issue(issue, @u_disabled) - end - - def should_email(user_id) - Notify.should_receive(:new_issue_email).with(user_id, issue.id) - end - - def should_not_email(user_id) - Notify.should_not_receive(:new_issue_email).with(user_id, issue.id) - end - end - - describe :reassigned_issue do - it 'should email new assignee' do - should_email(issue.assignee_id) - should_email(@u_watcher.id) - should_not_email(@u_participating.id) - should_not_email(@u_disabled.id) - - notification.reassigned_issue(issue, @u_disabled) - end - - def should_email(user_id) - Notify.should_receive(:reassigned_issue_email).with(user_id, issue.id, issue.assignee_id) - end - - def should_not_email(user_id) - Notify.should_not_receive(:reassigned_issue_email).with(user_id, issue.id, issue.assignee_id) - end - end - - describe :close_issue do - it 'should sent email to issue assignee and issue author' do - should_email(issue.assignee_id) - should_email(issue.author_id) - should_email(@u_watcher.id) - should_not_email(@u_participating.id) - should_not_email(@u_disabled.id) - - notification.close_issue(issue, @u_disabled) - end - - def should_email(user_id) - Notify.should_receive(:closed_issue_email).with(user_id, issue.id, @u_disabled.id) - end - - def should_not_email(user_id) - Notify.should_not_receive(:closed_issue_email).with(user_id, issue.id, @u_disabled.id) - end - end - end - - describe 'Merge Requests' do - let(:merge_request) { create :merge_request, assignee: create(:user) } - - before do - build_team(merge_request.project) - end - - describe :new_merge_request do - it do - should_email(merge_request.assignee_id) - should_email(@u_watcher.id) - should_not_email(@u_participating.id) - should_not_email(@u_disabled.id) - notification.new_merge_request(merge_request, @u_disabled) - end - - def should_email(user_id) - Notify.should_receive(:new_merge_request_email).with(user_id, merge_request.id) - end - - def should_not_email(user_id) - Notify.should_not_receive(:new_merge_request_email).with(user_id, merge_request.id) - end - end - - describe :reassigned_merge_request do - it do - should_email(merge_request.assignee_id) - should_email(@u_watcher.id) - should_not_email(@u_participating.id) - should_not_email(@u_disabled.id) - notification.reassigned_merge_request(merge_request, merge_request.author) - end - - def should_email(user_id) - Notify.should_receive(:reassigned_merge_request_email).with(user_id, merge_request.id, merge_request.assignee_id) - end - - def should_not_email(user_id) - Notify.should_not_receive(:reassigned_merge_request_email).with(user_id, merge_request.id, merge_request.assignee_id) - end - end - - describe :closed_merge_request do - it do - should_email(merge_request.assignee_id) - should_email(@u_watcher.id) - should_not_email(@u_participating.id) - should_not_email(@u_disabled.id) - notification.close_mr(merge_request, @u_disabled) - end - - def should_email(user_id) - Notify.should_receive(:closed_merge_request_email).with(user_id, merge_request.id, @u_disabled.id) - end - - def should_not_email(user_id) - Notify.should_not_receive(:closed_merge_request_email).with(user_id, merge_request.id, @u_disabled.id) - end - end - - describe :merged_merge_request do - it do - should_email(merge_request.assignee_id) - should_email(@u_watcher.id) - should_not_email(@u_participating.id) - should_not_email(@u_disabled.id) - notification.merge_mr(merge_request) - end - - def should_email(user_id) - Notify.should_receive(:merged_merge_request_email).with(user_id, merge_request.id) - end - - def should_not_email(user_id) - Notify.should_not_receive(:merged_merge_request_email).with(user_id, merge_request.id) - end - end - end - - def build_team(project) - @u_watcher = create(:user, notification_level: Notification::N_WATCH) - @u_participating = create(:user, notification_level: Notification::N_PARTICIPATING) - @u_disabled = create(:user, notification_level: Notification::N_DISABLED) - - project.team << [@u_watcher, :master] - project.team << [@u_participating, :master] - project.team << [@u_disabled, :master] - end -end diff --git a/spec/services/project_transfer_service_spec.rb b/spec/services/project_transfer_service_spec.rb deleted file mode 100644 index dea0b0b2..00000000 --- a/spec/services/project_transfer_service_spec.rb +++ /dev/null @@ -1,47 +0,0 @@ -require 'spec_helper' - -describe ProjectTransferService do - context 'namespace -> namespace' do - let(:user) { create(:user) } - let(:group) { create(:group) } - let(:project) { create(:project, namespace: user.namespace) } - - before do - @result = service.transfer(project, group) - end - - it { @result.should be_true } - it { project.namespace.should == group } - end - - context 'namespace -> no namespace' do - let(:user) { create(:user) } - let(:project) { create(:project, namespace: user.namespace) } - - before do - @result = service.transfer(project, nil) - end - - it { @result.should be_true } - it { project.namespace.should == nil } - end - - context 'no namespace -> namespace' do - let(:project) { create(:project) } - let(:user) { create(:user) } - - before do - @result = service.transfer(project, user.namespace) - end - - it { @result.should be_true } - it { project.namespace.should == user.namespace } - end - - def service - service = ProjectTransferService.new - service.gitlab_shell.stub(mv_repository: true) - service - end -end - diff --git a/spec/services/system_hooks_service_spec.rb b/spec/services/system_hooks_service_spec.rb deleted file mode 100644 index 7f1590f5..00000000 --- a/spec/services/system_hooks_service_spec.rb +++ /dev/null @@ -1,41 +0,0 @@ -require 'spec_helper' - -describe SystemHooksService do - let (:user) { create :user } - let (:project) { create :project } - let (:users_project) { create :users_project } - - context 'it should build event data' do - it 'should build event data for user' do - SystemHooksService.build_event_data(user, :create).should include(:event_name, :name, :created_at, :email) - end - - it 'should build event data for project' do - SystemHooksService.build_event_data(project, :create).should include(:event_name, :name, :created_at, :path, :project_id, :owner_name, :owner_email) - end - - it 'should build event data for users project' do - SystemHooksService.build_event_data(users_project, :create).should include(:event_name, :created_at, :project_name, :project_path, :project_id, :user_name, :user_email, :project_access) - end - end - - context 'it should build event names' do - it 'should build event names for user' do - SystemHooksService.build_event_name(user, :create).should eq "user_create" - - SystemHooksService.build_event_name(user, :destroy).should eq "user_destroy" - end - - it 'should build event names for project' do - SystemHooksService.build_event_name(project, :create).should eq "project_create" - - SystemHooksService.build_event_name(project, :destroy).should eq "project_destroy" - end - - it 'should build event names for users project' do - SystemHooksService.build_event_name(users_project, :create).should eq "user_add_to_team" - - SystemHooksService.build_event_name(users_project, :destroy).should eq "user_remove_from_team" - end - end -end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 8a01c930..ace5ca00 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,58 +1,44 @@ -require 'rubygems' -require 'spork' - -Spork.prefork do - require 'simplecov' unless ENV['CI'] - - if ENV['TRAVIS'] - require 'coveralls' - Coveralls.wear! - end - - # This file is copied to spec/ when you run 'rails generate rspec:install' - ENV["RAILS_ENV"] ||= 'test' - require File.expand_path("../../config/environment", __FILE__) - require 'rspec/rails' - require 'capybara/rails' - require 'capybara/rspec' - require 'webmock/rspec' - require 'email_spec' - require 'sidekiq/testing/inline' - require 'capybara/poltergeist' - - # Loading more in this block will cause your tests to run faster. However, - - # if you change any configuration or code from libraries loaded here, you'll - # need to restart spork for it take effect. - Capybara.javascript_driver = :poltergeist - Capybara.default_wait_time = 10 - - # Requires supporting ruby files with custom matchers and macros, etc, - # in spec/support/ and its subdirectories. - Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f} - - WebMock.disable_net_connect!(allow_localhost: true) - - RSpec.configure do |config| - config.mock_with :rspec - - config.include LoginHelpers, type: :feature - config.include LoginHelpers, type: :request - config.include FactoryGirl::Syntax::Methods - config.include Devise::TestHelpers, type: :controller - - # If you're not using ActiveRecord, or you'd prefer not to run each of your - # examples within a transaction, remove the following line or assign false - # instead of true. - config.use_transactional_fixtures = false - - config.before do - TestEnv.init - end - end +unless ENV['CI'] + require 'simplecov' + SimpleCov.start 'rails' end -Spork.each_run do - # This code will be run each time you run your specs. +# This file is copied to spec/ when you run 'rails generate rspec:install' +ENV["RAILS_ENV"] ||= 'test' +require File.expand_path("../../config/environment", __FILE__) +require 'rspec/rails' +require 'capybara/rails' +require 'capybara/rspec' +require 'webmock/rspec' +require 'email_spec' +# Requires supporting ruby files with custom matchers and macros, etc, +# in spec/support/ and its subdirectories. +Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f} + +require 'capybara/poltergeist' +Capybara.javascript_driver = :poltergeist + +WebMock.disable_net_connect!(allow_localhost: true) + +RSpec.configure do |config| + config.mock_with :rspec + + config.include LoginHelpers, type: :request + config.include GitoliteStub + config.include FactoryGirl::Syntax::Methods + config.include Devise::TestHelpers, type: :controller + + # If you're not using ActiveRecord, or you'd prefer not to run each of your + # examples within a transaction, remove the following line or assign false + # instead of true. + config.use_transactional_fixtures = false + + config.before do + stub_gitolite! + + # !!! Observers disabled by default in tests + ActiveRecord::Base.observers.disable(:all) + # ActiveRecord::Base.observers.enable(:all) + end end diff --git a/spec/support/api_helpers.rb b/spec/support/api_helpers.rb index c4514bf3..7d901197 100644 --- a/spec/support/api_helpers.rb +++ b/spec/support/api_helpers.rb @@ -18,7 +18,7 @@ module ApiHelpers # # Returns the relative path to the requested API resource def api(path, user = nil) - "/api/#{Gitlab::API.version}#{path}" + + "/api/#{Gitlab::API::VERSION}#{path}" + # Normalize query string (path.index('?') ? '' : '?') + diff --git a/spec/support/db_cleaner.rb b/spec/support/db_cleaner.rb index 8c9c74f1..f1e072aa 100644 --- a/spec/support/db_cleaner.rb +++ b/spec/support/db_cleaner.rb @@ -9,14 +9,10 @@ RSpec.configure do |config| DatabaseCleaner.strategy = :transaction end - unless example.metadata[:no_db] - DatabaseCleaner.start - end + DatabaseCleaner.start end config.after do - unless example.metadata[:no_db] - DatabaseCleaner.clean - end + DatabaseCleaner.clean end end diff --git a/spec/support/gitolite_stub.rb b/spec/support/gitolite_stub.rb new file mode 100644 index 00000000..574bb5a1 --- /dev/null +++ b/spec/support/gitolite_stub.rb @@ -0,0 +1,21 @@ +module GitoliteStub + def stub_gitolite! + stub_gitlab_gitolite + stub_gitolite_admin + end + + def stub_gitolite_admin + gitolite_admin = double('Gitolite::GitoliteAdmin') + gitolite_admin.as_null_object + + Gitolite::GitoliteAdmin.stub(new: gitolite_admin) + end + + def stub_gitlab_gitolite + gitolite_config = double('Gitlab::GitoliteConfig') + gitolite_config.stub(apply: ->() { yield(self) }) + gitolite_config.as_null_object + + Gitlab::GitoliteConfig.stub(new: gitolite_config) + end +end diff --git a/spec/support/login_helpers.rb b/spec/support/login_helpers.rb index d423ccf8..4579c971 100644 --- a/spec/support/login_helpers.rb +++ b/spec/support/login_helpers.rb @@ -12,7 +12,7 @@ module LoginHelpers # user - User instance to login with def login_with(user) visit new_user_session_path - fill_in "user_login", with: user.email + fill_in "user_email", with: user.email fill_in "user_password", with: "123456" click_button "Sign in" end diff --git a/spec/support/select2_helper.rb b/spec/support/select2_helper.rb deleted file mode 100644 index 20dd9bf4..00000000 --- a/spec/support/select2_helper.rb +++ /dev/null @@ -1,25 +0,0 @@ -# Select2 ajax programatic helper -# It allows you to select value from select2 -# -# Params -# value - real value of selected item -# opts - options containing css selector -# -# Usage: -# -# select2(2, from: '#user_ids') -# - -module Select2Helper - def select2(value, options={}) - raise "Must pass a hash containing 'from'" if not options.is_a?(Hash) or not options.has_key?(:from) - - selector = options[:from] - - if options[:multiple] - page.execute_script("$('#{selector}').select2('val', ['#{value}']);") - else - page.execute_script("$('#{selector}').select2('val', '#{value}');") - end - end -end diff --git a/spec/support/stubbed_repository.rb b/spec/support/stubbed_repository.rb new file mode 100644 index 00000000..5bf3ea46 --- /dev/null +++ b/spec/support/stubbed_repository.rb @@ -0,0 +1,31 @@ +# Stubs out all Git repository access done by models so that specs can run +# against fake repositories without Grit complaining that they don't exist. +module StubbedRepository + def path_to_repo + if new_record? || path == 'newproject' + # There are a couple Project specs and features that expect the Project's + # path to be in the returned path, so let's patronize them. + Rails.root.join('tmp', 'repositories', path) + else + # For everything else, just give it the path to one of our real seeded + # repos. + Rails.root.join('tmp', 'repositories', 'gitlabhq') + end + end + + def satellite + FakeSatellite.new + end + + class FakeSatellite + def exists? + true + end + + def create + true + end + end +end + +Project.send(:include, StubbedRepository) diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb deleted file mode 100644 index 370094d3..00000000 --- a/spec/support/test_env.rb +++ /dev/null @@ -1,63 +0,0 @@ -module TestEnv - extend self - - # Test environment - # - # all repositories and namespaces stored at - # RAILS_APP/tmp/test-git-base-path - # - # Next shell methods are stubbed and return true - # - mv_repository - # - remove_repository - # - add_key - # - remove_key - # - def init - # Use tmp dir for FS manipulations - repos_path = Rails.root.join('tmp', 'test-git-base-path') - Gitlab.config.gitlab_shell.stub(repos_path: repos_path) - - GollumWiki.any_instance.stub(:init_repo) do |path| - create_temp_repo(File.join(repos_path, "#{path}.git")) - end - - Gitlab::Shell.any_instance.stub( - add_repository: true, - mv_repository: true, - remove_repository: true, - add_key: true, - remove_key: true - ) - - Gitlab::Satellite::Satellite.any_instance.stub( - exists?: true, - destroy: true, - create: true - ) - - MergeRequest.any_instance.stub( - check_if_can_be_merged: true - ) - - Repository.any_instance.stub( - size: 12.45 - ) - - # Remove tmp/test-git-base-path - FileUtils.rm_rf Gitlab.config.gitlab_shell.repos_path - - # Recreate tmp/test-git-base-path - FileUtils.mkdir_p Gitlab.config.gitlab_shell.repos_path - - # Symlink tmp/repositories/gitlabhq to tmp/test-git-base-path/gitlabhq - seed_repo = Rails.root.join('tmp', 'repositories', 'gitlabhq') - target_repo = File.join(repos_path, 'gitlabhq.git') - system("ln -s #{seed_repo} #{target_repo}") - end - - def create_temp_repo(path) - FileUtils.mkdir_p path - command = "git init --quiet --bare #{path};" - system(command) - end -end diff --git a/spec/tasks/gitlab/backup_rake_spec.rb b/spec/tasks/gitlab/backup_rake_spec.rb deleted file mode 100644 index babbf291..00000000 --- a/spec/tasks/gitlab/backup_rake_spec.rb +++ /dev/null @@ -1,47 +0,0 @@ -require 'spec_helper' -require 'rake' - -describe 'gitlab:app namespace rake task' do - before :all do - Rake.application.rake_require "tasks/gitlab/task_helpers" - Rake.application.rake_require "tasks/gitlab/backup" - # empty task as env is already loaded - Rake::Task.define_task :environment - end - - describe 'backup_restore' do - before do - # avoid writing task output to spec progress - $stdout.stub :write - end - - let :run_rake_task do - Rake::Task["gitlab:backup:restore"].reenable - Rake.application.invoke_task "gitlab:backup:restore" - end - - context 'gitlab version' do - before do - Dir.stub :glob => [] - Dir.stub :chdir - File.stub :exists? => true - Kernel.stub :system => true - end - - let(:gitlab_version) { %x{git rev-parse HEAD}.gsub(/\n/,"") } - - it 'should fail on mismach' do - YAML.stub :load_file => {:gitlab_version => gitlab_version.reverse} - expect { run_rake_task }.to raise_error SystemExit - end - - it 'should invoke restoration on mach' do - YAML.stub :load_file => {:gitlab_version => gitlab_version} - Rake::Task["gitlab:backup:db:restore"].should_receive :invoke - Rake::Task["gitlab:backup:repo:restore"].should_receive :invoke - expect { run_rake_task }.to_not raise_error SystemExit - end - end - - end # backup_restore task -end # gitlab:app namespace diff --git a/spec/workers/post_receive_spec.rb b/spec/workers/post_receive_spec.rb index 46e86dbe..bbc91f44 100644 --- a/spec/workers/post_receive_spec.rb +++ b/spec/workers/post_receive_spec.rb @@ -4,39 +4,37 @@ describe PostReceive do context "as a resque worker" do it "reponds to #perform" do - PostReceive.new.should respond_to(:perform) + PostReceive.should respond_to(:perform) end end context "web hook" do - let(:project) { create(:project_with_code) } + let(:project) { create(:project) } let(:key) { create(:key, user: project.owner) } - let(:key_id) { key.shell_id } + let(:key_id) { key.identifier } it "fetches the correct project" do - Project.should_receive(:find_with_namespace).with(project.path_with_namespace).and_return(project) - PostReceive.new.perform(pwd(project), 'sha-old', 'sha-new', 'refs/heads/master', key_id) + Project.should_receive(:find_by_path).with(project.path).and_return(project) + PostReceive.perform(project.path, 'sha-old', 'sha-new', 'refs/heads/master', key_id) end it "does not run if the author is not in the project" do - Key.stub(find_by_id: nil) + Key.stub(find_by_identifier: nil) + project.should_not_receive(:observe_push) project.should_not_receive(:execute_hooks) - PostReceive.new.perform(pwd(project), 'sha-old', 'sha-new', 'refs/heads/master', key_id).should be_false + PostReceive.perform(project.path, 'sha-old', 'sha-new', 'refs/heads/master', key_id).should be_false end it "asks the project to trigger all hooks" do - Project.stub(find_with_namespace: project) + Project.stub(find_by_path: project) project.should_receive(:execute_hooks) project.should_receive(:execute_services) project.should_receive(:update_merge_requests) + project.should_receive(:observe_push) - PostReceive.new.perform(pwd(project), 'sha-old', 'sha-new', 'refs/heads/master', key_id) + PostReceive.perform(project.path, 'sha-old', 'sha-new', 'refs/heads/master', key_id) end end - - def pwd(project) - File.join(Gitlab.config.gitlab_shell.repos_path, project.path_with_namespace) - end end diff --git a/vendor/assets/javascripts/ace-src-noconflict/ace.js b/vendor/assets/javascripts/ace-src-noconflict/ace.js index 8bd2d9a6..11b4bbb8 100644 --- a/vendor/assets/javascripts/ace-src-noconflict/ace.js +++ b/vendor/assets/javascripts/ace-src-noconflict/ace.js @@ -4800,7 +4800,7 @@ var SearchHighlight = require("./search_highlight").SearchHighlight; /** * new EditSession(text, mode) * - text (Document | String): If `text` is a `Document`, it associates the `EditSession` with it. Otherwise, a new `Document` is created, with the initial text - * - mode (TextMode): The initial language mode to use for the document + * - mode (TextMode): The inital language mode to use for the document * * Sets up a new `EditSession` and associates it with the given `Document` and `TextMode`. * @@ -10068,7 +10068,7 @@ ace.define('ace/token_iterator', ['require', 'exports', 'module' ], function(req * - initialRow (Number): The row to start the tokenizing at * - initialColumn (Number): The column to start the tokenizing at * - * Creates a new token iterator object. The initial token index is set to the provided row and column coordinates. + * Creates a new token iterator object. The inital token index is set to the provided row and column coordinates. * **/ var TokenIterator = function(session, initialRow, initialColumn) { @@ -11946,7 +11946,7 @@ var VirtualRenderer = function(container, theme) { this.$horizScroll = horizScroll; if (horizScrollChanged) { this.scroller.style.overflowX = horizScroll ? "scroll" : "hidden"; - // when we hide scrollbar scroll event isn't emitted + // when we hide scrollbar scroll event isn't emited // leaving session with wrong scrollLeft value if (!horizScroll) this.session.setScrollLeft(0); @@ -13029,7 +13029,7 @@ var Text = function(parentEl) { var html = []; // Get the tokens per line as there might be some lines in between - // being folded. + // beeing folded. this.$renderLine(html, row, false, row == foldStart ? foldLine : false); // don't use setInnerHtml since we are working with an empty DIV @@ -14529,7 +14529,7 @@ var Editor = require("./editor").Editor; * - dir (Number): The direction of lines to select: -1 for up, 1 for down * - skip (Boolean): If `true`, removes the active selection range * - * Finds the next occurrence of text in an active selection and adds it to the selections. + * Finds the next occurence of text in an active selection and adds it to the selections. **/ this.selectMore = function(dir, skip) { var session = this.session; @@ -15878,4 +15878,4 @@ ace.define("text!ace/theme/textmate.css", [], ".ace-tm .ace_editor {\n" + ace[key] = a[key]; }); })(); - + \ No newline at end of file diff --git a/vendor/assets/javascripts/ace-src-noconflict/mode-coldfusion.js b/vendor/assets/javascripts/ace-src-noconflict/mode-coldfusion.js index aea42145..1b41d001 100644 --- a/vendor/assets/javascripts/ace-src-noconflict/mode-coldfusion.js +++ b/vendor/assets/javascripts/ace-src-noconflict/mode-coldfusion.js @@ -1170,7 +1170,7 @@ var JavaScriptHighlightRules = function() { } ], // regular expressions are only allowed after certain tokens. This - // makes sure we don't mix up regexps with the division operator + // makes sure we don't mix up regexps with the divison operator "regex_allowed": [ DocCommentHighlightRules.getStartRule("doc-start"), { diff --git a/vendor/assets/javascripts/ace-src-noconflict/mode-groovy.js b/vendor/assets/javascripts/ace-src-noconflict/mode-groovy.js index 80d153d8..ad7bdf58 100644 --- a/vendor/assets/javascripts/ace-src-noconflict/mode-groovy.js +++ b/vendor/assets/javascripts/ace-src-noconflict/mode-groovy.js @@ -337,7 +337,7 @@ var JavaScriptHighlightRules = function() { } ], // regular expressions are only allowed after certain tokens. This - // makes sure we don't mix up regexps with the division operator + // makes sure we don't mix up regexps with the divison operator "regex_allowed": [ DocCommentHighlightRules.getStartRule("doc-start"), { diff --git a/vendor/assets/javascripts/ace-src-noconflict/mode-html.js b/vendor/assets/javascripts/ace-src-noconflict/mode-html.js index 5ec11f14..0ea36845 100644 --- a/vendor/assets/javascripts/ace-src-noconflict/mode-html.js +++ b/vendor/assets/javascripts/ace-src-noconflict/mode-html.js @@ -389,7 +389,7 @@ var JavaScriptHighlightRules = function() { } ], // regular expressions are only allowed after certain tokens. This - // makes sure we don't mix up regexps with the division operator + // makes sure we don't mix up regexps with the divison operator "regex_allowed": [ DocCommentHighlightRules.getStartRule("doc-start"), { diff --git a/vendor/assets/javascripts/ace-src-noconflict/mode-jade.js b/vendor/assets/javascripts/ace-src-noconflict/mode-jade.js index 32aafd17..c01a3598 100644 --- a/vendor/assets/javascripts/ace-src-noconflict/mode-jade.js +++ b/vendor/assets/javascripts/ace-src-noconflict/mode-jade.js @@ -659,7 +659,7 @@ var JavaScriptHighlightRules = function() { } ], // regular expressions are only allowed after certain tokens. This - // makes sure we don't mix up regexps with the division operator + // makes sure we don't mix up regexps with the divison operator "regex_allowed": [ DocCommentHighlightRules.getStartRule("doc-start"), { diff --git a/vendor/assets/javascripts/ace-src-noconflict/mode-java.js b/vendor/assets/javascripts/ace-src-noconflict/mode-java.js index c05bf0f9..23f9e603 100644 --- a/vendor/assets/javascripts/ace-src-noconflict/mode-java.js +++ b/vendor/assets/javascripts/ace-src-noconflict/mode-java.js @@ -338,7 +338,7 @@ var JavaScriptHighlightRules = function() { } ], // regular expressions are only allowed after certain tokens. This - // makes sure we don't mix up regexps with the division operator + // makes sure we don't mix up regexps with the divison operator "regex_allowed": [ DocCommentHighlightRules.getStartRule("doc-start"), { diff --git a/vendor/assets/javascripts/ace-src-noconflict/mode-javascript.js b/vendor/assets/javascripts/ace-src-noconflict/mode-javascript.js index 90cf57eb..d266bffb 100644 --- a/vendor/assets/javascripts/ace-src-noconflict/mode-javascript.js +++ b/vendor/assets/javascripts/ace-src-noconflict/mode-javascript.js @@ -342,7 +342,7 @@ var JavaScriptHighlightRules = function() { } ], // regular expressions are only allowed after certain tokens. This - // makes sure we don't mix up regexps with the division operator + // makes sure we don't mix up regexps with the divison operator "regex_allowed": [ DocCommentHighlightRules.getStartRule("doc-start"), { diff --git a/vendor/assets/javascripts/ace-src-noconflict/mode-jsp.js b/vendor/assets/javascripts/ace-src-noconflict/mode-jsp.js index e4f4bcff..c96d25e9 100644 --- a/vendor/assets/javascripts/ace-src-noconflict/mode-jsp.js +++ b/vendor/assets/javascripts/ace-src-noconflict/mode-jsp.js @@ -597,7 +597,7 @@ var JavaScriptHighlightRules = function() { } ], // regular expressions are only allowed after certain tokens. This - // makes sure we don't mix up regexps with the division operator + // makes sure we don't mix up regexps with the divison operator "regex_allowed": [ DocCommentHighlightRules.getStartRule("doc-start"), { diff --git a/vendor/assets/javascripts/ace-src-noconflict/mode-liquid.js b/vendor/assets/javascripts/ace-src-noconflict/mode-liquid.js index 3886e739..2623396c 100644 --- a/vendor/assets/javascripts/ace-src-noconflict/mode-liquid.js +++ b/vendor/assets/javascripts/ace-src-noconflict/mode-liquid.js @@ -641,7 +641,7 @@ var JavaScriptHighlightRules = function() { } ], // regular expressions are only allowed after certain tokens. This - // makes sure we don't mix up regexps with the division operator + // makes sure we don't mix up regexps with the divison operator "regex_allowed": [ DocCommentHighlightRules.getStartRule("doc-start"), { diff --git a/vendor/assets/javascripts/ace-src-noconflict/mode-luahtml.js b/vendor/assets/javascripts/ace-src-noconflict/mode-luahtml.js index d8a1b653..89bc2ec7 100644 --- a/vendor/assets/javascripts/ace-src-noconflict/mode-luahtml.js +++ b/vendor/assets/javascripts/ace-src-noconflict/mode-luahtml.js @@ -466,7 +466,7 @@ var JavaScriptHighlightRules = function() { } ], // regular expressions are only allowed after certain tokens. This - // makes sure we don't mix up regexps with the division operator + // makes sure we don't mix up regexps with the divison operator "regex_allowed": [ DocCommentHighlightRules.getStartRule("doc-start"), { @@ -2412,4 +2412,4 @@ oop.inherits(LuaHtmlHighlightRules, HtmlHighlightRules); exports.LuaHtmlHighlightRules = LuaHtmlHighlightRules; -}); +}); \ No newline at end of file diff --git a/vendor/assets/javascripts/ace-src-noconflict/mode-luapage.js b/vendor/assets/javascripts/ace-src-noconflict/mode-luapage.js index 9be136de..8f03866d 100644 --- a/vendor/assets/javascripts/ace-src-noconflict/mode-luapage.js +++ b/vendor/assets/javascripts/ace-src-noconflict/mode-luapage.js @@ -382,7 +382,7 @@ var JavaScriptHighlightRules = function() { } ], // regular expressions are only allowed after certain tokens. This - // makes sure we don't mix up regexps with the division operator + // makes sure we don't mix up regexps with the divison operator "regex_allowed": [ DocCommentHighlightRules.getStartRule("doc-start"), { @@ -2477,4 +2477,4 @@ oop.inherits(LuaPageHighlightRules, HtmlHighlightRules); exports.LuaPageHighlightRules = LuaPageHighlightRules; -}); +}); \ No newline at end of file diff --git a/vendor/assets/javascripts/ace-src-noconflict/mode-markdown.js b/vendor/assets/javascripts/ace-src-noconflict/mode-markdown.js index e1c269ee..179376aa 100644 --- a/vendor/assets/javascripts/ace-src-noconflict/mode-markdown.js +++ b/vendor/assets/javascripts/ace-src-noconflict/mode-markdown.js @@ -384,7 +384,7 @@ var JavaScriptHighlightRules = function() { } ], // regular expressions are only allowed after certain tokens. This - // makes sure we don't mix up regexps with the division operator + // makes sure we don't mix up regexps with the divison operator "regex_allowed": [ DocCommentHighlightRules.getStartRule("doc-start"), { diff --git a/vendor/assets/javascripts/ace-src-noconflict/mode-php.js b/vendor/assets/javascripts/ace-src-noconflict/mode-php.js index 40710cee..d3deefc6 100644 --- a/vendor/assets/javascripts/ace-src-noconflict/mode-php.js +++ b/vendor/assets/javascripts/ace-src-noconflict/mode-php.js @@ -1685,7 +1685,7 @@ var JavaScriptHighlightRules = function() { } ], // regular expressions are only allowed after certain tokens. This - // makes sure we don't mix up regexps with the division operator + // makes sure we don't mix up regexps with the divison operator "regex_allowed": [ DocCommentHighlightRules.getStartRule("doc-start"), { diff --git a/vendor/assets/javascripts/ace-src-noconflict/mode-scala.js b/vendor/assets/javascripts/ace-src-noconflict/mode-scala.js index 87b8e12b..ee8ebb8f 100644 --- a/vendor/assets/javascripts/ace-src-noconflict/mode-scala.js +++ b/vendor/assets/javascripts/ace-src-noconflict/mode-scala.js @@ -338,7 +338,7 @@ var JavaScriptHighlightRules = function() { } ], // regular expressions are only allowed after certain tokens. This - // makes sure we don't mix up regexps with the division operator + // makes sure we don't mix up regexps with the divison operator "regex_allowed": [ DocCommentHighlightRules.getStartRule("doc-start"), { diff --git a/vendor/assets/javascripts/ace-src-noconflict/mode-svg.js b/vendor/assets/javascripts/ace-src-noconflict/mode-svg.js index 96b965ba..29c2b17b 100644 --- a/vendor/assets/javascripts/ace-src-noconflict/mode-svg.js +++ b/vendor/assets/javascripts/ace-src-noconflict/mode-svg.js @@ -1177,7 +1177,7 @@ var JavaScriptHighlightRules = function() { } ], // regular expressions are only allowed after certain tokens. This - // makes sure we don't mix up regexps with the division operator + // makes sure we don't mix up regexps with the divison operator "regex_allowed": [ DocCommentHighlightRules.getStartRule("doc-start"), { diff --git a/vendor/assets/javascripts/ace-src-noconflict/worker-javascript.js b/vendor/assets/javascripts/ace-src-noconflict/worker-javascript.js index 671d7bec..923bac6f 100644 --- a/vendor/assets/javascripts/ace-src-noconflict/worker-javascript.js +++ b/vendor/assets/javascripts/ace-src-noconflict/worker-javascript.js @@ -2912,7 +2912,7 @@ var JSHINT = (function () { immed : true, // if immediate invocations must be wrapped in parens iterator : true, // if the `__iterator__` property should be allowed jquery : true, // if jQuery globals should be predefined - lastsemic : true, // if semicolons may be omitted for the trailing + lastsemic : true, // if semicolons may be ommitted for the trailing // statements inside of a one-line blocks. latedef : true, // if the use before definition should not be tolerated laxbreak : true, // if line breaks should not be checked @@ -3674,7 +3674,7 @@ var JSHINT = (function () { line += 1; // If smarttabs option is used check for spaces followed by tabs only. - // Otherwise check for any occurrence of mixed tabs and spaces. + // Otherwise check for any occurence of mixed tabs and spaces. if (option.smarttabs) at = s.search(/ \t/); else diff --git a/vendor/assets/javascripts/ace-src-noconflict/worker-xquery.js b/vendor/assets/javascripts/ace-src-noconflict/worker-xquery.js index 1d8c512e..494066fd 100644 --- a/vendor/assets/javascripts/ace-src-noconflict/worker-xquery.js +++ b/vendor/assets/javascripts/ace-src-noconflict/worker-xquery.js @@ -7609,7 +7609,7 @@ org.antlr.runtime.BaseRecognizer.prototype = { * * Until then I'll leave this unimplemented. If there is enough clamor * it would be possible to keep track of the invocation stack using an - * auxiliary array, but that will definitely be a performance hit. + * auxillary array, but that will definitely be a performance hit. */ getRuleInvocationStack: function(e, recognizerClassName) { diff --git a/vendor/assets/javascripts/branch-graph.js b/vendor/assets/javascripts/branch-graph.js new file mode 100644 index 00000000..e8699bdf --- /dev/null +++ b/vendor/assets/javascripts/branch-graph.js @@ -0,0 +1,181 @@ +var commits = {}, + comms = {}, + pixelsX = [], + pixelsY = [], + mmax = Math.max, + mtime = 0, + mspace = 0, + parents = {}, + ii = 0, + colors = ["#000"]; + +function initGraph(){ + commits = chunk1.commits; + ii = commits.length; + for (var i = 0; i < ii; i++) { + for (var j = 0, jj = commits[i].parents.length; j < jj; j++) { + parents[commits[i].parents[j][0]] = true; + } + mtime = Math.max(mtime, commits[i].time); + mspace = Math.max(mspace, commits[i].space); + } + mtime = mtime + 4; + mspace = mspace + 10; + for (i = 0; i < ii; i++) { + if (commits[i].id in parents) { + commits[i].isParent = true; + } + comms[commits[i].id] = commits[i]; + } + for (var k = 0; k < mspace; k++) { + colors.push(Raphael.getColor()); + } +} + +function branchGraph(holder) { + var ch = mspace * 20 + 20, cw = mtime * 20 + 20, + r = Raphael("holder", cw, ch), + top = r.set(); + var cuday = 0, cumonth = ""; + r.rect(0, 0, days.length * 20 + 80, 30).attr({fill: "#222"}); + r.rect(0, 30, days.length * 20 + 80, 20).attr({fill: "#444"}); + + for (mm = 0; mm < days.length; mm++) { + if(days[mm] != null){ + if(cuday != days[mm][0]){ + r.text(10 + mm * 20, 40, days[mm][0]).attr({font: "14px Fontin-Sans, Arial", fill: "#DDD"}); + cuday = days[mm][0] + } + if(cumonth != days[mm][1]){ + r.text(10 + mm * 20, 15, days[mm][1]).attr({font: "14px Fontin-Sans, Arial", fill: "#EEE"}); + cumonth = days[mm][1] + } + + } + } + for (i = 0; i < ii; i++) { + var x = 10 + 20 * commits[i].time, + y = 70 + 20 * commits[i].space; + r.circle(x, y, 3).attr({fill: colors[commits[i].space], stroke: "none"}); + if (commits[i].refs != null && commits[i].refs != "") { + var longrefs = commits[i].refs + var shortrefs = commits[i].refs; + if (shortrefs.length > 15){ + shortrefs = shortrefs.substr(0,13) + "..."; + } + var t = r.text(x+5, y+5, shortrefs).attr({font: "12px Fontin-Sans, Arial", fill: "#666", + title: longrefs, cursor: "pointer", rotation: "90"}); + + var textbox = t.getBBox(); + t.translate(textbox.height/-4,textbox.width/2); + } + for (var j = 0, jj = commits[i].parents.length; j < jj; j++) { + var c = comms[commits[i].parents[j][0]]; + if (c) { + var cx = 10 + 20 * c.time, + cy = 70 + 20 * c.space; + if (c.space == commits[i].space) { + r.path("M" + (x - 5) + "," + (y + .0001) + "L" + (15 + 20 * c.time) + "," + (y + .0001)) + .attr({stroke: colors[c.space], "stroke-width": 2}); + + } else if (c.space < commits[i].space) { + r.path(["M", x - 5, y + .0001, "l-5-2,0,4,5,-2C", x - 5, y, x - 17, y + 2, x - 20, y - 5, "L", cx, y - 5, cx, cy]) + .attr({stroke: colors[commits[i].space], "stroke-width": 2}); + } else { + r.path(["M", x - 3, y + 6, "l-4,3,4,2,0,-5L", x - 10, y + 20, "L", x - 10, cy, cx, cy]) + .attr({stroke: colors[c.space], "stroke-width": 2}); + } + } + } + (function (c, x, y) { + top.push(r.circle(x, y, 10).attr({fill: "#000", opacity: 0, cursor: "pointer"}) + .click(function(){ + location.href = location.href.replace("graph", "commits/" + c.id); + }) + .hover(function () { + var s = r.text(100, 100,c.author + "\n \n" +c.id + "\n \n" + c.message).attr({fill: "#fff"}); + this.popup = r.popupit(x, y + 5, s, 0); + top.push(this.popup.insertBefore(this)); + }, function () { + this.popup && this.popup.remove() && delete this.popup; + })); + }(commits[i], x, y)); + } + top.toFront(); + var hw = holder.offsetWidth, + hh = holder.offsetHeight, + v = r.rect(hw - 8, 0, 4, Math.pow(hh, 2) / ch, 2).attr({fill: "#000", opacity: 0}), + h = r.rect(0, hh - 8, Math.pow(hw, 2) / cw, 4, 2).attr({fill: "#000", opacity: 0}), + bars = r.set(v, h), + drag, + dragger = function (e) { + if (drag) { + e = e || window.event; + holder.scrollLeft = drag.sl - (e.clientX - drag.x); + holder.scrollTop = drag.st - (e.clientY - drag.y); + } + }; + holder.onmousedown = function (e) { + e = e || window.event; + drag = {x: e.clientX, y: e.clientY, st: holder.scrollTop, sl: holder.scrollLeft}; + document.onmousemove = dragger; + bars.animate({opacity: .5}, 300); + }; + document.onmouseup = function () { + drag = false; + document.onmousemove = null; + bars.animate({opacity: 0}, 300); + }; + holder.scrollLeft = cw; +}; +Raphael.fn.popupit = function (x, y, set, dir, size) { + dir = dir == null ? 2 : dir; + size = size || 5; + x = Math.round(x); + y = Math.round(y); + var bb = set.getBBox(), + w = Math.round(bb.width / 2), + h = Math.round(bb.height / 2), + dx = [0, w + size * 2, 0, -w - size * 2], + dy = [-h * 2 - size * 3, -h - size, 0, -h - size], + p = ["M", x - dx[dir], y - dy[dir], "l", -size, (dir == 2) * -size, -mmax(w - size, 0), 0, "a", size, size, 0, 0, 1, -size, -size, + "l", 0, -mmax(h - size, 0), (dir == 3) * -size, -size, (dir == 3) * size, -size, 0, -mmax(h - size, 0), "a", size, size, 0, 0, 1, size, -size, + "l", mmax(w - size, 0), 0, size, !dir * -size, size, !dir * size, mmax(w - size, 0), 0, "a", size, size, 0, 0, 1, size, size, + "l", 0, mmax(h - size, 0), (dir == 1) * size, size, (dir == 1) * -size, size, 0, mmax(h - size, 0), "a", size, size, 0, 0, 1, -size, size, + "l", -mmax(w - size, 0), 0, "z"].join(","), + xy = [{x: x, y: y + size * 2 + h}, {x: x - size * 2 - w, y: y}, {x: x, y: y - size * 2 - h}, {x: x + size * 2 + w, y: y}][dir]; + set.translate(xy.x - w - bb.x, xy.y - h - bb.y); + return this.set(this.path(p).attr({fill: "#234", stroke: "none"}).insertBefore(set.node ? set : set[0]), set); +}; +Raphael.fn.popup = function (x, y, text, dir, size) { + dir = dir == null ? 2 : dir > 3 ? 3 : dir; + size = size || 5; + text = text || "$9.99"; + var res = this.set(), + d = 3; + res.push(this.path().attr({fill: "#000", stroke: "#000"})); + res.push(this.text(x, y, text).attr(this.g.txtattr).attr({fill: "#fff", "font-family": "Helvetica, Arial"})); + res.update = function (X, Y, withAnimation) { + X = X || x; + Y = Y || y; + var bb = this[1].getBBox(), + w = bb.width / 2, + h = bb.height / 2, + dx = [0, w + size * 2, 0, -w - size * 2], + dy = [-h * 2 - size * 3, -h - size, 0, -h - size], + p = ["M", X - dx[dir], Y - dy[dir], "l", -size, (dir == 2) * -size, -mmax(w - size, 0), 0, "a", size, size, 0, 0, 1, -size, -size, + "l", 0, -mmax(h - size, 0), (dir == 3) * -size, -size, (dir == 3) * size, -size, 0, -mmax(h - size, 0), "a", size, size, 0, 0, 1, size, -size, + "l", mmax(w - size, 0), 0, size, !dir * -size, size, !dir * size, mmax(w - size, 0), 0, "a", size, size, 0, 0, 1, size, size, + "l", 0, mmax(h - size, 0), (dir == 1) * size, size, (dir == 1) * -size, size, 0, mmax(h - size, 0), "a", size, size, 0, 0, 1, -size, size, + "l", -mmax(w - size, 0), 0, "z"].join(","), + xy = [{x: X, y: Y + size * 2 + h}, {x: X - size * 2 - w, y: Y}, {x: X, y: Y - size * 2 - h}, {x: X + size * 2 + w, y: Y}][dir]; + xy.path = p; + if (withAnimation) { + this.animate(xy, 500, ">"); + } else { + this.attr(xy); + } + return this; + }; + return res.update(x, y); +}; diff --git a/vendor/assets/javascripts/g.bar-min.js b/vendor/assets/javascripts/g.bar-min.js index 7620dabd..42f452af 100644 --- a/vendor/assets/javascripts/g.bar-min.js +++ b/vendor/assets/javascripts/g.bar-min.js @@ -3,6 +3,5 @@ * * Copyright (c) 2009 Dmitry Baranovskiy (http://g.raphaeljs.com) * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license. - * From: https://github.com/jhurt/g.raphael/blob/master/g.bar.js */ -(function(){function c(c,d,e,f,g,h,i,j){var k,l={round:"round",sharp:"sharp",soft:"soft",square:"square"};if(g&&!f||!g&&!e)return i?"":j.path();switch(h=l[h]||"square",f=Math.round(f),e=Math.round(e),c=Math.round(c),d=Math.round(d),h){case"round":if(g)m=~~(e/2),m>f?(m=f,k=["M",c-~~(e/2),d,"l",0,0,"a",~~(e/2),m,0,0,1,e,0,"l",0,0,"z"]):k=["M",c-m,d,"l",0,m-f,"a",m,m,0,1,1,e,0,"l",0,f-m,"z"];else{var m=~~(f/2);m>e?(m=e,k=["M",c+.5,d+.5-~~(f/2),"l",0,0,"a",m,~~(f/2),0,0,1,0,f,"l",0,0,"z"]):k=["M",c+.5,d+.5-m,"l",e-m,0,"a",m,m,0,1,1,0,f,"l",m-e,0,"z"]}break;case"sharp":if(g)n=~~(e/2),k=["M",c+n,d,"l",-e,0,0,-b(f-n,0),n,-a(n,f),n,a(n,f),n,"z"];else{var n=~~(f/2);k=["M",c,d+n,"l",0,-f,b(e-n,0),0,a(n,e),n,-a(n,e),n+(f>2*n),"z"]}break;case"square":k=g?["M",c+~~(e/2),d,"l",1-e,0,0,-f,e-1,0,"z"]:["M",c,d+~~(f/2),"l",0,-f,e,0,0,f,"z"];break;case"soft":g?(m=a(Math.round(e/5),f),k=["M",c-~~(e/2),d,"l",0,m-f,"a",m,m,0,0,1,m,-m,"l",e-2*m,0,"a",m,m,0,0,1,m,m,"l",0,f-m,"z"]):(m=a(e,Math.round(f/5)),k=["M",c+.5,d+.5-~~(f/2),"l",e-m,0,"a",m,m,0,0,1,m,m,"l",0,f-2*m,"a",m,m,0,0,1,-m,m,"l",m-e,0,"z"])}return i?k.join(","):j.path(k)}function d(a,b,d,e,f,g,h){h=h||{};var i=this,j=h.type||"square",k=parseFloat(h.gutter||"20%"),l=a.set(),m=a.set(),n=a.set(),o=a.set(),p=Math.max.apply(Math,g),q=[],r=0,s=h.colors||i.colors,t=g.length;if(Raphael.is(g[0],"array")){p=[],r=t,t=0;for(var u=g.length;u--;)m.push(a.set()),p.push(Math.max.apply(Math,g[u])),t=Math.max(t,g[u].length);if(h.stacked)for(var u=t;u--;){for(var v=0,w=g.length;w--;)v+=+g[w][u]||0;q.push(v)}for(var u=g.length;u--;)if(t>g[u].length)for(var w=t;w--;)g[u].push(0);p=Math.max.apply(Math,h.stacked?q:p)}p=h.to||p;var x=100*(e/(t*(100+k)+k)),y=x*k/100,z=null==h.vgutter?20:h.vgutter,A=[],B=b+y,C=(f-2*z)/p;h.stretch||(y=Math.round(y),x=Math.floor(x)),!h.stacked&&(x/=r||1);for(var u=0;t>u;u++){A=[];for(var w=0;(r||1)>w;w++){var D=Math.round((r?g[w][u]:g[u])*C),E=d+f-z-D,F=c(Math.round(B+x/2),E+D,x,D,!0,j,null,a).attr({stroke:"none",fill:s[r?w:u]});r?m[w].push(F):m.push(F),F.y=E,F.x=Math.round(B+x/2),F.w=x,F.h=D,F.value=r?g[w][u]:g[u],h.stacked?A.push(F):B+=x}if(h.stacked){var G;o.push(G=a.rect(A[0].x-A[0].w/2,d,x,f).attr(i.shim)),G.bars=a.set();for(var H=0,I=A.length;I--;)A[I].toFront();for(var I=0,J=A.length;J>I;I++){var K,F=A[I],D=(H+F.value)*C,L=c(F.x,d+f-z-.5*!!H,x,D,!0,j,1,a);G.bars.push(F),H&&F.attr({path:L}),F.h=D,F.y=d+f-z-.5*!!H-D,n.push(K=a.rect(F.x-F.w/2,F.y,x,F.value*C).attr(i.shim)),K.bar=F,K.value=F.value,H+=F.value}B+=x}B+=y}if(o.toFront(),B=b+y,!h.stacked)for(var u=0;t>u;u++){for(var w=0;(r||1)>w;w++){var K;n.push(K=a.rect(Math.round(B),d+z,x,f-z).attr(i.shim)),K.bar=r?m[w][u]:m[u],K.value=K.bar.value,B+=x}B+=y}return l.label=function(b,c){b=b||[],this.labels=a.set();var e,j=-1/0;if(h.stacked){for(var k=0;t>k;k++)for(var l=0,o=0;(r||1)>o;o++)if(l+=r?g[o][k]:g[k],o==r-1){var q=i.labelise(b[k],l,p);e=a.text(m[o][k].x,d+f-z/2,q).attr(i.txtattr).attr({fill:h.legendcolor||"#000","text-anchor":"start"}).insertBefore(n[k*(r||1)+o]);var s=e.getBBox();j>s.x-7?e.remove():(this.labels.push(e),j=s.x+s.width)}}else for(var k=0;t>k;k++)for(var o=0;(r||1)>o;o++){var q=i.labelise(r?b[o]&&b[o][k]:b[k],r?g[o][k]:g[k],p);e=a.text(m[o][k].x-x/2,c?d+f-z/2:m[o][k].y-10,q).attr(i.txtattr).attr({fill:h.legendcolor||"#000","text-anchor":"start"}).insertBefore(n[k*(r||1)+o]);var s=e.getBBox();e.translate((x-s.width)/2,1),j>s.x-7?e.remove():(this.labels.push(e),j=s.x+s.width)}return this},l.hover=function(a,b){return o.hide(),n.show(),n.mouseover(a).mouseout(b),this},l.hoverColumn=function(a,b){return n.hide(),o.show(),b=b||function(){},o.mouseover(a).mouseout(b),this},l.click=function(a){return o.hide(),n.show(),n.click(a),this},l.each=function(a){if(!Raphael.is(a,"function"))return this;for(var b=n.length;b--;)a.call(n[b]);return this},l.eachColumn=function(a){if(!Raphael.is(a,"function"))return this;for(var b=o.length;b--;)a.call(o[b]);return this},l.clickColumn=function(a){return n.hide(),o.show(),o.click(a),this},l.push(m,n,o),l.bars=m,l.covers=n,l}function e(a,b,d,e,f,g,h){h=h||{};var i=this,j=h.type||"square",k=parseFloat(h.gutter||"20%"),l=a.set(),m=a.set(),n=a.set(),o=a.set(),p=Math.max.apply(Math,g),q=[],r=0,s=h.colors||i.colors,t=g.length;if(Raphael.is(g[0],"array")){p=[],r=t,t=0;for(var u=g.length;u--;)m.push(a.set()),p.push(Math.max.apply(Math,g[u])),t=Math.max(t,g[u].length);if(h.stacked)for(var u=t;u--;){for(var v=0,w=g.length;w--;)v+=+g[w][u]||0;q.push(v)}for(var u=g.length;u--;)if(t>g[u].length)for(var w=t;w--;)g[u].push(0);p=Math.max.apply(Math,h.stacked?q:p)}p=h.to||p;var x=Math.floor(100*(f/(t*(100+k)+k))),y=Math.floor(x*k/100),z=[],A=d+y,B=(e-1)/p;!h.stacked&&(x/=r||1);for(var u=0;t>u;u++){z=[];for(var w=0;(r||1)>w;w++){var C=r?g[w][u]:g[u],D=c(b,A+x/2,Math.round(C*B),x-1,!1,j,null,a).attr({stroke:"none",fill:s[r?w:u]});r?m[w].push(D):m.push(D),D.x=b+Math.round(C*B),D.y=A+x/2,D.w=Math.round(C*B),D.h=x,D.value=+C,h.stacked?z.push(D):A+=x}if(h.stacked){var E=a.rect(b,z[0].y-z[0].h/2,e,x).attr(i.shim);o.push(E),E.bars=a.set();for(var F=0,G=z.length;G--;)z[G].toFront();for(var G=0,H=z.length;H>G;G++){var I,D=z[G],C=Math.round((F+D.value)*B),J=c(b,D.y,C,x-1,!1,j,1,a);E.bars.push(D),F&&D.attr({path:J}),D.w=C,D.x=b+C,n.push(I=a.rect(b+F*B,D.y-D.h/2,D.value*B,x).attr(i.shim)),I.bar=D,F+=D.value}A+=x}A+=y}if(o.toFront(),A=d+y,!h.stacked)for(var u=0;t>u;u++){for(var w=0;(r||1)>w;w++){var I=a.rect(b,A,e,x).attr(i.shim);n.push(I),I.bar=r?m[w][u]:m[u],I.value=I.bar.value,A+=x}A+=y}return l.label=function(c,d){c=c||[],this.labels=a.set();for(var e=0;t>e;e++)for(var f=0;r>f;f++){var o,j=i.labelise(r?c[f]&&c[f][e]:c[e],r?g[f][e]:g[e],p),k=d?m[f][e].x-x/2+3:b+5;this.labels.push(o=a.text(k,m[f][e].y,j).attr(i.txtattr).attr({fill:h.legendcolor||"#000","text-anchor":"start"}).insertBefore(n[0])),b+5>o.getBBox().x?o.attr({x:b+5,"text-anchor":"start"}):m[f][e].label=o}return this},l.hover=function(a,b){return o.hide(),n.show(),b=b||function(){},n.mouseover(a).mouseout(b),this},l.hoverColumn=function(a,b){return n.hide(),o.show(),b=b||function(){},o.mouseover(a).mouseout(b),this},l.each=function(a){if(!Raphael.is(a,"function"))return this;for(var b=n.length;b--;)a.call(n[b]);return this},l.eachColumn=function(a){if(!Raphael.is(a,"function"))return this;for(var b=o.length;b--;)a.call(o[b]);return this},l.click=function(a){return o.hide(),n.show(),n.click(a),this},l.clickColumn=function(a){return n.hide(),o.show(),o.click(a),this},l.push(m,n,o),l.bars=m,l.covers=n,l}var a=Math.min,b=Math.max,f=function(){};f.prototype=Raphael.g,e.prototype=d.prototype=new f,Raphael.fn.hbarchart=function(a,b,c,d,f,g){return new e(this,a,b,c,d,f,g)},Raphael.fn.barchart=function(a,b,c,e,f,g){return new d(this,a,b,c,e,f,g)}})(); \ No newline at end of file +(function(){var f=Math.min,a=Math.max;function e(o,m,h,p,j,k,l,i){var s,n={round:"round",sharp:"sharp",soft:"soft",square:"square"};if((j&&!p)||(!j&&!h)){return l?"":i.path()}k=n[k]||"square";p=Math.round(p);h=Math.round(h);o=Math.round(o);m=Math.round(m);switch(k){case"round":if(!j){var g=~~(p/2);if(h=2*b?g.attr({path:["M",c,d+b,"a",b,b,0,1,1,0,2*-b,b,b,0,1,1,0,2*b,"m",0,2*-b-e,"a",b+e,b+e,0,1,0,0,2*(b+e),"L",c+b+e,d+h.height/2+e,"l",h.width+2*e,0,0,-h.height-2*e,-h.width-2*e,0,"L",c,d-b-e].join(",")}):(i=Math.sqrt(Math.pow(b+e,2)-Math.pow(h.height/2+e,2)),g.attr({path:["M",c,d+b,"c",-j,0,-b,j-b,-b,-b,0,-j,b-j,-b,b,-b,j,0,b,b-j,b,b,0,j,j-b,b,-b,b,"M",c+i,d-h.height/2-e,"a",b+e,b+e,0,1,0,0,h.height+2*e,"l",b+e-i+h.width+2*e,0,0,-h.height-2*e,"L",c+i,d-h.height/2-e].join(",")})),a=360-a,g.rotate(a,c,d),this.attrs?(this.attr(this.attrs.x?"x":"cx",c+b+e+(k?h.width/2:"text"==this.type?h.width:0)).attr("y",k?d:d-h.height/2),this.rotate(a,c,d),a>90&&270>a&&this.attr(this.attrs.x?"x":"cx",c-b-e-(k?h.width/2:h.width)).rotate(180,c,d)):a>90&&270>a?(this.translate(c-h.x-h.width-b-e,d-h.y-h.height/2),this.rotate(a-180,h.x+h.width+b+e,h.y+h.height/2)):(this.translate(c-h.x+b+e,d-h.y-h.height/2),this.rotate(a,h.x-b-e,h.y+h.height/2)),g.insertBefore(this.node?this:this[0])}},Raphael.el.drop=function(a,b,c){var f,g,h,i,j,d=this.getBBox(),e=this.paper||this[0].paper;if(e){switch(this.type){case"text":case"circle":case"ellipse":f=!0;break;default:f=!1}return a=a||0,b="number"==typeof b?b:f?d.x+d.width/2:d.x,c="number"==typeof c?c:f?d.y+d.height/2:d.y,g=Math.max(d.width,d.height)+Math.min(d.width,d.height),h=e.path(["M",b,c,"l",g,0,"A",.4*g,.4*g,0,1,0,b+.7*g,c-.7*g,"z"]).attr({fill:"#000",stroke:"none"}).rotate(22.5-a,b,c),a=(a+90)*Math.PI/180,i=b+g*Math.sin(a)-(f?0:d.width/2),j=c+g*Math.cos(a)-(f?0:d.height/2),this.attrs?this.attr(this.attrs.x?"x":"cx",i).attr(this.attrs.y?"y":"cy",j):this.translate(i-d.x,j-d.y),h.insertBefore(this.node?this:this[0])}},Raphael.el.flag=function(a,b,c){var d=3,e=this.paper||this[0].paper;if(e){var i,f=e.path().attr({fill:"#000",stroke:"#000"}),g=this.getBBox(),h=g.height/2;switch(this.type){case"text":case"circle":case"ellipse":i=!0;break;default:i=!1}return a=a||0,b="number"==typeof b?b:i?g.x+g.width/2:g.x,c="number"==typeof c?c:i?g.y+g.height/2:g.y,f.attr({path:["M",b,c,"l",h+d,-h-d,g.width+2*d,0,0,g.height+2*d,-g.width-2*d,0,"z"].join(",")}),a=360-a,f.rotate(a,b,c),this.attrs?(this.attr(this.attrs.x?"x":"cx",b+h+d+(i?g.width/2:"text"==this.type?g.width:0)).attr("y",i?c:c-g.height/2),this.rotate(a,b,c),a>90&&270>a&&this.attr(this.attrs.x?"x":"cx",b-h-d-(i?g.width/2:g.width)).rotate(180,b,c)):a>90&&270>a?(this.translate(b-g.x-g.width-h-d,c-g.y-g.height/2),this.rotate(a-180,g.x+g.width+h+d,g.y+g.height/2)):(this.translate(b-g.x+h+d,c-g.y-g.height/2),this.rotate(a,g.x-h-d,g.y+g.height/2)),f.insertBefore(this.node?this:this[0])}},Raphael.el.label=function(){var a=this.getBBox(),b=this.paper||this[0].paper,c=Math.min(20,a.width+10,a.height+10)/2;if(b)return b.rect(a.x-c/2,a.y-c/2,a.width+c,a.height+c,c).attr({stroke:"none",fill:"#000"}).insertBefore(this.node?this:this[0])},Raphael.el.blob=function(a,b,c){var g,h,i,d=this.getBBox(),e=Math.PI/180,f=this.paper||this[0].paper;if(f){switch(this.type){case"text":case"circle":case"ellipse":h=!0;break;default:h=!1}g=f.path().attr({fill:"#000",stroke:"none"}),a=(+a+1?a:45)+90,i=Math.min(d.height,d.width),b="number"==typeof b?b:h?d.x+d.width/2:d.x,c="number"==typeof c?c:h?d.y+d.height/2:d.y;var j=Math.max(d.width+i,25*i/12),k=Math.max(d.height+i,25*i/12),l=b+i*Math.sin((a-22.5)*e),m=c+i*Math.cos((a-22.5)*e),n=b+i*Math.sin((a+22.5)*e),o=c+i*Math.cos((a+22.5)*e),p=(n-l)/2,q=(o-m)/2,r=j/2,s=k/2,t=-Math.sqrt(Math.abs(r*r*s*s-r*r*q*q-s*s*p*p)/(r*r*q*q+s*s*p*p)),u=t*r*q/s+(n+l)/2,v=t*-s*p/r+(o+m)/2;return g.attr({x:u,y:v,path:["M",b,c,"L",n,o,"A",r,s,0,1,1,l,m,"z"].join(",")}),this.translate(u-d.x-d.width/2,v-d.y-d.height/2),g.insertBefore(this.node?this:this[0])}},Raphael.fn.label=function(a,b,c){var d=this.set();return c=this.text(a,b,c).attr(Raphael.g.txtattr),d.push(c.label(),c)},Raphael.fn.popup=function(a,b,c,d,e){var f=this.set();return c=this.text(a,b,c).attr(Raphael.g.txtattr),f.push(c.popup(d,e),c)},Raphael.fn.tag=function(a,b,c,d,e){var f=this.set();return c=this.text(a,b,c).attr(Raphael.g.txtattr),f.push(c.tag(d,e),c)},Raphael.fn.flag=function(a,b,c,d){var e=this.set();return c=this.text(a,b,c).attr(Raphael.g.txtattr),e.push(c.flag(d),c)},Raphael.fn.drop=function(a,b,c,d){var e=this.set();return c=this.text(a,b,c).attr(Raphael.g.txtattr),e.push(c.drop(d),c)},Raphael.fn.blob=function(a,b,c,d){var e=this.set();return c=this.text(a,b,c).attr(Raphael.g.txtattr),e.push(c.blob(d),c)},Raphael.el.lighter=function(a){a=a||2;var b=[this.attrs.fill,this.attrs.stroke];return this.fs=this.fs||[b[0],b[1]],b[0]=Raphael.rgb2hsb(Raphael.getRGB(b[0]).hex),b[1]=Raphael.rgb2hsb(Raphael.getRGB(b[1]).hex),b[0].b=Math.min(b[0].b*a,1),b[0].s=b[0].s/a,b[1].b=Math.min(b[1].b*a,1),b[1].s=b[1].s/a,this.attr({fill:"hsb("+[b[0].h,b[0].s,b[0].b]+")",stroke:"hsb("+[b[1].h,b[1].s,b[1].b]+")"}),this},Raphael.el.darker=function(a){a=a||2;var b=[this.attrs.fill,this.attrs.stroke];return this.fs=this.fs||[b[0],b[1]],b[0]=Raphael.rgb2hsb(Raphael.getRGB(b[0]).hex),b[1]=Raphael.rgb2hsb(Raphael.getRGB(b[1]).hex),b[0].s=Math.min(b[0].s*a,1),b[0].b=b[0].b/a,b[1].s=Math.min(b[1].s*a,1),b[1].b=b[1].b/a,this.attr({fill:"hsb("+[b[0].h,b[0].s,b[0].b]+")",stroke:"hsb("+[b[1].h,b[1].s,b[1].b]+")"}),this},Raphael.el.resetBrightness=function(){return this.fs&&(this.attr({fill:this.fs[0],stroke:this.fs[1]}),delete this.fs),this},function(){var a=["lighter","darker","resetBrightness"],b=["popup","tag","flag","label","drop","blob"];for(var c in b)(function(a){Raphael.st[a]=function(){return Raphael.el[a].apply(this,arguments)}})(b[c]);for(var c in a)(function(a){Raphael.st[a]=function(){for(var b=0;this.length>b;b++)this[b][a].apply(this[b],arguments);return this}})(a[c])}(),Raphael.g={shim:{stroke:"none",fill:"#000","fill-opacity":0},txtattr:{font:"12px Arial, sans-serif",fill:"#fff"},colors:function(){for(var a=[.6,.2,.05,.1333,.75,0],b=[],c=0;10>c;c++)a.length>c?b.push("hsb("+a[c]+",.75, .75)"):b.push("hsb("+a[c-a.length]+", 1, .5)");return b}(),snapEnds:function(a,b,c){function f(a){return.25>Math.abs(a-.5)?~~a+.5:Math.round(a)}var d=a,e=b;if(d==e)return{from:d,to:e,power:0};var g=(e-d)/c,h=~~g,i=h,j=0;if(h){for(;i;)j--,i=~~(g*Math.pow(10,j))/Math.pow(10,j);j++}else{if(0!=g&&isFinite(g))for(;!h;)j=j||1,h=~~(g*Math.pow(10,j))/Math.pow(10,j),j++;else j=1;j&&j--}return e=f(b*Math.pow(10,j))/Math.pow(10,j),b>e&&(e=f((b+.5)*Math.pow(10,j))/Math.pow(10,j)),d=f((a-(j>0?0:.5))*Math.pow(10,j))/Math.pow(10,j),{from:d,to:e,power:j}},axis:function(a,b,c,d,e,f,g,h,i,j,k){j=null==j?2:j,i=i||"t",f=f||10,k=arguments[arguments.length-1];var t,l="|"==i||" "==i?["M",a+.5,b,"l",0,.001]:1==g||3==g?["M",a+.5,b,"l",0,-c]:["M",a,b+.5,"l",c,0],m=this.snapEnds(d,e,f),n=m.from,o=m.to,p=m.power,q=0,r={font:"11px 'Fontin Sans', Fontin-Sans, sans-serif"},s=k.set();t=(o-n)/f;var u=n,v=p>0?p:0;if(z=c/f,1==+g||3==+g){for(var w=b,x=(g-1?1:-1)*(j+3+!!(g-1));w>=b-c;)"-"!=i&&" "!=i&&(l=l.concat(["M",a-("+"==i||"|"==i?j:2*!(g-1)*j),w+.5,"l",2*j+1,0])),s.push(k.text(a+x,w,h&&h[q++]||(Math.round(u)==u?u:+u.toFixed(v))).attr(r).attr({"text-anchor":g-1?"start":"end"})),u+=t,w-=z;Math.round(w+z-(b-c))&&("-"!=i&&" "!=i&&(l=l.concat(["M",a-("+"==i||"|"==i?j:2*!(g-1)*j),b-c+.5,"l",2*j+1,0])),s.push(k.text(a+x,b-c,h&&h[q]||(Math.round(u)==u?u:+u.toFixed(v))).attr(r).attr({"text-anchor":g-1?"start":"end"})))}else{u=n,v=(p>0)*p,x=(g?-1:1)*(j+9+!g);for(var y=a,z=c/f,A=0,B=0;a+c>=y;){"-"!=i&&" "!=i&&(l=l.concat(["M",y+.5,b-("+"==i?j:2*!!g*j),"l",0,2*j+1])),s.push(A=k.text(y,b+x,h&&h[q++]||(Math.round(u)==u?u:+u.toFixed(v))).attr(r));var C=A.getBBox();B>=C.x-5?s.pop(s.length-1).remove():B=C.x+C.width,u+=t,y+=z}Math.round(y-z-a-c)&&("-"!=i&&" "!=i&&(l=l.concat(["M",a+c+.5,b-("+"==i?j:2*!!g*j),"l",0,2*j+1])),s.push(k.text(a+c,b+x,h&&h[q]||(Math.round(u)==u?u:+u.toFixed(v))).attr(r)))}var D=k.path(l);return D.text=s,D.all=k.set([D,s]),D.remove=function(){this.text.remove(),this.constructor.prototype.remove.call(this)},D},labelise:function(a,b,c){return a?(a+"").replace(/(##+(?:\.#+)?)|(%%+(?:\.%+)?)/g,function(a,d,e){return d?(+b).toFixed(d.replace(/^#+\.?/g,"").length):e?(100*b/c).toFixed(e.replace(/^%+\.?/g,"").length)+"%":void 0}):(+b).toFixed(0)}}; \ No newline at end of file +Raphael.el.popup=function(d,a,b,f){var e=this.paper||this[0].paper,c,g,i,h;if(e){switch(this.type){case "text":case "circle":case "ellipse":g=!0;break;default:g=!1}d=null==d?"up":d;a=a||5;c=this.getBBox();b="number"==typeof b?b:g?c.x+c.width/2:c.x;f="number"==typeof f?f:g?c.y+c.height/2:c.y;i=Math.max(c.width/2-a,0);h=Math.max(c.height/2-a,0);this.translate(b-c.x-(g?c.width/2:0),f-c.y-(g?c.height/2:0));c=this.getBBox();b={up:["M",b,f,"l",-a,-a,-i,0,"a",a,a,0,0,1,-a,-a,"l",0,-c.height,"a",a,a,0,0, +1,a,-a,"l",2*a+2*i,0,"a",a,a,0,0,1,a,a,"l",0,c.height,"a",a,a,0,0,1,-a,a,"l",-i,0,"z"].join(),down:["M",b,f,"l",a,a,i,0,"a",a,a,0,0,1,a,a,"l",0,c.height,"a",a,a,0,0,1,-a,a,"l",-(2*a+2*i),0,"a",a,a,0,0,1,-a,-a,"l",0,-c.height,"a",a,a,0,0,1,a,-a,"l",i,0,"z"].join(),left:["M",b,f,"l",-a,a,0,h,"a",a,a,0,0,1,-a,a,"l",-c.width,0,"a",a,a,0,0,1,-a,-a,"l",0,-(2*a+2*h),"a",a,a,0,0,1,a,-a,"l",c.width,0,"a",a,a,0,0,1,a,a,"l",0,h,"z"].join(),right:["M",b,f,"l",a,-a,0,-h,"a",a,a,0,0,1,a,-a,"l",c.width,0,"a",a, +a,0,0,1,a,a,"l",0,2*a+2*h,"a",a,a,0,0,1,-a,a,"l",-c.width,0,"a",a,a,0,0,1,-a,-a,"l",0,-h,"z"].join()};a={up:{x:-!g*(c.width/2),y:2*-a-(g?c.height/2:c.height)},down:{x:-!g*(c.width/2),y:2*a+(g?c.height/2:c.height)},left:{x:2*-a-(g?c.width/2:c.width),y:-!g*(c.height/2)},right:{x:2*a+(g?c.width/2:c.width),y:-!g*(c.height/2)}}[d];this.translate(a.x,a.y);return e.path(b[d]).attr({fill:"#000",stroke:"none"}).insertBefore(this.node?this:this[0])}}; +Raphael.el.tag=function(d,a,b,f){var e=this.paper||this[0].paper;if(e){var e=e.path().attr({fill:"#000",stroke:"#000"}),c=this.getBBox(),g,i,h;switch(this.type){case "text":case "circle":case "ellipse":h=!0;break;default:h=!1}d=d||0;b="number"==typeof b?b:h?c.x+c.width/2:c.x;f="number"==typeof f?f:h?c.y+c.height/2:c.y;a=null==a?5:a;i=0.5522*a;c.height>=2*a?e.attr({path:["M",b,f+a,"a",a,a,0,1,1,0,2*-a,a,a,0,1,1,0,2*a,"m",0,2*-a-3,"a",a+3,a+3,0,1,0,0,2*(a+3),"L",b+a+3,f+c.height/2+3,"l",c.width+6,0, +0,-c.height-6,-c.width-6,0,"L",b,f-a-3].join()}):(g=Math.sqrt(Math.pow(a+3,2)-Math.pow(c.height/2+3,2)),e.attr({path:["M",b,f+a,"c",-i,0,-a,i-a,-a,-a,0,-i,a-i,-a,a,-a,i,0,a,a-i,a,a,0,i,i-a,a,-a,a,"M",b+g,f-c.height/2-3,"a",a+3,a+3,0,1,0,0,c.height+6,"l",a+3-g+c.width+6,0,0,-c.height-6,"L",b+g,f-c.height/2-3].join()}));d=360-d;e.rotate(d,b,f);this.attrs?(this.attr(this.attrs.x?"x":"cx",b+a+3+(!h?"text"==this.type?c.width:0:c.width/2)).attr("y",h?f:f-c.height/2),this.rotate(d,b,f),90d&&this.attr(this.attrs.x? +"x":"cx",b-a-3-(!h?c.width:c.width/2)).rotate(180,b,f)):90d?(this.translate(b-c.x-c.width-a-3,f-c.y-c.height/2),this.rotate(d-180,c.x+c.width+a+3,c.y+c.height/2)):(this.translate(b-c.x+a+3,f-c.y-c.height/2),this.rotate(d,c.x-a-3,c.y+c.height/2));return e.insertBefore(this.node?this:this[0])}}; +Raphael.el.drop=function(d,a,b){var f=this.getBBox(),e=this.paper||this[0].paper,c,g;if(e){switch(this.type){case "text":case "circle":case "ellipse":c=!0;break;default:c=!1}d=d||0;a="number"==typeof a?a:c?f.x+f.width/2:f.x;b="number"==typeof b?b:c?f.y+f.height/2:f.y;g=Math.max(f.width,f.height)+Math.min(f.width,f.height);e=e.path(["M",a,b,"l",g,0,"A",0.4*g,0.4*g,0,1,0,a+0.7*g,b-0.7*g,"z"]).attr({fill:"#000",stroke:"none"}).rotate(22.5-d,a,b);d=(d+90)*Math.PI/180;a=a+g*Math.sin(d)-(c?0:f.width/2); +d=b+g*Math.cos(d)-(c?0:f.height/2);this.attrs?this.attr(this.attrs.x?"x":"cx",a).attr(this.attrs.y?"y":"cy",d):this.translate(a-f.x,d-f.y);return e.insertBefore(this.node?this:this[0])}}; +Raphael.el.flag=function(d,a,b){var f=this.paper||this[0].paper;if(f){var f=f.path().attr({fill:"#000",stroke:"#000"}),e=this.getBBox(),c=e.height/2,g;switch(this.type){case "text":case "circle":case "ellipse":g=!0;break;default:g=!1}d=d||0;a="number"==typeof a?a:g?e.x+e.width/2:e.x;b="number"==typeof b?b:g?e.y+e.height/2:e.y;f.attr({path:["M",a,b,"l",c+3,-c-3,e.width+6,0,0,e.height+6,-e.width-6,0,"z"].join()});d=360-d;f.rotate(d,a,b);this.attrs?(this.attr(this.attrs.x?"x":"cx",a+c+3+(!g?"text"== +this.type?e.width:0:e.width/2)).attr("y",g?b:b-e.height/2),this.rotate(d,a,b),90d&&this.attr(this.attrs.x?"x":"cx",a-c-3-(!g?e.width:e.width/2)).rotate(180,a,b)):90d?(this.translate(a-e.x-e.width-c-3,b-e.y-e.height/2),this.rotate(d-180,e.x+e.width+c+3,e.y+e.height/2)):(this.translate(a-e.x+c+3,b-e.y-e.height/2),this.rotate(d,e.x-c-3,e.y+e.height/2));return f.insertBefore(this.node?this:this[0])}}; +Raphael.el.label=function(){var d=this.getBBox(),a=this.paper||this[0].paper,b=Math.min(20,d.width+10,d.height+10)/2;if(a)return a.rect(d.x-b/2,d.y-b/2,d.width+b,d.height+b,b).attr({stroke:"none",fill:"#000"}).insertBefore(this.node?this:this[0])}; +Raphael.el.blob=function(d,a,b){var f=this.getBBox(),e=Math.PI/180,c=this.paper||this[0].paper,g,i;if(c){switch(this.type){case "text":case "circle":case "ellipse":g=!0;break;default:g=!1}c=c.path().attr({fill:"#000",stroke:"none"});d=(+d+1?d:45)+90;i=Math.min(f.height,f.width);var a="number"==typeof a?a:g?f.x+f.width/2:f.x,b="number"==typeof b?b:g?f.y+f.height/2:f.y,h=Math.max(f.width+i,25*i/12),j=Math.max(f.height+i,25*i/12);g=a+i*Math.sin((d-22.5)*e);var o=b+i*Math.cos((d-22.5)*e),l=a+i*Math.sin((d+ +22.5)*e),d=b+i*Math.cos((d+22.5)*e),e=(l-g)/2;i=(d-o)/2;var h=h/2,j=j/2,n=-Math.sqrt(Math.abs(h*h*j*j-h*h*i*i-j*j*e*e)/(h*h*i*i+j*j*e*e));i=n*h*i/j+(l+g)/2;e=n*-j*e/h+(d+o)/2;c.attr({x:i,y:e,path:["M",a,b,"L",l,d,"A",h,j,0,1,1,g,o,"z"].join()});this.translate(i-f.x-f.width/2,e-f.y-f.height/2);return c.insertBefore(this.node?this:this[0])}};Raphael.fn.label=function(d,a,b){var f=this.set(),b=this.text(d,a,b).attr(Raphael.g.txtattr);return f.push(b.label(),b)}; +Raphael.fn.popup=function(d,a,b,f,e){var c=this.set(),b=this.text(d,a,b).attr(Raphael.g.txtattr);return c.push(b.popup(f,e),b)};Raphael.fn.tag=function(d,a,b,f,e){var c=this.set(),b=this.text(d,a,b).attr(Raphael.g.txtattr);return c.push(b.tag(f,e),b)};Raphael.fn.flag=function(d,a,b,f){var e=this.set(),b=this.text(d,a,b).attr(Raphael.g.txtattr);return e.push(b.flag(f),b)};Raphael.fn.drop=function(d,a,b,f){var e=this.set(),b=this.text(d,a,b).attr(Raphael.g.txtattr);return e.push(b.drop(f),b)}; +Raphael.fn.blob=function(d,a,b,f){var e=this.set(),b=this.text(d,a,b).attr(Raphael.g.txtattr);return e.push(b.blob(f),b)};Raphael.el.lighter=function(d){var d=d||2,a=[this.attrs.fill,this.attrs.stroke];this.fs=this.fs||[a[0],a[1]];a[0]=Raphael.rgb2hsb(Raphael.getRGB(a[0]).hex);a[1]=Raphael.rgb2hsb(Raphael.getRGB(a[1]).hex);a[0].b=Math.min(a[0].b*d,1);a[0].s/=d;a[1].b=Math.min(a[1].b*d,1);a[1].s/=d;this.attr({fill:"hsb("+[a[0].h,a[0].s,a[0].b]+")",stroke:"hsb("+[a[1].h,a[1].s,a[1].b]+")"});return this}; +Raphael.el.darker=function(d){var d=d||2,a=[this.attrs.fill,this.attrs.stroke];this.fs=this.fs||[a[0],a[1]];a[0]=Raphael.rgb2hsb(Raphael.getRGB(a[0]).hex);a[1]=Raphael.rgb2hsb(Raphael.getRGB(a[1]).hex);a[0].s=Math.min(a[0].s*d,1);a[0].b/=d;a[1].s=Math.min(a[1].s*d,1);a[1].b/=d;this.attr({fill:"hsb("+[a[0].h,a[0].s,a[0].b]+")",stroke:"hsb("+[a[1].h,a[1].s,a[1].b]+")"});return this};Raphael.el.resetBrightness=function(){this.fs&&(this.attr({fill:this.fs[0],stroke:this.fs[1]}),delete this.fs);return this}; +(function(){var d=["lighter","darker","resetBrightness"],a="popup tag flag label drop blob".split(" "),b;for(b in a)(function(a){Raphael.st[a]=function(){return Raphael.el[a].apply(this,arguments)}})(a[b]);for(b in d)(function(a){Raphael.st[a]=function(){for(var b=0;bb;b++)bMath.abs(a-0.5)?~~a+0.5:Math.round(a)}var e=d,c=a;if(e==c)return{from:e,to:c,power:0};var e=(c-e)/b,g=c=~~e,b=0;if(c){for(;g;)b--,g=~~(e*Math.pow(10,b))/Math.pow(10,b); +b++}else{if(0==e||!isFinite(e))b=1;else for(;!c;)b=b||1,c=~~(e*Math.pow(10,b))/Math.pow(10,b),b++;b&&b--}c=f(a*Math.pow(10,b))/Math.pow(10,b);c=a-b;)"-"!=h&&" "!=h&&(l=l.concat(["M",d-("+"==h||"|"==h?j:2*!(g-1)*j),m+0.5,"l",2*j+1,0])),n.push(o.text(d+p,m,i&&i[u++]||(Math.round(k)==k?k:+k.toFixed(r))).attr(v).attr({"text-anchor":g-1?"start":"end"})),k+=t,m-=s;Math.round(m+s-(a-b))&&("-"!=h&&" "!=h&&(l=l.concat(["M",d-("+"==h||"|"==h?j:2*!(g-1)*j),a-b+0.5,"l",2*j+1,0])),n.push(o.text(d+ +p,a-b,i&&i[u]||(Math.round(k)==k?k:+k.toFixed(r))).attr(v).attr({"text-anchor":g-1?"start":"end"})))}else{for(var k=p,r=(0=q.x-5?n.pop(n.length-1).remove():w=q.x+q.width,k+=t,m+=s;Math.round(m-s-d-b)&&("-"!=h&&" "!=h&&(l=l.concat(["M",d+b+0.5,a-("+"==h?j:2*!!g*j),"l",0,2*j+1])),n.push(o.text(d+ +b,a+p,i&&i[u]||(Math.round(k)==k?k:+k.toFixed(r))).attr(v)))}l=o.path(l);l.text=n;l.all=o.set([l,n]);l.remove=function(){this.text.remove();this.constructor.prototype.remove.call(this)};return l},labelise:function(d,a,b){return d?(d+"").replace(/(##+(?:\.#+)?)|(%%+(?:\.%+)?)/g,function(d,e,c){if(e)return(+a).toFixed(e.replace(/^#+\.?/g,"").length);if(c)return(100*a/b).toFixed(c.replace(/^%+\.?/g,"").length)+"%"}):(+a).toFixed(0)}}; \ No newline at end of file diff --git a/vendor/assets/stylesheets/jquery.ui.aristo.css b/vendor/assets/stylesheets/jquery.ui.aristo.css new file mode 100644 index 00000000..8cc6e787 --- /dev/null +++ b/vendor/assets/stylesheets/jquery.ui.aristo.css @@ -0,0 +1,738 @@ +/* + * jQuery UI CSS Framework 1.8.7 + * + * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Theming/API + */ + +/* Layout helpers +----------------------------------*/ +.ui-helper-hidden { display: none; } +.ui-helper-hidden-accessible { position: absolute !important; clip: rect(1px 1px 1px 1px); clip: rect(1px,1px,1px,1px); } +.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; } +.ui-helper-clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; } +.ui-helper-clearfix { display: inline-block; } +/* required comment for clearfix to work in Opera \*/ +* html .ui-helper-clearfix { height:1%; } +.ui-helper-clearfix { display:block; } +/* end clearfix */ +.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); } + + +/* Interaction Cues +----------------------------------*/ +.ui-state-disabled { cursor: default !important; } + + +/* Icons +----------------------------------*/ + +/* states and images */ +.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; } + + +/* Misc visuals +----------------------------------*/ + +/* Overlays */ +.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } + + +/* + * jQuery UI CSS Framework 1.8.7 + * + * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Theming/API + * + * To view and modify this theme, visit http://jqueryui.com/themeroller/?ctl=themeroller + */ + + +/* Component containers +----------------------------------*/ +.ui-widget { font-family: Arial,sans-serif; font-size: 1.1em; } +.ui-widget .ui-widget { font-size: 1em; } +.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Arial,sans-serif; font-size: 1em; } +.ui-widget-content { border: 1px solid #B6B6B6; background: #ffffff; color: #4F4F4F; } +.ui-widget-content a { color: #4F4F4F; } +.ui-widget-header { border: 1px solid #B6B6B6; color: #4F4F4F; font-weight: bold; } +.ui-widget-header { + background: #ededed url(bg_fallback.png) 0 0 repeat-x; /* Old browsers */ + background: -moz-linear-gradient(top, #ededed 0%, #c4c4c4 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#ededed), color-stop(100%,#c4c4c4)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #ededed 0%,#c4c4c4 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #ededed 0%,#c4c4c4 100%); /* Opera11.10+ */ + background: -ms-linear-gradient(top, #ededed 0%,#c4c4c4 100%); /* IE10+ */ + background: linear-gradient(top, #ededed 0%,#c4c4c4 100%); /* W3C */ +} +.ui-widget-header a { color: #4F4F4F; } + +/* Interaction states +----------------------------------*/ +.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #B6B6B6; font-weight: normal; color: #4F4F4F; } +.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { + background: #ededed url(bg_fallback.png) 0 0 repeat-x; /* Old browsers */ + background: -moz-linear-gradient(top, #ededed 0%, #c4c4c4 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#ededed), color-stop(100%,#c4c4c4)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #ededed 0%,#c4c4c4 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #ededed 0%,#c4c4c4 100%); /* Opera11.10+ */ + background: -ms-linear-gradient(top, #ededed 0%,#c4c4c4 100%); /* IE10+ */ + background: linear-gradient(top, #ededed 0%,#c4c4c4 100%); /* W3C */ + -webkit-box-shadow: 0 1px 0 rgba(255,255,255,0.6) inset; + -moz-box-shadow: 0 1px 0 rgba(255,255,255,0.6) inset; + box-shadow: 0 1px 0 rgba(255,255,255,0.6) inset; +} +.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #4F4F4F; text-decoration: none; } +.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #9D9D9D; font-weight: normal; color: #313131; } +.ui-state-hover a, .ui-state-hover a:hover { color: #313131; text-decoration: none; } +.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { + outline: none; + color: #1c4257; border: 1px solid #7096ab; + background: #ededed url(bg_fallback.png) 0 -50px repeat-x; /* Old browsers */ + background: -moz-linear-gradient(top, #b9e0f5 0%, #92bdd6 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#b9e0f5), color-stop(100%,#92bdd6)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #b9e0f5 0%,#92bdd6 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #b9e0f5 0%,#92bdd6 100%); /* Opera11.10+ */ + background: -ms-linear-gradient(top, #b9e0f5 0%,#92bdd6 100%); /* IE10+ */ + background: linear-gradient(top, #b9e0f5 0%,#92bdd6 100%); /* W3C */ + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; +} +.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #313131; text-decoration: none; } +.ui-widget :active { outline: none; } + +/* Interaction Cues +----------------------------------*/ +.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight { border: 1px solid #d2dbf4; background: #f4f8fd; color: #0d2054; -moz-border-radius: 0 !important; -webkit-border-radius: 0 !important; border-radius: 0 !important; } +.ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #363636; } +.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error { border: 1px solid #e2d0d0; background: #fcf0f0; color: #280b0b; -moz-border-radius: 0 !important; -webkit-border-radius: 0 !important; border-radius: 0 !important; } +.ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #cd0a0a; } +.ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #cd0a0a; } +.ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; } +.ui-priority-secondary, .ui-widget-content .ui-priority-secondary, .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; } +.ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; } + +/* Icons +----------------------------------*/ + +/* states and images */ +.ui-icon { width: 16px; height: 16px; background-image: url(ui-icons_222222_256x240.png); } +.ui-widget-content .ui-icon {background-image: url(ui-icons_222222_256x240.png); } +.ui-widget-header .ui-icon {background-image: url(ui-icons_222222_256x240.png); } +.ui-state-default .ui-icon { background-image: url(ui-icons_454545_256x240.png); } +.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(ui-icons_454545_256x240.png); } +.ui-state-active .ui-icon {background-image: url(ui-icons_454545_256x240.png); } +.ui-state-highlight .ui-icon {background-image: url(ui-icons_454545_256x240.png); } +.ui-state-error .ui-icon, .ui-state-error-text .ui-icon { background: url(icon_sprite.png) -16px 0 no-repeat !important; } +.ui-state-highlight .ui-icon, .ui-state-error .ui-icon { margin-top: -1px; } + +/* positioning */ +.ui-icon-carat-1-n { background-position: 0 0; } +.ui-icon-carat-1-ne { background-position: -16px 0; } +.ui-icon-carat-1-e { background-position: -32px 0; } +.ui-icon-carat-1-se { background-position: -48px 0; } +.ui-icon-carat-1-s { background-position: -64px 0; } +.ui-icon-carat-1-sw { background-position: -80px 0; } +.ui-icon-carat-1-w { background-position: -96px 0; } +.ui-icon-carat-1-nw { background-position: -112px 0; } +.ui-icon-carat-2-n-s { background-position: -128px 0; } +.ui-icon-carat-2-e-w { background-position: -144px 0; } +.ui-icon-triangle-1-n { background-position: 0 -16px; } +.ui-icon-triangle-1-ne { background-position: -16px -16px; } +.ui-icon-triangle-1-e { background-position: -32px -16px; } +.ui-icon-triangle-1-se { background-position: -48px -16px; } +.ui-icon-triangle-1-s { background-position: -64px -16px; } +.ui-icon-triangle-1-sw { background-position: -80px -16px; } +.ui-icon-triangle-1-w { background-position: -96px -16px; } +.ui-icon-triangle-1-nw { background-position: -112px -16px; } +.ui-icon-triangle-2-n-s { background-position: -128px -16px; } +.ui-icon-triangle-2-e-w { background-position: -144px -16px; } +.ui-icon-arrow-1-n { background-position: 0 -32px; } +.ui-icon-arrow-1-ne { background-position: -16px -32px; } +.ui-icon-arrow-1-e { background-position: -32px -32px; } +.ui-icon-arrow-1-se { background-position: -48px -32px; } +.ui-icon-arrow-1-s { background-position: -64px -32px; } +.ui-icon-arrow-1-sw { background-position: -80px -32px; } +.ui-icon-arrow-1-w { background-position: -96px -32px; } +.ui-icon-arrow-1-nw { background-position: -112px -32px; } +.ui-icon-arrow-2-n-s { background-position: -128px -32px; } +.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; } +.ui-icon-arrow-2-e-w { background-position: -160px -32px; } +.ui-icon-arrow-2-se-nw { background-position: -176px -32px; } +.ui-icon-arrowstop-1-n { background-position: -192px -32px; } +.ui-icon-arrowstop-1-e { background-position: -208px -32px; } +.ui-icon-arrowstop-1-s { background-position: -224px -32px; } +.ui-icon-arrowstop-1-w { background-position: -240px -32px; } +.ui-icon-arrowthick-1-n { background-position: 0 -48px; } +.ui-icon-arrowthick-1-ne { background-position: -16px -48px; } +.ui-icon-arrowthick-1-e { background-position: -32px -48px; } +.ui-icon-arrowthick-1-se { background-position: -48px -48px; } +.ui-icon-arrowthick-1-s { background-position: -64px -48px; } +.ui-icon-arrowthick-1-sw { background-position: -80px -48px; } +.ui-icon-arrowthick-1-w { background-position: -96px -48px; } +.ui-icon-arrowthick-1-nw { background-position: -112px -48px; } +.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; } +.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; } +.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; } +.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; } +.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; } +.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; } +.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; } +.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; } +.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; } +.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; } +.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; } +.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; } +.ui-icon-arrowreturn-1-w { background-position: -64px -64px; } +.ui-icon-arrowreturn-1-n { background-position: -80px -64px; } +.ui-icon-arrowreturn-1-e { background-position: -96px -64px; } +.ui-icon-arrowreturn-1-s { background-position: -112px -64px; } +.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; } +.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; } +.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; } +.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; } +.ui-icon-arrow-4 { background-position: 0 -80px; } +.ui-icon-arrow-4-diag { background-position: -16px -80px; } +.ui-icon-extlink { background-position: -32px -80px; } +.ui-icon-newwin { background-position: -48px -80px; } +.ui-icon-refresh { background-position: -64px -80px; } +.ui-icon-shuffle { background-position: -80px -80px; } +.ui-icon-transfer-e-w { background-position: -96px -80px; } +.ui-icon-transferthick-e-w { background-position: -112px -80px; } +.ui-icon-folder-collapsed { background-position: 0 -96px; } +.ui-icon-folder-open { background-position: -16px -96px; } +.ui-icon-document { background-position: -32px -96px; } +.ui-icon-document-b { background-position: -48px -96px; } +.ui-icon-note { background-position: -64px -96px; } +.ui-icon-mail-closed { background-position: -80px -96px; } +.ui-icon-mail-open { background-position: -96px -96px; } +.ui-icon-suitcase { background-position: -112px -96px; } +.ui-icon-comment { background-position: -128px -96px; } +.ui-icon-person { background-position: -144px -96px; } +.ui-icon-print { background-position: -160px -96px; } +.ui-icon-trash { background-position: -176px -96px; } +.ui-icon-locked { background-position: -192px -96px; } +.ui-icon-unlocked { background-position: -208px -96px; } +.ui-icon-bookmark { background-position: -224px -96px; } +.ui-icon-tag { background-position: -240px -96px; } +.ui-icon-home { background-position: 0 -112px; } +.ui-icon-flag { background-position: -16px -112px; } +.ui-icon-calendar { background-position: -32px -112px; } +.ui-icon-cart { background-position: -48px -112px; } +.ui-icon-pencil { background-position: -64px -112px; } +.ui-icon-clock { background-position: -80px -112px; } +.ui-icon-disk { background-position: -96px -112px; } +.ui-icon-calculator { background-position: -112px -112px; } +.ui-icon-zoomin { background-position: -128px -112px; } +.ui-icon-zoomout { background-position: -144px -112px; } +.ui-icon-search { background-position: -160px -112px; } +.ui-icon-wrench { background-position: -176px -112px; } +.ui-icon-gear { background-position: -192px -112px; } +.ui-icon-heart { background-position: -208px -112px; } +.ui-icon-star { background-position: -224px -112px; } +.ui-icon-link { background-position: -240px -112px; } +.ui-icon-cancel { background-position: 0 -128px; } +.ui-icon-plus { background-position: -16px -128px; } +.ui-icon-plusthick { background-position: -32px -128px; } +.ui-icon-minus { background-position: -48px -128px; } +.ui-icon-minusthick { background-position: -64px -128px; } +.ui-icon-close { background-position: -80px -128px; } +.ui-icon-closethick { background-position: -96px -128px; } +.ui-icon-key { background-position: -112px -128px; } +.ui-icon-lightbulb { background-position: -128px -128px; } +.ui-icon-scissors { background-position: -144px -128px; } +.ui-icon-clipboard { background-position: -160px -128px; } +.ui-icon-copy { background-position: -176px -128px; } +.ui-icon-contact { background-position: -192px -128px; } +.ui-icon-image { background-position: -208px -128px; } +.ui-icon-video { background-position: -224px -128px; } +.ui-icon-script { background-position: -240px -128px; } +.ui-icon-alert { background-position: 0 -144px; } +.ui-icon-info { background: url(icon_sprite.png) 0 0 no-repeat !important; } +.ui-icon-notice { background-position: -32px -144px; } +.ui-icon-help { background-position: -48px -144px; } +.ui-icon-check { background-position: -64px -144px; } +.ui-icon-bullet { background-position: -80px -144px; } +.ui-icon-radio-off { background-position: -96px -144px; } +.ui-icon-radio-on { background-position: -112px -144px; } +.ui-icon-pin-w { background-position: -128px -144px; } +.ui-icon-pin-s { background-position: -144px -144px; } +.ui-icon-play { background-position: 0 -160px; } +.ui-icon-pause { background-position: -16px -160px; } +.ui-icon-seek-next { background-position: -32px -160px; } +.ui-icon-seek-prev { background-position: -48px -160px; } +.ui-icon-seek-end { background-position: -64px -160px; } +.ui-icon-seek-start { background-position: -80px -160px; } +/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */ +.ui-icon-seek-first { background-position: -80px -160px; } +.ui-icon-stop { background-position: -96px -160px; } +.ui-icon-eject { background-position: -112px -160px; } +.ui-icon-volume-off { background-position: -128px -160px; } +.ui-icon-volume-on { background-position: -144px -160px; } +.ui-icon-power { background-position: 0 -176px; } +.ui-icon-signal-diag { background-position: -16px -176px; } +.ui-icon-signal { background-position: -32px -176px; } +.ui-icon-battery-0 { background-position: -48px -176px; } +.ui-icon-battery-1 { background-position: -64px -176px; } +.ui-icon-battery-2 { background-position: -80px -176px; } +.ui-icon-battery-3 { background-position: -96px -176px; } +.ui-icon-circle-plus { background-position: 0 -192px; } +.ui-icon-circle-minus { background-position: -16px -192px; } +.ui-icon-circle-close { background-position: -32px -192px; } +.ui-icon-circle-triangle-e { background-position: -48px -192px; } +.ui-icon-circle-triangle-s { background-position: -64px -192px; } +.ui-icon-circle-triangle-w { background-position: -80px -192px; } +.ui-icon-circle-triangle-n { background-position: -96px -192px; } +.ui-icon-circle-arrow-e { background-position: -112px -192px; } +.ui-icon-circle-arrow-s { background-position: -128px -192px; } +.ui-icon-circle-arrow-w { background-position: -144px -192px; } +.ui-icon-circle-arrow-n { background-position: -160px -192px; } +.ui-icon-circle-zoomin { background-position: -176px -192px; } +.ui-icon-circle-zoomout { background-position: -192px -192px; } +.ui-icon-circle-check { background-position: -208px -192px; } +.ui-icon-circlesmall-plus { background-position: 0 -208px; } +.ui-icon-circlesmall-minus { background-position: -16px -208px; } +.ui-icon-circlesmall-close { background-position: -32px -208px; } +.ui-icon-squaresmall-plus { background-position: -48px -208px; } +.ui-icon-squaresmall-minus { background-position: -64px -208px; } +.ui-icon-squaresmall-close { background-position: -80px -208px; } +.ui-icon-grip-dotted-vertical { background-position: 0 -224px; } +.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; } +.ui-icon-grip-solid-vertical { background-position: -32px -224px; } +.ui-icon-grip-solid-horizontal { background-position: -48px -224px; } +.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; } +.ui-icon-grip-diagonal-se { background-position: -80px -224px; } + + +/* Misc visuals +----------------------------------*/ + +/* Corner radius */ +.ui-corner-tl { -moz-border-radius-topleft: 3px; -webkit-border-top-left-radius: 3px; border-top-left-radius: 3px; } +.ui-corner-tr { -moz-border-radius-topright: 3px; -webkit-border-top-right-radius: 3px; border-top-right-radius: 3px; } +.ui-corner-bl { -moz-border-radius-bottomleft: 3px; -webkit-border-bottom-left-radius: 3px; border-bottom-left-radius: 3px; } +.ui-corner-br { -moz-border-radius-bottomright: 3px; -webkit-border-bottom-right-radius: 3px; border-bottom-right-radius: 3px; } +.ui-corner-top { -moz-border-radius-topleft: 3px; -webkit-border-top-left-radius: 3px; border-top-left-radius: 3px; -moz-border-radius-topright: 3px; -webkit-border-top-right-radius: 3px; border-top-right-radius: 3px; } +.ui-corner-bottom { -moz-border-radius-bottomleft: 3px; -webkit-border-bottom-left-radius: 3px; border-bottom-left-radius: 3px; -moz-border-radius-bottomright: 3px; -webkit-border-bottom-right-radius: 3px; border-bottom-right-radius: 3px; } +.ui-corner-right { -moz-border-radius-topright: 3px; -webkit-border-top-right-radius: 3px; border-top-right-radius: 3px; -moz-border-radius-bottomright: 3px; -webkit-border-bottom-right-radius: 3px; border-bottom-right-radius: 3px; } +.ui-corner-left { -moz-border-radius-topleft: 3px; -webkit-border-top-left-radius: 3px; border-top-left-radius: 3px; -moz-border-radius-bottomleft: 3px; -webkit-border-bottom-left-radius: 3px; border-bottom-left-radius: 3px; } +.ui-corner-all { -moz-border-radius: 3px; -webkit-border-radius: 3px; border-radius: 3px; } + +/* Overlays */ +.ui-widget-overlay { background: #262b33; opacity: .70;filter:Alpha(Opacity=70); } +.ui-widget-shadow { margin: -8px 0 0 -8px; padding: 8px; background: #000000; opacity: .30;filter:Alpha(Opacity=30); -moz-border-radius: 8px; -webkit-border-radius: 8px; border-radius: 8px; }/* + * jQuery UI Resizable 1.8.7 + * + * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Resizable#theming + */ +.ui-resizable { position: relative;} +.ui-resizable-handle { position: absolute; font-size: 0.1px; z-index: 999; display: block;} +.ui-resizable-disabled .ui-resizable-handle, .ui-resizable-autohide .ui-resizable-handle { display: none; } +.ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0; } +.ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0; } +.ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0; height: 100%; } +.ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0; height: 100%; } +.ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; } +.ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; } +.ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; } +.ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;}/* + * jQuery UI Selectable 1.8.7 + * + * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Selectable#theming + */ +.ui-selectable-helper { position: absolute; z-index: 100; border:1px dotted black; } +/* + * jQuery UI Accordion 1.8.7 + * + * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Accordion#theming + */ +/* IE/Win - Fix animation bug - #4615 */ +.ui-accordion { width: 100%; } +.ui-accordion .ui-accordion-header { cursor: pointer; position: relative; margin-top: 1px; zoom: 1; } +.ui-accordion .ui-accordion-header, .ui-accordion .ui-accordion-content { -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; } +.ui-accordion .ui-accordion-li-fix { display: inline; } +.ui-accordion .ui-accordion-header-active { border-bottom: 0 !important; } +.ui-accordion .ui-accordion-header a { display: block; font-size: 12px; font-weight: bold; padding: .5em .5em .5em .7em; } +.ui-accordion-icons .ui-accordion-header a { padding-left: 2.2em; } +.ui-accordion .ui-accordion-header .ui-icon { position: absolute; left: .5em; top: 50%; margin-top: -8px; } +.ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; zoom: 1; } +.ui-accordion .ui-accordion-content-active { display: block; }/* + * jQuery UI Autocomplete 1.8.7 + * + * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Autocomplete#theming + */ +.ui-autocomplete { + position: absolute; cursor: default; z-index: 3; + -moz-border-radius: 0; + -webkit-border-radius: 0; + border-radius: 0; + -moz-box-shadow: 0 1px 5px rgba(0,0,0,0.3); + -webkit-box-shadow: 0 1px 5px rgba(0,0,0,0.3); + box-shadow: 0 1px 5px rgba(0,0,0,0.3); +} + +/* workarounds */ +* html .ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */ + +/* + * jQuery UI Menu 1.8.7 + * + * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Menu#theming + */ +.ui-menu { + list-style:none; + padding: 2px; + margin: 0; + display:block; + float: left; +} +.ui-menu .ui-menu { + margin-top: -3px; +} +.ui-menu .ui-menu-item { + margin:0; + padding: 0; + zoom: 1; + float: left; + clear: left; + width: 100%; +} +.ui-menu .ui-menu-item a { + text-decoration:none; + display:block; + padding:.2em .4em; + line-height:1.5; + zoom:1; +} +.ui-menu .ui-menu-item a.ui-state-hover, +.ui-menu .ui-menu-item a.ui-state-active { + font-weight: normal; + margin: -1px; + background: #5f83b9; + color: #FFFFFF; + text-shadow: 0px 1px 1px #234386; + border-color: #466086; + -moz-border-radius: 0; + -webkit-border-radius: 0; + border-radius: 0; +} +/* + * jQuery UI Button 1.8.7 + * + * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Button#theming + */ +.ui-button { display: inline-block; position: relative; padding: 0; margin-right: .1em; text-decoration: none !important; cursor: pointer; text-align: center; zoom: 1; overflow: visible; -webkit-user-select: none; -moz-user-select: none; user-select: none; } /* the overflow property removes extra width in IE */ +.ui-button-icon-only { width: 2.2em; } /* to make room for the icon, a width needs to be set here */ +button.ui-button-icon-only { width: 2.4em; } /* button elements seem to need a little more width */ +.ui-button-icons-only { width: 3.4em; } +button.ui-button-icons-only { width: 3.7em; } + +/* button animation properties */ +.ui-button { + -webkit-transition: all 250ms ease-in-out; + -moz-transition: all 250ms ease-in-out; + -o-transition: all 250ms ease-in-out; + transition: all 250ms ease-in-out; +} + +/*states*/ +.ui-button.ui-state-hover { + -moz-box-shadow: 0 0 8px rgba(0, 0, 0, 0.15), 0 1px 0 rgba(255,255,255,0.8) inset; + -webkit-box-shadow: 0 0 8px rgba(0, 0, 0, 0.15), 0 1px 0 rgba(255,255,255,0.8) inset; + box-shadow: 0 0 8px rgba(0, 0, 0, 0.15), 0 1px 0 rgba(255,255,255,0.8) inset; +} +.ui-button.ui-state-focus { + outline: none; + color: #1c4257; + border-color: #7096ab; + background: #ededed url(bg_fallback.png) 0 -50px repeat-x; /* Old browsers */ + background: -moz-linear-gradient(top, #b9e0f5 0%, #92bdd6 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#b9e0f5), color-stop(100%,#92bdd6)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #b9e0f5 0%,#92bdd6 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #b9e0f5 0%,#92bdd6 100%); /* Opera11.10+ */ + background: -ms-linear-gradient(top, #b9e0f5 0%,#92bdd6 100%); /* IE10+ */ + background: linear-gradient(top, #b9e0f5 0%,#92bdd6 100%); /* W3C */ + -moz-box-shadow: 0 0 8px rgba(0, 0, 0, 0.15), 0 1px 0 rgba(255,255,255,0.8) inset; + -webkit-box-shadow: 0 0 8px rgba(0, 0, 0, 0.15), 0 1px 0 rgba(255,255,255,0.8) inset; + box-shadow: 0 0 8px rgba(0, 0, 0, 0.15), 0 1px 0 rgba(255,255,255,0.8) inset; +} + +/*button text element */ +.ui-button .ui-button-text { display: block; line-height: 1.4; font-size: 14px; font-weight: bold; text-shadow: 0 1px 0 rgba(255, 255, 255, 0.6); } +.ui-button-text-only .ui-button-text { padding: .4em 1em; } +.ui-button-icon-only .ui-button-text, .ui-button-icons-only .ui-button-text { padding: .4em; text-indent: -9999999px; } +.ui-button-text-icon-primary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 1em .4em 2.1em; } +.ui-button-text-icon-secondary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 2.1em .4em 1em; } +.ui-button-text-icons .ui-button-text { padding-left: 2.1em; padding-right: 2.1em; } +/* no icon support for input elements, provide padding by default */ +input.ui-button, .ui-widget-content input.ui-button { font-size: 14px; font-weight: bold; text-shadow: 0 1px 0 rgba(255, 255, 255, 0.6); padding: 0 1em !important; height: 33px; } +/*remove submit button internal padding in Firefox*/ +input.ui-button::-moz-focus-inner { + border: 0; + padding: 0; +} +/* fix webkits handling of the box model */ +@media screen and (-webkit-min-device-pixel-ratio:0) { + input.ui-button { + height: 31px !important; + } +} + + +/*button icon element(s) */ +.ui-button-icon-only .ui-icon, .ui-button-text-icon-primary .ui-icon, .ui-button-text-icon-secondary .ui-icon, .ui-button-text-icons .ui-icon, .ui-button-icons-only .ui-icon { position: absolute; top: 50%; margin-top: -8px; } +.ui-button-icon-only .ui-icon { left: 50%; margin-left: -8px; } +.ui-button-text-icon-primary .ui-button-icon-primary, .ui-button-text-icons .ui-button-icon-primary, .ui-button-icons-only .ui-button-icon-primary { left: .5em; } +.ui-button-text-icon-secondary .ui-button-icon-secondary, .ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; } +.ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; } + +/*button sets*/ +.ui-buttonset { margin-right: 7px; } +.ui-buttonset .ui-button { margin-left: 0; margin-right: -.3em; } +.ui-buttonset .ui-button.ui-state-active { color: #1c4257; border-color: #7096ab; } +.ui-buttonset .ui-button.ui-state-active { + background: #ededed url(bg_fallback.png) 0 -50px repeat-x; /* Old browsers */ + background: -moz-linear-gradient(top, #b9e0f5 0%, #92bdd6 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#b9e0f5), color-stop(100%,#92bdd6)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #b9e0f5 0%,#92bdd6 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #b9e0f5 0%,#92bdd6 100%); /* Opera11.10+ */ + background: -ms-linear-gradient(top, #b9e0f5 0%,#92bdd6 100%); /* IE10+ */ + background: linear-gradient(top, #b9e0f5 0%,#92bdd6 100%); /* W3C */ + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; +} + +/* workarounds */ +button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra padding in Firefox */ +/* + * jQuery UI Dialog 1.8.7 + * + * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Dialog#theming + */ +.ui-dialog { position: absolute; padding: 0; width: 300px; overflow: hidden; } +.ui-dialog { + -webkit-box-shadow: 0 2px 12px rgba(0,0,0,0.6); + -moz-box-shadow: 0 2px 12px rgba(0,0,0,0.6); + box-shadow: 0 2px 12px rgba(0,0,0,0.6); +} +.ui-dialog .ui-dialog-titlebar { padding: 0.7em 1em 0.6em 1em; position: relative; border: none; border-bottom: 1px solid #979797; -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; } +.ui-dialog .ui-dialog-title { float: left; margin: .1em 16px .2em 0; font-size: 14px; text-shadow: 0 1px 0 rgba(255,255,255,0.5); } +.ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .8em; top: 55%; width: 16px; margin: -10px 0 0 0; padding: 0; height: 16px; } +.ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; background: url(icon_sprite.png) 0 -16px no-repeat; } +.ui-dialog .ui-dialog-titlebar-close:hover span { background-position: -16px -16px; } +.ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; border: 0; } +.ui-dialog .ui-dialog-content { position: relative; border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; } +.ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; } +.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { float: right; } +.ui-dialog .ui-dialog-buttonpane button { margin: .5em .4em .5em 0; cursor: pointer; } +.ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; } +.ui-draggable .ui-dialog-titlebar { cursor: move; } +/* + * jQuery UI Slider 1.8.7 + * + * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Slider#theming + */ +.ui-slider { position: relative; text-align: left; background: #d7d7d7; z-index: 1; } +.ui-slider { -moz-box-shadow: 0 1px 2px rgba(0,0,0,0.5) inset; -webkit-box-shadow: 0 1px 2px rgba(0,0,0,0.5) inset; box-shadow: 0 1px 2px rgba(0,0,0,0.5) inset; } +.ui-slider .ui-slider-handle { background: url(slider_handles.png) 0px -23px no-repeat; position: absolute; z-index: 2; width: 23px; height: 23px; cursor: default; border: none; outline: none; -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; } +.ui-slider .ui-state-hover, .ui-slider .ui-state-active { background-position: 0 0; } +.ui-slider .ui-slider-range { background: #a3cae0; position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; background-position: 0 0; } +.ui-slider .ui-slider-range { -moz-box-shadow: 0 1px 2px rgba(17,35,45,0.6) inset; -webkit-box-shadow: 0 1px 2px rgba(17,35,45,0.6) inset; box-shadow: 0 1px 2px rgba(17,35,45,0.6) inset; } + + +.ui-slider-horizontal { height: 5px; } +.ui-slider-horizontal .ui-slider-handle { top: -8px; margin-left: -13px; } +.ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; } +.ui-slider-horizontal .ui-slider-range-min { left: 0; } +.ui-slider-horizontal .ui-slider-range-max { right: 0; } + +.ui-slider-vertical { width: 5px; height: 100px; } +.ui-slider-vertical .ui-slider-handle { left: -8px; margin-left: 0; margin-bottom: -13px; } +.ui-slider-vertical .ui-slider-range { left: 0; width: 100%; } +.ui-slider-vertical .ui-slider-range-min { bottom: 0; } +.ui-slider-vertical .ui-slider-range-max { top: 0; }/* + * jQuery UI Tabs 1.8.7 + * + * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Tabs#theming + */ +.ui-tabs { position: relative; zoom: 1; border: 0; background: transparent; } /* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */ +.ui-tabs .ui-tabs-nav { margin: 0; padding: 0; background: transparent; border-width: 0 0 1px 0; } +.ui-tabs .ui-tabs-nav { + -moz-border-radius: 0; + -webkit-border-radius: 0; + border-radius: 0; +} +.ui-tabs .ui-tabs-nav li { list-style: none; float: left; position: relative; top: 1px; margin: 0 .2em 1px 0; border-bottom: 0 !important; padding: 0; white-space: nowrap; } +.ui-tabs .ui-tabs-nav li a { float: left; padding: .5em 1em; text-decoration: none; font-size: 12px; font-weight: bold; text-shadow: 0 1px 0 rgba(255,255,255,0.5); } +.ui-tabs .ui-tabs-nav li.ui-tabs-selected { margin-bottom: 0; padding-bottom: 1px; background: #fff; border-color: #B6B6B6; } +.ui-tabs .ui-tabs-nav li.ui-tabs-selected a, .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .ui-tabs .ui-tabs-nav li.ui-state-processing a { cursor: text; outline: none; } +.ui-tabs .ui-tabs-nav li a, .ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a { cursor: pointer; } /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */ +.ui-tabs .ui-tabs-panel { display: block; border-width: 0 1px 1px 1px; padding: 1em 1.4em; background: none; } +.ui-tabs .ui-tabs-panel { background: #FFF; + -moz-border-radius: 0; + -webkit-border-radius: 0; + border-radius: 0; +} +.ui-tabs .ui-tabs-hide { display: none !important; } +/* + * jQuery UI Datepicker 1.8.7 + * + * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Datepicker#theming + */ +.ui-datepicker { width: 17em; padding: 0; display: none; border-color: #DDDDDD; } +.ui-datepicker { + -moz-box-shadow: 0 4px 8px rgba(0,0,0,0.5); + -webkit-box-shadow: 0 4px 8px rgba(0,0,0,0.5); + box-shadow: 0 4px 8px rgba(0,0,0,0.5); +} +.ui-datepicker .ui-datepicker-header { position:relative; padding:.35em 0; border: none; border-bottom: 1px solid #B6B6B6; -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; } +.ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 6px; width: 1.8em; height: 1.8em; } +.ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { border: 1px none; } +.ui-datepicker .ui-datepicker-prev { left:2px; } +.ui-datepicker .ui-datepicker-next { right:2px; } +.ui-datepicker .ui-datepicker-prev span { background-position: 0px -32px !important; } +.ui-datepicker .ui-datepicker-next span { background-position: -16px -32px !important; } +.ui-datepicker .ui-datepicker-prev-hover span { background-position: 0px -48px !important; } +.ui-datepicker .ui-datepicker-next-hover span { background-position: -16px -48px !important; } +.ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px; background: url(icon_sprite.png) no-repeat; } +.ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; font-size: 12px; text-shadow: 0 1px 0 rgba(255,255,255,0.6); } +.ui-datepicker .ui-datepicker-title select { font-size:1em; margin:1px 0; } +.ui-datepicker select.ui-datepicker-month-year {width: 100%;} +.ui-datepicker select.ui-datepicker-month, +.ui-datepicker select.ui-datepicker-year { width: 49%;} +.ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; } +.ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0; } +.ui-datepicker td { border: 0; padding: 1px; } +.ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; } +.ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; } +.ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; } +.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; } +.ui-datepicker table .ui-state-highlight { border-color: #5F83B9; } +.ui-datepicker table .ui-state-hover { background: #5F83B9; color: #FFF; font-weight: bold; text-shadow: 0 1px 1px #234386; -webkit-box-shadow: 0 0px 0 rgba(255,255,255,0.6) inset; -moz-box-shadow: 0 0px 0 rgba(255,255,255,0.6) inset; box-shadow: 0 0px 0 rgba(255,255,255,0.6) inset; border-color: #5F83B9; } +.ui-datepicker-calendar .ui-state-default { background: transparent; border-color: #FFF; } +.ui-datepicker-calendar .ui-state-active { background: #5F83B9; border-color: #5F83B9; color: #FFF; font-weight: bold; text-shadow: 0 1px 1px #234386; } + +/* with multiple calendars */ +.ui-datepicker.ui-datepicker-multi { width:auto; } +.ui-datepicker-multi .ui-datepicker-group { float:left; } +.ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; } +.ui-datepicker-multi-2 .ui-datepicker-group { width:50%; } +.ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; } +.ui-datepicker-multi-4 .ui-datepicker-group { width:25%; } +.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; } +.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; } +.ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; } +.ui-datepicker-row-break { clear:both; width:100%; } + +/* RTL support */ +.ui-datepicker-rtl { direction: rtl; } +.ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; } +.ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; } +.ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; } +.ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; } +.ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; } +.ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; } +.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; } +.ui-datepicker-rtl .ui-datepicker-group { float:right; } +.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; } +.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; } + +/* IE6 IFRAME FIX (taken from datepicker 1.5.3 */ +.ui-datepicker-cover { + display: none; /*sorry for IE5*/ + display/**/: block; /*sorry for IE5*/ + position: absolute; /*must have*/ + z-index: -1; /*must have*/ + filter: mask(); /*must have*/ + top: -4px; /*must have*/ + left: -4px; /*must have*/ + width: 200px; /*must have*/ + height: 200px; /*must have*/ +}/* + * jQuery UI Progressbar 1.8.7 + * + * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Progressbar#theming + */ +.ui-progressbar { height: 12px; text-align: left; background: #FFF url(progress_bar.gif) 0 -14px repeat-x; } +.ui-progressbar .ui-progressbar-value {margin: -1px; height:100%; background: url(progress_bar.gif) 0 0 repeat-x; } + +/* Extra Input Field Styling */ +.ui-form textarea, .ui-form input:not([type="submit"]):not([type="button"]):not([type="checkbox"]):not([type="radio"]):not([type="file"]):not([type="range"]) { + padding: 3px; + -webkit-border-radius: 2px; + -moz-border-radius: 2px; + border-radius: 2px; + border: 1px solid #cecece; + outline: none; + -webkit-box-shadow: 0 1px 3px rgba(0,0,0,0.1) inset, 0 1px 0 rgba(255,255,255,0.2); + -moz-box-shadow: 0 1px 3px rgba(0,0,0,0.1) inset, 0 1px 0 rgba(255,255,255,0.2); + box-shadow: 0 1px 3px rgba(0,0,0,0.1) inset, 0 1px 0 rgba(255,255,255,0.2); + -webkit-transition: all 250ms ease-in-out; + -moz-transition: all 250ms ease-in-out; + -o-transition: all 250ms ease-in-out; + transition: all 250ms ease-in-out; +} +.ui-form textarea:hover, .ui-form input:not([type="submit"]):not([type="button"]):not([type="checkbox"]):not([type="radio"]):not([type="file"]):not([type="range"]):hover { + border: 1px solid #bdbdbd; + -webkit-box-shadow: 0 1px 3px rgba(0,0,0,0.2) inset, 0 1px 0 rgba(255,255,255,0.2); + -moz-box-shadow: 0 1px 3px rgba(0,0,0,0.2) inset, 0 1px 0 rgba(255,255,255,0.2); + box-shadow: 0 1px 3px rgba(0,0,0,0.2) inset, 0 1px 0 rgba(255,255,255,0.2); +} +.ui-form textarea:focus, .ui-form input:not([type="submit"]):not([type="button"]):not([type="checkbox"]):not([type="radio"]):not([type="file"]):not([type="range"]):focus { + border: 1px solid #95bdd4; + -webkit-box-shadow: 0 2px 3px rgba(161,202,226,0.5) inset, 0 1px 0 rgba(255,255,255,0.2); + -moz-box-shadow: 0 2px 3px rgba(161,202,226,0.5) inset, 0 1px 0 rgba(255,255,255,0.2); + box-shadow: 0 2px 3px rgba(161,202,226,0.5) inset, 0 1px 0 rgba(255,255,255,0.2); +}