From 31bc2b9495edcdfcc4c70c1e4d7bff715bcd57f5 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 6 Jul 2012 16:42:13 +0300 Subject: [PATCH 01/48] Fix tree css class conflict --- app/assets/stylesheets/main.scss | 2 +- app/assets/stylesheets/{ => sections}/tree.scss | 0 app/views/refs/_tree_item.html.haml | 4 ++-- 3 files changed, 3 insertions(+), 3 deletions(-) rename app/assets/stylesheets/{ => sections}/tree.scss (100%) diff --git a/app/assets/stylesheets/main.scss b/app/assets/stylesheets/main.scss index bff24dd6..5f13f301 100644 --- a/app/assets/stylesheets/main.scss +++ b/app/assets/stylesheets/main.scss @@ -136,7 +136,7 @@ $hover: #FDF5D9; /** * Code (files list) styles. Browsing project files there */ -@import "tree.scss"; +@import "sections/tree.scss"; /** * This file represent notes(comments) styles diff --git a/app/assets/stylesheets/tree.scss b/app/assets/stylesheets/sections/tree.scss similarity index 100% rename from app/assets/stylesheets/tree.scss rename to app/assets/stylesheets/sections/tree.scss diff --git a/app/views/refs/_tree_item.html.haml b/app/views/refs/_tree_item.html.haml index fe2f7293..45ea5371 100644 --- a/app/views/refs/_tree_item.html.haml +++ b/app/views/refs/_tree_item.html.haml @@ -13,10 +13,10 @@ - else = image_tag "file_dir.png" = link_to truncate(content.name, :length => 40), tree_file_project_ref_path(@project, @ref || @commit.id, file), :remote => :true - %td.cgray + %td.tree_time_ago.cgray = time_ago_in_words(content_commit.committed_date) ago - %td.commit + %td.tree_commit - tm = @project.team_member_by_name_or_email(content_commit.author_email, content_commit.author_name) - if tm %strong= link_to "[#{tm.user_name}]", project_team_member_path(@project, tm) From 9497b1a76ee84d12613604e37007e94a048b379e Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 5 Jul 2012 23:19:37 +0300 Subject: [PATCH 02/48] Compare images --- app/assets/stylesheets/sections/commits.scss | 15 +++++++++------ app/views/commits/_diffs.html.haml | 10 ++++++++-- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/app/assets/stylesheets/sections/commits.scss b/app/assets/stylesheets/sections/commits.scss index acab785a..6052ec3f 100644 --- a/app/assets/stylesheets/sections/commits.scss +++ b/app/assets/stylesheets/sections/commits.scss @@ -101,16 +101,19 @@ margin:50px; padding:1px; max-width:400px; - } - &.diff_image_removed { - img { + + &.diff_image_removed { border: 1px solid #C00; } + + &.diff_image_added { + border: 1px solid #0C0;; + } } - &.diff_image_added { - img { - border: 1px solid #0C0;; + &.img_compared { + img { + max-width:300px; } } } diff --git a/app/views/commits/_diffs.html.haml b/app/views/commits/_diffs.html.haml index 02a15633..d51561d9 100644 --- a/app/views/commits/_diffs.html.haml +++ b/app/views/commits/_diffs.html.haml @@ -35,7 +35,13 @@ - if file.text? = render "commits/text_file", :diff => diff, :index => i - elsif file.image? - .diff_file_content_image{:class => image_diff_class(diff)} - %img{:src => "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"} + - if diff.renamed_file || diff.new_file || diff.deleted_file + .diff_file_content_image + %img{:class => image_diff_class(diff), :src => "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"} + - else + - old_file = (@commit.prev_commit.tree / diff.old_path) + .diff_file_content_image.img_compared + %img{:class => "diff_image_removed", :src => "data:#{file.mime_type};base64,#{Base64.encode64(old_file.data)}"} + %img{:class => "diff_image_added", :src => "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"} - else %p.nothing_here_message No preview for this file type From 7359133798e8cc4fdd6569a11fd20f02110331fc Mon Sep 17 00:00:00 2001 From: Al Tobey Date: Thu, 5 Jul 2012 15:25:53 -0700 Subject: [PATCH 03/48] The gitlab.pub file probably shouldn't be world readable, but let's start with just making it read-only and not executable. --- doc/installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/installation.md b/doc/installation.md index bf579b17..2a8a0ea0 100644 --- a/doc/installation.md +++ b/doc/installation.md @@ -110,7 +110,7 @@ Setup: sudo -u git sh -c 'echo -e "PATH=\$PATH:/home/git/bin\nexport PATH" > /home/git/.profile' sudo -u git -H sh -c "PATH=/home/git/bin:$PATH; /home/git/gitolite/src/gl-system-install" sudo cp /home/gitlab/.ssh/id_rsa.pub /home/git/gitlab.pub - sudo chmod 777 /home/git/gitlab.pub + sudo chmod 0444 /home/git/gitlab.pub sudo -u git -H sed -i 's/0077/0007/g' /home/git/share/gitolite/conf/example.gitolite.rc sudo -u git -H sh -c "PATH=/home/git/bin:$PATH; gl-setup -q /home/git/gitlab.pub" From 2abd054b0c3c3f1116840cf51444fc63e49057f6 Mon Sep 17 00:00:00 2001 From: Nihad Abbasov Date: Thu, 5 Jul 2012 23:50:24 -0700 Subject: [PATCH 04/48] update devise --- Gemfile | 2 +- Gemfile.lock | 13 +++--- app/controllers/application_controller.rb | 2 +- ...vise.html.haml => devise_layout.html.haml} | 0 config/initializers/devise.rb | 8 +--- config/locales/devise.en.yml | 8 ++-- .../20110913200833_devise_create_users.rb | 46 +++++++++++++++---- 7 files changed, 51 insertions(+), 28 deletions(-) rename app/views/layouts/{devise.html.haml => devise_layout.html.haml} (100%) diff --git a/Gemfile b/Gemfile index bf2320a8..c89d2443 100644 --- a/Gemfile +++ b/Gemfile @@ -7,7 +7,7 @@ gem "sqlite3" gem "mysql2" # Auth -gem "devise", "~> 1.5" +gem "devise", "~> 2.1.0" # GITLAB patched libs gem "grit", :git => "https://github.com/gitlabhq/grit.git", :ref => "7f35cb98ff17d534a07e3ce6ec3d580f67402837" diff --git a/Gemfile.lock b/Gemfile.lock index e6a488f2..e4c06fed 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -148,10 +148,11 @@ GEM nokogiri (>= 1.5.0) daemons (1.1.8) database_cleaner (0.8.0) - devise (1.5.3) + devise (2.1.2) bcrypt-ruby (~> 3.0) - orm_adapter (~> 0.0.3) - warden (~> 1.1) + orm_adapter (~> 0.1) + railties (~> 3.1) + warden (~> 1.2.1) diff-lcs (1.1.3) drapper (0.8.4) email_spec (1.2.1) @@ -225,7 +226,7 @@ GEM omniauth (1.1.0) hashie (~> 1.2) rack - orm_adapter (0.0.7) + orm_adapter (0.3.0) polyglot (0.3.3) posix-spawn (0.3.6) pry (0.9.9.6) @@ -356,7 +357,7 @@ GEM raindrops (~> 0.7) vegas (0.1.11) rack (>= 1.0.0) - warden (1.2.0) + warden (1.2.1) rack (>= 1.0) webmock (1.8.7) addressable (>= 2.2.7) @@ -383,7 +384,7 @@ DEPENDENCIES colored cucumber-rails database_cleaner - devise (~> 1.5) + devise (~> 2.1.0) drapper email_spec ffaker diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 9a0f95bf..3265046d 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -52,7 +52,7 @@ class ApplicationController < ActionController::Base def layout_by_resource if devise_controller? - "devise" + "devise_layout" else "application" end diff --git a/app/views/layouts/devise.html.haml b/app/views/layouts/devise_layout.html.haml similarity index 100% rename from app/views/layouts/devise.html.haml rename to app/views/layouts/devise_layout.html.haml diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index cb1ae0ac..a778be5f 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -93,10 +93,6 @@ Devise.setup do |config| # If true, extends the user's remember period when remembered via cookie. # config.extend_remember_period = false - # If true, uses the password salt as remember token. This should be turned - # to false if you are not using database authenticatable. - config.use_salt_as_remember_token = true - # Options to be passed to the created cookie. For instance, you can set # :secure => true in order to force SSL only cookies. # config.cookie_options = {} @@ -160,9 +156,9 @@ Devise.setup do |config| # Defines name of the authentication token params key config.token_authentication_key = :private_token - # If true, authentication through token does not store user in session and needs + # Authentication through token does not store user in session and needs # to be supplied on each request. Useful if you are using the token as API token. - config.stateless_token = true + config.skip_session_storage << :token_auth # ==> Scopes configuration # Turn scoped views on. Before rendering "sessions/new", it will first check for diff --git a/config/locales/devise.en.yml b/config/locales/devise.en.yml index b1826351..a78cb6b6 100644 --- a/config/locales/devise.en.yml +++ b/config/locales/devise.en.yml @@ -35,13 +35,11 @@ en: confirmed: 'Your account was successfully confirmed. You are now signed in.' registrations: signed_up: 'Welcome! You have signed up successfully.' - inactive_signed_up: 'You have signed up successfully. However, we could not sign you in because your account is %{reason}.' updated: 'You updated your account successfully.' destroyed: 'Bye! Your account was successfully cancelled. We hope to see you again soon.' - reasons: - inactive: 'inactive' - unconfirmed: 'unconfirmed' - locked: 'locked' + signed_up_but_unconfirmed: 'A message with a confirmation link has been sent to your email address. Please open the link to activate your account.' + signed_up_but_inactive: 'You have signed up successfully. However, we could not sign you in because your account is not yet activated.' + signed_up_but_locked: 'You have signed up successfully. However, we could not sign you in because your account is locked.' unlocks: send_instructions: 'You will receive an email with instructions about how to unlock your account in a few minutes.' unlocked: 'Your account was successfully unlocked. You are now signed in.' diff --git a/db/migrate/20110913200833_devise_create_users.rb b/db/migrate/20110913200833_devise_create_users.rb index 01869a9e..b76edbee 100644 --- a/db/migrate/20110913200833_devise_create_users.rb +++ b/db/migrate/20110913200833_devise_create_users.rb @@ -1,15 +1,43 @@ class DeviseCreateUsers < ActiveRecord::Migration def self.up create_table(:users) do |t| - t.database_authenticatable :null => false - t.recoverable - t.rememberable - t.trackable + ## Database authenticatable + t.string :email, :null => false, :default => "" + t.string :encrypted_password, :null => false, :default => "" - # t.encryptable - # t.confirmable - # t.lockable :lock_strategy => :failed_attempts, :unlock_strategy => :both - # t.token_authenticatable + ## Recoverable + t.string :reset_password_token + t.datetime :reset_password_sent_at + + ## Rememberable + t.datetime :remember_created_at + + ## Trackable + t.integer :sign_in_count, :default => 0 + t.datetime :current_sign_in_at + t.datetime :last_sign_in_at + t.string :current_sign_in_ip + t.string :last_sign_in_ip + + ## Encryptable + # t.string :password_salt + + ## Confirmable + # t.string :confirmation_token + # t.datetime :confirmed_at + # t.datetime :confirmation_sent_at + # t.string :unconfirmed_email # Only if using reconfirmable + + ## Lockable + # t.integer :failed_attempts, :default => 0 # Only if lock strategy is :failed_attempts + # t.string :unlock_token # Only if unlock strategy is :email or :both + # t.datetime :locked_at + + # Token authenticatable + # t.string :authentication_token + + ## Invitable + # t.string :invitation_token t.timestamps end @@ -18,7 +46,7 @@ class DeviseCreateUsers < ActiveRecord::Migration add_index :users, :reset_password_token, :unique => true # add_index :users, :confirmation_token, :unique => true # add_index :users, :unlock_token, :unique => true - # add_index :users, :authentication_token, :unique => true + add_index :users, :authentication_token, :unique => true end def self.down From 6533711825c3f197470be041b95e2885bae50bc5 Mon Sep 17 00:00:00 2001 From: Nihad Abbasov Date: Fri, 6 Jul 2012 00:05:31 -0700 Subject: [PATCH 05/48] enable lockable strategy for users --- app/models/user.rb | 4 ++-- config/initializers/devise.rb | 8 ++++---- db/migrate/20120706065612_add_lockable_to_users.rb | 6 ++++++ db/schema.rb | 4 +++- 4 files changed, 15 insertions(+), 7 deletions(-) create mode 100644 db/migrate/20120706065612_add_lockable_to_users.rb diff --git a/app/models/user.rb b/app/models/user.rb index ccb1dddf..eaae6708 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,11 +1,11 @@ class User < ActiveRecord::Base include Account - devise :database_authenticatable, :token_authenticatable, + devise :database_authenticatable, :token_authenticatable, :lockable, :recoverable, :rememberable, :trackable, :validatable, :omniauthable attr_accessible :email, :password, :password_confirmation, :remember_me, :bio, - :name, :projects_limit, :skype, :linkedin, :twitter, :dark_scheme, + :name, :projects_limit, :skype, :linkedin, :twitter, :dark_scheme, :theme_id, :force_random_password attr_accessor :force_random_password diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index a778be5f..54011ba5 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -115,7 +115,7 @@ Devise.setup do |config| # Defines which strategy will be used to lock an account. # :failed_attempts = Locks an account after a number of failed attempts to sign in. # :none = No lock strategy. You should handle locking by yourself. - # config.lock_strategy = :failed_attempts + config.lock_strategy = :failed_attempts # Defines which key will be used when locking and unlocking an account # config.unlock_keys = [ :email ] @@ -125,14 +125,14 @@ Devise.setup do |config| # :time = Re-enables login after a certain amount of time (see :unlock_in below) # :both = Enables both strategies # :none = No unlock strategy. You should handle unlocking by yourself. - # config.unlock_strategy = :both + config.unlock_strategy = :time # Number of authentication tries before locking an account if lock_strategy # is failed attempts. - # config.maximum_attempts = 20 + config.maximum_attempts = 10 # Time interval to unlock the account if :time is enabled as unlock_strategy. - # config.unlock_in = 1.hour + config.unlock_in = 10.minutes # ==> Configuration for :recoverable # diff --git a/db/migrate/20120706065612_add_lockable_to_users.rb b/db/migrate/20120706065612_add_lockable_to_users.rb new file mode 100644 index 00000000..cf86e660 --- /dev/null +++ b/db/migrate/20120706065612_add_lockable_to_users.rb @@ -0,0 +1,6 @@ +class AddLockableToUsers < ActiveRecord::Migration + def change + add_column :users, :failed_attempts, :integer, :default => 0 + add_column :users, :locked_at, :datetime + end +end diff --git a/db/schema.rb b/db/schema.rb index f2bb1693..f40ee260 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 => 20120627145613) do +ActiveRecord::Schema.define(:version => 20120706065612) do create_table "events", :force => true do |t| t.string "target_type" @@ -169,6 +169,8 @@ ActiveRecord::Schema.define(:version => 20120627145613) do 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" end add_index "users", ["email"], :name => "index_users_on_email", :unique => true From 0833b7ec5ba22ad4949db79443d3157c4edb13f5 Mon Sep 17 00:00:00 2001 From: Nihad Abbasov Date: Fri, 6 Jul 2012 21:33:08 +0600 Subject: [PATCH 06/48] oops, uncomment authentication_token --- db/migrate/20110913200833_devise_create_users.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/migrate/20110913200833_devise_create_users.rb b/db/migrate/20110913200833_devise_create_users.rb index b76edbee..1603bfd6 100644 --- a/db/migrate/20110913200833_devise_create_users.rb +++ b/db/migrate/20110913200833_devise_create_users.rb @@ -34,7 +34,7 @@ class DeviseCreateUsers < ActiveRecord::Migration # t.datetime :locked_at # Token authenticatable - # t.string :authentication_token + t.string :authentication_token ## Invitable # t.string :invitation_token From 0daf53b759dd25bea0b3db880d03cc19abe6e2bb Mon Sep 17 00:00:00 2001 From: Nihad Abbasov Date: Fri, 6 Jul 2012 20:46:26 +0500 Subject: [PATCH 07/48] fix devise migrations issue --- db/migrate/20110913200833_devise_create_users.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/db/migrate/20110913200833_devise_create_users.rb b/db/migrate/20110913200833_devise_create_users.rb index 1603bfd6..e00f275c 100644 --- a/db/migrate/20110913200833_devise_create_users.rb +++ b/db/migrate/20110913200833_devise_create_users.rb @@ -34,7 +34,7 @@ class DeviseCreateUsers < ActiveRecord::Migration # t.datetime :locked_at # Token authenticatable - t.string :authentication_token + # t.string :authentication_token ## Invitable # t.string :invitation_token @@ -46,7 +46,7 @@ class DeviseCreateUsers < ActiveRecord::Migration add_index :users, :reset_password_token, :unique => true # add_index :users, :confirmation_token, :unique => true # add_index :users, :unlock_token, :unique => true - add_index :users, :authentication_token, :unique => true + # add_index :users, :authentication_token, :unique => true end def self.down From 6451e837bf2cf11f8956824329ddde0e14171a8e Mon Sep 17 00:00:00 2001 From: randx Date: Fri, 6 Jul 2012 02:16:41 +0300 Subject: [PATCH 08/48] Better event feed for dashboard --- app/decorators/event_decorator.rb | 25 +++++++++++++++++++++++++ app/views/dashboard/index.atom.builder | 13 +++---------- 2 files changed, 28 insertions(+), 10 deletions(-) create mode 100644 app/decorators/event_decorator.rb diff --git a/app/decorators/event_decorator.rb b/app/decorators/event_decorator.rb new file mode 100644 index 00000000..50aaa615 --- /dev/null +++ b/app/decorators/event_decorator.rb @@ -0,0 +1,25 @@ +class EventDecorator < ApplicationDecorator + decorates :event + + def feed_title + if self.issue? + "#{self.author_name} #{self.action_name} issue ##{self.target_id}:" + self.issue_title + elsif self.merge_request? + "#{self.author_name} #{self.action_name} MR ##{self.target_id}:" + self.merge_request_title + elsif self.push? + "#{self.author_name} #{self.push_action_name} #{self.ref_type} " + self.ref_name + else + "" + end + end + + 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? + h.project_commits_url(self.project, :ref => self.ref_name) + end + end +end diff --git a/app/views/dashboard/index.atom.builder b/app/views/dashboard/index.atom.builder index 706b808e..fa3bfade 100644 --- a/app/views/dashboard/index.atom.builder +++ b/app/views/dashboard/index.atom.builder @@ -8,17 +8,10 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear @events.each do |event| if event.allowed? + event = EventDecorator.decorate(event) xml.entry do - if event.issue? - event_link = project_issue_url(event.project, event.issue) - event_title = event.issue_title - elsif event.merge_request? - event_link = project_merge_request_url(event.project, event.merge_request) - event_title = event.merge_request_title - elsif event.push? - event_link = project_commits_url(event.project, :ref => event.ref_name) - event_title = event.ref_name - end + 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 From be1b40802dcbfd59ecdd3c7439fc48d6cec97e2b Mon Sep 17 00:00:00 2001 From: randx Date: Fri, 6 Jul 2012 03:05:12 +0300 Subject: [PATCH 09/48] Api page in help --- app/assets/stylesheets/gitlab_bootstrap.scss | 34 ++++++++++++++++ app/views/help/api.html.haml | 41 ++++++++++++++++++++ app/views/help/index.html.haml | 3 ++ config/routes.rb | 1 + 4 files changed, 79 insertions(+) create mode 100644 app/views/help/api.html.haml diff --git a/app/assets/stylesheets/gitlab_bootstrap.scss b/app/assets/stylesheets/gitlab_bootstrap.scss index 1b86cdde..4f1ffa36 100644 --- a/app/assets/stylesheets/gitlab_bootstrap.scss +++ b/app/assets/stylesheets/gitlab_bootstrap.scss @@ -634,3 +634,37 @@ pre.logs { line-height:18px; } } + +/** + * File content holder + * + */ +.file_holder { + border:1px solid #CCC; + margin-bottom:1em; + -moz-box-shadow: 0 0 3px #ddd; + -webkit-box-shadow: 0 0 3px #ddd; + box-shadow: 0 0 3px #ddd; + + .file_title { + border-bottom: 1px solid #bbb; + 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); + margin: 0; + font-weight: normal; + font-weight: bold; + text-align: left; + color: #666; + padding: 9px 10px; + height:18px; + + } + .file_content { + &.wiki { + padding:20px; + } + } +} diff --git a/app/views/help/api.html.haml b/app/views/help/api.html.haml new file mode 100644 index 00000000..008e06df --- /dev/null +++ b/app/views/help/api.html.haml @@ -0,0 +1,41 @@ +%h3 API +.back_link + = link_to help_path do + ← to index +%hr + +%ol + %li + %a{:href => "#README"} README + %li + %a{:href => "#Projects"} Projects + %li + %a{:href => "#Users"} Users + +.file_holder#README + .file_title + %i.icon-file + README + .file_content.wiki + = preserve do + = markdown File.read(Rails.root.join("doc", "api", "README.md")) + +%br + +.file_holder#projects + .file_title + %i.icon-file + Projects + .file_content.wiki + = preserve do + = markdown File.read(Rails.root.join("doc", "api", "projects.md")) + +%br + +.file_holder#users + .file_title + %i.icon-file + Users + .file_content.wiki + = preserve do + = markdown File.read(Rails.root.join("doc", "api", "users.md")) diff --git a/app/views/help/index.html.haml b/app/views/help/index.html.haml index 25b9e3e5..b6c52712 100644 --- a/app/views/help/index.html.haml +++ b/app/views/help/index.html.haml @@ -22,3 +22,6 @@ %li %h5= link_to "Web Hooks", help_web_hooks_path + + %li + %h5= link_to "API", help_api_path diff --git a/config/routes.rb b/config/routes.rb index af991098..3b33ed8d 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -26,6 +26,7 @@ Gitlab::Application.routes.draw do 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' # From 12b34c8115bbb35524880bca4f21ad0939de4b77 Mon Sep 17 00:00:00 2001 From: Stefan Morgenthaler Date: Mon, 9 Jul 2012 23:45:43 +0200 Subject: [PATCH 10/48] add: rake task to backup/restore gitlab db and repos --- config/gitlab.yml.example | 2 + lib/tasks/gitlab/backup.rake | 197 +++++++++++++++++++++++++++++++++++ 2 files changed, 199 insertions(+) create mode 100644 lib/tasks/gitlab/backup.rake diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index 12c28675..473be138 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -21,6 +21,8 @@ email: # Like default project limit for user etc app: default_projects_limit: 10 + # backup_path: "/vol/backups" # default: Rails.root + backups/ + # backup_keep_time: 7.days # default: 0 (forever) # diff --git a/lib/tasks/gitlab/backup.rake b/lib/tasks/gitlab/backup.rake new file mode 100644 index 00000000..3735d5b2 --- /dev/null +++ b/lib/tasks/gitlab/backup.rake @@ -0,0 +1,197 @@ +require 'active_record/fixtures' + +# load config +GITLAB_OPTS = YAML.load_file("#{Rails.root}/config/gitlab.yml")['app'] + +# backup path options +backup_path = GITLAB_OPTS['backup_path'].nil? ? "backups/" : GITLAB_OPTS['backup_path'] # set default path if not specified in settings. +backup_path = /^\//.match(backup_path) ? backup_path : File.join(Rails.root + backup_path) # check for absolut or relative path. +FileUtils.mkdir_p(backup_path) until Dir.exists?(backup_path) + +# keep last n backups +backup_keep_time = GITLAB_OPTS['backup_keep_time'].nil? ? 0 : eval(GITLAB_OPTS['backup_keep_time']) + +backup_path_repo = File.join(backup_path, "repositories") +backup_path_db = File.join(backup_path, "db") + +namespace :gitlab do + namespace :app do + + # Create backup of gitlab system + desc "GITLAB | Create a backup of the gitlab system" + task :backup_create => :environment do + + Dir.chdir(backup_path) + + Rake::Task["gitlab:app:db_dump"].invoke + Rake::Task["gitlab:app:repo_dump"].invoke + + # saving additional informations + s = Hash.new + s["db_version"] = "#{ActiveRecord::Migrator.current_version}" + s["backup_created_at"] = "#{Time.now}" + s["gitlab_version"] = %x{git rev-parse HEAD}.gsub(/\n/,"") + s["tar_version"] = %x{tar --version | head -1}.gsub(/\n/,"") + + File.open("#{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 " + if Kernel.system("tar -cf #{Time.now.to_i}_gitlab_backup.tar repositories/ db/ backup_information.yml") + puts "[DONE]".green + else + puts "[FAILED]".red + end + + # cleanup: remove tmp files + print "Deletion of tmp directories..." + if Kernel.system("rm -rf repositories/ db/ backup_information.yml") + puts "[DONE]".green + else + puts "[FAILED]".red + end + + # delete backups + print "Deleting old backups... " + if 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 - backup_keep_time) + %x{rm #{timestamp}_gitlab_backup.tar} + end + end + puts "[DONE]".green + else + puts "[SKIPPING]".yellow + end + + end + + + desc "GITLAB | Restore a previously created backup" + task :backup_restore => :environment do + + Dir.chdir(backup_path) + + file_list = Dir.glob("*_gitlab_backup.tar").each.map { |f| f.split(/_/).first.to_i } + puts "no backup 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:app:backup_restore BACKUP=timestamp_of_backup" + exit 1; + end + + tar_file = ENV["BACKUP"].nil? ? File.join(file_list.first.to_s + "_gitlab_backup.tar") : File.join(ENV["BACKUP"] + "_gitlab_backup.tar") + + if ! File.exists?(tar_file) + puts "The specified backup doesn't exist!" + end + + print "Unpacking backup... " + if ! Kernel.system("tar -xf #{tar_file}") + puts "[FAILED]".red + exit 1 + else + puts "[DONE]".green + end + + settings = YAML.load_file("backup_information.yml") + ENV["VERSION"] = "#{settings["db_version"]}" if settings["db_version"].to_i > 0 + + # restoring mismatching backups can lead to unexpected problems + if settings["gitlab_version"] != %x{git rev-parse HEAD}.gsub(/\n/,"") + puts "gitlab_version mismatch:".red + puts " Your current HEAD differs from the HEAD in the backup!".red + puts " Please switch to the following revision and try again:".red + puts " revision: #{settings["gitlab_version"]}".red + exit 1 + end + + Rake::Task["gitlab:app:db_restore"].invoke + Rake::Task["gitlab:app:repo_restore"].invoke + + # cleanup: remove tmp files + print "Deletion of tmp directories..." + if Kernel.system("rm -rf repositories/ db/ backup_information.yml") + puts "[DONE]".green + else + puts "[FAILED]".red + end + + end + + + ################################################################################ + ################################# invoked tasks ################################ + + ################################# REPOSITORIES ################################# + + task :repo_dump => :environment do + FileUtils.mkdir_p(backup_path_repo) until Dir.exists?(backup_path_repo) + puts "Dumping repositories:" + project = Project.all.map { |n| [n.name,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 :repo_restore => :environment do + puts "Restoring repositories:" + project = Project.all.map { |n| [n.name,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) # delet 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") + puts "[DONE]".green + else + puts "[FAILED]".red + end + end + end + + ###################################### DB ###################################### + + task :db_dump => :environment do + FileUtils.mkdir_p(backup_path_db) until Dir.exists?(backup_path_db) + 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 + + task :db_restore=> :environment do + 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 From ba7b85d0d986e7c511aa91bde97b3a992bb5d436 Mon Sep 17 00:00:00 2001 From: Staicu Ionut Date: Tue, 10 Jul 2012 07:33:04 +0300 Subject: [PATCH 11/48] =?UTF-8?q?issues=20counter=20are=20now=20live=20upd?= =?UTF-8?q?ated=20on=20creation/deleteion/reopening=20=C2=85=20buttons=20o?= =?UTF-8?q?n=20forms=20are=20disabled=20on=20submit=20(re-enabled=20after?= =?UTF-8?q?=20the=20request=20is=20complete)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/assets/javascripts/application.js | 16 ++++++++++++++++ app/assets/javascripts/issues.js | 21 +++++++++++++++++++++ app/assets/stylesheets/common.scss | 2 +- app/views/issues/_issues.html.haml | 4 +++- app/views/issues/_show.html.haml | 8 +++----- app/views/issues/index.html.haml | 4 ++-- app/views/layouts/_project_menu.html.haml | 4 ++-- 7 files changed, 48 insertions(+), 11 deletions(-) diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 42290f8e..683d8fa8 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -20,10 +20,26 @@ //= require_tree . $(document).ready(function(){ + $(".one_click_select").live("click", function(){ $(this).select(); }); + + $('body').on('ajax:complete, ajax:beforeSend, submit', 'form', function(e){ + var buttons = $('[type="submit"]', this); + switch( e.type ){ + case 'ajax:beforeSend': + case 'submit': + buttons.attr('disabled', 'disabled'); + break; + case ' ajax:complete': + default: + buttons.removeAttr('disabled'); + break; + } + }) + $(".account-box").mouseenter(showMenu); $(".account-box").mouseleave(resetMenu); diff --git a/app/assets/javascripts/issues.js b/app/assets/javascripts/issues.js index 49936e3f..0acf9ec8 100644 --- a/app/assets/javascripts/issues.js +++ b/app/assets/javascripts/issues.js @@ -73,4 +73,25 @@ function issuesPage(){ $("#milestone_id, #assignee_id, #label_name").on("change", function(){ $(this).closest("form").submit(); }); + + $('body').on('ajax:success', '.close_issue, .reopen_issue, #new_issue', function(){ + var t = $(this), + totalIssues, + 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( newIssue || ( reopen && issue.closest('.main_menu').length ) ){ + $(this).html( totalIssues+1 ); + }else { + $(this).html( totalIssues-1 ); + } + }); + + }); } diff --git a/app/assets/stylesheets/common.scss b/app/assets/stylesheets/common.scss index 19092073..32dbf830 100644 --- a/app/assets/stylesheets/common.scss +++ b/app/assets/stylesheets/common.scss @@ -695,4 +695,4 @@ li.note { border:1px solid #B8B; } } -} +} \ No newline at end of file diff --git a/app/views/issues/_issues.html.haml b/app/views/issues/_issues.html.haml index a20df176..17141cc4 100644 --- a/app/views/issues/_issues.html.haml +++ b/app/views/issues/_issues.html.haml @@ -6,7 +6,9 @@ .row .span7= paginate @issues, :remote => true, :theme => "gitlab" .span3.right - %span.cgray.right #{@issues.total_count} issues for this filter + %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/_show.html.haml b/app/views/issues/_show.html.haml index 03524901..e12c3c1a 100644 --- a/app/views/issues/_show.html.haml +++ b/app/views/issues/_show.html.haml @@ -12,9 +12,9 @@ = issue.notes.count - if can? current_user, :modify_issue, issue - if issue.closed - = link_to 'Reopen', project_issue_path(issue.project, issue, :issue => {:closed => false }, :status_only => true), :method => :put, :class => "btn small grouped", :remote => true + = 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 'Resolve', project_issue_path(issue.project, issue, :issue => {:closed => true }, :status_only => true), :method => :put, :class => "success btn small grouped", :remote => true + = link_to 'Resolve', project_issue_path(issue.project, issue, :issue => {:closed => true }, :status_only => true), :method => :put, :class => "success btn small grouped close_issue", :remote => true = link_to edit_project_issue_path(issue.project, issue), :class => "btn small edit-issue-link", :remote => true do %i.icon-edit Edit @@ -35,6 +35,4 @@   - if issue.upvotes > 0 - %span.badge.badge-success= "+#{issue.upvotes}" - - + %span.badge.badge-success= "+#{issue.upvotes}" \ No newline at end of file diff --git a/app/views/issues/index.html.haml b/app/views/issues/index.html.haml index 7328fa88..fb8b9f8e 100644 --- a/app/views/issues/index.html.haml +++ b/app/views/issues/index.html.haml @@ -2,7 +2,7 @@ .issues_content %h3.page_title Issues - %small (#{@issues.total_count}) + %small (#{@issues.total_count}) .right .span5 - if can? current_user, :write_issue, @project @@ -45,4 +45,4 @@ :javascript $(function(){ issuesPage(); - }) + }) \ No newline at end of file diff --git a/app/views/layouts/_project_menu.html.haml b/app/views/layouts/_project_menu.html.haml index 62279bb0..3f58fc5a 100644 --- a/app/views/layouts/_project_menu.html.haml +++ b/app/views/layouts/_project_menu.html.haml @@ -17,14 +17,14 @@ %li{:class => tab_class(:issues)} = link_to project_issues_filter_path(@project) do Issues - %span.count= @project.issues.opened.count + %span.count.issue_counter= @project.issues.opened.count - if @project.repo_exists? - if @project.merge_requests_enabled %li{:class => tab_class(:merge_requests)} = link_to project_merge_requests_path(@project) do Merge Requests - %span.count= @project.merge_requests.opened.count + %span.count.merge_counter= @project.merge_requests.opened.count - if @project.wall_enabled %li{:class => tab_class(:wall)} From c5b16f0998f0290381808e31b6f58c9d311ba76b Mon Sep 17 00:00:00 2001 From: Saito Date: Tue, 10 Jul 2012 15:57:43 +0800 Subject: [PATCH 12/48] fix blame encoding error. --- app/views/refs/blame.html.haml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/views/refs/blame.html.haml b/app/views/refs/blame.html.haml index 7307d557..497a2e39 100644 --- a/app/views/refs/blame.html.haml +++ b/app/views/refs/blame.html.haml @@ -37,8 +37,7 @@ %td.lines = preserve do %pre - - lines.each do |line| - = line + = Gitlab::Encode.utf8 lines.join("\n") :javascript $(function(){ From 4a21f090292f05d3bbc017e6cee6b9a653403ac0 Mon Sep 17 00:00:00 2001 From: Daniel Thomas Date: Tue, 10 Jul 2012 12:47:30 +0200 Subject: [PATCH 13/48] Should append to .profile rather than overwriting it as it might have existing contents --- doc/installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/installation.md b/doc/installation.md index bf579b17..f15eff88 100644 --- a/doc/installation.md +++ b/doc/installation.md @@ -107,7 +107,7 @@ Get gitolite source code: Setup: - sudo -u git sh -c 'echo -e "PATH=\$PATH:/home/git/bin\nexport PATH" > /home/git/.profile' + sudo -u git sh -c 'echo -e "PATH=\$PATH:/home/git/bin\nexport PATH" >> /home/git/.profile' sudo -u git -H sh -c "PATH=/home/git/bin:$PATH; /home/git/gitolite/src/gl-system-install" sudo cp /home/gitlab/.ssh/id_rsa.pub /home/git/gitlab.pub sudo chmod 777 /home/git/gitlab.pub From 4dae41d5dc90473e3c5c08064c25433fecf5abd3 Mon Sep 17 00:00:00 2001 From: Stefan Morgenthaler Date: Tue, 10 Jul 2012 16:12:19 +0200 Subject: [PATCH 14/48] Changes done as per feedback --- config/gitlab.yml.example | 2 +- config/initializers/1_settings.rb | 10 +++++++++ lib/tasks/gitlab/backup.rake | 37 +++++++++++++------------------ 3 files changed, 26 insertions(+), 23 deletions(-) diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index 473be138..1818f2c0 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -22,7 +22,7 @@ email: app: default_projects_limit: 10 # backup_path: "/vol/backups" # default: Rails.root + backups/ - # backup_keep_time: 7.days # default: 0 (forever) + # backup_keep_time: 604800 # default: 0 (forever) (in seconds) # diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index 8b9ed8ae..bbf5976b 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -101,5 +101,15 @@ class Settings < Settingslogic def default_projects_limit app['default_projects_limit'] || 10 end + + def backup_path + t = app['backup_path'] || "backups/" + t = /^\//.match(t) ? t : File.join(Rails.root + t) + t + end + + def backup_keep_time + app['backup_keep_time'] || 0 + end end end diff --git a/lib/tasks/gitlab/backup.rake b/lib/tasks/gitlab/backup.rake index 3735d5b2..4fbde37e 100644 --- a/lib/tasks/gitlab/backup.rake +++ b/lib/tasks/gitlab/backup.rake @@ -1,19 +1,5 @@ require 'active_record/fixtures' -# load config -GITLAB_OPTS = YAML.load_file("#{Rails.root}/config/gitlab.yml")['app'] - -# backup path options -backup_path = GITLAB_OPTS['backup_path'].nil? ? "backups/" : GITLAB_OPTS['backup_path'] # set default path if not specified in settings. -backup_path = /^\//.match(backup_path) ? backup_path : File.join(Rails.root + backup_path) # check for absolut or relative path. -FileUtils.mkdir_p(backup_path) until Dir.exists?(backup_path) - -# keep last n backups -backup_keep_time = GITLAB_OPTS['backup_keep_time'].nil? ? 0 : eval(GITLAB_OPTS['backup_keep_time']) - -backup_path_repo = File.join(backup_path, "repositories") -backup_path_db = File.join(backup_path, "db") - namespace :gitlab do namespace :app do @@ -21,11 +7,11 @@ namespace :gitlab do desc "GITLAB | Create a backup of the gitlab system" task :backup_create => :environment do - Dir.chdir(backup_path) - Rake::Task["gitlab:app:db_dump"].invoke Rake::Task["gitlab:app:repo_dump"].invoke + Dir.chdir(Gitlab.config.backup_path) + # saving additional informations s = Hash.new s["db_version"] = "#{ActiveRecord::Migrator.current_version}" @@ -33,7 +19,7 @@ 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("#{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 @@ -55,10 +41,10 @@ namespace :gitlab do # delete backups print "Deleting old backups... " - if backup_keep_time > 0 + 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 - backup_keep_time) + if Time.at(timestamp) < (Time.now - Gitlab.config.backup_keep_time) %x{rm #{timestamp}_gitlab_backup.tar} end end @@ -70,11 +56,13 @@ namespace :gitlab do end + # Restore backup of gitlab system desc "GITLAB | Restore a previously created backup" task :backup_restore => :environment do - Dir.chdir(backup_path) + 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 backup found" if file_list.count == 0 if file_list.count > 1 && ENV["BACKUP"].nil? @@ -85,12 +73,13 @@ namespace :gitlab do tar_file = ENV["BACKUP"].nil? ? File.join(file_list.first.to_s + "_gitlab_backup.tar") : File.join(ENV["BACKUP"] + "_gitlab_backup.tar") - if ! File.exists?(tar_file) + unless File.exists?(tar_file) puts "The specified backup doesn't exist!" + exit 1; end print "Unpacking backup... " - if ! Kernel.system("tar -xf #{tar_file}") + unless Kernel.system("tar -xf #{tar_file}") puts "[FAILED]".red exit 1 else @@ -129,6 +118,7 @@ namespace :gitlab do ################################# REPOSITORIES ################################# 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.name,n.path_to_repo] } @@ -144,6 +134,7 @@ namespace :gitlab do 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.name,n.path_to_repo] } project << ["gitolite-admin.git", File.join(File.dirname(project.first.second), "gitolite-admin.git")] @@ -161,6 +152,7 @@ namespace :gitlab do ###################################### DB ###################################### task :db_dump => :environment do + backup_path_db = File.join(Gitlab.config.backup_path, "db") FileUtils.mkdir_p(backup_path_db) until Dir.exists?(backup_path_db) puts "Dumping database tables:" ActiveRecord::Base.connection.tables.each do |tbl| @@ -179,6 +171,7 @@ namespace :gitlab do end 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| From d3862c0e5739ed09373fea2f52673c8ca1d3cbe4 Mon Sep 17 00:00:00 2001 From: randx Date: Tue, 10 Jul 2012 20:15:33 +0300 Subject: [PATCH 15/48] CSS improvements & refactoring --- app/assets/stylesheets/gitlab_bootstrap.scss | 111 ++++++++++++++- app/assets/stylesheets/notes.scss | 15 +- app/assets/stylesheets/sections/tree.scss | 136 ------------------- app/views/help/api.html.haml | 4 +- app/views/keys/new.html.haml | 4 +- app/views/notes/_reply_button.html.haml | 1 + app/views/refs/_tree.html.haml | 8 +- app/views/refs/_tree_file.html.haml | 32 +++-- app/views/refs/blame.html.haml | 8 +- app/views/snippets/show.html.haml | 20 ++- 10 files changed, 156 insertions(+), 183 deletions(-) diff --git a/app/assets/stylesheets/gitlab_bootstrap.scss b/app/assets/stylesheets/gitlab_bootstrap.scss index 4f1ffa36..e2d63906 100644 --- a/app/assets/stylesheets/gitlab_bootstrap.scss +++ b/app/assets/stylesheets/gitlab_bootstrap.scss @@ -642,9 +642,7 @@ pre.logs { .file_holder { border:1px solid #CCC; margin-bottom:1em; - -moz-box-shadow: 0 0 3px #ddd; - -webkit-box-shadow: 0 0 3px #ddd; - box-shadow: 0 0 3px #ddd; + @include shade; .file_title { border-bottom: 1px solid #bbb; @@ -661,10 +659,111 @@ pre.logs { padding: 9px 10px; height:18px; + .options { + float:right; + margin-top: -5px; + } + + .file_name { + color:$style_color; + font-size:14px; + text-shadow: 0 1px 1px #fff; + small { + color:#999; + font-size:13px; + } + } } .file_content { - &.wiki { - padding:20px; - } + background:#fff; + color:#514721; + font-size: 11px; + + &.wiki { + code { + padding:0 4px; + } + padding:20px; + h1, h2 { + line-height: 46px; + } + h3, h4 { + line-height: 40px; + } + } + + &.image_file { + background:#eee; + text-align:center; + img { + padding:100px; + max-width:300px; + } + } + + &.blob_file { + + } + + /** + * Blame file + */ + &.blame { + tr { + border-bottom: 1px solid #eee; + } + td { + padding:5px; + } + .author, + .blame_commit { + background:#f5f5f5; + vertical-align:top; + } + .lines { + pre { + padding:0; + margin:0; + background:none; + border:none; + } + } + } + + /** + * Code file + */ + &.code { + padding:0; + td.code { + width: 100%; + .highlight { + margin-left: 55px; + overflow:auto; + overflow-y:hidden; + } + } + .highlight pre { + white-space: pre; + word-wrap:normal; + } + + table.highlighttable { + border: none; + } + body.project-page table.highlighttable td { border: none } + table.highlighttable tr:hover { background:none;} + + table.highlighttable pre{ + line-height:16px !important; + font-size:12px !important; + } + + table.highlighttable .linenodiv pre { + text-align: right; + padding-right: 4px; + color:#666; + } + } } } diff --git a/app/assets/stylesheets/notes.scss b/app/assets/stylesheets/notes.scss index 70a31dbb..39db704b 100644 --- a/app/assets/stylesheets/notes.scss +++ b/app/assets/stylesheets/notes.scss @@ -63,18 +63,22 @@ p.notify_controls span{ tr.line_notes_row { border-bottom:1px solid #DDD; + border-left: 7px solid #2A79A3; + &.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); - border-color:#aaa; - background: #bbb; - padding: 3px 20px; + padding: 3px 10px; + margin-left:5px; color: white; + background: #2A79A3; + border-color: #2A79A3; } } ul { @@ -95,6 +99,9 @@ tr.line_notes_row { td { border-bottom:1px solid #ddd; } + .actions { + margin:0; + } } td .line_note_link { diff --git a/app/assets/stylesheets/sections/tree.scss b/app/assets/stylesheets/sections/tree.scss index 912c63ee..072d6103 100644 --- a/app/assets/stylesheets/sections/tree.scss +++ b/app/assets/stylesheets/sections/tree.scss @@ -25,103 +25,6 @@ } } - - /** FILE CONTENT VIEW **/ - .view_file_content{ - .old_line, .new_line { - background:#ECECEC; - color:#777; - width:15px; - float:left; - padding: 0px 10px; - border-right: 1px solid #ccc; - } - .old_line{ - display:none; - } - } - - .view_file .view_file_header, - .diff_file .diff_file_header { - border-bottom: 1px solid #bbb; - 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); - margin: 0; - font-weight: normal; - font-weight: bold; - text-align: left; - color: #666; - padding: 9px 10px; - height:18px; - - .options { - float:right; - margin-top: -5px; - } - - .file_name { - color:$style_color; - font-size:14px; - text-shadow: 0 1px 1px #fff; - small { - color:#999; - font-size:13px; - } - } - } - - .view_file { - border:1px solid #CCC; - margin-bottom:1em; - - .view_file_content { - background:#fff; - color:#514721; - font-size: 11px; - } - .view_file_content_image { - background:#eee; - text-align:center; - img { - padding:100px; - max-width:300px; - } - } - } - - td.code { - width: 100%; - .highlight { - margin-left: 55px; - overflow:auto; - overflow-y:hidden; - } - } - .highlight pre { - white-space: pre; - word-wrap:normal; - } - - table.highlighttable { - border: none; - } - body.project-page table.highlighttable td { border: none } - table.highlighttable tr:hover { background:none;} - - table.highlighttable pre{ - line-height:16px !important; - font-size:12px !important; - } - - table.highlighttable .linenodiv pre { - text-align: right; - padding-right: 4px; - color:#666; - } - #tree-slider { @include border-radius(0); .tree-item { @@ -183,21 +86,6 @@ color:#333; } - #tree-content-holder .view_file{ - @include shade; - } - - #tree-readme-holder .readme { - @include shade; - margin-bottom:20px; - h1, h2 { - line-height: 56px; - } - h3, h4 { - line-height: 46px; - } - } - a.tree-commit-link { color: #666; &:hover { @@ -206,27 +94,3 @@ } } - -.blame_file { - .view_file_content { - tr { - border-bottom: 1px solid #eee; - } - td { - padding:5px; - } - .author, - .commit { - background:#f5f5f5; - vertical-align:top; - } - .lines { - pre { - padding:0; - margin:0; - background:none; - border:none; - } - } - } -} diff --git a/app/views/help/api.html.haml b/app/views/help/api.html.haml index 008e06df..4964c1bb 100644 --- a/app/views/help/api.html.haml +++ b/app/views/help/api.html.haml @@ -8,9 +8,9 @@ %li %a{:href => "#README"} README %li - %a{:href => "#Projects"} Projects + %a{:href => "#projects"} Projects %li - %a{:href => "#Users"} Users + %a{:href => "#users"} Users .file_holder#README .file_title diff --git a/app/views/keys/new.html.haml b/app/views/keys/new.html.haml index 277936c6..02e782b9 100644 --- a/app/views/keys/new.html.haml +++ b/app/views/keys/new.html.haml @@ -1,4 +1,4 @@ -%h3 New key +%h3.page_title New key %hr = render 'form' @@ -11,4 +11,4 @@ if( key_mail && key_mail.length > 0 && title.val() == '' ){ $('#key_title').val( key_mail ); } - }); \ No newline at end of file + }); diff --git a/app/views/notes/_reply_button.html.haml b/app/views/notes/_reply_button.html.haml index f03ba4d7..db6b35b7 100644 --- a/app/views/notes/_reply_button.html.haml +++ b/app/views/notes/_reply_button.html.haml @@ -1,3 +1,4 @@ %tr.line_notes_row.reply %td{:colspan => 3} + %i.icon-comment = link_to "Reply", "#", :class => "line_note_reply_link", "line_code" => line_code, :title => "Add note for this line" diff --git a/app/views/refs/_tree.html.haml b/app/views/refs/_tree.html.haml index ee2f2786..4450afdf 100644 --- a/app/views/refs/_tree.html.haml +++ b/app/views/refs/_tree.html.haml @@ -37,9 +37,11 @@ = render :partial => "refs/submodule_item", :locals => { :content => content } - if content = contents.select{ |c| c.is_a?(Grit::Blob) and c.name =~ /^readme/i }.first - #tree-readme-holder - %h3= content.name - .readme + .file_holder#README + .file_title + %i.icon-file + = content.name + .file_content.wiki - if content.name =~ /\.(md|markdown)$/i = preserve do = markdown(content.data) diff --git a/app/views/refs/_tree_file.html.haml b/app/views/refs/_tree_file.html.haml index ee56ab36..d45a03df 100644 --- a/app/views/refs/_tree_file.html.haml +++ b/app/views/refs/_tree_file.html.haml @@ -1,5 +1,5 @@ -.view_file - .view_file_header +.file_holder + .file_title %i.icon-file %span.file_name = name @@ -10,26 +10,28 @@ = link_to "blame", blame_file_project_ref_path(@project, @ref, :path => params[:path]), :class => "btn very_small" - if file.text? - if name =~ /\.(md|markdown)$/i - #tree-readme-holder - .readme - = preserve do - = markdown(file.data) + .file_content.wiki + = preserve do + = markdown(file.data) - else - .view_file_content + .file_content.code - unless file.empty? %div{:class => current_user.dark_scheme ? "black" : "white"} = preserve do = raw file.colorize(options: { linenos: 'True'}) - else %h4.nothing_here_message Empty file + - elsif file.image? - .view_file_content_image + .file_content.image_file %img{ :src => "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"} + - else - %center - = link_to blob_project_ref_path(@project, @ref, :path => params[:path]) do - %div.padded - %br - = image_tag "download.png", :width => 64 - %h3 - Download (#{file.mb_size}) + .file_content.blob_file + %center + = link_to blob_project_ref_path(@project, @ref, :path => params[:path]) do + %div.padded + %br + = image_tag "download.png", :width => 64 + %h3 + Download (#{file.mb_size}) diff --git a/app/views/refs/blame.html.haml b/app/views/refs/blame.html.haml index 497a2e39..6a86b91f 100644 --- a/app/views/refs/blame.html.haml +++ b/app/views/refs/blame.html.haml @@ -11,8 +11,8 @@ %li= link .clear - .view_file.blame_file - .view_file_header + .file_holder + .file_title %i.icon-file %span.file_name = @tree.name @@ -21,7 +21,7 @@ = link_to "raw", blob_project_ref_path(@project, @ref, :path => params[:path]), :class => "btn very_small", :target => "_blank" = link_to "history", project_commits_path(@project, :path => params[:path], :ref => @ref), :class => "btn very_small" = link_to "source", tree_file_project_ref_path(@project, @ref, :path => params[:path]), :class => "btn very_small" - .view_file_content + .file_content.blame %table - @blame.each do |commit, lines| - commit = Commit.new(commit) @@ -29,7 +29,7 @@ %td.author = image_tag gravatar_icon(commit.author_email, 16) = commit.author_name - %td.commit + %td.blame_commit   = link_to project_commit_path(@project, :id => commit.id) do %code= commit.id.to_s[0..10] diff --git a/app/views/snippets/show.html.haml b/app/views/snippets/show.html.haml index c934e262..b266e4d2 100644 --- a/app/views/snippets/show.html.haml +++ b/app/views/snippets/show.html.haml @@ -7,16 +7,14 @@ = link_to "Edit", edit_project_snippet_path(@project, @snippet), :class => "btn small right" %br -#tree-holder - #tree-content-holder - .view_file - .view_file_header - %i.icon-file - %strong= @snippet.file_name - %span.options - = link_to "raw", raw_project_snippet_path(@project, @snippet), :class => "btn very_small", :target => "_blank" - .view_file_content - %div{:class => current_user.dark_scheme ? "black" : ""} - = raw @snippet.colorize(options: { linenos: 'True'}) +.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'}) = render "notes/notes", :tid => @snippet.id, :tt => "snippet" From 2600e80b412a6e7256fe1f4e3ca32cc5d410b449 Mon Sep 17 00:00:00 2001 From: VonC Date: Wed, 11 Jul 2012 16:36:00 +0200 Subject: [PATCH 16/48] Look for 'gitolite_admin_uri' in the right section of 'gitlab.yml'. gitlabhq\config\initializers\1_settings.rb looks for 'gitolite_admin_uri' in the 'git' section of 'gitlab.yml' Actually, that setting is in the 'git_host' section. If not fixed, the 'gitolite_admin_uri' would always be equals to 'git@localhost:gitolite-admin', even if the administrator wants to have another user than 'git' in charge of that repo. --- config/initializers/1_settings.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index bbf5976b..5c5987a8 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -95,7 +95,7 @@ class Settings < Settingslogic end def gitolite_admin_uri - git['admin_uri'] || 'git@localhost:gitolite-admin' + git_host['admin_uri'] || 'git@localhost:gitolite-admin' end def default_projects_limit From b8425cf173841eb2f7ba95141f364241a81a52a8 Mon Sep 17 00:00:00 2001 From: randx Date: Tue, 10 Jul 2012 21:26:01 +0300 Subject: [PATCH 17/48] Last push widget improved --- app/assets/stylesheets/common.scss | 8 ++++++-- app/views/events/_event_last_push.html.haml | 5 +---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/app/assets/stylesheets/common.scss b/app/assets/stylesheets/common.scss index 32dbf830..0db803aa 100644 --- a/app/assets/stylesheets/common.scss +++ b/app/assets/stylesheets/common.scss @@ -604,7 +604,11 @@ li.note { border-style: solid; border-width: 1px; @include border-radius(4px); - min-height:42px; + min-height:22px; + + .avatar { + width:24px; + } } .supp_diff_link, @@ -695,4 +699,4 @@ li.note { border:1px solid #B8B; } } -} \ No newline at end of file +} diff --git a/app/views/events/_event_last_push.html.haml b/app/views/events/_event_last_push.html.haml index fb4a728e..212ef817 100644 --- a/app/views/events/_event_last_push.html.haml +++ b/app/views/events/_event_last_push.html.haml @@ -5,12 +5,9 @@ %span Your pushed to = event.ref_type = link_to project_commits_path(event.project, :ref => event.ref_name) do - %strong= event.ref_name + %strong= truncate(event.ref_name, :length => 28) at %strong= link_to event.project.name, event.project - %span.cgray - = time_ago_in_words(event.created_at) - ago. = link_to new_mr_path_from_push_event(event), :title => "New Merge Request", :class => "btn very_small primary" do Create Merge Request From 1a2bacfb4b4b8f4d79df0335b4daf1d2cfa16d88 Mon Sep 17 00:00:00 2001 From: randx Date: Tue, 10 Jul 2012 22:52:38 +0300 Subject: [PATCH 18/48] Feature: ajax load for tree commit log --- app/assets/javascripts/application.js | 1 + app/controllers/refs_controller.rb | 23 ++- app/views/refs/_tree.html.haml | 13 +- app/views/refs/_tree_commit.html.haml | 3 + app/views/refs/_tree_item.html.haml | 18 +-- app/views/refs/logs_tree.js.haml | 9 ++ app/views/refs/tree.js.haml | 5 + config/routes.rb | 10 ++ .../javascripts/jquery.waitforimages.js | 144 ++++++++++++++++++ 9 files changed, 206 insertions(+), 20 deletions(-) create mode 100644 app/views/refs/_tree_commit.html.haml create mode 100644 app/views/refs/logs_tree.js.haml create mode 100644 vendor/assets/javascripts/jquery.waitforimages.js diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 683d8fa8..4815d180 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -12,6 +12,7 @@ //= require jquery.cookie //= require jquery.endless-scroll //= require jquery.highlight +//= require jquery.waitforimages //= require bootstrap-modal //= require modernizr //= require chosen-jquery diff --git a/app/controllers/refs_controller.rb b/app/controllers/refs_controller.rb index ddf5f675..b610c9f3 100644 --- a/app/controllers/refs_controller.rb +++ b/app/controllers/refs_controller.rb @@ -9,7 +9,7 @@ class RefsController < ApplicationController before_filter :require_non_empty_project before_filter :ref - before_filter :define_tree_vars, :only => [:tree, :blob, :blame] + before_filter :define_tree_vars, :only => [:tree, :blob, :blame, :logs_tree] before_filter :render_full_content layout "project" @@ -46,6 +46,18 @@ class RefsController < ApplicationController end end + 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 + { + :file_name => content.name, + :commit => last_commit + } + end + end + def blob if @tree.is_blob? if @tree.text? @@ -79,6 +91,15 @@ class RefsController < ApplicationController @commit = project.commit(@ref) @tree = Tree.new(@commit.tree, project, @ref, params[:path]) @tree = TreeDecorator.new(@tree) + @hex_path = Digest::SHA1.hexdigest(params[:path] || "/") + + if params[:path] + @history_path = tree_file_project_ref_path(@project, @ref, params[:path]) + @logs_path = logs_file_project_ref_path(@project, @ref, params[:path]) + else + @history_path = tree_project_ref_path(@project, @ref) + @logs_path = logs_tree_project_ref_path(@project, @ref) + end rescue return render_404 end diff --git a/app/views/refs/_tree.html.haml b/app/views/refs/_tree.html.haml index 4450afdf..1965fe45 100644 --- a/app/views/refs/_tree.html.haml +++ b/app/views/refs/_tree.html.haml @@ -13,7 +13,7 @@ = render :partial => "refs/tree_file", :locals => { :name => tree.name, :content => tree.data, :file => tree } - else - contents = tree.contents - %table#tree-slider.bordered-table.table + %table#tree-slider.bordered-table.table{:class => "table_#{@hex_path}" } %thead %th Name %th Last Update @@ -48,17 +48,18 @@ - else = simple_format(content.data) -- if params[:path] - - history_path = tree_file_project_ref_path(@project, @ref, params[:path]) -- else - - history_path = tree_project_ref_path(@project, @ref) :javascript $(function(){ $('select#branch').selectmenu({style:'popup', width:200}); $('select#tag').selectmenu({style:'popup', width:200}); $('.project-refs-select').chosen(); - history.pushState({ path: this.path }, '', "#{history_path}") + history.pushState({ path: this.path }, '', "#{@history_path}"); + + }); + + $(window).load(function(){ + $.ajax({type: "GET", url: '#{@logs_path}', dataType: "script"}); }); diff --git a/app/views/refs/_tree_commit.html.haml b/app/views/refs/_tree_commit.html.haml new file mode 100644 index 00000000..1f2524a4 --- /dev/null +++ b/app/views/refs/_tree_commit.html.haml @@ -0,0 +1,3 @@ +- if tm + %strong= link_to "[#{tm.user_name}]", project_team_member_path(@project, tm) += link_to truncate(content_commit.safe_message, :length => tm ? 30 : 50), project_commit_path(@project, content_commit.id), :class => "tree-commit-link" diff --git a/app/views/refs/_tree_item.html.haml b/app/views/refs/_tree_item.html.haml index 45ea5371..30331944 100644 --- a/app/views/refs/_tree_item.html.haml +++ b/app/views/refs/_tree_item.html.haml @@ -1,23 +1,15 @@ - file = params[:path] ? File.join(params[:path], content.name) : content.name -- content_commit = @project.commits(@commit.id, file, 1).last -- return unless content_commit -%tr{ :class => "tree-item", :url => tree_file_project_ref_path(@project, @ref, file) } +%tr{ :class => "tree-item file_#{Digest::SHA1.hexdigest(content.name)}", :url => tree_file_project_ref_path(@project, @ref, file) } %td.tree-item-file-name - if content.is_a?(Grit::Blob) - if content.text? - = image_tag "file_txt.png" + = image_tag "file_txt.png", :class => "tree-ico" - elsif content.image? - = image_tag "file_img.png" + = image_tag "file_img.png", :class => "tree-ico" - else - = image_tag "file_bin.png" + = image_tag "file_bin.png", :class => "tree-ico" - else - = image_tag "file_dir.png" + = image_tag "file_dir.png", :class => "tree-ico" = link_to truncate(content.name, :length => 40), tree_file_project_ref_path(@project, @ref || @commit.id, file), :remote => :true %td.tree_time_ago.cgray - = time_ago_in_words(content_commit.committed_date) - ago %td.tree_commit - - tm = @project.team_member_by_name_or_email(content_commit.author_email, content_commit.author_name) - - if tm - %strong= link_to "[#{tm.user_name}]", project_team_member_path(@project, tm) - = link_to truncate(content_commit.safe_message, :length => tm ? 30 : 50), project_commit_path(@project, content_commit.id), :class => "tree-commit-link" diff --git a/app/views/refs/logs_tree.js.haml b/app/views/refs/logs_tree.js.haml new file mode 100644 index 00000000..ea833739 --- /dev/null +++ b/app/views/refs/logs_tree.js.haml @@ -0,0 +1,9 @@ +- @logs.each do |content_data| + - file_name = content_data[:file_name] + - content_commit = content_data[:commit] + - tm = @project.team_member_by_name_or_email(content_commit.author_email, content_commit.author_name) + + :plain + var row = $("table.table_#{@hex_path} tr.file_#{Digest::SHA1.hexdigest(file_name)}"); + row.find("td.tree_time_ago").html('#{escape_javascript(time_ago_in_words(content_commit.committed_date))} ago'); + row.find("td.tree_commit").html('#{escape_javascript(render("tree_commit", :tm => tm, :content_commit => content_commit))}'); diff --git a/app/views/refs/tree.js.haml b/app/views/refs/tree.js.haml index f4f28adc..600ba626 100644 --- a/app/views/refs/tree.js.haml +++ b/app/views/refs/tree.js.haml @@ -2,3 +2,8 @@ $("#tree-holder").html("#{escape_javascript(render(:partial => "tree", :locals => {:repo => @repo, :commit => @commit, :tree => @tree}))}"); $("#tree-content-holder").show("slide", { direction: "right" }, 150); $('.project-refs-form #path').val("#{params[:path]}"); + + + $('#tree-slider').waitForImages(function() { + $.ajax({type: "GET", url: '#{@logs_path}', dataType: "script"}); + }); diff --git a/config/routes.rb b/config/routes.rb index 3b33ed8d..73b9f643 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -117,6 +117,8 @@ Gitlab::Application.routes.draw do member do get "tree", :constraints => { :id => /[a-zA-Z.\/0-9_\-]+/ } + get "logs_tree", :constraints => { :id => /[a-zA-Z.\/0-9_\-]+/ } + get "blob", :constraints => { :id => /[a-zA-Z.0-9\/_\-]+/, @@ -132,6 +134,14 @@ Gitlab::Application.routes.draw do :path => /.*/ } + # tree viewer + get "logs_tree/:path" => "refs#logs_tree", + :as => :logs_file, + :constraints => { + :id => /[a-zA-Z.0-9\/_\-]+/, + :path => /.*/ + } + # blame get "blame/:path" => "refs#blame", :as => :blame_file, diff --git a/vendor/assets/javascripts/jquery.waitforimages.js b/vendor/assets/javascripts/jquery.waitforimages.js new file mode 100644 index 00000000..95b39c2e --- /dev/null +++ b/vendor/assets/javascripts/jquery.waitforimages.js @@ -0,0 +1,144 @@ +/* + * waitForImages 1.4 + * ----------------- + * Provides a callback when all images have loaded in your given selector. + * http://www.alexanderdickson.com/ + * + * + * Copyright (c) 2011 Alex Dickson + * Licensed under the MIT licenses. + * See website for more info. + * + */ + +;(function($) { + // Namespace all events. + var eventNamespace = 'waitForImages'; + + // CSS properties which contain references to images. + $.waitForImages = { + hasImageProperties: [ + 'backgroundImage', + 'listStyleImage', + 'borderImage', + 'borderCornerImage' + ] + }; + + // Custom selector to find `img` elements that have a valid `src` attribute and have not already loaded. + $.expr[':'].uncached = function(obj) { + // Ensure we are dealing with an `img` element with a valid `src` attribute. + if ( ! $(obj).is('img[src!=""]')) { + return false; + } + + // Firefox's `complete` property will always be`true` even if the image has not been downloaded. + // Doing it this way works in Firefox. + var img = document.createElement('img'); + img.src = obj.src; + return ! img.complete; + }; + + $.fn.waitForImages = function(finishedCallback, eachCallback, waitForAll) { + + // Handle options object. + if ($.isPlainObject(arguments[0])) { + eachCallback = finishedCallback.each; + waitForAll = finishedCallback.waitForAll; + finishedCallback = finishedCallback.finished; + } + + // Handle missing callbacks. + finishedCallback = finishedCallback || $.noop; + eachCallback = eachCallback || $.noop; + + // Convert waitForAll to Boolean + waitForAll = !! waitForAll; + + // Ensure callbacks are functions. + if (!$.isFunction(finishedCallback) || !$.isFunction(eachCallback)) { + throw new TypeError('An invalid callback was supplied.'); + }; + + return this.each(function() { + // Build a list of all imgs, dependent on what images will be considered. + var obj = $(this), + allImgs = []; + + if (waitForAll) { + // CSS properties which may contain an image. + var hasImgProperties = $.waitForImages.hasImageProperties || [], + matchUrl = /url\((['"]?)(.*?)\1\)/g; + + // Get all elements, as any one of them could have a background image. + obj.find('*').each(function() { + var element = $(this); + + // If an `img` element, add it. But keep iterating in case it has a background image too. + if (element.is('img:uncached')) { + allImgs.push({ + src: element.attr('src'), + element: element[0] + }); + } + + $.each(hasImgProperties, function(i, property) { + var propertyValue = element.css(property); + // If it doesn't contain this property, skip. + if ( ! propertyValue) { + return true; + } + + // Get all url() of this element. + var match; + while (match = matchUrl.exec(propertyValue)) { + allImgs.push({ + src: match[2], + element: element[0] + }); + }; + }); + }); + } else { + // For images only, the task is simpler. + obj + .find('img:uncached') + .each(function() { + allImgs.push({ + src: this.src, + element: this + }); + }); + }; + + var allImgsLength = allImgs.length, + allImgsLoaded = 0; + + // If no images found, don't bother. + if (allImgsLength == 0) { + finishedCallback.call(obj[0]); + }; + + $.each(allImgs, function(i, img) { + + var image = new Image; + + // Handle the image loading and error with the same callback. + $(image).bind('load.' + eventNamespace + ' error.' + eventNamespace, function(event) { + allImgsLoaded++; + + // If an error occurred with loading the image, set the third argument accordingly. + eachCallback.call(img.element, allImgsLoaded, allImgsLength, event.type == 'load'); + + if (allImgsLoaded == allImgsLength) { + finishedCallback.call(obj[0]); + return false; + }; + + }); + + image.src = img.src; + }); + }); + }; +})(jQuery); From f88a2617e65ecc037d3f885965efcee39745163d Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 10 Jul 2012 23:12:38 +0300 Subject: [PATCH 19/48] Tree ajax log. progress bar & refactoring --- app/assets/images/ajax_loader_tree.gif | Bin 0 -> 6531 bytes app/assets/javascripts/application.js | 5 +++++ app/helpers/application_helper.rb | 4 ++++ app/helpers/tree_helper.rb | 27 +++++++++++++++++++++++++ app/views/refs/_tree.html.haml | 10 +++++---- app/views/refs/_tree_item.html.haml | 18 +++++++---------- app/views/refs/logs_tree.js.haml | 2 +- app/views/refs/tree.js.haml | 5 +++-- 8 files changed, 53 insertions(+), 18 deletions(-) create mode 100644 app/assets/images/ajax_loader_tree.gif create mode 100644 app/helpers/tree_helper.rb diff --git a/app/assets/images/ajax_loader_tree.gif b/app/assets/images/ajax_loader_tree.gif new file mode 100644 index 0000000000000000000000000000000000000000..99d5a0f37f3e0fda2d0564e5977e3afac9bc08eb GIT binary patch literal 6531 zcmdUzYgAKrx`%VW?cB4I?49gf_D({rby~D6ms*z#6F@`@p?VzkP*95&9Y#@+0gXr? z35bb-5Gl9H-LW0pqC+k1QM70kDb*3_8S8R-TBX(L>CCAut841Wm|A9=U$)x-Z~6d+zMlfBiatTYghRQ}%}J z6TK%!z8o1I8oqb;UT}Y~<4{NU@$QiuBZC(QN53ESmHKiw=VoMPbRX-!{^|8#WpHp{ zaP-^J{DSi$k4L_g!fhQ8cpEKyci~eu21d;fNd)l42c_r?O_py29JeQxk5gQ%&a@mderMk$i zfBAvfk@Weky!nIfq)j_&?e%d(Tc4Qwd)3Itzm&?c!ac4O89Puya>e`BkyyB@oD?b! z)anTTz7(ra->@n}x4M~ha^$Ki@2_oZill2_cd1nhp5<`9+Fqm+DTu^dcQ zORR{>I$$`s@Zo=^|31uXG)xEr;%GY91aUAyL~ElE1b~G^0H$ZB=fry_AP!&)ab&N{ zhIHs4_6H#a$O7_$ESMA^P)Gstf;b>4w_fCYvC$Xcc zTYqDc$#h~KcJ4?(!xO8-CZ49(IT`EaE0)u}|MWWamx_9|T6@hNy}?K!0k93+m%*+*d!zOD&z%0H;AggC8h{rd{j-@=TS- zMFX@U1?cg^uA!et0sTD2MFX@UP#{0Wv>Sl-oa$J?L05Z1owV8MZ*Rrd#=bgmGrpuj zSFvzWS79m|T5^8wy*UfkzqPTCg#}6@iSkYswcWPZk&F&wnSpGHxLt}NsZD~(Y% zLSbZmTBGP-vop+-baYY@qpvkr1-5Wml(Ic*K)#zSjAS&dyN;| z1fNKKJKX0%G?zZPmNpsg`{f3yKPWfU5y}l@fjAg~DK|75D35m$}$m_S&5bgU1}kpheR`t6sm7@ z%P3sl3PD&~e(QWqZmnshWCD)yjrMN6U~BX>r_t8a*j{5id93ZUOQud8$a;<1C3~;? z>T8!bT)A?2GT0Ayo0*_M8gP!+8?Zscf7Wd}pUH~N3{faHgf`x7Xf@P(%6SVSr@`7P zE75*#nzbHG(%*UWo8{AA5jbq#S}ek%`U-W6h{$%VbjHA^LnG^~GOc+bVMxyDHK_A; zC2$F&#K8Bt5d{w;%Yxeo8Ala$+bkSwLzUf zqA1s?hMuyX)9>`9oq9L(+t^!?W))lY_ zkY?V%&o&mY2t7;z_9hxOqirZR-g>AG#_@&mRb1Ev&o-S!K zaFB%~#O$~O?2bFbd2zYVRF)QI2l#;`x!)6$#o>TLkS9!h1K;nq>j_HY3uP$;RPaEW z*}^w9rp1U&>szPAU}&__rk6Q>yIrX6I^x%`tkWDvTa6uACmUs`-F33p(>36{@Xp{z z8!kAGUkY9RBc$e(KQ~8ansHO+@w@`%iLxwrsr*`bSe=`dBkne)n%eWf@pHF zn_JsR9NZyM2_!^yL@HO4zpGPnh5D?XN{i;Wv*plE4hwgGa4ez4{88`V(ZR~D4_f}7 zH|Qcx*u3dNkBzf&X+V{sl)#_1@0h$Nj>U0L(+VG3zL_dBJ0H+yN(_Yt&rCUUK@_~v z^EXk9nddBP1^{QQR>E4n8jX#Fw)Z4wOh33aE`4R5A1!6i?8u$HHwDyhzh)ICdw@73>$6+sG7Z$#; zKP*)qbB6wH3#!!-?)Mv991}}Wp74JC6{{@&?V;!&CN;-|8wFOqi8l)E)}dkOD4>UC z!;5D`0^J2<;BipPk0JrY-;dIffRz7AUp4@mM*QXxcoI|2cf8hMj%xSSY4dp|f7^FC z09tzMPV)R2oZ$JFylTWB++Md>IlX#jy8Zhkt$eWS&JR7Us!;sOQ!XQ0zRt3#*r}76 zb_ddALiWC(ODnC8PzcxeiVGoYBXVU+K*lxX`vWGywvNp*y&9+T3xaavHqTq>a*@Hk zqcJ2=qr&bzXMAkEk!9p{p3#W(<^k8=2MuL}QbQAg5iN#uXXy&pMu9Bw1 z8M3$1Y38MeT|(>rurl(SsMN|X4s+!SM_u7TRc2b#9!%R=@WyJCZK9sfATl>Aa2;|Y z%O)48G>F(F(wYw|aSKZ?5DWYM(dqJFyJJ>6;LR-4<6{u8(fh}P1&`4*kOus;sV6LW z^otm~GAB+yNaJyA8}xQp6CcUz7x|MT&#yg$&GRR|^z5G(&zGdN&K%C4HCz0L_TPQ6 zVeejkAZNjImWG5+{wF&@6r}2(9?dr=d3Kl#-qKW+C{XT1Y`+fLRpCX|c5Sp)ii&nt zV-~A@pQlmye0sS^*|uJ*-Yga2qBH|brrM?ESSbB2r&g{bEH<`~OW;VpS}W1W<&@CS zl{#siANn*x3;Lm><-VYI(t>7OF>&K~#G?h?2LPYiMhgza*W~pm@enewt{>G^wg=yN1uSjrFA?Xy!^aM<21#K$_Xrm{Kz*3$xozY&5e@Kmv5Be|oh*so_m9Ckx%{ zv8#m{h*M9x8c1Ko@NlR#QbB0LACj}K9Zre7@N#t8lX-Pl|5C-h+Yr35-qqT(=}Jm! zpe*o|v2^L57IHBZAksxd36DmBg)@Va7*sL&wC#Jz}y+NcgG9bA$vZBBW#K0}!S^gqp^QU!YM zSK{FyP)4=YOUV2m&|icu-2SG2aML?0HvVnNdh}#A*&fRdUHPgnW@hO#h_&R*n==wd zJuWN9vcv08abyCGS4v`91?lA*tQvxwlciA`LIyFrfMd40>=u1!wG>fCT^crF0 zA~y0yiW)~{Y%Nb@H|eqLgKVM9W-^nfCav;6xCWUs1@;Z5$IMpuurZk4-@j4dd>LEp zK<2dJAKWO9Z?}UPSd-TeRi+^m3}j*q@*H&QvDF(QHJGpc+%mp(?y^@6$kkt%aAI`Y zP3OQSXZ~@g3&&UI^|KhvJiwfuv}Wnk%`IPehxSfyJ|m95 z@#2a^ewuCB?3bU2CjR$VLF~wPt(WuquV~rDDM@B+=@K%#`~}>|EiA(Me4fElj+<~? zFU&2Hip7gDQdDfx@m&f%QI(~%h_e(bxvokII#za37PV2X!7P~O+>7g%5p1@Wz}lBQ znlI@22M5YeKib`&e`5#A!)^Wv>K5c4frWLE7h zp$xYD&P7sru`fx({nx$7Hq@@I`xQzg#*lKIkKk`}TI|NI46B&G1kssP>N "refs/tree_item", :locals => { :content => content } + = render :partial => "refs/tree_item", :locals => { :content => content, :index => (index += 1) } - contents.select{ |i| i.is_a?(Grit::Blob)}.each do |content| - = render :partial => "refs/tree_item", :locals => { :content => content } + = render :partial => "refs/tree_item", :locals => { :content => content, :index => (index += 1) } - contents.select{ |i| i.is_a?(Grit::Submodule)}.each do |content| - = render :partial => "refs/submodule_item", :locals => { :content => content } + = render :partial => "refs/submodule_item", :locals => { :content => content, :index => (index += 1) } - if content = contents.select{ |c| c.is_a?(Grit::Blob) and c.name =~ /^readme/i }.first .file_holder#README @@ -58,8 +59,9 @@ }); + // Load last commit log for each file in tree $(window).load(function(){ - $.ajax({type: "GET", url: '#{@logs_path}', dataType: "script"}); + ajaxGet('#{@logs_path}'); }); diff --git a/app/views/refs/_tree_item.html.haml b/app/views/refs/_tree_item.html.haml index 30331944..4ce16b22 100644 --- a/app/views/refs/_tree_item.html.haml +++ b/app/views/refs/_tree_item.html.haml @@ -1,15 +1,11 @@ -- file = params[:path] ? File.join(params[:path], content.name) : content.name -%tr{ :class => "tree-item file_#{Digest::SHA1.hexdigest(content.name)}", :url => tree_file_project_ref_path(@project, @ref, file) } +- file = tree_full_path(content) +%tr{ :class => "tree-item #{tree_hex_class(content)}", :url => tree_file_project_ref_path(@project, @ref, file) } %td.tree-item-file-name - - if content.is_a?(Grit::Blob) - - if content.text? - = image_tag "file_txt.png", :class => "tree-ico" - - elsif content.image? - = image_tag "file_img.png", :class => "tree-ico" - - else - = image_tag "file_bin.png", :class => "tree-ico" - - else - = image_tag "file_dir.png", :class => "tree-ico" + = tree_icon(content) = link_to truncate(content.name, :length => 40), tree_file_project_ref_path(@project, @ref || @commit.id, file), :remote => :true %td.tree_time_ago.cgray + - if index == 1 + %span.log_loading + Loading commit data.. + = image_tag "ajax_loader_tree.gif", :width => 14 %td.tree_commit diff --git a/app/views/refs/logs_tree.js.haml b/app/views/refs/logs_tree.js.haml index ea833739..402f5aa7 100644 --- a/app/views/refs/logs_tree.js.haml +++ b/app/views/refs/logs_tree.js.haml @@ -4,6 +4,6 @@ - tm = @project.team_member_by_name_or_email(content_commit.author_email, content_commit.author_name) :plain - var row = $("table.table_#{@hex_path} tr.file_#{Digest::SHA1.hexdigest(file_name)}"); + var row = $("table.table_#{@hex_path} tr.file_#{hexdigest(file_name)}"); row.find("td.tree_time_ago").html('#{escape_javascript(time_ago_in_words(content_commit.committed_date))} ago'); row.find("td.tree_commit").html('#{escape_javascript(render("tree_commit", :tm => tm, :content_commit => content_commit))}'); diff --git a/app/views/refs/tree.js.haml b/app/views/refs/tree.js.haml index 600ba626..9cf55057 100644 --- a/app/views/refs/tree.js.haml +++ b/app/views/refs/tree.js.haml @@ -1,9 +1,10 @@ :plain + // Load Files list $("#tree-holder").html("#{escape_javascript(render(:partial => "tree", :locals => {:repo => @repo, :commit => @commit, :tree => @tree}))}"); $("#tree-content-holder").show("slide", { direction: "right" }, 150); $('.project-refs-form #path').val("#{params[:path]}"); - + // Load last commit log for each file in tree $('#tree-slider').waitForImages(function() { - $.ajax({type: "GET", url: '#{@logs_path}', dataType: "script"}); + ajaxGet('#{@logs_path}'); }); From a46a172ad99d5b3aba7de7bd49fb0d48f5fd7b65 Mon Sep 17 00:00:00 2001 From: m16a1 Date: Fri, 13 Jul 2012 17:10:30 +0400 Subject: [PATCH 20/48] Update master --- spec/api/projects_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/api/projects_spec.rb b/spec/api/projects_spec.rb index a4e875f7..9998ee50 100644 --- a/spec/api/projects_spec.rb +++ b/spec/api/projects_spec.rb @@ -78,7 +78,7 @@ describe Gitlab::API do end describe "DELETE /projects/:id/snippets/:snippet_id" do - it "should create a new project snippet" do + it "should delete existing project snippet" do expect { delete "#{api_prefix}/projects/#{project.code}/snippets/#{snippet.id}?private_token=#{user.private_token}" }.should change { Snippet.count }.by(-1) From 491d674a969d05d8624676b0aa97ee02d197772d Mon Sep 17 00:00:00 2001 From: rjmooney Date: Fri, 13 Jul 2012 10:26:52 -0700 Subject: [PATCH 21/48] Clarify the nginx configuration. --- doc/installation.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/doc/installation.md b/doc/installation.md index bf579b17..c4823d35 100644 --- a/doc/installation.md +++ b/doc/installation.md @@ -216,15 +216,15 @@ Application can be started with next command: sudo -u gitlab cp config/unicorn.rb.orig config/unicorn.rb sudo -u gitlab bundle exec unicorn_rails -c config/unicorn.rb -E production -D -Edit /etc/nginx/nginx.conf. Add in **http** section: +Edit /etc/nginx/nginx.conf. In the *http* section add: upstream gitlab { server unix:/home/gitlab/gitlab/tmp/sockets/gitlab.socket; } server { - listen YOUR_SERVER_IP:80; - server_name gitlab.YOUR_DOMAIN.com; + listen YOUR_SERVER_IP:80; # e.g., listen 192.168.1.1:80; + server_name YOUR_SERVER_FQDN; # e.g., server_name source.example.com; root /home/gitlab/gitlab/public; # individual nginx logs for this gitlab vhost @@ -232,26 +232,26 @@ Edit /etc/nginx/nginx.conf. Add in **http** section: error_log /var/log/nginx/gitlab_error.log; location / { - # serve static files from defined root folder;. - # @gitlab is a named location for the upstream fallback, see below - try_files $uri $uri/index.html $uri.html @gitlab; + # serve static files from defined root folder;. + # @gitlab is a named location for the upstream fallback, see below + try_files $uri $uri/index.html $uri.html @gitlab; } # if a file, which is not found in the root folder is requested, # then the proxy pass the request to the upsteam (gitlab unicorn) location @gitlab { proxy_redirect off; + # you need to change this to "https", if you set "ssl" directive to "on" proxy_set_header X-FORWARDED_PROTO http; - proxy_set_header Host gitlab.YOUR_SUBDOMAIN.com:80; + proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_pass http://gitlab; } - } -gitlab.YOUR_DOMAIN.com - change to your domain. +Change **YOUR_SERVER_IP** and **YOUR_SERVER_FQDN** to the IP address and fully-qualified domain name of the host serving GitLab. Restart nginx: From f2da12e9608bab186f673397530be9832da8cc00 Mon Sep 17 00:00:00 2001 From: Pavel Savchenko Date: Sat, 14 Jul 2012 10:09:26 +0300 Subject: [PATCH 22/48] Add step to make sure tmp directory exists in /home/gitlab/gitlab/ (since the step Setup DB fails if it doesn't. see issue #1038 for more detials. --- doc/installation.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/installation.md b/doc/installation.md index bf579b17..ca570913 100644 --- a/doc/installation.md +++ b/doc/installation.md @@ -139,6 +139,8 @@ Permissions: cd /home/gitlab sudo -H -u gitlab git clone -b stable git://github.com/gitlabhq/gitlabhq.git gitlab cd gitlab + + sudo -u gitlab mkdir tmp # Rename config files sudo -u gitlab cp config/gitlab.yml.example config/gitlab.yml From f003abf729570bb3b55a27b18e59c58c8698f5b8 Mon Sep 17 00:00:00 2001 From: Hans Fase Date: Sun, 15 Jul 2012 17:17:46 -0300 Subject: [PATCH 23/48] libreadline-gplv2-dev >> libreadline6-dev --- doc/installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/installation.md b/doc/installation.md index f15eff88..73f6f4c2 100644 --- a/doc/installation.md +++ b/doc/installation.md @@ -60,7 +60,7 @@ Also read the [Read this before you submit an issue](https://github.com/gitlabhq sudo apt-get update sudo apt-get upgrade - sudo apt-get install -y wget curl gcc checkinstall libxml2-dev libxslt-dev sqlite3 libsqlite3-dev libcurl4-openssl-dev libreadline-gplv2-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 sendmail + sudo apt-get install -y wget curl gcc checkinstall libxml2-dev libxslt-dev sqlite3 libsqlite3-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 sendmail # If you want to use MySQL: sudo apt-get install -y mysql-server mysql-client libmysqlclient-dev From 2a705c4f40a32b1313952a212e92a5d8c5407fc6 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 16 Jul 2012 08:03:10 +0300 Subject: [PATCH 24/48] Admin logs improved. Commits list box styled --- app/assets/stylesheets/gitlab_bootstrap.scss | 47 +++++++++++++++---- app/views/admin/logs/show.html.haml | 15 +++--- app/views/commits/_commits.html.haml | 4 +- app/views/commits/_head.html.haml | 4 +- app/views/commits/compare.html.haml | 2 +- .../merge_requests/show/_commits.html.haml | 4 +- lib/gitlab/logger.rb | 2 +- 7 files changed, 58 insertions(+), 20 deletions(-) diff --git a/app/assets/stylesheets/gitlab_bootstrap.scss b/app/assets/stylesheets/gitlab_bootstrap.scss index e2d63906..8197590f 100644 --- a/app/assets/stylesheets/gitlab_bootstrap.scss +++ b/app/assets/stylesheets/gitlab_bootstrap.scss @@ -202,6 +202,10 @@ a:focus { color:$style_color; } +.nav-tabs > .active > a { + font-weight:bold; +} + /** COLORS **/ .cgray { color:gray; } .cred { color:#D12F19; } @@ -443,6 +447,13 @@ form { background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf); background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf); + &.small { + line-height: 28px; + font-size: 14px; + line-height:28px; + text-shadow: 0 1px 1px white; + } + form { padding:9px 0; margin:0px; @@ -628,13 +639,6 @@ h3.page_title { line-height: 28px; } -pre.logs { - .log { - font-size:12px; - line-height:18px; - } -} - /** * File content holder * @@ -676,10 +680,10 @@ pre.logs { } .file_content { background:#fff; - color:#514721; font-size: 11px; &.wiki { + font-size: 13px; code { padding:0 4px; } @@ -730,6 +734,33 @@ pre.logs { } } + &.logs { + background:#eee; + max-height: 700px; + overflow-y: auto; + + ol { + margin-left:40px; + padding: 10px 0; + border-left: 1px solid #CCC; + margin-bottom:0; + background: white; + li { + color:#888; + p { + margin:0; + color:#333; + line-height:24px; + padding-left: 10px; + } + + &:hover { + background:$hover; + } + } + } + } + /** * Code file */ diff --git a/app/views/admin/logs/show.html.haml b/app/views/admin/logs/show.html.haml index 7963e18a..800d3bb2 100644 --- a/app/views/admin/logs/show.html.haml +++ b/app/views/admin/logs/show.html.haml @@ -1,6 +1,9 @@ -%h4 - %i.icon-file - githost.log -%pre.logs - - Gitlab::Logger.read_latest.each do |line| - %span.log= line +.file_holder#README + .file_title + %i.icon-file + githost.log + .file_content.logs + %ol + - Gitlab::Logger.read_latest.each do |line| + %li + %p= line diff --git a/app/views/commits/_commits.html.haml b/app/views/commits/_commits.html.haml index c2c9ca62..c3c7d49c 100644 --- a/app/views/commits/_commits.html.haml +++ b/app/views/commits/_commits.html.haml @@ -1,4 +1,6 @@ - @commits.group_by { |c| c.committed_date.to_date }.each do |day, commits| %div.ui-box - %h5= day.stamp("28 Aug, 2010") + %h5.small + %i.icon-calendar + = day.stamp("28 Aug, 2010") %ul.unstyled= render commits diff --git a/app/views/commits/_head.html.haml b/app/views/commits/_head.html.haml index 8e9195c5..453ca4ea 100644 --- a/app/views/commits/_head.html.haml +++ b/app/views/commits/_head.html.haml @@ -13,12 +13,12 @@ %li{:class => "#{branches_tab_class}"} = link_to project_repository_path(@project) do Branches - %span.number= @project.repo.branch_count + %span.badge= @project.repo.branch_count %li{:class => "#{'active' if current_page?(tags_project_repository_path(@project)) }"} = link_to tags_project_repository_path(@project) do Tags - %span.number= @project.repo.tag_count + %span.badge= @project.repo.tag_count - if current_page?(project_commits_path(@project)) && current_user.private_token diff --git a/app/views/commits/compare.html.haml b/app/views/commits/compare.html.haml index c0226329..66ed8dad 100644 --- a/app/views/commits/compare.html.haml +++ b/app/views/commits/compare.html.haml @@ -20,7 +20,7 @@ = "..." = text_field_tag :to, params[:to], :placeholder => "aa8b4ef", :class => "xlarge" .actions - = submit_tag "Compare", :class => "btn primary" + = submit_tag "Compare", :class => "btn btn-primary" - unless @commits.empty? diff --git a/app/views/merge_requests/show/_commits.html.haml b/app/views/merge_requests/show/_commits.html.haml index 78fe1a06..d10e8fd5 100644 --- a/app/views/merge_requests/show/_commits.html.haml +++ b/app/views/merge_requests/show/_commits.html.haml @@ -1,6 +1,8 @@ - if @commits.present? .ui-box - %h5 Commits (#{@commits.count}) + %h5 + %i.icon-list + Commits (#{@commits.count}) .merge-request-commits - if @commits.count > 8 %ul.first_mr_commits.unstyled diff --git a/lib/gitlab/logger.rb b/lib/gitlab/logger.rb index e8d20ad9..5249fdbc 100644 --- a/lib/gitlab/logger.rb +++ b/lib/gitlab/logger.rb @@ -8,7 +8,7 @@ module Gitlab def self.read_latest path = Rails.root.join("log/githost.log") - logs = `tail -n 50 #{path}`.split("\n") + logs = File.read(path).split("\n") end end end From 990b9217d9a55e26a53d4143d4a3c89123384327 Mon Sep 17 00:00:00 2001 From: moregeek Date: Mon, 16 Jul 2012 14:01:32 +0200 Subject: [PATCH 25/48] show flash notice after deletion of a project --- app/controllers/admin/projects_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/admin/projects_controller.rb b/app/controllers/admin/projects_controller.rb index 5266b406..c0508428 100644 --- a/app/controllers/admin/projects_controller.rb +++ b/app/controllers/admin/projects_controller.rb @@ -72,6 +72,6 @@ class Admin::ProjectsController < ApplicationController @admin_project = Project.find_by_code(params[:id]) @admin_project.destroy - redirect_to admin_projects_url + redirect_to admin_projects_url, notice: 'Project was successfully deleted.' end end From f322975c506966e080e58dd3eb0c38b22183415a Mon Sep 17 00:00:00 2001 From: Pat Thoyts Date: Mon, 16 Jul 2012 23:31:28 +0100 Subject: [PATCH 26/48] Improve handling of misconfigured LDAP accounts. Gitlab requires an email address for all user accounts as this is the default account id and is used for sending notifications. LDAP accounts may be missing email fields so handle this by showing a sensible error message before redirecting to the login screen again. Resolves github issue #899 Signed-off-by: Pat Thoyts --- app/controllers/omniauth_callbacks_controller.rb | 13 +++++++++++++ app/models/user.rb | 3 ++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb index 629b6819..fb759c37 100644 --- a/app/controllers/omniauth_callbacks_controller.rb +++ b/app/controllers/omniauth_callbacks_controller.rb @@ -1,4 +1,17 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController + + # Extend the standard message generation to accept our custom exception + def failure_message + exception = env["omniauth.error"] + if exception.class == OmniAuth::Error + error = exception.message + else + error = exception.error_reason if exception.respond_to?(:error_reason) + error ||= exception.error if exception.respond_to?(:error) + error ||= env["omniauth.error.type"].to_s + end + error.to_s.humanize if error + end def ldap # We only find ourselves here if the authentication to LDAP was successful. diff --git a/app/models/user.rb b/app/models/user.rb index b87e149c..a3e08fa7 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -80,7 +80,8 @@ class User < ActiveRecord::Base def self.find_for_ldap_auth(omniauth_info) name = omniauth_info.name.force_encoding("utf-8") - email = omniauth_info.email.downcase + email = omniauth_info.email.downcase unless omniauth_info.email.nil? + raise OmniAuth::Error, "LDAP accounts must provide an email address" if email.nil? if @user = User.find_by_email(email) @user From 80f2ef2d028b9ba2d18df47ae248caec8a7bfce3 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 17 Jul 2012 08:17:16 +0300 Subject: [PATCH 27/48] MR index pollished --- app/assets/stylesheets/gitlab_bootstrap.scss | 2 +- app/assets/stylesheets/sections/merge_requests.scss | 12 ++++++++++++ app/views/merge_requests/_merge_request.html.haml | 10 ++++++---- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/app/assets/stylesheets/gitlab_bootstrap.scss b/app/assets/stylesheets/gitlab_bootstrap.scss index 8197590f..119180c5 100644 --- a/app/assets/stylesheets/gitlab_bootstrap.scss +++ b/app/assets/stylesheets/gitlab_bootstrap.scss @@ -429,7 +429,7 @@ form { */ .ui-box { background:#F9F9F9; - margin-bottom: 40px; + margin-bottom: 25px; @include round-borders-all(4px); border-color: #CCC; diff --git a/app/assets/stylesheets/sections/merge_requests.scss b/app/assets/stylesheets/sections/merge_requests.scss index ad496238..34f43acf 100644 --- a/app/assets/stylesheets/sections/merge_requests.scss +++ b/app/assets/stylesheets/sections/merge_requests.scss @@ -82,3 +82,15 @@ } } } + +li.merge_request { + padding:7px 10px; + img.avatar { + width: 32px; + margin-top: 4px; + } + p { + padding: 0px; + padding-bottom: 2px; + } +} diff --git a/app/views/merge_requests/_merge_request.html.haml b/app/views/merge_requests/_merge_request.html.haml index d9634a4f..b9a005e0 100644 --- a/app/views/merge_requests/_merge_request.html.haml +++ b/app/views/merge_requests/_merge_request.html.haml @@ -15,12 +15,14 @@ → = merge_request.target_branch = image_tag gravatar_icon(merge_request.author_email), :class => "avatar" + + = link_to project_merge_request_path(merge_request.project, merge_request) do + %p.row_title= truncate(merge_request.title, :length => 80) + %span.update-author - %strong= merge_request.author_name - authored + %small.cdark= "##{merge_request.id}" + authored by #{merge_request.author_name} = time_ago_in_words(merge_request.created_at) ago - if merge_request.upvotes > 0 %span.badge.badge-success= "+#{merge_request.upvotes}" - = link_to project_merge_request_path(merge_request.project, merge_request) do - %p.row_title= truncate(merge_request.title, :length => 80) From a3bb9ca1fa2e6c3e6a75d416e09cbdcb61fcd1e0 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 17 Jul 2012 08:19:16 +0300 Subject: [PATCH 28/48] Feature: Unassigned Merge Requests --- app/models/merge_request.rb | 2 +- app/observers/mailer_observer.rb | 2 +- app/views/merge_requests/_form.html.haml | 20 +++++++++++++------ app/views/merge_requests/edit.html.haml | 2 +- app/views/merge_requests/new.html.haml | 2 +- .../merge_requests/show/_mr_box.html.haml | 7 ++++--- spec/models/merge_request_spec.rb | 1 - 7 files changed, 22 insertions(+), 14 deletions(-) diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index d3e531f7..27b8e07d 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -22,7 +22,6 @@ class MergeRequest < ActiveRecord::Base :should_remove_source_branch validates_presence_of :project_id - validates_presence_of :assignee_id validates_presence_of :author_id validates_presence_of :source_branch validates_presence_of :target_branch @@ -36,6 +35,7 @@ class MergeRequest < ActiveRecord::Base delegate :name, :email, :to => :assignee, + :allow_nil => true, :prefix => true validates :title, diff --git a/app/observers/mailer_observer.rb b/app/observers/mailer_observer.rb index 880fd502..451deccd 100644 --- a/app/observers/mailer_observer.rb +++ b/app/observers/mailer_observer.rb @@ -43,7 +43,7 @@ class MailerObserver < ActiveRecord::Observer end def new_merge_request(merge_request) - if merge_request.assignee != current_user + if merge_request.assignee && merge_request.assignee != current_user Notify.new_merge_request_email(merge_request.id).deliver end end diff --git a/app/views/merge_requests/_form.html.haml b/app/views/merge_requests/_form.html.haml index d69faa14..4f20a06f 100644 --- a/app/views/merge_requests/_form.html.haml +++ b/app/views/merge_requests/_form.html.haml @@ -5,7 +5,8 @@ - @merge_request.errors.full_messages.each do |msg| %li= msg - %h3.padded.cgray 1. Select Branches + %h4.cdark 1. Select Branches + %br .row .span6 @@ -30,14 +31,21 @@ .bottom_commit .mr_target_commit - %h3.padded.cgray 2. Fill info + %h4.cdark 2. Fill info + .clearfix - = f.label :assignee_id, "Assign to", :class => "control-label" - .controls= f.select(:assignee_id, @project.users.all.collect {|p| [ p.name, p.id ] }, { :include_blank => "Select user" }, :style => "width:250px") + .main_box + .top_box_content + = f.label :title do + %strong= "Title *" + .input= f.text_field :title, :class => "input-xxlarge pad", :maxlength => 255, :rows => 5 + .middle_box_content + = 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" }, :style => "width:250px") .control-group - = f.label :title, :class => "control-label" - .controls= f.text_field :title, :class => "input-xxlarge pad", :maxlength => 255, :rows => 5 .form-actions = f.submit 'Save', :class => "btn-primary btn" diff --git a/app/views/merge_requests/edit.html.haml b/app/views/merge_requests/edit.html.haml index 9e4f9327..eee14899 100644 --- a/app/views/merge_requests/edit.html.haml +++ b/app/views/merge_requests/edit.html.haml @@ -1,4 +1,4 @@ -%h3 +%h3.page_title = "Edit merge request #{@merge_request.id}" %hr = render 'form' diff --git a/app/views/merge_requests/new.html.haml b/app/views/merge_requests/new.html.haml index efafa45d..59408999 100644 --- a/app/views/merge_requests/new.html.haml +++ b/app/views/merge_requests/new.html.haml @@ -1,3 +1,3 @@ -%h3 New Merge Request +%h3.page_title New Merge Request %hr = render 'form' diff --git a/app/views/merge_requests/show/_mr_box.html.haml b/app/views/merge_requests/show/_mr_box.html.haml index 3027719d..b542dac9 100644 --- a/app/views/merge_requests/show/_mr_box.html.haml +++ b/app/views/merge_requests/show/_mr_box.html.haml @@ -13,9 +13,10 @@ = image_tag gravatar_icon(@merge_request.author_email), :width => 16, :class => "lil_av" %strong.author= link_to_merge_request_author(@merge_request) - %cite.cgray and 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.assignee + %cite.cgray and 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.closed diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 8d750bef..ac986cce 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -13,7 +13,6 @@ describe MergeRequest do it { should validate_presence_of(:title) } it { should validate_presence_of(:author_id) } it { should validate_presence_of(:project_id) } - it { should validate_presence_of(:assignee_id) } end describe "Scope" do From 494cd02b38eb2bfbeeb69dec0688a7a1e7ccf50f Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 17 Jul 2012 08:21:16 +0300 Subject: [PATCH 29/48] Colored labels for events on dashboard --- app/assets/stylesheets/gitlab_bootstrap.scss | 17 ++++++++++++++--- app/views/events/_event_issue.html.haml | 4 ++-- app/views/events/_event_merge_request.html.haml | 4 ++-- app/views/events/_event_push.html.haml | 2 +- 4 files changed, 19 insertions(+), 8 deletions(-) diff --git a/app/assets/stylesheets/gitlab_bootstrap.scss b/app/assets/stylesheets/gitlab_bootstrap.scss index 119180c5..9feb2b81 100644 --- a/app/assets/stylesheets/gitlab_bootstrap.scss +++ b/app/assets/stylesheets/gitlab_bootstrap.scss @@ -303,9 +303,20 @@ table.no-borders { } .event_label { - background: #FCEEC1; - padding: 2px 2px 0; - font-family: monospace; + @extend .label; + background-color: #999; + + &.pushed { + background-color: #3A87AD; + } + + &.opened { + background-color: #468847; + } + + &.closed { + background-color: #B94A48; + } } img.avatar { diff --git a/app/views/events/_event_issue.html.haml b/app/views/events/_event_issue.html.haml index 13fb20cd..4293be82 100644 --- a/app/views/events/_event_issue.html.haml +++ b/app/views/events/_event_issue.html.haml @@ -1,7 +1,7 @@ = image_tag gravatar_icon(event.author_email), :class => "avatar" %strong #{event.author_name} -%span.event_label= event.action_name - issue +%span.event_label{:class => event.action_name}= event.action_name +issue = link_to project_issue_path(event.project, event.issue) do %strong= truncate event.issue_title at diff --git a/app/views/events/_event_merge_request.html.haml b/app/views/events/_event_merge_request.html.haml index e59a74b9..774921a7 100644 --- a/app/views/events/_event_merge_request.html.haml +++ b/app/views/events/_event_merge_request.html.haml @@ -2,8 +2,8 @@ .event_icon= image_tag "event_mr_merged.png" = image_tag gravatar_icon(event.author_email), :class => "avatar" %strong #{event.author_name} -%span.event_label= event.action_name - merge request +%span.event_label{:class => event.action_name}= event.action_name +merge request = link_to project_merge_request_path(event.project, event.merge_request) do %strong= truncate event.merge_request_title at diff --git a/app/views/events/_event_push.html.haml b/app/views/events/_event_push.html.haml index 3aadd226..59d8962b 100644 --- a/app/views/events/_event_push.html.haml +++ b/app/views/events/_event_push.html.haml @@ -2,7 +2,7 @@ .event_icon= image_tag "event_push.png" = image_tag gravatar_icon(event.author_email), :class => "avatar" %strong #{event.author_name} - %span.event_label= event.push_action_name + %span.event_label.pushed= event.push_action_name = event.ref_type = link_to project_commits_path(event.project, :ref => event.ref_name) do %strong= event.ref_name From 88033500232f12234a9546aa9b89111bcdbfecef Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 17 Jul 2012 08:23:16 +0300 Subject: [PATCH 30/48] CHANGELOG updated. Fixed MR bug. Logger improved --- CHANGELOG | 4 ++++ app/assets/stylesheets/gitlab_bootstrap.scss | 4 ++++ app/models/merge_request.rb | 2 +- lib/gitlab/logger.rb | 18 ++++++++++++++---- 4 files changed, 23 insertions(+), 5 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 17235760..fe243d65 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,9 @@ v 2.7.0 - Issue Labels + - Inline diff + - Git HTTP + - API + - UI improved v 2.6.0 - UI polished diff --git a/app/assets/stylesheets/gitlab_bootstrap.scss b/app/assets/stylesheets/gitlab_bootstrap.scss index 9feb2b81..c0a20762 100644 --- a/app/assets/stylesheets/gitlab_bootstrap.scss +++ b/app/assets/stylesheets/gitlab_bootstrap.scss @@ -317,6 +317,10 @@ table.no-borders { &.closed { background-color: #B94A48; } + + &.merged { + background-color: #2A2; + } } img.avatar { diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 27b8e07d..2581f3be 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -128,7 +128,7 @@ class MergeRequest < ActiveRecord::Base def unmerged_diffs commits = project.repo.commits_between(target_branch, source_branch).map {|c| Commit.new(c)} - diffs = project.repo.diff(commits.first.prev_commit.id, commits.last.id) + diffs = project.repo.diff(commits.first.prev_commit.id, commits.last.id) rescue [] end def last_commit diff --git a/lib/gitlab/logger.rb b/lib/gitlab/logger.rb index 5249fdbc..aff13baf 100644 --- a/lib/gitlab/logger.rb +++ b/lib/gitlab/logger.rb @@ -1,14 +1,24 @@ module Gitlab - class Logger + class Logger < ::Logger def self.error(message) - @@logger ||= ::Logger.new(File.join(Rails.root, "log/githost.log")) - message = Time.now.to_s(:long) + " -> " + message - @@logger.error(message) + build.error(message) + end + + def self.info(message) + build.info(message) end def self.read_latest path = Rails.root.join("log/githost.log") logs = File.read(path).split("\n") end + + def self.build + new(File.join(Rails.root, "log/githost.log")) + end + + def format_message(severity, timestamp, progname, msg) + "#{timestamp.to_s(:long)} -> #{severity} -> #{msg}\n" + end end end From 0456ceddb4d56e2090bfd03cf7e6c0bd7a2d8db9 Mon Sep 17 00:00:00 2001 From: Michael Richardson Date: Wed, 18 Jul 2012 11:59:32 +0100 Subject: [PATCH 31/48] Fixing MySQL syntax error --- lib/tasks/gitlab/backup.rake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tasks/gitlab/backup.rake b/lib/tasks/gitlab/backup.rake index 4fbde37e..014483d4 100644 --- a/lib/tasks/gitlab/backup.rake +++ b/lib/tasks/gitlab/backup.rake @@ -159,7 +159,7 @@ namespace :gitlab do 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| + 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" From 65dc68b35c0ad455336abf33def5d920166f7c83 Mon Sep 17 00:00:00 2001 From: Valeriy Sizov Date: Thu, 12 Jul 2012 15:36:33 +0300 Subject: [PATCH 32/48] Refactoring of hook functionality & bootsrap system hooks --- Gemfile | 2 +- app/controllers/hooks_controller.rb | 12 ++++++------ app/models/project.rb | 2 +- app/models/project_hook.rb | 3 +++ app/models/system_hook.rb | 3 +++ app/models/web_hook.rb | 2 -- app/roles/git_push.rb | 2 +- db/migrate/20120712080407_add_type_to_web_hook.rb | 5 +++++ db/schema.rb | 7 ++++--- 9 files changed, 24 insertions(+), 14 deletions(-) create mode 100644 app/models/project_hook.rb create mode 100644 app/models/system_hook.rb create mode 100644 db/migrate/20120712080407_add_type_to_web_hook.rb diff --git a/Gemfile b/Gemfile index c89d2443..76dc1856 100644 --- a/Gemfile +++ b/Gemfile @@ -71,7 +71,6 @@ group :development, :test do gem "awesome_print" gem "database_cleaner" gem "launchy" - gem "webmock" end group :test do @@ -82,4 +81,5 @@ group :test do gem "shoulda-matchers" gem 'email_spec' gem 'resque_spec' + gem "webmock" end diff --git a/app/controllers/hooks_controller.rb b/app/controllers/hooks_controller.rb index 9627aba9..ad2fb3ae 100644 --- a/app/controllers/hooks_controller.rb +++ b/app/controllers/hooks_controller.rb @@ -11,24 +11,24 @@ class HooksController < ApplicationController respond_to :html def index - @hooks = @project.web_hooks.all - @hook = WebHook.new + @hooks = @project.hooks.all + @hook = ProjectHook.new end def create - @hook = @project.web_hooks.new(params[:hook]) + @hook = @project.hooks.new(params[:hook]) @hook.save if @hook.valid? redirect_to project_hooks_path(@project) else - @hooks = @project.web_hooks.all + @hooks = @project.hooks.all render :index end end def test - @hook = @project.web_hooks.find(params[:id]) + @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) @@ -37,7 +37,7 @@ class HooksController < ApplicationController end def destroy - @hook = @project.web_hooks.find(params[:id]) + @hook = @project.hooks.find(params[:id]) @hook.destroy redirect_to project_hooks_path(@project) diff --git a/app/models/project.rb b/app/models/project.rb index ec4893e2..4773cf37 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -19,7 +19,7 @@ class Project < ActiveRecord::Base 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 :web_hooks, :dependent => :destroy + has_many :hooks, :dependent => :destroy, :class_name => "ProjectHook" has_many :wikis, :dependent => :destroy has_many :protected_branches, :dependent => :destroy diff --git a/app/models/project_hook.rb b/app/models/project_hook.rb new file mode 100644 index 00000000..06388aae --- /dev/null +++ b/app/models/project_hook.rb @@ -0,0 +1,3 @@ +class ProjectHook < WebHook + belongs_to :project +end diff --git a/app/models/system_hook.rb b/app/models/system_hook.rb new file mode 100644 index 00000000..178b7585 --- /dev/null +++ b/app/models/system_hook.rb @@ -0,0 +1,3 @@ +class SystemHook < WebHook + +end diff --git a/app/models/web_hook.rb b/app/models/web_hook.rb index 26288476..43b4f16b 100644 --- a/app/models/web_hook.rb +++ b/app/models/web_hook.rb @@ -4,8 +4,6 @@ class WebHook < ActiveRecord::Base # HTTParty timeout default_timeout 10 - belongs_to :project - validates :url, presence: true, format: { diff --git a/app/roles/git_push.rb b/app/roles/git_push.rb index b4c59472..d0267b59 100644 --- a/app/roles/git_push.rb +++ b/app/roles/git_push.rb @@ -35,7 +35,7 @@ module GitPush data = post_receive_data(oldrev, newrev, ref, user) - web_hooks.each { |web_hook| web_hook.execute(data) } + hooks.each { |web_hook| web_hook.execute(data) } end def post_receive_data(oldrev, newrev, ref, user) diff --git a/db/migrate/20120712080407_add_type_to_web_hook.rb b/db/migrate/20120712080407_add_type_to_web_hook.rb new file mode 100644 index 00000000..18ab024c --- /dev/null +++ b/db/migrate/20120712080407_add_type_to_web_hook.rb @@ -0,0 +1,5 @@ +class AddTypeToWebHook < ActiveRecord::Migration + def change + add_column :web_hooks, :type, :string, :default => "ProjectHook" + end +end diff --git a/db/schema.rb b/db/schema.rb index f40ee260..c4c54f56 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 => 20120706065612) do +ActiveRecord::Schema.define(:version => 20120712080407) do create_table "events", :force => true do |t| t.string "target_type" @@ -187,8 +187,9 @@ ActiveRecord::Schema.define(:version => 20120706065612) do create_table "web_hooks", :force => true do |t| t.string "url" 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.string "type", :default => "ProjectHook" end create_table "wikis", :force => true do |t| From c38578428b19b6375f24266ea5f4a98ca290eb58 Mon Sep 17 00:00:00 2001 From: Valeriy Sizov Date: Thu, 12 Jul 2012 22:10:34 +0300 Subject: [PATCH 33/48] System Hooks: CRUD has done --- app/controllers/admin/hooks_controller.rb | 39 +++++++++++++++++++ app/controllers/admin/mailer_controller.rb | 45 ---------------------- app/models/web_hook.rb | 2 - app/views/admin/hooks/_data_ex.html.erb | 45 ++++++++++++++++++++++ app/views/admin/hooks/index.html.haml | 39 +++++++++++++++++++ app/views/admin/mailer/preview.html.haml | 28 -------------- app/views/help/system_hooks.html.haml | 13 +++++++ app/views/layouts/admin.html.haml | 2 +- config/routes.rb | 5 ++- 9 files changed, 141 insertions(+), 77 deletions(-) create mode 100644 app/controllers/admin/hooks_controller.rb delete mode 100644 app/controllers/admin/mailer_controller.rb create mode 100644 app/views/admin/hooks/_data_ex.html.erb create mode 100644 app/views/admin/hooks/index.html.haml delete mode 100644 app/views/admin/mailer/preview.html.haml create mode 100644 app/views/help/system_hooks.html.haml diff --git a/app/controllers/admin/hooks_controller.rb b/app/controllers/admin/hooks_controller.rb new file mode 100644 index 00000000..2e47b6c2 --- /dev/null +++ b/app/controllers/admin/hooks_controller.rb @@ -0,0 +1,39 @@ +class Admin::HooksController < ApplicationController + layout "admin" + before_filter :authenticate_user! + before_filter :authenticate_admin! + + def index + @hooks = SystemHook.all + @hook = SystemHook.new + end + + def create + @hook = SystemHook.new(params[:hook]) + + respond_to do |format| + if @hook.save + format.html { redirect_to admin_hooks_path, notice: 'Hook was successfully created.' } + else + format.html { render :index } + end + end + end + + def destroy + @hook = SystemHook.find(params[:id]) + @hook.destroy + + redirect_to admin_hooks_path + end + + + def test + @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) + + redirect_to :back + end +end diff --git a/app/controllers/admin/mailer_controller.rb b/app/controllers/admin/mailer_controller.rb deleted file mode 100644 index 2352e189..00000000 --- a/app/controllers/admin/mailer_controller.rb +++ /dev/null @@ -1,45 +0,0 @@ -class Admin::MailerController < ApplicationController - layout "admin" - before_filter :authenticate_user! - before_filter :authenticate_admin! - - def preview - - end - - def preview_note - @note = Note.first - @user = @note.author - @project = @note.project - case params[:type] - when "Commit" then - @commit = @project.commit - render :file => 'notify/note_commit_email', :layout => 'notify' - when "Issue" then - @issue = Issue.first - render :file => 'notify/note_issue_email', :layout => 'notify' - else - render :file => 'notify/note_wall_email', :layout => 'notify' - end - rescue - render :text => "Preview not available" - end - - def preview_user_new - @user = User.first - @password = "DHasJKDHAS!" - - render :file => 'notify/new_user_email', :layout => 'notify' - rescue - render :text => "Preview not available" - end - - def preview_issue_new - @issue = Issue.first - @user = @issue.assignee - @project = @issue.project - render :file => 'notify/new_issue_email', :layout => 'notify' - rescue - render :text => "Preview not available" - end -end diff --git a/app/models/web_hook.rb b/app/models/web_hook.rb index 43b4f16b..7a042348 100644 --- a/app/models/web_hook.rb +++ b/app/models/web_hook.rb @@ -12,8 +12,6 @@ class WebHook < ActiveRecord::Base def execute(data) WebHook.post(url, body: data.to_json, headers: { "Content-Type" => "application/json" }) - rescue - # There was a problem calling this web hook, let's forget about it. end end # == Schema Information diff --git a/app/views/admin/hooks/_data_ex.html.erb b/app/views/admin/hooks/_data_ex.html.erb new file mode 100644 index 00000000..8d3de3f0 --- /dev/null +++ b/app/views/admin/hooks/_data_ex.html.erb @@ -0,0 +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 + }, + :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 => 3 +} +eos +%> +<% js_lexer = Pygments::Lexer[:js] %> +<%= raw js_lexer.highlight(data_ex_str) %> diff --git a/app/views/admin/hooks/index.html.haml b/app/views/admin/hooks/index.html.haml new file mode 100644 index 00000000..030e6136 --- /dev/null +++ b/app/views/admin/hooks/index.html.haml @@ -0,0 +1,39 @@ +.alert.alert-info + %span + Post receive hooks for binding events. + %br + Read more about system hooks + %strong #{link_to "here", help_system_hooks_path, :class => "vlink"} + += form_for @hook, :as => :hook, :url => admin_hooks_path do |f| + -if @hook.errors.any? + .alert-message.block-message.error + - @hook.errors.full_messages.each do |msg| + %p= msg + .clearfix + = f.label :url, "URL:" + .input + = f.text_field :url, :class => "text_field xxlarge" +   + = f.submit "Add System Hook", :class => "btn primary" +%hr + +-if @hooks.any? + %h3 + Hooks + %small (#{@hooks.count}) + %br + %table.admin-table + %tr + %th URL + %th Method + %th + - @hooks.each do |hook| + %tr + %td + = link_to admin_hook_path(hook) do + %strong= hook.url + = 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 => "danger btn small right" diff --git a/app/views/admin/mailer/preview.html.haml b/app/views/admin/mailer/preview.html.haml deleted file mode 100644 index 23ea7381..00000000 --- a/app/views/admin/mailer/preview.html.haml +++ /dev/null @@ -1,28 +0,0 @@ -%p This is page with preview for all system emails that are sent to user -%p Email previews built based on existing Project/Commit/Issue base - so some preview maybe unavailable unless object appear in system - -#accordion - %h3 - %a New user - %div - %iframe{ :src=> admin_mailer_preview_user_new_path, :width=>"100%", :height=>"350"} - %h3 - %a New issue - %div - %iframe{ :src=> admin_mailer_preview_issue_new_path, :width=>"100%", :height=>"350"} - %h3 - %a Commit note - %div - %iframe{ :src=> admin_mailer_preview_note_path(:type => "Commit"), :width=>"100%", :height=>"350"} - %h3 - %a Issue note - %div - %iframe{ :src=> admin_mailer_preview_note_path(:type => "Issue"), :width=>"100%", :height=>"350"} - %h3 - %a Wall note - %div - %iframe{ :src=> admin_mailer_preview_note_path(:type => "Wall"), :width=>"100%", :height=>"350"} - -:javascript - $(function() { - $("#accordion").accordion(); }); diff --git a/app/views/help/system_hooks.html.haml b/app/views/help/system_hooks.html.haml new file mode 100644 index 00000000..2088208a --- /dev/null +++ b/app/views/help/system_hooks.html.haml @@ -0,0 +1,13 @@ +%h3 System hooks +.back_link + = link_to :back do + ← back +%hr + +%p.slead + Your Gitlab instance can perform HTTP POST request on next event: create_project, delete_project, create_user, delete_user, change_team_member. + %br + System Hooks can be used for logging or change information in LDAP server. + %br +%h5 Hooks request example: += render "admin/hooks/data_ex" diff --git a/app/views/layouts/admin.html.haml b/app/views/layouts/admin.html.haml index 8de25821..69304ede 100644 --- a/app/views/layouts/admin.html.haml +++ b/app/views/layouts/admin.html.haml @@ -15,7 +15,7 @@ %li{:class => tab_class(:admin_logs)} = link_to "Logs", admin_logs_path %li{:class => tab_class(:admin_emails)} - = link_to "Emails", admin_emails_path + = link_to "Hooks", admin_hooks_path %li{:class => tab_class(:admin_resque)} = link_to "Resque", admin_resque_path diff --git a/config/routes.rb b/config/routes.rb index 73b9f643..dea4df46 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -28,6 +28,7 @@ Gitlab::Application.routes.draw do get 'help/workflow' => 'help#workflow' get 'help/api' => 'help#api' get 'help/web_hooks' => 'help#web_hooks' + get 'help/system_hooks' => 'help#system_hooks' # # Admin Area @@ -47,11 +48,13 @@ Gitlab::Application.routes.draw do end end resources :team_members, :only => [:edit, :update, :destroy] - get 'emails', :to => 'mailer#preview' get 'mailer/preview_note' get 'mailer/preview_user_new' get 'mailer/preview_issue_new' + resources :hooks, :only => [:index, :create, :destroy] do + get :test + end resource :logs resource :resque, :controller => 'resque' root :to => "dashboard#index" From f5908cef19a3cd2c44be02b453fecb568b2c6c8e Mon Sep 17 00:00:00 2001 From: Valeriy Sizov Date: Sun, 15 Jul 2012 15:29:06 +0300 Subject: [PATCH 34/48] System Hook: implemented --- app/controllers/admin/hooks_controller.rb | 11 ++++----- app/models/project.rb | 28 ++++++++++++++++++++- app/models/system_hook.rb | 10 ++++++++ app/models/user.rb | 19 ++++++++++++++ app/models/users_project.rb | 30 ++++++++++++++++++++++- app/models/web_hook.rb | 1 + app/workers/system_hook_worker.rb | 7 ++++++ resque.sh | 2 +- resque_dev.sh | 2 +- 9 files changed, 100 insertions(+), 10 deletions(-) create mode 100644 app/workers/system_hook_worker.rb diff --git a/app/controllers/admin/hooks_controller.rb b/app/controllers/admin/hooks_controller.rb index 2e47b6c2..446d4ea9 100644 --- a/app/controllers/admin/hooks_controller.rb +++ b/app/controllers/admin/hooks_controller.rb @@ -11,12 +11,11 @@ class Admin::HooksController < ApplicationController def create @hook = SystemHook.new(params[:hook]) - respond_to do |format| - if @hook.save - format.html { redirect_to admin_hooks_path, notice: 'Hook was successfully created.' } - else - format.html { render :index } - end + if @hook.save + redirect_to admin_hooks_path, notice: 'Hook was successfully created.' + else + @hooks = SystemHook.all + render :index end end diff --git a/app/models/project.rb b/app/models/project.rb index 4773cf37..d3dac34a 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -107,6 +107,32 @@ class Project < ActiveRecord::Base validate :check_limit validate :repo_name + after_create :create_hooks + after_destroy :destroy_hooks + + def create_hooks + SystemHook.all_hooks_fire({ + event_name: "project_create", + name: self.name, + path: self.path, + project_id: self.id, + owner_name: self.owner.name, + owner_email: self.owner.email, + created_at: self.created_at + }) + end + + def destroy_hooks + SystemHook.all_hooks_fire({ + event_name: "project_destroy", + name: self.name, + path: self.path, + project_id: self.id, + owner_name: self.owner.name, + owner_email: self.owner.email, + }) + end + def check_limit unless owner.can_create_project? errors[:base] << ("Your own projects limit is #{owner.projects_limit}! Please contact administrator to increase it") @@ -120,7 +146,7 @@ class Project < ActiveRecord::Base errors.add(:path, " like 'gitolite-admin' is not allowed") end end - + def self.access_options UsersProject.access_roles end diff --git a/app/models/system_hook.rb b/app/models/system_hook.rb index 178b7585..8517d43a 100644 --- a/app/models/system_hook.rb +++ b/app/models/system_hook.rb @@ -1,3 +1,13 @@ class SystemHook < WebHook + + def async_execute(data) + Resque.enqueue(SystemHookWorker, id, data) + end + def self.all_hooks_fire(data) + SystemHook.all.each do |sh| + sh.async_execute data + end + end + end diff --git a/app/models/user.rb b/app/models/user.rb index a3e08fa7..8eb114c4 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -57,6 +57,25 @@ class User < ActiveRecord::Base scope :active, where(:blocked => false) before_validation :generate_password, :on => :create + after_create :create_hooks + after_destroy :destroy_hooks + + def create_hooks + SystemHook.all_hooks_fire({ + event_name: "user_create", + name: self.name, + email: self.email, + created_at: self.created_at + }) + end + + def destroy_hooks + SystemHook.all_hooks_fire({ + event_name: "user_destroy", + name: self.name, + email: self.email + }) + end def generate_password if self.force_random_password diff --git a/app/models/users_project.rb b/app/models/users_project.rb index 6ba72370..128e61b7 100644 --- a/app/models/users_project.rb +++ b/app/models/users_project.rb @@ -11,6 +11,9 @@ class UsersProject < ActiveRecord::Base after_save :update_repository after_destroy :update_repository + after_create :add_to_team_hooks + after_destroy :remove_from_team_hooks + validates_uniqueness_of :user_id, :scope => [:project_id] validates_presence_of :user_id @@ -18,6 +21,31 @@ class UsersProject < ActiveRecord::Base delegate :name, :email, :to => :user, :prefix => true + def add_to_team_hooks + SystemHook.all_hooks_fire({ + event_name: "user_add_to_team", + project_name: self.project.name, + project_path: self.project.path, + project_id: self.project_id, + user_name: self.user.name, + user_email: self.user.email, + project_access: self.repo_access_human, + created_at: self.created_at + }) + end + + def remove_from_team_hooks + SystemHook.all_hooks_fire({ + event_name: "user_remove_from_team", + project_name: self.project.name, + project_path: self.project.path, + project_id: self.project_id, + user_name: self.user.name, + user_email: self.user.email, + project_access: self.repo_access_human + }) + end + def self.bulk_import(project, user_ids, project_access) UsersProject.transaction do user_ids.each do |user_id| @@ -68,7 +96,7 @@ class UsersProject < ActiveRecord::Base end def repo_access_human - "" + self.class.access_roles.invert[self.project_access] end end # == Schema Information diff --git a/app/models/web_hook.rb b/app/models/web_hook.rb index 7a042348..85d87898 100644 --- a/app/models/web_hook.rb +++ b/app/models/web_hook.rb @@ -13,6 +13,7 @@ class WebHook < ActiveRecord::Base def execute(data) WebHook.post(url, body: data.to_json, headers: { "Content-Type" => "application/json" }) end + end # == Schema Information # diff --git a/app/workers/system_hook_worker.rb b/app/workers/system_hook_worker.rb new file mode 100644 index 00000000..ca154136 --- /dev/null +++ b/app/workers/system_hook_worker.rb @@ -0,0 +1,7 @@ +class SystemHookWorker + @queue = :system_hook + + def self.perform(hook_id, data) + SystemHook.find(hook_id).execute data + end +end diff --git a/resque.sh b/resque.sh index ce7c944b..ab67c650 100755 --- a/resque.sh +++ b/resque.sh @@ -1,2 +1,2 @@ mkdir -p tmp/pids -bundle exec rake environment resque:work QUEUE=post_receive,mailer RAILS_ENV=production PIDFILE=tmp/pids/resque_worker.pid BACKGROUND=yes +bundle exec rake environment resque:work QUEUE=post_receive,mailer,system_hook RAILS_ENV=production PIDFILE=tmp/pids/resque_worker.pid BACKGROUND=yes diff --git a/resque_dev.sh b/resque_dev.sh index 9df4dc1d..b09cfd9e 100755 --- a/resque_dev.sh +++ b/resque_dev.sh @@ -1 +1 @@ -bundle exec rake environment resque:work QUEUE=* VVERBOSE=1 +bundle exec rake environment resque:work QUEUE=post_receive,mailer,system_hook VVERBOSE=1 From 655418bed2f81651c54988963713769f85d8b5da Mon Sep 17 00:00:00 2001 From: Valeriy Sizov Date: Sun, 15 Jul 2012 17:36:06 +0300 Subject: [PATCH 35/48] System hooks: fix broken tests --- app/roles/git_push.rb | 6 +++--- spec/factories.rb | 2 +- spec/models/project_hooks_spec.rb | 28 ++++++++++++++-------------- spec/models/project_spec.rb | 2 +- spec/models/web_hook_spec.rb | 20 ++++++++++---------- spec/requests/admin/security_spec.rb | 8 ++++---- spec/requests/hooks_spec.rb | 7 ++++--- spec/workers/post_receive_spec.rb | 4 ++-- 8 files changed, 39 insertions(+), 38 deletions(-) diff --git a/app/roles/git_push.rb b/app/roles/git_push.rb index d0267b59..4ee7e62a 100644 --- a/app/roles/git_push.rb +++ b/app/roles/git_push.rb @@ -27,7 +27,7 @@ module GitPush true end - def execute_web_hooks(oldrev, newrev, ref, user) + 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) @@ -35,7 +35,7 @@ module GitPush data = post_receive_data(oldrev, newrev, ref, user) - hooks.each { |web_hook| web_hook.execute(data) } + hooks.each { |hook| hook.execute(data) } end def post_receive_data(oldrev, newrev, ref, user) @@ -97,7 +97,7 @@ module GitPush self.update_merge_requests(oldrev, newrev, ref, user) # Execute web hooks - self.execute_web_hooks(oldrev, newrev, ref, user) + self.execute_hooks(oldrev, newrev, ref, user) # Create satellite self.satellite.create unless self.satellite.exists? diff --git a/spec/factories.rb b/spec/factories.rb index ea8c7aef..d6570551 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -60,7 +60,7 @@ Factory.add(:key, Key) do |obj| obj.key = File.read(File.join(Rails.root, "db", "pkey.example")) end -Factory.add(:web_hook, WebHook) do |obj| +Factory.add(:project_hook, ProjectHook) do |obj| obj.url = Faker::Internet.uri("http") end diff --git a/spec/models/project_hooks_spec.rb b/spec/models/project_hooks_spec.rb index fcc969ce..5544c5a8 100644 --- a/spec/models/project_hooks_spec.rb +++ b/spec/models/project_hooks_spec.rb @@ -21,44 +21,44 @@ describe Project, "Hooks" do end end - describe "Web hooks" do + describe "Project hooks" do context "with no web hooks" do it "raises no errors" do lambda { - project.execute_web_hooks('oldrev', 'newrev', 'ref', @user) + project.execute_hooks('oldrev', 'newrev', 'ref', @user) }.should_not raise_error end end context "with web hooks" do before do - @webhook = Factory(:web_hook) - @webhook_2 = Factory(:web_hook) - project.web_hooks << [@webhook, @webhook_2] + @project_hook = Factory(:project_hook) + @project_hook_2 = Factory(:project_hook) + project.hooks << [@project_hook, @project_hook_2] end it "executes multiple web hook" do - @webhook.should_receive(:execute).once - @webhook_2.should_receive(:execute).once + @project_hook.should_receive(:execute).once + @project_hook_2.should_receive(:execute).once - project.execute_web_hooks('oldrev', 'newrev', 'refs/heads/master', @user) + project.execute_hooks('oldrev', 'newrev', 'refs/heads/master', @user) end end context "does not execute web hooks" do before do - @webhook = Factory(:web_hook) - project.web_hooks << [@webhook] + @project_hook = Factory(:project_hook) + project.hooks << [@project_hook] end it "when pushing a branch for the first time" do - @webhook.should_not_receive(:execute) - project.execute_web_hooks('00000000000000000000000000000000', 'newrev', 'refs/heads/master', @user) + @project_hook.should_not_receive(:execute) + project.execute_hooks('00000000000000000000000000000000', 'newrev', 'refs/heads/master', @user) end it "when pushing tags" do - @webhook.should_not_receive(:execute) - project.execute_web_hooks('oldrev', 'newrev', 'refs/tags/v1.0.0', @user) + @project_hook.should_not_receive(:execute) + project.execute_hooks('oldrev', 'newrev', 'refs/tags/v1.0.0', @user) end end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 381fe759..351f5748 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -11,7 +11,7 @@ describe Project do it { should have_many(:issues).dependent(:destroy) } it { should have_many(:notes).dependent(:destroy) } it { should have_many(:snippets).dependent(:destroy) } - it { should have_many(:web_hooks).dependent(:destroy) } + it { should have_many(:hooks).dependent(:destroy) } it { should have_many(:deploy_keys).dependent(:destroy) } end diff --git a/spec/models/web_hook_spec.rb b/spec/models/web_hook_spec.rb index 9971bd58..88594761 100644 --- a/spec/models/web_hook_spec.rb +++ b/spec/models/web_hook_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe WebHook do +describe ProjectHook do describe "Associations" do it { should belong_to :project } end @@ -23,32 +23,32 @@ describe WebHook do describe "execute" do before(:each) do - @webhook = Factory :web_hook + @project_hook = Factory :project_hook @project = Factory :project - @project.web_hooks << [@webhook] + @project.hooks << [@project_hook] @data = { before: 'oldrev', after: 'newrev', ref: 'ref'} - WebMock.stub_request(:post, @webhook.url) + WebMock.stub_request(:post, @project_hook.url) end it "POSTs to the web hook URL" do - @webhook.execute(@data) - WebMock.should have_requested(:post, @webhook.url).once + @project_hook.execute(@data) + WebMock.should have_requested(:post, @project_hook.url).once end it "POSTs the data as JSON" do json = @data.to_json - @webhook.execute(@data) - WebMock.should have_requested(:post, @webhook.url).with(body: json).once + @project_hook.execute(@data) + WebMock.should have_requested(:post, @project_hook.url).with(body: json).once end it "catches exceptions" do WebHook.should_receive(:post).and_raise("Some HTTP Post error") lambda { - @webhook.execute(@data) - }.should_not raise_error + @project_hook.execute(@data) + }.should raise_error end end end diff --git a/spec/requests/admin/security_spec.rb b/spec/requests/admin/security_spec.rb index 0b0edb85..0c369740 100644 --- a/spec/requests/admin/security_spec.rb +++ b/spec/requests/admin/security_spec.rb @@ -13,9 +13,9 @@ describe "Admin::Projects" do it { admin_users_path.should be_denied_for :visitor } end - describe "GET /admin/emails" do - it { admin_emails_path.should be_allowed_for :admin } - it { admin_emails_path.should be_denied_for :user } - it { admin_emails_path.should be_denied_for :visitor } + describe "GET /admin/hooks" do + it { admin_hooks_path.should be_allowed_for :admin } + it { admin_hooks_path.should be_denied_for :user } + it { admin_hooks_path.should be_denied_for :visitor } end end diff --git a/spec/requests/hooks_spec.rb b/spec/requests/hooks_spec.rb index a508e5ea..05432f13 100644 --- a/spec/requests/hooks_spec.rb +++ b/spec/requests/hooks_spec.rb @@ -9,7 +9,7 @@ describe "Hooks" do describe "GET index" do it "should be available" do - @hook = Factory :web_hook, :project => @project + @hook = Factory :project_hook, :project => @project visit project_hooks_path(@project) page.should have_content "Hooks" page.should have_content @hook.url @@ -21,7 +21,7 @@ describe "Hooks" do @url = Faker::Internet.uri("http") visit project_hooks_path(@project) fill_in "hook_url", :with => @url - expect { click_button "Add Web Hook" }.to change(WebHook, :count).by(1) + expect { click_button "Add Web Hook" }.to change(ProjectHook, :count).by(1) end it "should open new team member popup" do @@ -32,7 +32,8 @@ describe "Hooks" do describe "Test" do before do - @hook = Factory :web_hook, :project => @project + @hook = Factory :project_hook, :project => @project + stub_request(:post, @hook.url) visit project_hooks_path(@project) click_link "Test Hook" end diff --git a/spec/workers/post_receive_spec.rb b/spec/workers/post_receive_spec.rb index c70b2f6c..5f3b6d3d 100644 --- a/spec/workers/post_receive_spec.rb +++ b/spec/workers/post_receive_spec.rb @@ -22,14 +22,14 @@ describe PostReceive do Key.stub(find_by_identifier: nil) project.should_not_receive(:observe_push) - project.should_not_receive(:execute_web_hooks) + project.should_not_receive(:execute_hooks) PostReceive.perform(project.path, 'sha-old', 'sha-new', 'refs/heads/master', key_id).should be_false end it "asks the project to execute web hooks" do Project.stub(find_by_path: project) - project.should_receive(:execute_web_hooks).with('sha-old', 'sha-new', 'refs/heads/master', project.owner) + project.should_receive(:execute_hooks).with('sha-old', 'sha-new', 'refs/heads/master', project.owner) PostReceive.perform(project.path, 'sha-old', 'sha-new', 'refs/heads/master', key_id) end From 86bd11cbd8796adc31849aa02317604abb1576e2 Mon Sep 17 00:00:00 2001 From: Valeriy Sizov Date: Thu, 19 Jul 2012 00:24:37 +0300 Subject: [PATCH 36/48] System Hooks: rspec --- app/controllers/admin/hooks_controller.rb | 12 +++-- spec/factories.rb | 10 ++++ spec/models/system_hook_spec.rb | 61 +++++++++++++++++++++++ spec/requests/admin/admin_hooks_spec.rb | 53 ++++++++++++++++++++ 4 files changed, 133 insertions(+), 3 deletions(-) create mode 100644 spec/models/system_hook_spec.rb create mode 100644 spec/requests/admin/admin_hooks_spec.rb diff --git a/app/controllers/admin/hooks_controller.rb b/app/controllers/admin/hooks_controller.rb index 446d4ea9..7f832fd5 100644 --- a/app/controllers/admin/hooks_controller.rb +++ b/app/controllers/admin/hooks_controller.rb @@ -28,9 +28,15 @@ class Admin::HooksController < ApplicationController def test - @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 = 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 diff --git a/spec/factories.rb b/spec/factories.rb index d6570551..ab2ca468 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -7,6 +7,12 @@ Factory.add(:project, Project) do |obj| obj.code = 'LGT' end +Factory.add(:project_without_owner, Project) do |obj| + obj.name = Faker::Internet.user_name + obj.path = 'gitlabhq' + obj.code = 'LGT' +end + Factory.add(:public_project, Project) do |obj| obj.name = Faker::Internet.user_name obj.path = 'gitlabhq' @@ -64,6 +70,10 @@ Factory.add(:project_hook, ProjectHook) do |obj| obj.url = Faker::Internet.uri("http") end +Factory.add(:system_hook, SystemHook) do |obj| + obj.url = Faker::Internet.uri("http") +end + Factory.add(:wiki, Wiki) do |obj| obj.title = Faker::Lorem.sentence obj.content = Faker::Lorem.sentence diff --git a/spec/models/system_hook_spec.rb b/spec/models/system_hook_spec.rb new file mode 100644 index 00000000..661ba6b8 --- /dev/null +++ b/spec/models/system_hook_spec.rb @@ -0,0 +1,61 @@ +require "spec_helper" + +describe SystemHook do + describe "execute" do + before(:each) do + @system_hook = Factory :system_hook + WebMock.stub_request(:post, @system_hook.url) + end + + it "project_create hook" do + user = Factory :user + with_resque do + project = Factory :project_without_owner, :owner => user + end + WebMock.should have_requested(:post, @system_hook.url).with(body: /project_create/).once + end + + it "project_destroy hook" do + project = Factory :project + 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 + with_resque do + Factory :user + end + WebMock.should have_requested(:post, @system_hook.url).with(body: /user_create/).once + end + + it "user_destroy hook" do + user = Factory :user + 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 = Factory :user + project = Factory :project + 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 = Factory :user + project = Factory :project + 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/requests/admin/admin_hooks_spec.rb b/spec/requests/admin/admin_hooks_spec.rb new file mode 100644 index 00000000..e8a345b6 --- /dev/null +++ b/spec/requests/admin/admin_hooks_spec.rb @@ -0,0 +1,53 @@ +require 'spec_helper' + +describe "Admin::Hooks" do + before do + @project = Factory :project, + :name => "LeGiT", + :code => "LGT" + login_as :admin + + @system_hook = Factory :system_hook + + end + + describe "GET /admin/hooks" do + it "should be ok" do + visit admin_root_path + within ".main_menu" do + click_on "Hooks" + end + current_path.should == admin_hooks_path + end + + it "should have hooks list" do + visit admin_hooks_path + page.should have_content(@system_hook.url) + end + end + + describe "New Hook" do + before do + @url = Faker::Internet.uri("http") + visit admin_hooks_path + fill_in "hook_url", :with => @url + expect { click_button "Add System Hook" }.to change(SystemHook, :count).by(1) + end + + it "should open new hook popup" do + page.current_path.should == admin_hooks_path + page.should have_content(@url) + end + end + + describe "Test" do + before do + WebMock.stub_request(:post, @system_hook.url) + visit admin_hooks_path + click_link "Test Hook" + end + + it { page.current_path.should == admin_hooks_path } + end + +end From 214fdd2d2f5fdac53d08da780f98edaed877be52 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 19 Jul 2012 12:52:28 +0300 Subject: [PATCH 37/48] Ignore backups dir --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 8c626989..725f289d 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ log/*.log tmp/ .sass-cache/ coverage/* +backups/* *.swp public/uploads/ .rvmrc From 14649525971d7351120f659213aad0e40722bc44 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 19 Jul 2012 07:05:57 +0300 Subject: [PATCH 38/48] Fix comments in MR. Fixed event destroy with user --- app/assets/javascripts/note.js | 4 ++-- app/assets/stylesheets/gitlab_bootstrap.scss | 3 ++- app/assets/stylesheets/header.scss | 11 +++++++---- app/controllers/admin/projects_controller.rb | 2 +- app/models/user.rb | 5 +++++ app/views/admin/projects/index.html.haml | 6 +++--- app/views/admin/users/index.html.haml | 4 ++-- app/views/notes/_form.html.haml | 2 +- app/views/notes/_per_line_form.html.haml | 2 +- 9 files changed, 24 insertions(+), 15 deletions(-) diff --git a/app/assets/javascripts/note.js b/app/assets/javascripts/note.js index 4d97ffef..c45a45d2 100644 --- a/app/assets/javascripts/note.js +++ b/app/assets/javascripts/note.js @@ -25,11 +25,11 @@ init: $(this).closest('li').fadeOut(); }); $("#new_note").live("ajax:before", function(){ - $("#submit_note").attr("disabled", "disabled"); + $(".submit_note").attr("disabled", "disabled"); }) $("#new_note").live("ajax:complete", function(){ - $("#submit_note").removeAttr("disabled"); + $(".submit_note").removeAttr("disabled"); }) $("#note_note").live("focus", function(){ diff --git a/app/assets/stylesheets/gitlab_bootstrap.scss b/app/assets/stylesheets/gitlab_bootstrap.scss index c0a20762..b2bc4593 100644 --- a/app/assets/stylesheets/gitlab_bootstrap.scss +++ b/app/assets/stylesheets/gitlab_bootstrap.scss @@ -213,6 +213,7 @@ a:focus { .cblack { color:#111; } .cdark { color:#444 } .cwhite { color:#fff !important } +.bgred { background: #F2DEDE !important} /** COMMON STYLES **/ .left { @@ -650,7 +651,7 @@ p { h3.page_title { color:#456; font-size:20px; - font-weight: 600; + font-weight: normal; line-height: 28px; } diff --git a/app/assets/stylesheets/header.scss b/app/assets/stylesheets/header.scss index 5e2e4100..c3a57003 100644 --- a/app/assets/stylesheets/header.scss +++ b/app/assets/stylesheets/header.scss @@ -96,7 +96,7 @@ header { */ .search { float: right; - margin-right: 55px; + margin-right: 50px; .search-input { @extend .span2; @@ -125,11 +125,14 @@ header { display: block; cursor: pointer; img { + -moz-box-shadow: 0 0 5px #ccc; + -webkit-box-shadow: 0 0 5px #ccc; + box-shadow: 0 0 5px #ccc; border-radius: 4px; - right: 0px; + right: 5px; position: absolute; - width: 33px; - height: 33px; + width: 31px; + height: 31px; display: block; top: 0; &:after { diff --git a/app/controllers/admin/projects_controller.rb b/app/controllers/admin/projects_controller.rb index c0508428..0ff97bf2 100644 --- a/app/controllers/admin/projects_controller.rb +++ b/app/controllers/admin/projects_controller.rb @@ -6,7 +6,7 @@ class Admin::ProjectsController < ApplicationController def index @admin_projects = Project.scoped @admin_projects = @admin_projects.search(params[:name]) if params[:name].present? - @admin_projects = @admin_projects.page(params[:page]) + @admin_projects = @admin_projects.page(params[:page]).per(20) end def show diff --git a/app/models/user.rb b/app/models/user.rb index a3e08fa7..0836ca49 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -15,6 +15,11 @@ class User < ActiveRecord::Base has_many :my_own_projects, :class_name => "Project", :foreign_key => :owner_id has_many :keys, :dependent => :destroy + has_many :events, + :class_name => "Event", + :foreign_key => :author_id, + :dependent => :destroy + has_many :recent_events, :class_name => "Event", :foreign_key => :author_id, diff --git a/app/views/admin/projects/index.html.haml b/app/views/admin/projects/index.html.haml index 932fb37d..7218eebb 100644 --- a/app/views/admin/projects/index.html.haml +++ b/app/views/admin/projects/index.html.haml @@ -13,8 +13,8 @@ %th Team Members %th Post Receive %th Last Commit - %th - %th + %th Edit + %th.cred Danger Zone! - @admin_projects.each do |project| %tr @@ -24,5 +24,5 @@ %td= check_box_tag :post_receive_file, 1, project.has_post_receive_file?, :disabled => true %td= last_commit(project) %td= link_to 'Edit', edit_admin_project_path(project), :id => "edit_#{dom_id(project)}", :class => "btn small" - %td= link_to 'Destroy', [:admin, project], :confirm => 'Are you sure?', :method => :delete, :class => "btn small danger" + %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/users/index.html.haml b/app/views/admin/users/index.html.haml index 481bf37b..5d5320db 100644 --- a/app/views/admin/users/index.html.haml +++ b/app/views/admin/users/index.html.haml @@ -27,7 +27,7 @@ %th Projects %th Edit %th Blocked - %th + %th.cred Danger Zone! - @admin_users.each do |user| %tr @@ -41,6 +41,6 @@ = 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= link_to 'Destroy', [:admin, user], :confirm => 'USER WILL BE REMOVED! Are you sure?', :method => :delete, :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/notes/_form.html.haml b/app/views/notes/_form.html.haml index 03774d16..f5aa1495 100644 --- a/app/views/notes/_form.html.haml +++ b/app/views/notes/_form.html.haml @@ -32,4 +32,4 @@ %span Any file less than 10 MB - = f.submit 'Add Comment', :class => "btn primary", :id => "submit_note" + = f.submit 'Add Comment', :class => "btn primary submit_note", :id => "submit_note" diff --git a/app/views/notes/_per_line_form.html.haml b/app/views/notes/_per_line_form.html.haml index 94c55802..8beaf9b5 100644 --- a/app/views/notes/_per_line_form.html.haml +++ b/app/views/notes/_per_line_form.html.haml @@ -24,7 +24,7 @@ = check_box_tag :notify_author, 1 , @note.noteable_type == "Commit" %span Commit author .actions - = f.submit 'Add note', :class => "btn primary", :id => "submit_note" + = f.submit 'Add note', :class => "btn primary submit_note", :id => "submit_note" = link_to "Close", "#", :class => "btn hide-button" :javascript From d9cd6269e991e187c2acf272240fcb332270a5c4 Mon Sep 17 00:00:00 2001 From: Valeriy Sizov Date: Fri, 20 Jul 2012 01:01:29 +0300 Subject: [PATCH 39/48] System Hooks: move callback to observer --- app/models/project.rb | 26 ----------- app/models/user.rb | 20 +------- app/models/users_project.rb | 28 ----------- app/observers/system_hook_observer.rb | 67 +++++++++++++++++++++++++++ config/application.rb | 2 +- 5 files changed, 69 insertions(+), 74 deletions(-) create mode 100644 app/observers/system_hook_observer.rb diff --git a/app/models/project.rb b/app/models/project.rb index d3dac34a..a49b3f51 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -107,32 +107,6 @@ class Project < ActiveRecord::Base validate :check_limit validate :repo_name - after_create :create_hooks - after_destroy :destroy_hooks - - def create_hooks - SystemHook.all_hooks_fire({ - event_name: "project_create", - name: self.name, - path: self.path, - project_id: self.id, - owner_name: self.owner.name, - owner_email: self.owner.email, - created_at: self.created_at - }) - end - - def destroy_hooks - SystemHook.all_hooks_fire({ - event_name: "project_destroy", - name: self.name, - path: self.path, - project_id: self.id, - owner_name: self.owner.name, - owner_email: self.owner.email, - }) - end - def check_limit unless owner.can_create_project? errors[:base] << ("Your own projects limit is #{owner.projects_limit}! Please contact administrator to increase it") diff --git a/app/models/user.rb b/app/models/user.rb index 8eb114c4..99d50ffc 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,4 +1,5 @@ class User < ActiveRecord::Base + include Account devise :database_authenticatable, :token_authenticatable, :lockable, @@ -57,25 +58,6 @@ class User < ActiveRecord::Base scope :active, where(:blocked => false) before_validation :generate_password, :on => :create - after_create :create_hooks - after_destroy :destroy_hooks - - def create_hooks - SystemHook.all_hooks_fire({ - event_name: "user_create", - name: self.name, - email: self.email, - created_at: self.created_at - }) - end - - def destroy_hooks - SystemHook.all_hooks_fire({ - event_name: "user_destroy", - name: self.name, - email: self.email - }) - end def generate_password if self.force_random_password diff --git a/app/models/users_project.rb b/app/models/users_project.rb index 128e61b7..4ff86290 100644 --- a/app/models/users_project.rb +++ b/app/models/users_project.rb @@ -11,9 +11,6 @@ class UsersProject < ActiveRecord::Base after_save :update_repository after_destroy :update_repository - after_create :add_to_team_hooks - after_destroy :remove_from_team_hooks - validates_uniqueness_of :user_id, :scope => [:project_id] validates_presence_of :user_id @@ -21,31 +18,6 @@ class UsersProject < ActiveRecord::Base delegate :name, :email, :to => :user, :prefix => true - def add_to_team_hooks - SystemHook.all_hooks_fire({ - event_name: "user_add_to_team", - project_name: self.project.name, - project_path: self.project.path, - project_id: self.project_id, - user_name: self.user.name, - user_email: self.user.email, - project_access: self.repo_access_human, - created_at: self.created_at - }) - end - - def remove_from_team_hooks - SystemHook.all_hooks_fire({ - event_name: "user_remove_from_team", - project_name: self.project.name, - project_path: self.project.path, - project_id: self.project_id, - user_name: self.user.name, - user_email: self.user.email, - project_access: self.repo_access_human - }) - end - def self.bulk_import(project, user_ids, project_access) UsersProject.transaction do user_ids.each do |user_id| diff --git a/app/observers/system_hook_observer.rb b/app/observers/system_hook_observer.rb new file mode 100644 index 00000000..312cd2b3 --- /dev/null +++ b/app/observers/system_hook_observer.rb @@ -0,0 +1,67 @@ +class SystemHookObserver < ActiveRecord::Observer + observe :user, :project, :users_project + + 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 + + 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 +end diff --git a/config/application.rb b/config/application.rb index 3585c4b0..93726223 100644 --- a/config/application.rb +++ b/config/application.rb @@ -23,7 +23,7 @@ module Gitlab # config.plugins = [ :exception_notification, :ssl_requirement, :all ] # Activate observers that should always be running. - config.active_record.observers = :mailer_observer, :activity_observer, :project_observer, :key_observer, :issue_observer, :user_observer + config.active_record.observers = :mailer_observer, :activity_observer, :project_observer, :key_observer, :issue_observer, :user_observer, :system_hook_observer # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. From 3063af5adcf1c0331681fbbc13e679de1eb96487 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 20 Jul 2012 08:39:34 +0300 Subject: [PATCH 40/48] BaseContext Controllers refactoring with contexts Move commit compare logic to model --- app/contexts/base_context.rb | 8 ++++ app/contexts/commit_load.rb | 26 ++++++++++++ app/contexts/merge_requests_load.rb | 16 ++++++++ app/contexts/notes_load.rb | 30 ++++++++++++++ app/controllers/commits_controller.rb | 42 +++++++------------- app/controllers/dashboard_controller.rb | 7 +--- app/controllers/merge_requests_controller.rb | 11 +---- app/controllers/notes_controller.rb | 21 +--------- app/models/commit.rb | 23 +++++++++++ app/models/event.rb | 4 ++ app/roles/account.rb | 4 ++ 11 files changed, 130 insertions(+), 62 deletions(-) create mode 100644 app/contexts/base_context.rb create mode 100644 app/contexts/commit_load.rb create mode 100644 app/contexts/merge_requests_load.rb create mode 100644 app/contexts/notes_load.rb diff --git a/app/contexts/base_context.rb b/app/contexts/base_context.rb new file mode 100644 index 00000000..6eb8ee46 --- /dev/null +++ b/app/contexts/base_context.rb @@ -0,0 +1,8 @@ +class BaseContext + attr_accessor :project, :current_user, :params + + def initialize(project, user, params) + @project, @current_user, @params = project, user, params.dup + end +end + diff --git a/app/contexts/commit_load.rb b/app/contexts/commit_load.rb new file mode 100644 index 00000000..bab30d61 --- /dev/null +++ b/app/contexts/commit_load.rb @@ -0,0 +1,26 @@ +class CommitLoad < BaseContext + def execute + result = { + :commit => nil, + :suppress_diff => false, + :line_notes => [], + :notes_count => 0, + :note => nil + } + + commit = project.commit(params[:id]) + + if commit + commit = CommitDecorator.decorate(commit) + line_notes = project.commit_line_notes(commit) + + result[:suppress_diff] = true if commit.diffs.size > 200 && !params[:force_show_diff] + 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 + end + + result + end +end diff --git a/app/contexts/merge_requests_load.rb b/app/contexts/merge_requests_load.rb new file mode 100644 index 00000000..6778db3b --- /dev/null +++ b/app/contexts/merge_requests_load.rb @@ -0,0 +1,16 @@ +class MergeRequestsLoad < BaseContext + def execute + type = params[:f].to_i + + merge_requests = project.merge_requests + + merge_requests = case type + when 1 then merge_requests + when 2 then merge_requests.closed + when 3 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 +end diff --git a/app/contexts/notes_load.rb b/app/contexts/notes_load.rb new file mode 100644 index 00000000..d1f8da9c --- /dev/null +++ b/app/contexts/notes_load.rb @@ -0,0 +1,30 @@ +class NotesLoad < BaseContext + def execute + target_type = params[:target_type] + target_id = params[:target_id] + first_id = params[:first_id] + last_id = params[:last_id] + + + @notes = case target_type + when "commit" + then project.commit_notes(project.commit(target_id)).fresh.limit(20) + when "snippet" + then project.snippets.find(target_id).notes + when "wall" + then project.common_notes.order("created_at DESC").fresh.limit(50) + when "issue" + then project.issues.find(target_id).notes.inc_author.order("created_at DESC").limit(20) + when "merge_request" + then project.merge_requests.find(target_id).notes.inc_author.order("created_at DESC").limit(20) + end + + @notes = if last_id + @notes.where("id > ?", last_id) + elsif first_id + @notes.where("id < ?", first_id) + else + @notes + end + end +end diff --git a/app/controllers/commits_controller.rb b/app/controllers/commits_controller.rb index 25e980e0..cb1f7452 100644 --- a/app/controllers/commits_controller.rb +++ b/app/controllers/commits_controller.rb @@ -26,43 +26,31 @@ class CommitsController < ApplicationController end def show - @commit = project.commit(params[:id]) + result = CommitLoad.new(project, current_user, params).execute - git_not_found! and return unless @commit + @commit = result[:commit] - @commit = CommitDecorator.decorate(@commit) - - @note = @project.build_commit_note(@commit) - @comments_allowed = true - @line_notes = project.commit_line_notes(@commit) - - @notes_count = @line_notes.count + project.commit_notes(@commit).count - - if @commit.diffs.size > 200 && !params[:force_show_diff] - @suppress_diff = true + if @commit + @suppress_diff = result[:suppress_diff] + @note = result[:note] + @line_notes = result[:line_notes] + @notes_count = result[:notes_count] + @comments_allowed = true + else + return git_not_found! end + rescue Grit::Git::GitTimeout render "huge_commit" end def compare - first = project.commit(params[:to].try(:strip)) - last = project.commit(params[:from].try(:strip)) + result = Commit.compare(project, params[:from], params[:to]) - @diffs = [] - @commits = [] + @commits = result[:commits] + @commit = result[:commit] + @diffs = result[:diffs] @line_notes = [] - - if first && last - commits = [first, last].sort_by(&:created_at) - younger = commits.first - older = commits.last - - - @commits = project.repo.commits_between(younger.id, older.id).map {|c| Commit.new(c)} - @diffs = project.repo.diff(younger.id, older.id) rescue [] - @commit = Commit.new(older) - end end def patch diff --git a/app/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb index a0549407..14f6ab1c 100644 --- a/app/controllers/dashboard_controller.rb +++ b/app/controllers/dashboard_controller.rb @@ -2,11 +2,8 @@ class DashboardController < ApplicationController respond_to :html def index - @projects = current_user.projects.includes(:events).order("events.created_at DESC") - @projects = @projects.page(params[:page]).per(40) - - @events = Event.where(:project_id => current_user.projects.map(&:id)).recent.limit(20) - + @projects = current_user.projects_with_events.page(params[:page]).per(40) + @events = Event.recent_for_user(current_user).limit(20) @last_push = current_user.recent_push respond_to do |format| diff --git a/app/controllers/merge_requests_controller.rb b/app/controllers/merge_requests_controller.rb index ec4ed45f..1cb1d388 100644 --- a/app/controllers/merge_requests_controller.rb +++ b/app/controllers/merge_requests_controller.rb @@ -24,16 +24,7 @@ class MergeRequestsController < ApplicationController def index - @merge_requests = @project.merge_requests - - @merge_requests = case params[:f].to_i - when 1 then @merge_requests - when 2 then @merge_requests.closed - when 3 then @merge_requests.opened.assigned(current_user) - else @merge_requests.opened - end.page(params[:page]).per(20) - - @merge_requests = @merge_requests.includes(:author, :project).order("closed, created_at desc") + @merge_requests = MergeRequestsLoad.new(project, current_user, params).execute end def show diff --git a/app/controllers/notes_controller.rb b/app/controllers/notes_controller.rb index a2638d95..1c997e38 100644 --- a/app/controllers/notes_controller.rb +++ b/app/controllers/notes_controller.rb @@ -40,25 +40,6 @@ class NotesController < ApplicationController protected def notes - @notes = case params[:target_type] - when "commit" - then project.commit_notes(project.commit((params[:target_id]))).fresh.limit(20) - when "snippet" - then project.snippets.find(params[:target_id]).notes - when "wall" - then project.common_notes.order("created_at DESC").fresh.limit(50) - when "issue" - then project.issues.find(params[:target_id]).notes.inc_author.order("created_at DESC").limit(20) - when "merge_request" - then project.merge_requests.find(params[:target_id]).notes.inc_author.order("created_at DESC").limit(20) - end - - @notes = if params[:last_id] - @notes.where("id > ?", params[:last_id]) - elsif params[:first_id] - @notes.where("id < ?", params[:first_id]) - else - @notes - end + @notes = NotesLoad.new(project, current_user, params).execute end end diff --git a/app/models/commit.rb b/app/models/commit.rb index 800ad19b..859bee29 100644 --- a/app/models/commit.rb +++ b/app/models/commit.rb @@ -80,6 +80,29 @@ class Commit def commits_between(repo, from, to) repo.commits_between(from, to).map { |c| Commit.new(c) } end + + def compare(project, from, to) + first = project.commit(to.try(:strip)) + last = project.commit(from.try(:strip)) + + result = { + :commits => [], + :diffs => [], + :commit => nil + } + + if first && last + commits = [first, last].sort_by(&:created_at) + younger = commits.first + older = commits.last + + 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 def persisted? diff --git a/app/models/event.rb b/app/models/event.rb index dc7dfa16..c75924e7 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -28,6 +28,10 @@ class Event < ActiveRecord::Base end end + def self.recent_for_user user + where(:project_id => user.projects.map(&:id)).recent + end + # Next events currently enabled for system # - push # - new issue diff --git a/app/roles/account.rb b/app/roles/account.rb index afa1f8a3..e86dc593 100644 --- a/app/roles/account.rb +++ b/app/roles/account.rb @@ -55,4 +55,8 @@ module Account # Take only latest one events = events.recent.limit(1).first end + + def projects_with_events + projects.includes(:events).order("events.created_at DESC") + end end From 60ee383eb96285d1bc21e7f439306d5eb55048bd Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 20 Jul 2012 12:08:59 +0300 Subject: [PATCH 41/48] Enable observe for system hooks --- spec/models/system_hook_spec.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spec/models/system_hook_spec.rb b/spec/models/system_hook_spec.rb index 661ba6b8..4ad4d168 100644 --- a/spec/models/system_hook_spec.rb +++ b/spec/models/system_hook_spec.rb @@ -2,6 +2,8 @@ require "spec_helper" describe SystemHook do describe "execute" do + before(:each) { ActiveRecord::Base.observers.enable(:all) } + before(:each) do @system_hook = Factory :system_hook WebMock.stub_request(:post, @system_hook.url) From 791208431ccc12bebec81d464b29ca0acc2e731a Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 20 Jul 2012 08:39:34 +0300 Subject: [PATCH 42/48] Box shadow for boxes --- app/assets/stylesheets/gitlab_bootstrap.scss | 6 +++++- app/assets/stylesheets/main.scss | 6 ++++++ app/assets/stylesheets/sections/tree.scss | 2 +- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/app/assets/stylesheets/gitlab_bootstrap.scss b/app/assets/stylesheets/gitlab_bootstrap.scss index b2bc4593..39e59983 100644 --- a/app/assets/stylesheets/gitlab_bootstrap.scss +++ b/app/assets/stylesheets/gitlab_bootstrap.scss @@ -448,6 +448,7 @@ form { margin-bottom: 25px; @include round-borders-all(4px); border-color: #CCC; + @include solid_shade; ul { margin:0; @@ -538,6 +539,7 @@ form { table.admin-table { @extend .table-bordered; @extend .zebra-striped; + @include solid_shade; th { border-color: #CCC; border-bottom: 1px solid #bbb; @@ -595,6 +597,8 @@ ul.breadcrumb { @extend .prepend-top-20; @extend .append-bottom-20; border-width:1px; + @include solid_shade; + img { max-width: 100%; } @@ -662,7 +666,7 @@ h3.page_title { .file_holder { border:1px solid #CCC; margin-bottom:1em; - @include shade; + @include solid_shade; .file_title { border-bottom: 1px solid #bbb; diff --git a/app/assets/stylesheets/main.scss b/app/assets/stylesheets/main.scss index 5f13f301..7d60cd25 100644 --- a/app/assets/stylesheets/main.scss +++ b/app/assets/stylesheets/main.scss @@ -31,6 +31,12 @@ $hover: #FDF5D9; box-shadow: 0 0 3px #ddd; } +@mixin solid_shade { + -moz-box-shadow: 0 0 0 3px #eee; + -webkit-box-shadow: 0 0 0 3px #eee; + box-shadow: 0 0 0 3px #eee; +} + @mixin border-radius($radius) { -moz-border-radius: $radius; -webkit-border-radius: $radius; diff --git a/app/assets/stylesheets/sections/tree.scss b/app/assets/stylesheets/sections/tree.scss index 072d6103..c915e0a9 100644 --- a/app/assets/stylesheets/sections/tree.scss +++ b/app/assets/stylesheets/sections/tree.scss @@ -55,7 +55,7 @@ #tree-slider { - @include shade; + @include solid_shade; width:100%; border-color:#ccc; From 787e5e94acf5e20280416c9fda105ef5b77576b3 Mon Sep 17 00:00:00 2001 From: Pat Thoyts Date: Wed, 18 Jul 2012 00:11:51 +0100 Subject: [PATCH 43/48] Fix english on the edit user form. Signed-off-by: Pat Thoyts --- app/views/admin/users/_form.html.haml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/views/admin/users/_form.html.haml b/app/views/admin/users/_form.html.haml index bd2e1362..c1955b32 100644 --- a/app/views/admin/users/_form.html.haml +++ b/app/views/admin/users/_form.html.haml @@ -50,7 +50,7 @@ .alert .clearfix - %p Give user ability to manage application. + %p Make the user a GitLab administrator. = f.label :admin, :class => "checkbox" do = f.check_box :admin %span Administrator @@ -59,11 +59,11 @@ - if @admin_user.blocked %span = link_to 'Unblock', unblock_admin_user_path(@admin_user), :method => :put, :class => "btn small" - This user is blocked and is not able to login GitLab + This user is blocked and is not able to login to GitLab - else %span = link_to 'Block', block_admin_user_path(@admin_user), :confirm => 'USER WILL BE BLOCKED! Are you sure?', :method => :put, :class => "btn small danger" - Blocked user will removed from all projects & will not be able to login to GitLab. + Blocked users will be removed from all projects & will not be able to login to GitLab. .actions = f.submit 'Save', :class => "btn primary" - if @admin_user.new_record? From a69fc5dd23bd502fd36892a80eec21a4c53891f8 Mon Sep 17 00:00:00 2001 From: randx Date: Sat, 21 Jul 2012 10:23:05 +0300 Subject: [PATCH 44/48] Endless event loading for dsahboard --- app/assets/stylesheets/header.scss | 3 --- app/assets/stylesheets/themes/ui_mars.scss | 3 +-- app/controllers/dashboard_controller.rb | 3 ++- app/views/dashboard/index.html.haml | 7 ++++++- app/views/dashboard/index.js.haml | 2 +- 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/app/assets/stylesheets/header.scss b/app/assets/stylesheets/header.scss index c3a57003..07eba39b 100644 --- a/app/assets/stylesheets/header.scss +++ b/app/assets/stylesheets/header.scss @@ -125,9 +125,6 @@ header { display: block; cursor: pointer; img { - -moz-box-shadow: 0 0 5px #ccc; - -webkit-box-shadow: 0 0 5px #ccc; - box-shadow: 0 0 5px #ccc; border-radius: 4px; right: 5px; position: absolute; diff --git a/app/assets/stylesheets/themes/ui_mars.scss b/app/assets/stylesheets/themes/ui_mars.scss index 0fea6144..39dcab1d 100644 --- a/app/assets/stylesheets/themes/ui_mars.scss +++ b/app/assets/stylesheets/themes/ui_mars.scss @@ -70,8 +70,7 @@ } } .separator { - border-color:#444; - background:#31363E; + display:none; } } diff --git a/app/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb index 14f6ab1c..8508e245 100644 --- a/app/controllers/dashboard_controller.rb +++ b/app/controllers/dashboard_controller.rb @@ -3,11 +3,12 @@ class DashboardController < ApplicationController def index @projects = current_user.projects_with_events.page(params[:page]).per(40) - @events = Event.recent_for_user(current_user).limit(20) + @events = Event.recent_for_user(current_user).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 diff --git a/app/views/dashboard/index.html.haml b/app/views/dashboard/index.html.haml index b3854450..e1d77819 100644 --- a/app/views/dashboard/index.html.haml +++ b/app/views/dashboard/index.html.haml @@ -10,9 +10,10 @@ add new key to your profile - if @events.any? - = render @events + .content_list= render @events - else %h4.nothing_here_message Projects activity will be displayed here + .loading.hide .side = render "events/event_last_push", :event => @last_push .projects_box @@ -54,3 +55,7 @@ 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/index.js.haml b/app/views/dashboard/index.js.haml index aa038e75..7e5a148e 100644 --- a/app/views/dashboard/index.js.haml +++ b/app/views/dashboard/index.js.haml @@ -1,2 +1,2 @@ :plain - $(".projects .activities").append("#{escape_javascript(render(@events))}"); + Pager.append(#{@events.count}, "#{escape_javascript(render(@events))}"); From 4261acda90ff4c61326d80cba026ae76e8551f8f Mon Sep 17 00:00:00 2001 From: randx Date: Sat, 21 Jul 2012 10:27:09 +0300 Subject: [PATCH 45/48] move SSH keys tab closer to begining --- app/views/layouts/profile.html.haml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/views/layouts/profile.html.haml b/app/views/layouts/profile.html.haml index dfe48d68..957a68bf 100644 --- a/app/views/layouts/profile.html.haml +++ b/app/views/layouts/profile.html.haml @@ -12,16 +12,17 @@ %li{:class => tab_class(:password)} = link_to "Password", profile_password_path + %li{:class => tab_class(:ssh_keys)} + = link_to keys_path do + SSH Keys + %span.count= current_user.keys.count + %li{:class => tab_class(:token)} = link_to "Token", profile_token_path %li{:class => tab_class(:design)} = link_to "Design", profile_design_path - %li{:class => tab_class(:ssh_keys)} - = link_to keys_path do - SSH Keys - %span.count= current_user.keys.count .content = yield From 9c8a1e651716212cf50a623d98e03b8dbdb2c64a Mon Sep 17 00:00:00 2001 From: randx Date: Sat, 21 Jul 2012 10:32:42 +0300 Subject: [PATCH 46/48] Fix system hook example --- app/views/admin/hooks/_data_ex.html.erb | 45 ++++--------------------- 1 file changed, 7 insertions(+), 38 deletions(-) diff --git a/app/views/admin/hooks/_data_ex.html.erb b/app/views/admin/hooks/_data_ex.html.erb index 8d3de3f0..875c20a7 100644 --- a/app/views/admin/hooks/_data_ex.html.erb +++ b/app/views/admin/hooks/_data_ex.html.erb @@ -1,43 +1,12 @@ <% 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 - }, - :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 => 3 + "created_at": "2012-07-21T07:30:54Z", + "event_name": "project_create", + "name": "StoreCloud", + "owner_email": "johnsmith@gmail.com", + "owner_name": "John Smith", + "path": "storecloud", + "project_id": 74 } eos %> From 11721b0dbe82c35789be3e4fa8e14663934b2ff5 Mon Sep 17 00:00:00 2001 From: randx Date: Sat, 21 Jul 2012 10:47:57 +0300 Subject: [PATCH 47/48] Help section for system hooks completed --- app/views/admin/hooks/_data_ex.html.erb | 52 +++++++++++++++++++++++++ app/views/help/index.html.haml | 3 ++ 2 files changed, 55 insertions(+) diff --git a/app/views/admin/hooks/_data_ex.html.erb b/app/views/admin/hooks/_data_ex.html.erb index 875c20a7..652ee5aa 100644 --- a/app/views/admin/hooks/_data_ex.html.erb +++ b/app/views/admin/hooks/_data_ex.html.erb @@ -1,4 +1,5 @@ <% data_ex_str = < <% js_lexer = Pygments::Lexer[:js] %> diff --git a/app/views/help/index.html.haml b/app/views/help/index.html.haml index b6c52712..e9602e33 100644 --- a/app/views/help/index.html.haml +++ b/app/views/help/index.html.haml @@ -23,5 +23,8 @@ %li %h5= link_to "Web Hooks", help_web_hooks_path + %li + %h5= link_to "System Hooks", help_system_hooks_path + %li %h5= link_to "API", help_api_path From 8b7e404b5b6944e9c92cc270b2e5d0005781d49d Mon Sep 17 00:00:00 2001 From: randx Date: Sat, 21 Jul 2012 10:53:55 +0300 Subject: [PATCH 48/48] Up to 2.7.0 --- CHANGELOG | 4 ++++ VERSION | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index fe243d65..ec5d2f22 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -4,6 +4,10 @@ v 2.7.0 - Git HTTP - API - UI improved + - System hooks + - UI improved + - Dashboard events endless scroll + - Source perfomance increased v 2.6.0 - UI polished diff --git a/VERSION b/VERSION index 1daf7c48..24ba9a38 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.7.0pre +2.7.0