From fa5a53f5ed2914052e07ef8fd46879576c978563 Mon Sep 17 00:00:00 2001 From: Jakub Jirutka Date: Sun, 29 Jul 2012 16:15:52 +0200 Subject: [PATCH 01/46] Change identification of users with extern auth provider (LDAP) --- .../omniauth_callbacks_controller.rb | 3 +-- app/models/user.rb | 19 +++++++++++++------ ...31232_add_extern_auth_provider_to_users.rb | 8 ++++++++ db/schema.rb | 5 ++++- 4 files changed, 26 insertions(+), 9 deletions(-) create mode 100644 db/migrate/20120729131232_add_extern_auth_provider_to_users.rb diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb index fb759c37..5bad6093 100644 --- a/app/controllers/omniauth_callbacks_controller.rb +++ b/app/controllers/omniauth_callbacks_controller.rb @@ -15,8 +15,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController def ldap # We only find ourselves here if the authentication to LDAP was successful. - info = request.env["omniauth.auth"]["info"] - @user = User.find_for_ldap_auth(info) + @user = User.find_for_ldap_auth(request.env["omniauth.auth"], current_user) if @user.persisted? @user.remember_me = true end diff --git a/app/models/user.rb b/app/models/user.rb index ff27660a..f5ea70d5 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -7,7 +7,7 @@ class User < ActiveRecord::Base attr_accessible :email, :password, :password_confirmation, :remember_me, :bio, :name, :projects_limit, :skype, :linkedin, :twitter, :dark_scheme, - :theme_id, :force_random_password + :theme_id, :force_random_password, :extern_uid, :provider attr_accessor :force_random_password @@ -54,6 +54,8 @@ class User < ActiveRecord::Base validates :bio, :length => { :within => 0..255 } + validates :extern_uid, :allow_blank => true, :uniqueness => {:scope => :provider} + before_save :ensure_authentication_token alias_attribute :private_token, :authentication_token @@ -84,16 +86,21 @@ class User < ActiveRecord::Base where('id NOT IN (SELECT DISTINCT(user_id) FROM users_projects)') end - def self.find_for_ldap_auth(omniauth_info) - name = omniauth_info.name.force_encoding("utf-8") - email = omniauth_info.email.downcase unless omniauth_info.email.nil? - raise OmniAuth::Error, "LDAP accounts must provide an email address" if email.nil? + def self.find_for_ldap_auth(auth, signed_in_resource=nil) + uid = auth.info.uid + provider = auth.provider + name = auth.info.name.force_encoding("utf-8") + email = auth.info.email.downcase unless auth.info.email.nil? + raise OmniAuth::Error, "LDAP accounts must provide an uid and email address" if uid.nil? and email.nil? - if @user = User.find_by_email(email) + if @user = User.find_by_extern_uid_and_provider(uid, provider) @user else + logger.info "Creating user from LDAP login; uid = #{uid}, name = #{name}, email = #{email}" password = Devise.friendly_token[0, 8].downcase @user = User.create( + :extern_uid => uid, + :provider => provider, :name => name, :email => email, :password => password, diff --git a/db/migrate/20120729131232_add_extern_auth_provider_to_users.rb b/db/migrate/20120729131232_add_extern_auth_provider_to_users.rb new file mode 100644 index 00000000..d5e66ba4 --- /dev/null +++ b/db/migrate/20120729131232_add_extern_auth_provider_to_users.rb @@ -0,0 +1,8 @@ +class AddExternAuthProviderToUsers < ActiveRecord::Migration + def change + add_column :users, :extern_uid, :string + add_column :users, :provider, :string + + add_index :users, [:extern_uid, :provider], :unique => true + end +end diff --git a/db/schema.rb b/db/schema.rb index c4c54f56..46461e44 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 => 20120712080407) do +ActiveRecord::Schema.define(:version => 20120729131232) do create_table "events", :force => true do |t| t.string "target_type" @@ -171,9 +171,12 @@ ActiveRecord::Schema.define(:version => 20120712080407) do t.boolean "blocked", :default => false, :null => false t.integer "failed_attempts", :default => 0 t.datetime "locked_at" + t.string "extern_uid" + t.string "provider" end add_index "users", ["email"], :name => "index_users_on_email", :unique => true + add_index "users", ["extern_uid", "provider"], :name => "index_users_on_extern_uid_and_provider", :unique => true add_index "users", ["reset_password_token"], :name => "index_users_on_reset_password_token", :unique => true create_table "users_projects", :force => true do |t| From ad265b9610b474132752da43cf845d82ddf5fd8c Mon Sep 17 00:00:00 2001 From: Jakub Jirutka Date: Sun, 5 Aug 2012 11:27:17 +0200 Subject: [PATCH 02/46] Adding workaround for backward compatibility with legacy LDAP users --- app/models/user.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/models/user.rb b/app/models/user.rb index f5ea70d5..1b53bda2 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -95,8 +95,13 @@ class User < ActiveRecord::Base if @user = User.find_by_extern_uid_and_provider(uid, provider) @user + # workaround for backward compatibility + elsif @user = User.find_by_email(email) + logger.info "Updating legacy LDAP user #{email} with extern_uid => #{uid}" + @user.update_attributes(:extern_uid => uid, :provider => provider) + @user else - logger.info "Creating user from LDAP login; uid = #{uid}, name = #{name}, email = #{email}" + logger.info "Creating user from LDAP login {uid => #{uid}, name => #{name}, email => #{email}}" password = Devise.friendly_token[0, 8].downcase @user = User.create( :extern_uid => uid, From 0b7e67ad12dff04da6d7825a6125099ef0d21174 Mon Sep 17 00:00:00 2001 From: Nihad Abbasov Date: Tue, 14 Aug 2012 01:24:27 -0700 Subject: [PATCH 03/46] fix note preview link, and hide preview and errors after posting a comment --- app/assets/javascripts/application.js | 2 +- app/views/notes/_create_common.js.haml | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 25732ae5..28e9d362 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -74,7 +74,7 @@ $(document).ready(function(){ * Note markdown preview * */ - $('#preview-link').on('click', function(e) { + $(document).on('click', '#preview-link', function(e) { $('#preview-note').text('Loading...'); var previewLinkText = ($(this).text() == 'Preview' ? 'Edit' : 'Preview'); diff --git a/app/views/notes/_create_common.js.haml b/app/views/notes/_create_common.js.haml index 847ff383..e9538902 100644 --- a/app/views/notes/_create_common.js.haml +++ b/app/views/notes/_create_common.js.haml @@ -1,7 +1,9 @@ - if note.valid? :plain - $("#new_note .errors").remove(); + $("#new_note .error").remove(); $('#new_note textarea').val(""); + $('#preview-link').text('Preview'); + $('#preview-note').hide(); $('#note_note').show(); NoteList.prepend(#{note.id}, "#{escape_javascript(render partial: "notes/show", locals: {note: note})}"); - else :plain From 65bcc41f3e0a8b678e201e7f3d6a63c5b463fbe3 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 15 Aug 2012 21:06:08 -0400 Subject: [PATCH 04/46] Allow disabling Gravatars in gitlab.yml settings Closes #1237 --- app/helpers/application_helper.rb | 11 +++++++---- config/gitlab.yml.example | 13 ++++++------- config/initializers/1_settings.rb | 4 ++++ spec/helpers/application_helper_spec.rb | 26 +++++++++++++++++++++++++ 4 files changed, 43 insertions(+), 11 deletions(-) create mode 100644 spec/helpers/application_helper_spec.rb diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 7a9f0e9d..2d7e4fda 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -2,10 +2,13 @@ require 'digest/md5' module ApplicationHelper def gravatar_icon(user_email = '', size = 40) - return unless user_email - gravatar_host = request.ssl? ? "https://secure.gravatar.com" : "http://www.gravatar.com" - user_email.strip! - "#{gravatar_host}/avatar/#{Digest::MD5.hexdigest(user_email.downcase)}?s=#{size}&d=identicon" + if Gitlab.config.disable_gravatar? || user_email.blank? + 'no_avatar.png' + else + gravatar_prefix = request.ssl? ? "https://secure" : "http://www" + user_email.strip! + "#{gravatar_prefix}.gravatar.com/avatar/#{Digest::MD5.hexdigest(user_email.downcase)}?s=#{size}&d=identicon" + end end def request_protocol diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index 1818f2c0..be36ee6d 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -1,4 +1,4 @@ -# # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # Gitlab application config file # # # # # # # # # # # # # # # # # # # @@ -19,14 +19,14 @@ email: # Application specific settings # Like default project limit for user etc -app: - default_projects_limit: 10 +app: + default_projects_limit: 10 # backup_path: "/vol/backups" # default: Rails.root + backups/ # backup_keep_time: 604800 # default: 0 (forever) (in seconds) + # disable_gravatar: true # default: false - Disable user avatars from Gravatar.com - -# -# 2. Advanced settings: +# +# 2. Advanced settings: # ========================== # Git Hosting configuration @@ -39,7 +39,6 @@ git_host: receive_pack: true # port: 22 - # Git settings # Use default values unless you understand it git: diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index 5c5987a8..8165d6c2 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -111,5 +111,9 @@ class Settings < Settingslogic def backup_keep_time app['backup_keep_time'] || 0 end + + def disable_gravatar? + app['disable_gravatar'] || false + end end end diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb new file mode 100644 index 00000000..9a2df314 --- /dev/null +++ b/spec/helpers/application_helper_spec.rb @@ -0,0 +1,26 @@ +require 'spec_helper' + +describe ApplicationHelper do + describe "gravatar_icon" do + let(:user_email) { 'user@email.com' } + + it "should return a generic avatar path when Gravatar is disabled" do + Gitlab.config.stub(:disable_gravatar?).and_return(true) + gravatar_icon(user_email).should == 'no_avatar.png' + end + + it "should return a generic avatar path when email is blank" do + gravatar_icon('').should == 'no_avatar.png' + end + + it "should use SSL when appropriate" do + stub!(:request).and_return(double(:ssl? => true)) + gravatar_icon(user_email).should match('https://secure.gravatar.com') + end + + it "should accept a custom size" do + stub!(:request).and_return(double(:ssl? => false)) + gravatar_icon(user_email, 64).should match(/\?s=64/) + end + end +end From 813814f02ed7bf226782861820570117fa196026 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 17 Aug 2012 09:09:11 +0300 Subject: [PATCH 05/46] Application cleanup --- Gemfile | 6 +- Gemfile.lock | 8 +- app/assets/javascripts/application.js | 2 - app/assets/stylesheets/application.css | 3 +- app/assets/stylesheets/jquery_ui.scss | 33 - app/assets/stylesheets/main.scss | 30 +- app/models/issue.rb | 2 - lib/tasks/dev/tests.rake | 2 +- vendor/assets/images/bg_fallback.png | Bin 0 -> 3721 bytes vendor/assets/images/icon_sprite.png | Bin 0 -> 3217 bytes vendor/assets/images/progress_bar.gif | Bin 0 -> 502 bytes vendor/assets/images/slider_handles.png | Bin 0 -> 4453 bytes .../assets/images/ui-icons_222222_256x240.png | Bin 0 -> 4369 bytes .../assets/images/ui-icons_454545_256x240.png | Bin 0 -> 4369 bytes vendor/assets/javascripts/jquery.tagify.js | 143 --- .../javascripts/jquery.ui.selectmenu.js | 844 ------------------ .../stylesheets/jquery-ui/jquery.tagify.css | 34 - .../jquery-ui/jquery.ui.selectmenu.css | 33 - .../assets/stylesheets/jquery.ui.aristo.css | 738 +++++++++++++++ 19 files changed, 755 insertions(+), 1123 deletions(-) delete mode 100644 app/assets/stylesheets/jquery_ui.scss create mode 100644 vendor/assets/images/bg_fallback.png create mode 100644 vendor/assets/images/icon_sprite.png create mode 100644 vendor/assets/images/progress_bar.gif create mode 100644 vendor/assets/images/slider_handles.png create mode 100644 vendor/assets/images/ui-icons_222222_256x240.png create mode 100644 vendor/assets/images/ui-icons_454545_256x240.png delete mode 100644 vendor/assets/javascripts/jquery.tagify.js delete mode 100644 vendor/assets/javascripts/jquery.ui.selectmenu.js delete mode 100644 vendor/assets/stylesheets/jquery-ui/jquery.tagify.css delete mode 100644 vendor/assets/stylesheets/jquery-ui/jquery.ui.selectmenu.css create mode 100644 vendor/assets/stylesheets/jquery.ui.aristo.css diff --git a/Gemfile b/Gemfile index d2a5728f..26e4e195 100644 --- a/Gemfile +++ b/Gemfile @@ -76,10 +76,6 @@ gem 'settingslogic' gem "foreman" gem "git" -# Unused -gem 'tabs_on_rails' -gem "acts_as_list" - group :assets do gem "sass-rails", "3.2.5" gem "coffee-rails", "3.2.2" @@ -91,7 +87,7 @@ group :assets do gem "jquery-ui-rails", "0.5.0" gem "modernizr", "2.5.3" gem "raphael-rails", "1.5.2" - gem 'bootstrap-sass', "2.0.3.1" + gem 'bootstrap-sass', "2.0.4" end group :development do diff --git a/Gemfile.lock b/Gemfile.lock index 7356c35e..57ad1935 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -98,7 +98,6 @@ GEM multi_json (~> 1.0) acts-as-taggable-on (2.3.1) rails (~> 3.0) - acts_as_list (0.1.6) addressable (2.2.8) ansi (1.4.2) arel (3.0.2) @@ -109,7 +108,7 @@ GEM awesome_print (1.0.2) bcrypt-ruby (3.0.1) blankslate (2.1.2.4) - bootstrap-sass (2.0.3.1) + bootstrap-sass (2.0.4.0) builder (3.0.0) capybara (1.1.2) mime-types (>= 1.16) @@ -338,7 +337,6 @@ GEM tilt (~> 1.1, != 1.3.0) sqlite3 (1.3.6) stamp (0.1.6) - tabs_on_rails (2.1.1) therubyracer (0.10.1) libv8 (~> 3.3.10) thin (1.3.1) @@ -375,12 +373,11 @@ PLATFORMS DEPENDENCIES acts-as-taggable-on (= 2.3.1) - acts_as_list annotate! autotest autotest-rails awesome_print - bootstrap-sass (= 2.0.3.1) + bootstrap-sass (= 2.0.4) capybara capybara-webkit carrierwave @@ -431,7 +428,6 @@ DEPENDENCIES six sqlite3 stamp - tabs_on_rails therubyracer thin turn diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 25732ae5..d9cbd5d6 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -7,8 +7,6 @@ //= require jquery //= require jquery.ui.all //= require jquery_ujs -//= require jquery.ui.selectmenu -//= require jquery.tagify //= require jquery.cookie //= require jquery.endless-scroll //= require jquery.highlight diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css index 6ce23320..92d542a9 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css @@ -3,8 +3,7 @@ * and any sub-directories. You're free to add application-wide styles to this file and they'll appear at * the top of the compiled file, but it's generally better to create a new file per style scope. *= require jquery.ui.all - *= require jquery-ui/jquery.ui.selectmenu - *= require jquery-ui/jquery.tagify + *= require jquery.ui.aristo *= require chosen *= require_self *= require main diff --git a/app/assets/stylesheets/jquery_ui.scss b/app/assets/stylesheets/jquery_ui.scss deleted file mode 100644 index 1063f1d0..00000000 --- a/app/assets/stylesheets/jquery_ui.scss +++ /dev/null @@ -1,33 +0,0 @@ -/** - * JQUERY UI datepicker - * - */ -.ui-datepicker { - border-color:#eee; - padding:20px; - - .ui-state-default { - background:#f1f1f1; - padding:5px; - } - .ui-state-active { - background:#fff; - } -} - -/** - * JQUERY UI progressbar - * - */ -.ui-progressbar { - border:1px solid #ddd; - height:6px; - margin:0; - padding:0; - - .ui-progressbar-value { - background-color: #62C462;//$blue_link; - margin:0; - } -} - diff --git a/app/assets/stylesheets/main.scss b/app/assets/stylesheets/main.scss index 5613f1e8..71d09883 100644 --- a/app/assets/stylesheets/main.scss +++ b/app/assets/stylesheets/main.scss @@ -3,8 +3,8 @@ /** GITLAB colors **/ $text_color:#222; -$lite_text_color: #666; -$link_color:#2A79A3; +$lite_text_color: #666; +$link_color:#2A79A3; $active_link_color:#2FA0BB; $active_bg_color:#79C3E0; $active_bd_color: #2FA0BB; @@ -31,7 +31,7 @@ $hover: #FDF5D9; box-shadow: 0 0 3px #ddd; } -@mixin solid_shade { +@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; @@ -73,21 +73,21 @@ $hover: #FDF5D9; /** - * Header of application. + * Header of application. * Contain application logo, search panel, profile icon */ @import "sections/header.scss"; /** - * Navigation menu of application. + * Navigation menu of application. * Panel with links to pages depends on project, profile or admin area */ @import "sections/nav.scss"; /** - * This file represent some UI that can be changed - * during web app restyle or theme select. - * + * This file represent some UI that can be changed + * during web app restyle or theme select. + * * Next items should be placed there * - link, button colors * - header restyles @@ -118,7 +118,7 @@ $hover: #FDF5D9; * Most of application styles placed here. * This file represent common UI that should not be changed between themes * or project restyling like form width or user avatar class or commit title - * + * * TODO: clean it */ @import "common.scss"; @@ -140,17 +140,17 @@ $hover: #FDF5D9; @import "ref_select.scss"; /** - * Code (files list) styles. Browsing project files there + * Code (files list) styles. Browsing project files there */ @import "sections/tree.scss"; /** - * This file represent notes(comments) styles + * This file represent notes(comments) styles */ @import "sections/notes.scss"; /** - * Devise styles + * Devise styles */ @import "sections/login.scss"; @@ -165,9 +165,3 @@ $hover: #FDF5D9; * */ @import "highlight/dark.scss"; - -/** - * JQUERY UI ext - * - */ -@import "jquery_ui.scss"; diff --git a/app/models/issue.rb b/app/models/issue.rb index 454b1358..6409eeba 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -9,8 +9,6 @@ class Issue < ActiveRecord::Base validates :description, length: { within: 0..2000 } - acts_as_list - def self.open_for(user) opened.assigned(user) end diff --git a/lib/tasks/dev/tests.rake b/lib/tasks/dev/tests.rake index f91320eb..d5c0e75e 100644 --- a/lib/tasks/dev/tests.rake +++ b/lib/tasks/dev/tests.rake @@ -3,7 +3,7 @@ namespace :dev do task :tests do ["cucumber", "rspec spec"].each do |cmd| puts "Starting to run #{cmd}..." - system("bundle exec #{cmd}") + system("export DISPLAY=:99.0 && bundle exec #{cmd}") raise "#{cmd} failed!" unless $?.exitstatus == 0 end end diff --git a/vendor/assets/images/bg_fallback.png b/vendor/assets/images/bg_fallback.png new file mode 100644 index 0000000000000000000000000000000000000000..4b2754b8040e4bb430bd910225bb9760d25e8794 GIT binary patch literal 3721 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 E05H1-Q~&?~ literal 0 HcmV?d00001 diff --git a/vendor/assets/images/icon_sprite.png b/vendor/assets/images/icon_sprite.png new file mode 100644 index 0000000000000000000000000000000000000000..636c80f221621c2461cb2f78764b21a1d5ce2f5a GIT binary patch literal 3217 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 literal 0 HcmV?d00001 diff --git a/vendor/assets/images/progress_bar.gif b/vendor/assets/images/progress_bar.gif new file mode 100644 index 0000000000000000000000000000000000000000..156fbb53137ff519901e8b1021e2468974131c39 GIT binary patch literal 502 zcmZ?wbhEHbv|x~6xXQrr=g*(_@85s=^y%BTZ@+&1`ug?j@87?F{P^+n=g+@?|DJvE zeba@9n{IxXyzknygEv=SezpGE+gV5MY`ybo(doy_&p(@Y;z8GjW2c{gJM!SezT2;N zTzkIz=F3BO-)+0{Gq zG5^Af60fyE`eOXar{7lXf3U&g&Bg|W7h9^%=Iy?pH-Yu@ireph?5R6{KUqgcq^Y^3 zwXMCQvqMHqQn-J@#7UEKVDKS~T zZQFP3+_ihp-aWEn^1O$S96fgY#K}`9<;A$qU$}Vb@|COCu8T3=x_#&Fz55RyK4N5g z`t13Om#<#GdCSE5;p3;zU%r0({)3hI_n*K2{xdLh$yhvSIM~b~tQB)&!@|SN97FI0?C+2u=UUqi2Me!?^2@95= c7oP9X%q8o!C8KGs$7HS8Q(IPEQedzK0Ol*~A^-pY literal 0 HcmV?d00001 diff --git a/vendor/assets/images/slider_handles.png b/vendor/assets/images/slider_handles.png new file mode 100644 index 0000000000000000000000000000000000000000..b95a46eca97b9001da25067948cb05a0021be2a1 GIT binary patch literal 4453 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 L^>bP0l+XkK15at` literal 0 HcmV?d00001 diff --git a/vendor/assets/images/ui-icons_222222_256x240.png b/vendor/assets/images/ui-icons_222222_256x240.png new file mode 100644 index 0000000000000000000000000000000000000000..b273ff111d219c9b9a8b96d57683d0075fb7871a GIT binary patch literal 4369 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 B0@(ln literal 0 HcmV?d00001 diff --git a/vendor/assets/images/ui-icons_454545_256x240.png b/vendor/assets/images/ui-icons_454545_256x240.png new file mode 100644 index 0000000000000000000000000000000000000000..59bd45b907c4fd965697774ce8c5fc6b2fd9c105 GIT binary patch literal 4369 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 BAu9j? literal 0 HcmV?d00001 diff --git a/vendor/assets/javascripts/jquery.tagify.js b/vendor/assets/javascripts/jquery.tagify.js deleted file mode 100644 index f22d4c71..00000000 --- a/vendor/assets/javascripts/jquery.tagify.js +++ /dev/null @@ -1,143 +0,0 @@ -/* Author: Alicia Liu */ - -(function ($) { - - $.widget("ui.tagify", { - options: { - delimiters: [13, 188], // what user can type to complete a tag in char codes: [enter], [comma] - outputDelimiter: ',', // delimiter for tags in original input field - cssClass: 'tagify-container', // CSS class to style the tagify div and tags, see stylesheet - addTagPrompt: 'add tags' // placeholder text - }, - - _create: function() { - var self = this, - el = self.element, - opts = self.options; - - this.tags = []; - - // hide text field and replace with a div that contains it's own input field for entering tags - this.tagInput = $("") - .attr( 'placeholder', opts.addTagPrompt ) - .keypress( function(e) { - var $this = $(this), - pressed = e.which; - - for ( i in opts.delimiters ) { - - if (pressed == opts.delimiters[i]) { - self.add( $this.val() ); - e.preventDefault(); - return false; - } - } - }) - // for some reason, in Safari, backspace is only recognized on keyup - .keyup( function(e) { - var $this = $(this), - pressed = e.which; - - // if backspace is hit with no input, remove the last tag - if (pressed == 8) { // backspace - if ( $this.val() == "" ) { - self.remove(); - return false; - } - return; - } - }); - - this.tagDiv = $("
") - .addClass( opts.cssClass ) - .click( function() { - $(this).children('input').focus(); - }) - .append( this.tagInput ) - .insertAfter( el.hide() ); - - // if the field isn't empty, parse the field for tags, and prepopulate existing tags - var initVal = $.trim( el.val() ); - - if ( initVal ) { - var initTags = initVal.split( opts.outputDelimiter ); - $.each( initTags, function(i, tag) { - self.add( tag ); - }); - } - }, - - _setOption: function( key, value ) { - options.key = value; - }, - - // add a tag, public function - add: function(text) { - var self = this; - text = text || self.tagInput.val(); - if (text) { - var tagIndex = self.tags.length; - - var removeButton = $("x") - .click( function() { - self.remove( tagIndex ); - return false; - }); - var newTag = $("") - .text( text ) - .append( removeButton ); - - self.tagInput.before( newTag ); - self.tags.push( text ); - self.tagInput.val(''); - } - }, - - // remove a tag by index, public function - // if index is blank, remove the last tag - remove: function( tagIndex ) { - var self = this; - if ( tagIndex == null || tagIndex === (self.tags.length - 1) ) { - this.tagDiv.children("span").last().remove(); - self.tags.pop(); - } - if ( typeof(tagIndex) == 'number' ) { - // otherwise just hide this tag, and we don't mess up the index - this.tagDiv.children( "span:eq(" + tagIndex + ")" ).hide(); - // we rely on the serialize function to remove null values - delete( self.tags[tagIndex] ); - } - }, - - // serialize the tags with the given delimiter, and write it back into the tagified field - serialize: function() { - var self = this; - var delim = self.options.outputDelimiter; - var tagsStr = self.tags.join( delim ); - - // our tags might have deleted entries, remove them here - var dupes = new RegExp(delim + delim + '+', 'g'); // regex: /,,+/g - var ends = new RegExp('^' + delim + '|' + delim + '$', 'g'); // regex: /^,|,$/g - var outputStr = tagsStr.replace( dupes, delim ).replace(ends, ''); - - self.element.val(outputStr); - return outputStr; - }, - - inputField: function() { - return this.tagInput; - }, - - containerDiv: function() { - return this.tagDiv; - }, - - // remove the div, and show original input - destroy: function() { - $.Widget.prototype.destroy.apply(this); - this.tagDiv.remove(); - this.element.show(); - } - }); - -})(jQuery); \ No newline at end of file diff --git a/vendor/assets/javascripts/jquery.ui.selectmenu.js b/vendor/assets/javascripts/jquery.ui.selectmenu.js deleted file mode 100644 index 957fe4d8..00000000 --- a/vendor/assets/javascripts/jquery.ui.selectmenu.js +++ /dev/null @@ -1,844 +0,0 @@ - /* - * jQuery UI selectmenu dev version - * - * Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT (MIT-LICENSE.txt) - * and GPL (GPL-LICENSE.txt) licenses. - * - * http://docs.jquery.com/UI - * https://github.com/fnagel/jquery-ui/wiki/Selectmenu - */ - -(function($) { - -$.widget("ui.selectmenu", { - getter: "value", - version: "1.8", - eventPrefix: "selectmenu", - options: { - transferClasses: true, - typeAhead: "sequential", - style: 'dropdown', - positionOptions: { - my: "left top", - at: "left bottom", - offset: null - }, - width: null, - menuWidth: null, - handleWidth: 26, - maxHeight: null, - icons: null, - format: null, - bgImage: function() {}, - wrapperElement: "
" - }, - - _create: function() { - var self = this, o = this.options; - - // set a default id value, generate a new random one if not set by developer - var selectmenuId = this.element.attr( 'id' ) || 'ui-selectmenu-' + Math.random().toString( 16 ).slice( 2, 10 ); - - // quick array of button and menu id's - this.ids = [ selectmenuId + '-button', selectmenuId + '-menu' ]; - - // define safe mouseup for future toggling - this._safemouseup = true; - - // create menu button wrapper - this.newelement = $( '', { - 'class': this.widgetBaseClass + ' ui-widget ui-state-default ui-corner-all', - 'id' : this.ids[ 0 ], - 'role': 'button', - 'href': '#nogo', - 'tabindex': this.element.attr( 'disabled' ) ? 1 : 0, - 'aria-haspopup': true, - 'aria-owns': this.ids[ 1 ] - }); - this.newelementWrap = $( o.wrapperElement ) - .append( this.newelement ) - .insertAfter( this.element ); - - // transfer tabindex - var tabindex = this.element.attr( 'tabindex' ); - if ( tabindex ) { - this.newelement.attr( 'tabindex', tabindex ); - } - - // save reference to select in data for ease in calling methods - this.newelement.data( 'selectelement', this.element ); - - // menu icon - this.selectmenuIcon = $( '' ) - .prependTo( this.newelement ); - - // append status span to button - this.newelement.prepend( '' ); - - // make associated form label trigger focus - $( 'label[for="' + selectmenuId + '"]' ) - .attr( 'for', this.ids[0] ) - .bind( 'click.selectmenu', function() { - self.newelement[0].focus(); - return false; - }); - - // click toggle for menu visibility - this.newelement - .bind('mousedown.selectmenu', function(event) { - self._toggle(event, true); - // make sure a click won't open/close instantly - if (o.style == "popup") { - self._safemouseup = false; - setTimeout(function() { self._safemouseup = true; }, 300); - } - return false; - }) - .bind('click.selectmenu', function() { - return false; - }) - .bind("keydown.selectmenu", function(event) { - var ret = false; - switch (event.keyCode) { - case $.ui.keyCode.ENTER: - ret = true; - break; - case $.ui.keyCode.SPACE: - self._toggle(event); - break; - case $.ui.keyCode.UP: - if (event.altKey) { - self.open(event); - } else { - self._moveSelection(-1); - } - break; - case $.ui.keyCode.DOWN: - if (event.altKey) { - self.open(event); - } else { - self._moveSelection(1); - } - break; - case $.ui.keyCode.LEFT: - self._moveSelection(-1); - break; - case $.ui.keyCode.RIGHT: - self._moveSelection(1); - break; - case $.ui.keyCode.TAB: - ret = true; - break; - default: - ret = true; - } - return ret; - }) - .bind('keypress.selectmenu', function(event) { - self._typeAhead(event.which, 'mouseup'); - return true; - }) - .bind('mouseover.selectmenu focus.selectmenu', function() { - if (!o.disabled) { - $(this).addClass(self.widgetBaseClass + '-focus ui-state-hover'); - } - }) - .bind('mouseout.selectmenu blur.selectmenu', function() { - if (!o.disabled) { - $(this).removeClass(self.widgetBaseClass + '-focus ui-state-hover'); - } - }); - - // document click closes menu - $(document).bind("mousedown.selectmenu", function(event) { - self.close(event); - }); - - // change event on original selectmenu - this.element - .bind("click.selectmenu", function() { - self._refreshValue(); - }) - // FIXME: newelement can be null under unclear circumstances in IE8 - // TODO not sure if this is still a problem (fnagel 20.03.11) - .bind("focus.selectmenu", function() { - if (self.newelement) { - self.newelement[0].focus(); - } - }); - - // set width when not set via options - if (!o.width) { - o.width = this.element.outerWidth(); - } - // set menu button width - this.newelement.width(o.width); - - // hide original selectmenu element - this.element.hide(); - - // create menu portion, append to body - this.list = $( '
    ', { - 'class': 'ui-widget ui-widget-content', - 'aria-hidden': true, - 'role': 'listbox', - 'aria-labelledby': this.ids[0], - 'id': this.ids[1] - }); - this.listWrap = $( o.wrapperElement ) - .addClass( self.widgetBaseClass + '-menu' ) - .append( this.list ) - .appendTo( 'body' ); - - // transfer menu click to menu button - this.list - .bind("keydown.selectmenu", function(event) { - var ret = false; - switch (event.keyCode) { - case $.ui.keyCode.UP: - if (event.altKey) { - self.close(event, true); - } else { - self._moveFocus(-1); - } - break; - case $.ui.keyCode.DOWN: - if (event.altKey) { - self.close(event, true); - } else { - self._moveFocus(1); - } - break; - case $.ui.keyCode.LEFT: - self._moveFocus(-1); - break; - case $.ui.keyCode.RIGHT: - self._moveFocus(1); - break; - case $.ui.keyCode.HOME: - self._moveFocus(':first'); - break; - case $.ui.keyCode.PAGE_UP: - self._scrollPage('up'); - break; - case $.ui.keyCode.PAGE_DOWN: - self._scrollPage('down'); - break; - case $.ui.keyCode.END: - self._moveFocus(':last'); - break; - case $.ui.keyCode.ENTER: - case $.ui.keyCode.SPACE: - self.close(event, true); - $(event.target).parents('li:eq(0)').trigger('mouseup'); - break; - case $.ui.keyCode.TAB: - ret = true; - self.close(event, true); - $(event.target).parents('li:eq(0)').trigger('mouseup'); - break; - case $.ui.keyCode.ESCAPE: - self.close(event, true); - break; - default: - ret = true; - } - return ret; - }) - .bind('keypress.selectmenu', function(event) { - self._typeAhead(event.which, 'focus'); - return true; - }) - // this allows for using the scrollbar in an overflowed list - .bind( 'mousedown.selectmenu mouseup.selectmenu', function() { return false; }); - - // needed when window is resized - // TODO seems to be useless, but causes errors (fnagel 01.08.11) - // see: https://github.com/fnagel/jquery-ui/issues/147 - // $(window).bind( "resize.selectmenu", $.proxy( self._refreshPosition, this ) ); - }, - - _init: function() { - var self = this, o = this.options; - - // serialize selectmenu element options - var selectOptionData = []; - this.element - .find('option') - .each(function() { - var opt = $(this); - selectOptionData.push({ - value: opt.attr('value'), - text: self._formatText(opt.text()), - selected: opt.attr('selected'), - disabled: opt.attr('disabled'), - classes: opt.attr('class'), - typeahead: opt.attr('typeahead'), - parentOptGroup: opt.parent('optgroup'), - bgImage: o.bgImage.call(opt) - }); - }); - - // active state class is only used in popup style - var activeClass = (self.options.style == "popup") ? " ui-state-active" : ""; - - // empty list so we can refresh the selectmenu via selectmenu() - this.list.html(""); - - // write li's - if (selectOptionData.length) { - for (var i = 0; i < selectOptionData.length; i++) { - var thisLiAttr = { role : 'presentation' }; - if ( selectOptionData[ i ].disabled ) { - thisLiAttr[ 'class' ] = this.namespace + '-state-disabled'; - } - var thisAAttr = { - html: selectOptionData[i].text, - href : '#nogo', - tabindex : -1, - role : 'option', - 'aria-selected' : false - }; - if ( selectOptionData[ i ].disabled ) { - thisAAttr[ 'aria-disabled' ] = selectOptionData[ i ].disabled; - } - if ( selectOptionData[ i ].typeahead ) { - thisAAttr[ 'typeahead' ] = selectOptionData[ i ].typeahead; - } - var thisA = $('', thisAAttr); - var thisLi = $('
  • ', thisLiAttr) - .append(thisA) - .data('index', i) - .addClass(selectOptionData[i].classes) - .data('optionClasses', selectOptionData[i].classes || '') - .bind("mouseup.selectmenu", function(event) { - if (self._safemouseup && !self._disabled(event.currentTarget) && !self._disabled($( event.currentTarget ).parents( "ul>li." + self.widgetBaseClass + "-group " )) ) { - var changed = $(this).data('index') != self._selectedIndex(); - self.index($(this).data('index')); - self.select(event); - if (changed) { - self.change(event); - } - self.close(event, true); - } - return false; - }) - .bind("click.selectmenu", function() { - return false; - }) - .bind('mouseover.selectmenu focus.selectmenu', function(e) { - // no hover if diabled - if (!$(e.currentTarget).hasClass(self.namespace + '-state-disabled') && !$(e.currentTarget).parent("ul").parent("li").hasClass(self.namespace + '-state-disabled')) { - self._selectedOptionLi().addClass(activeClass); - self._focusedOptionLi().removeClass(self.widgetBaseClass + '-item-focus ui-state-hover'); - $(this).removeClass('ui-state-active').addClass(self.widgetBaseClass + '-item-focus ui-state-hover'); - } - }) - .bind('mouseout.selectmenu blur.selectmenu', function() { - if ($(this).is(self._selectedOptionLi().selector)) { - $(this).addClass(activeClass); - } - $(this).removeClass(self.widgetBaseClass + '-item-focus ui-state-hover'); - }); - - // optgroup or not... - if ( selectOptionData[i].parentOptGroup.length ) { - var optGroupName = self.widgetBaseClass + '-group-' + this.element.find( 'optgroup' ).index( selectOptionData[i].parentOptGroup ); - if (this.list.find( 'li.' + optGroupName ).length ) { - this.list.find( 'li.' + optGroupName + ':last ul' ).append( thisLi ); - } else { - $('
  • ') - .appendTo( this.list ) - .find( 'ul' ) - .append( thisLi ); - } - } else { - thisLi.appendTo(this.list); - } - - // append icon if option is specified - if (o.icons) { - for (var j in o.icons) { - if (thisLi.is(o.icons[j].find)) { - thisLi - .data('optionClasses', selectOptionData[i].classes + ' ' + self.widgetBaseClass + '-hasIcon') - .addClass(self.widgetBaseClass + '-hasIcon'); - var iconClass = o.icons[j].icon || ""; - thisLi - .find('a:eq(0)') - .prepend(''); - if (selectOptionData[i].bgImage) { - thisLi.find('span').css('background-image', selectOptionData[i].bgImage); - } - } - } - } - } - } else { - $('
  • ').appendTo(this.list); - } - // we need to set and unset the CSS classes for dropdown and popup style - var isDropDown = ( o.style == 'dropdown' ); - this.newelement - .toggleClass( self.widgetBaseClass + '-dropdown', isDropDown ) - .toggleClass( self.widgetBaseClass + '-popup', !isDropDown ); - this.list - .toggleClass( self.widgetBaseClass + '-menu-dropdown ui-corner-bottom', isDropDown ) - .toggleClass( self.widgetBaseClass + '-menu-popup ui-corner-all', !isDropDown ) - // add corners to top and bottom menu items - .find( 'li:first' ) - .toggleClass( 'ui-corner-top', !isDropDown ) - .end().find( 'li:last' ) - .addClass( 'ui-corner-bottom' ); - this.selectmenuIcon - .toggleClass( 'ui-icon-triangle-1-s', isDropDown ) - .toggleClass( 'ui-icon-triangle-2-n-s', !isDropDown ); - - // transfer classes to selectmenu and list - if ( o.transferClasses ) { - var transferClasses = this.element.attr( 'class' ) || ''; - this.newelement.add( this.list ).addClass( transferClasses ); - } - - // set menu width to either menuWidth option value, width option value, or select width - if ( o.style == 'dropdown' ) { - this.list.width( o.menuWidth ? o.menuWidth : o.width ); - } else { - this.list.width( o.menuWidth ? o.menuWidth : o.width - o.handleWidth ); - } - - // reset height to auto - this.list.css( 'height', 'auto' ); - var listH = this.listWrap.height(); - // calculate default max height - if ( o.maxHeight && o.maxHeight < listH ) { - this.list.height( o.maxHeight ); - } else { - var winH = $( window ).height() / 3; - if ( winH < listH ) this.list.height( winH ); - } - - // save reference to actionable li's (not group label li's) - this._optionLis = this.list.find( 'li:not(.' + self.widgetBaseClass + '-group)' ); - - // transfer disabled state - if ( this.element.attr( 'disabled' ) ) { - this.disable(); - } else { - this.enable() - } - - // update value - this.index( this._selectedIndex() ); - - // needed when selectmenu is placed at the very bottom / top of the page - window.setTimeout( function() { - self._refreshPosition(); - }, 200 ); - }, - - destroy: function() { - this.element.removeData( this.widgetName ) - .removeClass( this.widgetBaseClass + '-disabled' + ' ' + this.namespace + '-state-disabled' ) - .removeAttr( 'aria-disabled' ) - .unbind( ".selectmenu" ); - - // TODO unneded as event binding has been disabled - // $( window ).unbind( ".selectmenu" ); - $( document ).unbind( ".selectmenu" ); - - // unbind click on label, reset its for attr - $( 'label[for=' + this.newelement.attr('id') + ']' ) - .attr( 'for', this.element.attr( 'id' ) ) - .unbind( '.selectmenu' ); - - this.newelementWrap.remove(); - this.listWrap.remove(); - - this.element.show(); - - // call widget destroy function - $.Widget.prototype.destroy.apply(this, arguments); - }, - - _typeAhead: function( code, eventType ){ - var self = this, focusFound = false, C = String.fromCharCode(code).toUpperCase(); - c = C.toLowerCase(); - - if ( self.options.typeAhead == 'sequential' ) { - // clear the timeout so we can use _prevChar - window.clearTimeout('ui.selectmenu-' + self.selectmenuId); - - // define our find var - var find = typeof( self._prevChar ) == 'undefined' ? '' : self._prevChar.join( '' ); - - function focusOptSeq( elem, ind, c ){ - focusFound = true; - $( elem ).trigger( eventType ); - typeof( self._prevChar ) == 'undefined' ? self._prevChar = [ c ] : self._prevChar[ self._prevChar.length ] = c; - } - this.list.find( 'li a' ).each( function( i ) { - if ( !focusFound ) { - // allow the typeahead attribute on the option tag for a more specific lookup - var thisText = $( this ).attr( 'typeahead' ) || $(this).text(); - if ( thisText.indexOf( find + C ) === 0 ) { - focusOptSeq( this, i, C ); - } else if (thisText.indexOf(find+c) === 0 ) { - focusOptSeq( this, i, c ); - } - } - }); - // set a 1 second timeout for sequenctial typeahead - // keep this set even if we have no matches so it doesnt typeahead somewhere else - window.setTimeout( function( el ) { - self._prevChar = undefined; - }, 1000, self ); - - } else { - // define self._prevChar if needed - if ( !self._prevChar ) { self._prevChar = [ '' , 0 ]; } - - focusFound = false; - function focusOpt( elem, ind ){ - focusFound = true; - $( elem ).trigger( eventType ); - self._prevChar[ 1 ] = ind; - } - this.list.find( 'li a' ).each(function( i ){ - if (!focusFound){ - var thisText = $(this).text(); - if ( thisText.indexOf( C ) === 0 || thisText.indexOf( c ) === 0 ) { - if (self._prevChar[0] == C){ - if ( self._prevChar[ 1 ] < i ){ focusOpt( this, i ); } - } else{ - focusOpt( this, i ); - } - } - } - }); - this._prevChar[ 0 ] = C; - } - }, - - // returns some usefull information, called by callbacks only - _uiHash: function() { - var index = this.index(); - return { - index: index, - option: $("option", this.element).get(index), - value: this.element[0].value - }; - }, - - open: function(event) { - var self = this, o = this.options; - if ( self.newelement.attr("aria-disabled") != 'true' ) { - self._closeOthers(event); - self.newelement.addClass('ui-state-active'); - - self.listWrap.appendTo( o.appendTo ); - self.list.attr('aria-hidden', false); - - if ( o.style == "dropdown" ) { - self.newelement.removeClass('ui-corner-all').addClass('ui-corner-top'); - } - - self.listWrap.addClass( self.widgetBaseClass + '-open' ); - // positioning needed for IE7 (tested 01.08.11 on MS VPC Image) - // see https://github.com/fnagel/jquery-ui/issues/147 - if ( $.browser.msie && $.browser.version.substr( 0,1 ) == 7 ) { - self._refreshPosition(); - } - var selected = self.list.attr('aria-hidden', false).find('li:not(.' + self.widgetBaseClass + '-group):eq(' + self._selectedIndex() + '):visible a'); - if (selected.length) selected[0].focus(); - // positioning needed for FF, Chrome, IE8, IE7, IE6 (tested 01.08.11 on MS VPC Image) - self._refreshPosition(); - - self._trigger("open", event, self._uiHash()); - } - }, - - close: function(event, retainFocus) { - if ( this.newelement.is('.ui-state-active') ) { - this.newelement - .removeClass('ui-state-active'); - this.listWrap.removeClass(this.widgetBaseClass + '-open'); - this.list.attr('aria-hidden', true); - if ( this.options.style == "dropdown" ) { - this.newelement.removeClass('ui-corner-top').addClass('ui-corner-all'); - } - if ( retainFocus ) { - this.newelement.focus(); - } - this._trigger("close", event, this._uiHash()); - } - }, - - change: function(event) { - this.element.trigger("change"); - this._trigger("change", event, this._uiHash()); - }, - - select: function(event) { - if (this._disabled(event.currentTarget)) { return false; } - this._trigger("select", event, this._uiHash()); - }, - - _closeOthers: function(event) { - $('.' + this.widgetBaseClass + '.ui-state-active').not(this.newelement).each(function() { - $(this).data('selectelement').selectmenu('close', event); - }); - $('.' + this.widgetBaseClass + '.ui-state-hover').trigger('mouseout'); - }, - - _toggle: function(event, retainFocus) { - if ( this.list.parent().is('.' + this.widgetBaseClass + '-open') ) { - this.close(event, retainFocus); - } else { - this.open(event); - } - }, - - _formatText: function(text) { - return (this.options.format ? this.options.format(text) : text); - }, - - _selectedIndex: function() { - return this.element[0].selectedIndex; - }, - - _selectedOptionLi: function() { - return this._optionLis.eq(this._selectedIndex()); - }, - - _focusedOptionLi: function() { - return this.list.find('.' + this.widgetBaseClass + '-item-focus'); - }, - - _moveSelection: function(amt, recIndex) { - // do nothing if disabled - if (!this.options.disabled) { - var currIndex = parseInt(this._selectedOptionLi().data('index') || 0, 10); - var newIndex = currIndex + amt; - // do not loop when using up key - - if (newIndex < 0) { - newIndex = 0; - } - if (newIndex > this._optionLis.size() - 1) { - newIndex = this._optionLis.size() - 1; - } - // Occurs when a full loop has been made - if (newIndex === recIndex) { return false; } - - if (this._optionLis.eq(newIndex).hasClass( this.namespace + '-state-disabled' )) { - // if option at newIndex is disabled, call _moveFocus, incrementing amt by one - (amt > 0) ? ++amt : --amt; - this._moveSelection(amt, newIndex); - } else { - return this._optionLis.eq(newIndex).trigger('mouseup'); - } - } - }, - - _moveFocus: function(amt, recIndex) { - if (!isNaN(amt)) { - var currIndex = parseInt(this._focusedOptionLi().data('index') || 0, 10); - var newIndex = currIndex + amt; - } else { - var newIndex = parseInt(this._optionLis.filter(amt).data('index'), 10); - } - - if (newIndex < 0) { - newIndex = 0; - } - if (newIndex > this._optionLis.size() - 1) { - newIndex = this._optionLis.size() - 1; - } - - //Occurs when a full loop has been made - if (newIndex === recIndex) { return false; } - - var activeID = this.widgetBaseClass + '-item-' + Math.round(Math.random() * 1000); - - this._focusedOptionLi().find('a:eq(0)').attr('id', ''); - - if (this._optionLis.eq(newIndex).hasClass( this.namespace + '-state-disabled' )) { - // if option at newIndex is disabled, call _moveFocus, incrementing amt by one - (amt > 0) ? ++amt : --amt; - this._moveFocus(amt, newIndex); - } else { - this._optionLis.eq(newIndex).find('a:eq(0)').attr('id',activeID).focus(); - } - - this.list.attr('aria-activedescendant', activeID); - }, - - _scrollPage: function(direction) { - var numPerPage = Math.floor(this.list.outerHeight() / this.list.find('li:first').outerHeight()); - numPerPage = (direction == 'up' ? -numPerPage : numPerPage); - this._moveFocus(numPerPage); - }, - - _setOption: function(key, value) { - this.options[key] = value; - // set - if (key == 'disabled') { - this.close(); - this.element - .add(this.newelement) - .add(this.list)[value ? 'addClass' : 'removeClass']( - this.widgetBaseClass + '-disabled' + ' ' + - this.namespace + '-state-disabled') - .attr("aria-disabled", value); - } - }, - - disable: function(index, type){ - // if options is not provided, call the parents disable function - if ( typeof( index ) == 'undefined' ) { - this._setOption( 'disabled', true ); - } else { - if ( type == "optgroup" ) { - this._disableOptgroup(index); - } else { - this._disableOption(index); - } - } - }, - - enable: function(index, type) { - // if options is not provided, call the parents enable function - if ( typeof( index ) == 'undefined' ) { - this._setOption('disabled', false); - } else { - if ( type == "optgroup" ) { - this._enableOptgroup(index); - } else { - this._enableOption(index); - } - } - }, - - _disabled: function(elem) { - return $(elem).hasClass( this.namespace + '-state-disabled' ); - }, - - _disableOption: function(index) { - var optionElem = this._optionLis.eq(index); - if (optionElem) { - optionElem.addClass(this.namespace + '-state-disabled') - .find("a").attr("aria-disabled", true); - this.element.find("option").eq(index).attr("disabled", "disabled"); - } - }, - - _enableOption: function(index) { - var optionElem = this._optionLis.eq(index); - if (optionElem) { - optionElem.removeClass( this.namespace + '-state-disabled' ) - .find("a").attr("aria-disabled", false); - this.element.find("option").eq(index).removeAttr("disabled"); - } - }, - - _disableOptgroup: function(index) { - var optGroupElem = this.list.find( 'li.' + this.widgetBaseClass + '-group-' + index ); - if (optGroupElem) { - optGroupElem.addClass(this.namespace + '-state-disabled') - .attr("aria-disabled", true); - this.element.find("optgroup").eq(index).attr("disabled", "disabled"); - } - }, - - _enableOptgroup: function(index) { - var optGroupElem = this.list.find( 'li.' + this.widgetBaseClass + '-group-' + index ); - if (optGroupElem) { - optGroupElem.removeClass(this.namespace + '-state-disabled') - .attr("aria-disabled", false); - this.element.find("optgroup").eq(index).removeAttr("disabled"); - } - }, - - index: function(newValue) { - if (arguments.length) { - if (!this._disabled($(this._optionLis[newValue]))) { - this.element[0].selectedIndex = newValue; - this._refreshValue(); - } else { - return false; - } - } else { - return this._selectedIndex(); - } - }, - - value: function(newValue) { - if (arguments.length) { - this.element[0].value = newValue; - this._refreshValue(); - } else { - return this.element[0].value; - } - }, - - _refreshValue: function() { - var activeClass = (this.options.style == "popup") ? " ui-state-active" : ""; - var activeID = this.widgetBaseClass + '-item-' + Math.round(Math.random() * 1000); - // deselect previous - this.list - .find('.' + this.widgetBaseClass + '-item-selected') - .removeClass(this.widgetBaseClass + "-item-selected" + activeClass) - .find('a') - .attr('aria-selected', 'false') - .attr('id', ''); - // select new - this._selectedOptionLi() - .addClass(this.widgetBaseClass + "-item-selected" + activeClass) - .find('a') - .attr('aria-selected', 'true') - .attr('id', activeID); - - // toggle any class brought in from option - var currentOptionClasses = (this.newelement.data('optionClasses') ? this.newelement.data('optionClasses') : ""); - var newOptionClasses = (this._selectedOptionLi().data('optionClasses') ? this._selectedOptionLi().data('optionClasses') : ""); - this.newelement - .removeClass(currentOptionClasses) - .data('optionClasses', newOptionClasses) - .addClass( newOptionClasses ) - .find('.' + this.widgetBaseClass + '-status') - .html( - this._selectedOptionLi() - .find('a:eq(0)') - .html() - ); - - this.list.attr('aria-activedescendant', activeID); - }, - - _refreshPosition: function() { - var o = this.options; - - // if its a native pop-up we need to calculate the position of the selected li - if ( o.style == "popup" && !o.positionOptions.offset ) { - var selected = this._selectedOptionLi(); - var _offset = "0 -" + ( selected.outerHeight() + selected.offset().top - this.list.offset().top ); - } - // update zIndex if jQuery UI is able to process - var zIndexElement = this.element.zIndex(); - if ( zIndexElement ) { - this.listWrap.css( 'zIndex', zIndexElement ); - } - this.listWrap.position({ - // set options for position plugin - of: o.positionOptions.of || this.newelement, - my: o.positionOptions.my, - at: o.positionOptions.at, - offset: o.positionOptions.offset || _offset, - collision: o.positionOptions.collision || 'flip' - }); - } -}); - -})(jQuery); diff --git a/vendor/assets/stylesheets/jquery-ui/jquery.tagify.css b/vendor/assets/stylesheets/jquery-ui/jquery.tagify.css deleted file mode 100644 index d6c178f7..00000000 --- a/vendor/assets/stylesheets/jquery-ui/jquery.tagify.css +++ /dev/null @@ -1,34 +0,0 @@ -/* Tagify styles -Author: Alicia Liu test -*/ - -.tagify-container { -} - -.tagify-container > span { - display: inline-block; - padding: 8px 11px 8px 11px; - margin: 1px 5px 0px 0px; - border-radius: 4px; - border: 1px solid #d0e1ff; - background-color: #d0e1ff; - color: #0f326d; - font-weight: bold; - font-size: 14px; -} - -.tagify-container > span > a { - padding-left: 5px !important; - color: #83a5e1; - text-decoration: none; - font-weight: bold; -} - -.tagify-container > input { - border: 0 none; - width: 100px !important; -} - -.tagify-container > input:focus { - outline: none; -} \ No newline at end of file diff --git a/vendor/assets/stylesheets/jquery-ui/jquery.ui.selectmenu.css b/vendor/assets/stylesheets/jquery-ui/jquery.ui.selectmenu.css deleted file mode 100644 index 481adf5a..00000000 --- a/vendor/assets/stylesheets/jquery-ui/jquery.ui.selectmenu.css +++ /dev/null @@ -1,33 +0,0 @@ -/* Selectmenu -----------------------------------*/ -.ui-selectmenu { background:none; font-size:12px;display: block; display: inline-block; position: relative; height: 2.2em; vertical-align: middle; text-decoration: none; overflow: hidden; zoom: 1; } -.ui-selectmenu-icon { position:absolute; right:6px; margin-top:-8px; top: 50%; } -.ui-selectmenu-menu { padding:0; margin:0; position:absolute; top: 0; display: none; z-index: 1005;} /* z-index: 1005 to make selectmenu work with dialog */ -.ui-selectmenu-menu ul { padding:0; margin:0; list-style:none; position: relative; overflow: auto; overflow-y: auto ; overflow-x: hidden; } -.ui-selectmenu-open { display: block; } -.ui-selectmenu.ui-widget { background:none; } -.ui-selectmenu-menu-popup { margin-top: -1px; } -.ui-selectmenu-menu-dropdown { } -.ui-selectmenu-menu li.ui-state-active { background:#F7FBFC; border:none; padding:1px 0;} -.ui-selectmenu-menu li { padding:0; margin:0; display: block; border-top: 1px dotted transparent; border-bottom: 1px dotted transparent; border-right-width: 0 !important; border-left-width: 0 !important; font-weight: normal !important; } -.ui-selectmenu-menu li a,.ui-selectmenu-status { line-height: 1.4em; display: block; padding: .405em 2.1em .405em 1em; outline:none; text-decoration:none; } -.ui-selectmenu-menu li.ui-state-disabled a, .ui-state-disabled { cursor: default; } -.ui-selectmenu-menu li.ui-selectmenu-hasIcon a, -.ui-selectmenu-hasIcon .ui-selectmenu-status { padding-left: 20px; position: relative; margin-left: 5px; } -.ui-selectmenu-menu li .ui-icon, .ui-selectmenu-status .ui-icon { position: absolute; top: 1em; margin-top: -8px; left: 0; } -.ui-selectmenu-status { line-height: 1.4em; } -.ui-selectmenu-open li.ui-selectmenu-item-focus { background: none repeat scroll 0 0 #FFF6BF; border:1px solid #eaeaea;} -.ui-selectmenu-open li.ui-selectmenu-item-selected { } -.ui-selectmenu-menu li span,.ui-selectmenu-status span { display:block; margin-bottom: .2em; } -.ui-selectmenu-menu li .ui-selectmenu-item-header { font-weight: bold; } -.ui-selectmenu-menu li .ui-selectmenu-item-content { } -.ui-selectmenu-menu li .ui-selectmenu-item-footer { opacity: .8; } -/* for optgroups */ -.ui-selectmenu-menu .ui-selectmenu-group { font-size: 1em; } -.ui-selectmenu-menu .ui-selectmenu-group .ui-selectmenu-group-label { line-height: 1.4em; display:block; padding: .6em .5em 0; font-weight: bold; } -.ui-selectmenu-menu .ui-selectmenu-group ul { margin: 0; padding: 0; } -/* IE6 workaround (dotted transparent borders) */ -* html .ui-selectmenu-menu li { border-color: pink; filter:chroma(color=pink); width:100%; } -* html .ui-selectmenu-menu li a { position: relative } -/* IE7 workaround (opacity disabled) */ -*+html .ui-state-disabled, *+html .ui-state-disabled a { color: silver; } diff --git a/vendor/assets/stylesheets/jquery.ui.aristo.css b/vendor/assets/stylesheets/jquery.ui.aristo.css new file mode 100644 index 00000000..8cc6e787 --- /dev/null +++ b/vendor/assets/stylesheets/jquery.ui.aristo.css @@ -0,0 +1,738 @@ +/* + * jQuery UI CSS Framework 1.8.7 + * + * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Theming/API + */ + +/* Layout helpers +----------------------------------*/ +.ui-helper-hidden { display: none; } +.ui-helper-hidden-accessible { position: absolute !important; clip: rect(1px 1px 1px 1px); clip: rect(1px,1px,1px,1px); } +.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; } +.ui-helper-clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; } +.ui-helper-clearfix { display: inline-block; } +/* required comment for clearfix to work in Opera \*/ +* html .ui-helper-clearfix { height:1%; } +.ui-helper-clearfix { display:block; } +/* end clearfix */ +.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); } + + +/* Interaction Cues +----------------------------------*/ +.ui-state-disabled { cursor: default !important; } + + +/* Icons +----------------------------------*/ + +/* states and images */ +.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; } + + +/* Misc visuals +----------------------------------*/ + +/* Overlays */ +.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } + + +/* + * jQuery UI CSS Framework 1.8.7 + * + * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Theming/API + * + * To view and modify this theme, visit http://jqueryui.com/themeroller/?ctl=themeroller + */ + + +/* Component containers +----------------------------------*/ +.ui-widget { font-family: Arial,sans-serif; font-size: 1.1em; } +.ui-widget .ui-widget { font-size: 1em; } +.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Arial,sans-serif; font-size: 1em; } +.ui-widget-content { border: 1px solid #B6B6B6; background: #ffffff; color: #4F4F4F; } +.ui-widget-content a { color: #4F4F4F; } +.ui-widget-header { border: 1px solid #B6B6B6; color: #4F4F4F; font-weight: bold; } +.ui-widget-header { + background: #ededed url(bg_fallback.png) 0 0 repeat-x; /* Old browsers */ + background: -moz-linear-gradient(top, #ededed 0%, #c4c4c4 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#ededed), color-stop(100%,#c4c4c4)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #ededed 0%,#c4c4c4 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #ededed 0%,#c4c4c4 100%); /* Opera11.10+ */ + background: -ms-linear-gradient(top, #ededed 0%,#c4c4c4 100%); /* IE10+ */ + background: linear-gradient(top, #ededed 0%,#c4c4c4 100%); /* W3C */ +} +.ui-widget-header a { color: #4F4F4F; } + +/* Interaction states +----------------------------------*/ +.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #B6B6B6; font-weight: normal; color: #4F4F4F; } +.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { + background: #ededed url(bg_fallback.png) 0 0 repeat-x; /* Old browsers */ + background: -moz-linear-gradient(top, #ededed 0%, #c4c4c4 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#ededed), color-stop(100%,#c4c4c4)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #ededed 0%,#c4c4c4 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #ededed 0%,#c4c4c4 100%); /* Opera11.10+ */ + background: -ms-linear-gradient(top, #ededed 0%,#c4c4c4 100%); /* IE10+ */ + background: linear-gradient(top, #ededed 0%,#c4c4c4 100%); /* W3C */ + -webkit-box-shadow: 0 1px 0 rgba(255,255,255,0.6) inset; + -moz-box-shadow: 0 1px 0 rgba(255,255,255,0.6) inset; + box-shadow: 0 1px 0 rgba(255,255,255,0.6) inset; +} +.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #4F4F4F; text-decoration: none; } +.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #9D9D9D; font-weight: normal; color: #313131; } +.ui-state-hover a, .ui-state-hover a:hover { color: #313131; text-decoration: none; } +.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { + outline: none; + color: #1c4257; border: 1px solid #7096ab; + background: #ededed url(bg_fallback.png) 0 -50px repeat-x; /* Old browsers */ + background: -moz-linear-gradient(top, #b9e0f5 0%, #92bdd6 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#b9e0f5), color-stop(100%,#92bdd6)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #b9e0f5 0%,#92bdd6 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #b9e0f5 0%,#92bdd6 100%); /* Opera11.10+ */ + background: -ms-linear-gradient(top, #b9e0f5 0%,#92bdd6 100%); /* IE10+ */ + background: linear-gradient(top, #b9e0f5 0%,#92bdd6 100%); /* W3C */ + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; +} +.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #313131; text-decoration: none; } +.ui-widget :active { outline: none; } + +/* Interaction Cues +----------------------------------*/ +.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight { border: 1px solid #d2dbf4; background: #f4f8fd; color: #0d2054; -moz-border-radius: 0 !important; -webkit-border-radius: 0 !important; border-radius: 0 !important; } +.ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #363636; } +.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error { border: 1px solid #e2d0d0; background: #fcf0f0; color: #280b0b; -moz-border-radius: 0 !important; -webkit-border-radius: 0 !important; border-radius: 0 !important; } +.ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #cd0a0a; } +.ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #cd0a0a; } +.ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; } +.ui-priority-secondary, .ui-widget-content .ui-priority-secondary, .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; } +.ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; } + +/* Icons +----------------------------------*/ + +/* states and images */ +.ui-icon { width: 16px; height: 16px; background-image: url(ui-icons_222222_256x240.png); } +.ui-widget-content .ui-icon {background-image: url(ui-icons_222222_256x240.png); } +.ui-widget-header .ui-icon {background-image: url(ui-icons_222222_256x240.png); } +.ui-state-default .ui-icon { background-image: url(ui-icons_454545_256x240.png); } +.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(ui-icons_454545_256x240.png); } +.ui-state-active .ui-icon {background-image: url(ui-icons_454545_256x240.png); } +.ui-state-highlight .ui-icon {background-image: url(ui-icons_454545_256x240.png); } +.ui-state-error .ui-icon, .ui-state-error-text .ui-icon { background: url(icon_sprite.png) -16px 0 no-repeat !important; } +.ui-state-highlight .ui-icon, .ui-state-error .ui-icon { margin-top: -1px; } + +/* positioning */ +.ui-icon-carat-1-n { background-position: 0 0; } +.ui-icon-carat-1-ne { background-position: -16px 0; } +.ui-icon-carat-1-e { background-position: -32px 0; } +.ui-icon-carat-1-se { background-position: -48px 0; } +.ui-icon-carat-1-s { background-position: -64px 0; } +.ui-icon-carat-1-sw { background-position: -80px 0; } +.ui-icon-carat-1-w { background-position: -96px 0; } +.ui-icon-carat-1-nw { background-position: -112px 0; } +.ui-icon-carat-2-n-s { background-position: -128px 0; } +.ui-icon-carat-2-e-w { background-position: -144px 0; } +.ui-icon-triangle-1-n { background-position: 0 -16px; } +.ui-icon-triangle-1-ne { background-position: -16px -16px; } +.ui-icon-triangle-1-e { background-position: -32px -16px; } +.ui-icon-triangle-1-se { background-position: -48px -16px; } +.ui-icon-triangle-1-s { background-position: -64px -16px; } +.ui-icon-triangle-1-sw { background-position: -80px -16px; } +.ui-icon-triangle-1-w { background-position: -96px -16px; } +.ui-icon-triangle-1-nw { background-position: -112px -16px; } +.ui-icon-triangle-2-n-s { background-position: -128px -16px; } +.ui-icon-triangle-2-e-w { background-position: -144px -16px; } +.ui-icon-arrow-1-n { background-position: 0 -32px; } +.ui-icon-arrow-1-ne { background-position: -16px -32px; } +.ui-icon-arrow-1-e { background-position: -32px -32px; } +.ui-icon-arrow-1-se { background-position: -48px -32px; } +.ui-icon-arrow-1-s { background-position: -64px -32px; } +.ui-icon-arrow-1-sw { background-position: -80px -32px; } +.ui-icon-arrow-1-w { background-position: -96px -32px; } +.ui-icon-arrow-1-nw { background-position: -112px -32px; } +.ui-icon-arrow-2-n-s { background-position: -128px -32px; } +.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; } +.ui-icon-arrow-2-e-w { background-position: -160px -32px; } +.ui-icon-arrow-2-se-nw { background-position: -176px -32px; } +.ui-icon-arrowstop-1-n { background-position: -192px -32px; } +.ui-icon-arrowstop-1-e { background-position: -208px -32px; } +.ui-icon-arrowstop-1-s { background-position: -224px -32px; } +.ui-icon-arrowstop-1-w { background-position: -240px -32px; } +.ui-icon-arrowthick-1-n { background-position: 0 -48px; } +.ui-icon-arrowthick-1-ne { background-position: -16px -48px; } +.ui-icon-arrowthick-1-e { background-position: -32px -48px; } +.ui-icon-arrowthick-1-se { background-position: -48px -48px; } +.ui-icon-arrowthick-1-s { background-position: -64px -48px; } +.ui-icon-arrowthick-1-sw { background-position: -80px -48px; } +.ui-icon-arrowthick-1-w { background-position: -96px -48px; } +.ui-icon-arrowthick-1-nw { background-position: -112px -48px; } +.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; } +.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; } +.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; } +.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; } +.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; } +.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; } +.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; } +.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; } +.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; } +.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; } +.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; } +.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; } +.ui-icon-arrowreturn-1-w { background-position: -64px -64px; } +.ui-icon-arrowreturn-1-n { background-position: -80px -64px; } +.ui-icon-arrowreturn-1-e { background-position: -96px -64px; } +.ui-icon-arrowreturn-1-s { background-position: -112px -64px; } +.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; } +.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; } +.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; } +.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; } +.ui-icon-arrow-4 { background-position: 0 -80px; } +.ui-icon-arrow-4-diag { background-position: -16px -80px; } +.ui-icon-extlink { background-position: -32px -80px; } +.ui-icon-newwin { background-position: -48px -80px; } +.ui-icon-refresh { background-position: -64px -80px; } +.ui-icon-shuffle { background-position: -80px -80px; } +.ui-icon-transfer-e-w { background-position: -96px -80px; } +.ui-icon-transferthick-e-w { background-position: -112px -80px; } +.ui-icon-folder-collapsed { background-position: 0 -96px; } +.ui-icon-folder-open { background-position: -16px -96px; } +.ui-icon-document { background-position: -32px -96px; } +.ui-icon-document-b { background-position: -48px -96px; } +.ui-icon-note { background-position: -64px -96px; } +.ui-icon-mail-closed { background-position: -80px -96px; } +.ui-icon-mail-open { background-position: -96px -96px; } +.ui-icon-suitcase { background-position: -112px -96px; } +.ui-icon-comment { background-position: -128px -96px; } +.ui-icon-person { background-position: -144px -96px; } +.ui-icon-print { background-position: -160px -96px; } +.ui-icon-trash { background-position: -176px -96px; } +.ui-icon-locked { background-position: -192px -96px; } +.ui-icon-unlocked { background-position: -208px -96px; } +.ui-icon-bookmark { background-position: -224px -96px; } +.ui-icon-tag { background-position: -240px -96px; } +.ui-icon-home { background-position: 0 -112px; } +.ui-icon-flag { background-position: -16px -112px; } +.ui-icon-calendar { background-position: -32px -112px; } +.ui-icon-cart { background-position: -48px -112px; } +.ui-icon-pencil { background-position: -64px -112px; } +.ui-icon-clock { background-position: -80px -112px; } +.ui-icon-disk { background-position: -96px -112px; } +.ui-icon-calculator { background-position: -112px -112px; } +.ui-icon-zoomin { background-position: -128px -112px; } +.ui-icon-zoomout { background-position: -144px -112px; } +.ui-icon-search { background-position: -160px -112px; } +.ui-icon-wrench { background-position: -176px -112px; } +.ui-icon-gear { background-position: -192px -112px; } +.ui-icon-heart { background-position: -208px -112px; } +.ui-icon-star { background-position: -224px -112px; } +.ui-icon-link { background-position: -240px -112px; } +.ui-icon-cancel { background-position: 0 -128px; } +.ui-icon-plus { background-position: -16px -128px; } +.ui-icon-plusthick { background-position: -32px -128px; } +.ui-icon-minus { background-position: -48px -128px; } +.ui-icon-minusthick { background-position: -64px -128px; } +.ui-icon-close { background-position: -80px -128px; } +.ui-icon-closethick { background-position: -96px -128px; } +.ui-icon-key { background-position: -112px -128px; } +.ui-icon-lightbulb { background-position: -128px -128px; } +.ui-icon-scissors { background-position: -144px -128px; } +.ui-icon-clipboard { background-position: -160px -128px; } +.ui-icon-copy { background-position: -176px -128px; } +.ui-icon-contact { background-position: -192px -128px; } +.ui-icon-image { background-position: -208px -128px; } +.ui-icon-video { background-position: -224px -128px; } +.ui-icon-script { background-position: -240px -128px; } +.ui-icon-alert { background-position: 0 -144px; } +.ui-icon-info { background: url(icon_sprite.png) 0 0 no-repeat !important; } +.ui-icon-notice { background-position: -32px -144px; } +.ui-icon-help { background-position: -48px -144px; } +.ui-icon-check { background-position: -64px -144px; } +.ui-icon-bullet { background-position: -80px -144px; } +.ui-icon-radio-off { background-position: -96px -144px; } +.ui-icon-radio-on { background-position: -112px -144px; } +.ui-icon-pin-w { background-position: -128px -144px; } +.ui-icon-pin-s { background-position: -144px -144px; } +.ui-icon-play { background-position: 0 -160px; } +.ui-icon-pause { background-position: -16px -160px; } +.ui-icon-seek-next { background-position: -32px -160px; } +.ui-icon-seek-prev { background-position: -48px -160px; } +.ui-icon-seek-end { background-position: -64px -160px; } +.ui-icon-seek-start { background-position: -80px -160px; } +/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */ +.ui-icon-seek-first { background-position: -80px -160px; } +.ui-icon-stop { background-position: -96px -160px; } +.ui-icon-eject { background-position: -112px -160px; } +.ui-icon-volume-off { background-position: -128px -160px; } +.ui-icon-volume-on { background-position: -144px -160px; } +.ui-icon-power { background-position: 0 -176px; } +.ui-icon-signal-diag { background-position: -16px -176px; } +.ui-icon-signal { background-position: -32px -176px; } +.ui-icon-battery-0 { background-position: -48px -176px; } +.ui-icon-battery-1 { background-position: -64px -176px; } +.ui-icon-battery-2 { background-position: -80px -176px; } +.ui-icon-battery-3 { background-position: -96px -176px; } +.ui-icon-circle-plus { background-position: 0 -192px; } +.ui-icon-circle-minus { background-position: -16px -192px; } +.ui-icon-circle-close { background-position: -32px -192px; } +.ui-icon-circle-triangle-e { background-position: -48px -192px; } +.ui-icon-circle-triangle-s { background-position: -64px -192px; } +.ui-icon-circle-triangle-w { background-position: -80px -192px; } +.ui-icon-circle-triangle-n { background-position: -96px -192px; } +.ui-icon-circle-arrow-e { background-position: -112px -192px; } +.ui-icon-circle-arrow-s { background-position: -128px -192px; } +.ui-icon-circle-arrow-w { background-position: -144px -192px; } +.ui-icon-circle-arrow-n { background-position: -160px -192px; } +.ui-icon-circle-zoomin { background-position: -176px -192px; } +.ui-icon-circle-zoomout { background-position: -192px -192px; } +.ui-icon-circle-check { background-position: -208px -192px; } +.ui-icon-circlesmall-plus { background-position: 0 -208px; } +.ui-icon-circlesmall-minus { background-position: -16px -208px; } +.ui-icon-circlesmall-close { background-position: -32px -208px; } +.ui-icon-squaresmall-plus { background-position: -48px -208px; } +.ui-icon-squaresmall-minus { background-position: -64px -208px; } +.ui-icon-squaresmall-close { background-position: -80px -208px; } +.ui-icon-grip-dotted-vertical { background-position: 0 -224px; } +.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; } +.ui-icon-grip-solid-vertical { background-position: -32px -224px; } +.ui-icon-grip-solid-horizontal { background-position: -48px -224px; } +.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; } +.ui-icon-grip-diagonal-se { background-position: -80px -224px; } + + +/* Misc visuals +----------------------------------*/ + +/* Corner radius */ +.ui-corner-tl { -moz-border-radius-topleft: 3px; -webkit-border-top-left-radius: 3px; border-top-left-radius: 3px; } +.ui-corner-tr { -moz-border-radius-topright: 3px; -webkit-border-top-right-radius: 3px; border-top-right-radius: 3px; } +.ui-corner-bl { -moz-border-radius-bottomleft: 3px; -webkit-border-bottom-left-radius: 3px; border-bottom-left-radius: 3px; } +.ui-corner-br { -moz-border-radius-bottomright: 3px; -webkit-border-bottom-right-radius: 3px; border-bottom-right-radius: 3px; } +.ui-corner-top { -moz-border-radius-topleft: 3px; -webkit-border-top-left-radius: 3px; border-top-left-radius: 3px; -moz-border-radius-topright: 3px; -webkit-border-top-right-radius: 3px; border-top-right-radius: 3px; } +.ui-corner-bottom { -moz-border-radius-bottomleft: 3px; -webkit-border-bottom-left-radius: 3px; border-bottom-left-radius: 3px; -moz-border-radius-bottomright: 3px; -webkit-border-bottom-right-radius: 3px; border-bottom-right-radius: 3px; } +.ui-corner-right { -moz-border-radius-topright: 3px; -webkit-border-top-right-radius: 3px; border-top-right-radius: 3px; -moz-border-radius-bottomright: 3px; -webkit-border-bottom-right-radius: 3px; border-bottom-right-radius: 3px; } +.ui-corner-left { -moz-border-radius-topleft: 3px; -webkit-border-top-left-radius: 3px; border-top-left-radius: 3px; -moz-border-radius-bottomleft: 3px; -webkit-border-bottom-left-radius: 3px; border-bottom-left-radius: 3px; } +.ui-corner-all { -moz-border-radius: 3px; -webkit-border-radius: 3px; border-radius: 3px; } + +/* Overlays */ +.ui-widget-overlay { background: #262b33; opacity: .70;filter:Alpha(Opacity=70); } +.ui-widget-shadow { margin: -8px 0 0 -8px; padding: 8px; background: #000000; opacity: .30;filter:Alpha(Opacity=30); -moz-border-radius: 8px; -webkit-border-radius: 8px; border-radius: 8px; }/* + * jQuery UI Resizable 1.8.7 + * + * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Resizable#theming + */ +.ui-resizable { position: relative;} +.ui-resizable-handle { position: absolute; font-size: 0.1px; z-index: 999; display: block;} +.ui-resizable-disabled .ui-resizable-handle, .ui-resizable-autohide .ui-resizable-handle { display: none; } +.ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0; } +.ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0; } +.ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0; height: 100%; } +.ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0; height: 100%; } +.ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; } +.ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; } +.ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; } +.ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;}/* + * jQuery UI Selectable 1.8.7 + * + * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Selectable#theming + */ +.ui-selectable-helper { position: absolute; z-index: 100; border:1px dotted black; } +/* + * jQuery UI Accordion 1.8.7 + * + * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Accordion#theming + */ +/* IE/Win - Fix animation bug - #4615 */ +.ui-accordion { width: 100%; } +.ui-accordion .ui-accordion-header { cursor: pointer; position: relative; margin-top: 1px; zoom: 1; } +.ui-accordion .ui-accordion-header, .ui-accordion .ui-accordion-content { -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; } +.ui-accordion .ui-accordion-li-fix { display: inline; } +.ui-accordion .ui-accordion-header-active { border-bottom: 0 !important; } +.ui-accordion .ui-accordion-header a { display: block; font-size: 12px; font-weight: bold; padding: .5em .5em .5em .7em; } +.ui-accordion-icons .ui-accordion-header a { padding-left: 2.2em; } +.ui-accordion .ui-accordion-header .ui-icon { position: absolute; left: .5em; top: 50%; margin-top: -8px; } +.ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; zoom: 1; } +.ui-accordion .ui-accordion-content-active { display: block; }/* + * jQuery UI Autocomplete 1.8.7 + * + * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Autocomplete#theming + */ +.ui-autocomplete { + position: absolute; cursor: default; z-index: 3; + -moz-border-radius: 0; + -webkit-border-radius: 0; + border-radius: 0; + -moz-box-shadow: 0 1px 5px rgba(0,0,0,0.3); + -webkit-box-shadow: 0 1px 5px rgba(0,0,0,0.3); + box-shadow: 0 1px 5px rgba(0,0,0,0.3); +} + +/* workarounds */ +* html .ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */ + +/* + * jQuery UI Menu 1.8.7 + * + * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Menu#theming + */ +.ui-menu { + list-style:none; + padding: 2px; + margin: 0; + display:block; + float: left; +} +.ui-menu .ui-menu { + margin-top: -3px; +} +.ui-menu .ui-menu-item { + margin:0; + padding: 0; + zoom: 1; + float: left; + clear: left; + width: 100%; +} +.ui-menu .ui-menu-item a { + text-decoration:none; + display:block; + padding:.2em .4em; + line-height:1.5; + zoom:1; +} +.ui-menu .ui-menu-item a.ui-state-hover, +.ui-menu .ui-menu-item a.ui-state-active { + font-weight: normal; + margin: -1px; + background: #5f83b9; + color: #FFFFFF; + text-shadow: 0px 1px 1px #234386; + border-color: #466086; + -moz-border-radius: 0; + -webkit-border-radius: 0; + border-radius: 0; +} +/* + * jQuery UI Button 1.8.7 + * + * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Button#theming + */ +.ui-button { display: inline-block; position: relative; padding: 0; margin-right: .1em; text-decoration: none !important; cursor: pointer; text-align: center; zoom: 1; overflow: visible; -webkit-user-select: none; -moz-user-select: none; user-select: none; } /* the overflow property removes extra width in IE */ +.ui-button-icon-only { width: 2.2em; } /* to make room for the icon, a width needs to be set here */ +button.ui-button-icon-only { width: 2.4em; } /* button elements seem to need a little more width */ +.ui-button-icons-only { width: 3.4em; } +button.ui-button-icons-only { width: 3.7em; } + +/* button animation properties */ +.ui-button { + -webkit-transition: all 250ms ease-in-out; + -moz-transition: all 250ms ease-in-out; + -o-transition: all 250ms ease-in-out; + transition: all 250ms ease-in-out; +} + +/*states*/ +.ui-button.ui-state-hover { + -moz-box-shadow: 0 0 8px rgba(0, 0, 0, 0.15), 0 1px 0 rgba(255,255,255,0.8) inset; + -webkit-box-shadow: 0 0 8px rgba(0, 0, 0, 0.15), 0 1px 0 rgba(255,255,255,0.8) inset; + box-shadow: 0 0 8px rgba(0, 0, 0, 0.15), 0 1px 0 rgba(255,255,255,0.8) inset; +} +.ui-button.ui-state-focus { + outline: none; + color: #1c4257; + border-color: #7096ab; + background: #ededed url(bg_fallback.png) 0 -50px repeat-x; /* Old browsers */ + background: -moz-linear-gradient(top, #b9e0f5 0%, #92bdd6 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#b9e0f5), color-stop(100%,#92bdd6)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #b9e0f5 0%,#92bdd6 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #b9e0f5 0%,#92bdd6 100%); /* Opera11.10+ */ + background: -ms-linear-gradient(top, #b9e0f5 0%,#92bdd6 100%); /* IE10+ */ + background: linear-gradient(top, #b9e0f5 0%,#92bdd6 100%); /* W3C */ + -moz-box-shadow: 0 0 8px rgba(0, 0, 0, 0.15), 0 1px 0 rgba(255,255,255,0.8) inset; + -webkit-box-shadow: 0 0 8px rgba(0, 0, 0, 0.15), 0 1px 0 rgba(255,255,255,0.8) inset; + box-shadow: 0 0 8px rgba(0, 0, 0, 0.15), 0 1px 0 rgba(255,255,255,0.8) inset; +} + +/*button text element */ +.ui-button .ui-button-text { display: block; line-height: 1.4; font-size: 14px; font-weight: bold; text-shadow: 0 1px 0 rgba(255, 255, 255, 0.6); } +.ui-button-text-only .ui-button-text { padding: .4em 1em; } +.ui-button-icon-only .ui-button-text, .ui-button-icons-only .ui-button-text { padding: .4em; text-indent: -9999999px; } +.ui-button-text-icon-primary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 1em .4em 2.1em; } +.ui-button-text-icon-secondary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 2.1em .4em 1em; } +.ui-button-text-icons .ui-button-text { padding-left: 2.1em; padding-right: 2.1em; } +/* no icon support for input elements, provide padding by default */ +input.ui-button, .ui-widget-content input.ui-button { font-size: 14px; font-weight: bold; text-shadow: 0 1px 0 rgba(255, 255, 255, 0.6); padding: 0 1em !important; height: 33px; } +/*remove submit button internal padding in Firefox*/ +input.ui-button::-moz-focus-inner { + border: 0; + padding: 0; +} +/* fix webkits handling of the box model */ +@media screen and (-webkit-min-device-pixel-ratio:0) { + input.ui-button { + height: 31px !important; + } +} + + +/*button icon element(s) */ +.ui-button-icon-only .ui-icon, .ui-button-text-icon-primary .ui-icon, .ui-button-text-icon-secondary .ui-icon, .ui-button-text-icons .ui-icon, .ui-button-icons-only .ui-icon { position: absolute; top: 50%; margin-top: -8px; } +.ui-button-icon-only .ui-icon { left: 50%; margin-left: -8px; } +.ui-button-text-icon-primary .ui-button-icon-primary, .ui-button-text-icons .ui-button-icon-primary, .ui-button-icons-only .ui-button-icon-primary { left: .5em; } +.ui-button-text-icon-secondary .ui-button-icon-secondary, .ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; } +.ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; } + +/*button sets*/ +.ui-buttonset { margin-right: 7px; } +.ui-buttonset .ui-button { margin-left: 0; margin-right: -.3em; } +.ui-buttonset .ui-button.ui-state-active { color: #1c4257; border-color: #7096ab; } +.ui-buttonset .ui-button.ui-state-active { + background: #ededed url(bg_fallback.png) 0 -50px repeat-x; /* Old browsers */ + background: -moz-linear-gradient(top, #b9e0f5 0%, #92bdd6 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#b9e0f5), color-stop(100%,#92bdd6)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #b9e0f5 0%,#92bdd6 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #b9e0f5 0%,#92bdd6 100%); /* Opera11.10+ */ + background: -ms-linear-gradient(top, #b9e0f5 0%,#92bdd6 100%); /* IE10+ */ + background: linear-gradient(top, #b9e0f5 0%,#92bdd6 100%); /* W3C */ + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; +} + +/* workarounds */ +button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra padding in Firefox */ +/* + * jQuery UI Dialog 1.8.7 + * + * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Dialog#theming + */ +.ui-dialog { position: absolute; padding: 0; width: 300px; overflow: hidden; } +.ui-dialog { + -webkit-box-shadow: 0 2px 12px rgba(0,0,0,0.6); + -moz-box-shadow: 0 2px 12px rgba(0,0,0,0.6); + box-shadow: 0 2px 12px rgba(0,0,0,0.6); +} +.ui-dialog .ui-dialog-titlebar { padding: 0.7em 1em 0.6em 1em; position: relative; border: none; border-bottom: 1px solid #979797; -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; } +.ui-dialog .ui-dialog-title { float: left; margin: .1em 16px .2em 0; font-size: 14px; text-shadow: 0 1px 0 rgba(255,255,255,0.5); } +.ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .8em; top: 55%; width: 16px; margin: -10px 0 0 0; padding: 0; height: 16px; } +.ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; background: url(icon_sprite.png) 0 -16px no-repeat; } +.ui-dialog .ui-dialog-titlebar-close:hover span { background-position: -16px -16px; } +.ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; border: 0; } +.ui-dialog .ui-dialog-content { position: relative; border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; } +.ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; } +.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { float: right; } +.ui-dialog .ui-dialog-buttonpane button { margin: .5em .4em .5em 0; cursor: pointer; } +.ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; } +.ui-draggable .ui-dialog-titlebar { cursor: move; } +/* + * jQuery UI Slider 1.8.7 + * + * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Slider#theming + */ +.ui-slider { position: relative; text-align: left; background: #d7d7d7; z-index: 1; } +.ui-slider { -moz-box-shadow: 0 1px 2px rgba(0,0,0,0.5) inset; -webkit-box-shadow: 0 1px 2px rgba(0,0,0,0.5) inset; box-shadow: 0 1px 2px rgba(0,0,0,0.5) inset; } +.ui-slider .ui-slider-handle { background: url(slider_handles.png) 0px -23px no-repeat; position: absolute; z-index: 2; width: 23px; height: 23px; cursor: default; border: none; outline: none; -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; } +.ui-slider .ui-state-hover, .ui-slider .ui-state-active { background-position: 0 0; } +.ui-slider .ui-slider-range { background: #a3cae0; position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; background-position: 0 0; } +.ui-slider .ui-slider-range { -moz-box-shadow: 0 1px 2px rgba(17,35,45,0.6) inset; -webkit-box-shadow: 0 1px 2px rgba(17,35,45,0.6) inset; box-shadow: 0 1px 2px rgba(17,35,45,0.6) inset; } + + +.ui-slider-horizontal { height: 5px; } +.ui-slider-horizontal .ui-slider-handle { top: -8px; margin-left: -13px; } +.ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; } +.ui-slider-horizontal .ui-slider-range-min { left: 0; } +.ui-slider-horizontal .ui-slider-range-max { right: 0; } + +.ui-slider-vertical { width: 5px; height: 100px; } +.ui-slider-vertical .ui-slider-handle { left: -8px; margin-left: 0; margin-bottom: -13px; } +.ui-slider-vertical .ui-slider-range { left: 0; width: 100%; } +.ui-slider-vertical .ui-slider-range-min { bottom: 0; } +.ui-slider-vertical .ui-slider-range-max { top: 0; }/* + * jQuery UI Tabs 1.8.7 + * + * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Tabs#theming + */ +.ui-tabs { position: relative; zoom: 1; border: 0; background: transparent; } /* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */ +.ui-tabs .ui-tabs-nav { margin: 0; padding: 0; background: transparent; border-width: 0 0 1px 0; } +.ui-tabs .ui-tabs-nav { + -moz-border-radius: 0; + -webkit-border-radius: 0; + border-radius: 0; +} +.ui-tabs .ui-tabs-nav li { list-style: none; float: left; position: relative; top: 1px; margin: 0 .2em 1px 0; border-bottom: 0 !important; padding: 0; white-space: nowrap; } +.ui-tabs .ui-tabs-nav li a { float: left; padding: .5em 1em; text-decoration: none; font-size: 12px; font-weight: bold; text-shadow: 0 1px 0 rgba(255,255,255,0.5); } +.ui-tabs .ui-tabs-nav li.ui-tabs-selected { margin-bottom: 0; padding-bottom: 1px; background: #fff; border-color: #B6B6B6; } +.ui-tabs .ui-tabs-nav li.ui-tabs-selected a, .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .ui-tabs .ui-tabs-nav li.ui-state-processing a { cursor: text; outline: none; } +.ui-tabs .ui-tabs-nav li a, .ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a { cursor: pointer; } /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */ +.ui-tabs .ui-tabs-panel { display: block; border-width: 0 1px 1px 1px; padding: 1em 1.4em; background: none; } +.ui-tabs .ui-tabs-panel { background: #FFF; + -moz-border-radius: 0; + -webkit-border-radius: 0; + border-radius: 0; +} +.ui-tabs .ui-tabs-hide { display: none !important; } +/* + * jQuery UI Datepicker 1.8.7 + * + * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Datepicker#theming + */ +.ui-datepicker { width: 17em; padding: 0; display: none; border-color: #DDDDDD; } +.ui-datepicker { + -moz-box-shadow: 0 4px 8px rgba(0,0,0,0.5); + -webkit-box-shadow: 0 4px 8px rgba(0,0,0,0.5); + box-shadow: 0 4px 8px rgba(0,0,0,0.5); +} +.ui-datepicker .ui-datepicker-header { position:relative; padding:.35em 0; border: none; border-bottom: 1px solid #B6B6B6; -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; } +.ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 6px; width: 1.8em; height: 1.8em; } +.ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { border: 1px none; } +.ui-datepicker .ui-datepicker-prev { left:2px; } +.ui-datepicker .ui-datepicker-next { right:2px; } +.ui-datepicker .ui-datepicker-prev span { background-position: 0px -32px !important; } +.ui-datepicker .ui-datepicker-next span { background-position: -16px -32px !important; } +.ui-datepicker .ui-datepicker-prev-hover span { background-position: 0px -48px !important; } +.ui-datepicker .ui-datepicker-next-hover span { background-position: -16px -48px !important; } +.ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px; background: url(icon_sprite.png) no-repeat; } +.ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; font-size: 12px; text-shadow: 0 1px 0 rgba(255,255,255,0.6); } +.ui-datepicker .ui-datepicker-title select { font-size:1em; margin:1px 0; } +.ui-datepicker select.ui-datepicker-month-year {width: 100%;} +.ui-datepicker select.ui-datepicker-month, +.ui-datepicker select.ui-datepicker-year { width: 49%;} +.ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; } +.ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0; } +.ui-datepicker td { border: 0; padding: 1px; } +.ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; } +.ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; } +.ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; } +.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; } +.ui-datepicker table .ui-state-highlight { border-color: #5F83B9; } +.ui-datepicker table .ui-state-hover { background: #5F83B9; color: #FFF; font-weight: bold; text-shadow: 0 1px 1px #234386; -webkit-box-shadow: 0 0px 0 rgba(255,255,255,0.6) inset; -moz-box-shadow: 0 0px 0 rgba(255,255,255,0.6) inset; box-shadow: 0 0px 0 rgba(255,255,255,0.6) inset; border-color: #5F83B9; } +.ui-datepicker-calendar .ui-state-default { background: transparent; border-color: #FFF; } +.ui-datepicker-calendar .ui-state-active { background: #5F83B9; border-color: #5F83B9; color: #FFF; font-weight: bold; text-shadow: 0 1px 1px #234386; } + +/* with multiple calendars */ +.ui-datepicker.ui-datepicker-multi { width:auto; } +.ui-datepicker-multi .ui-datepicker-group { float:left; } +.ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; } +.ui-datepicker-multi-2 .ui-datepicker-group { width:50%; } +.ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; } +.ui-datepicker-multi-4 .ui-datepicker-group { width:25%; } +.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; } +.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; } +.ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; } +.ui-datepicker-row-break { clear:both; width:100%; } + +/* RTL support */ +.ui-datepicker-rtl { direction: rtl; } +.ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; } +.ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; } +.ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; } +.ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; } +.ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; } +.ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; } +.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; } +.ui-datepicker-rtl .ui-datepicker-group { float:right; } +.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; } +.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; } + +/* IE6 IFRAME FIX (taken from datepicker 1.5.3 */ +.ui-datepicker-cover { + display: none; /*sorry for IE5*/ + display/**/: block; /*sorry for IE5*/ + position: absolute; /*must have*/ + z-index: -1; /*must have*/ + filter: mask(); /*must have*/ + top: -4px; /*must have*/ + left: -4px; /*must have*/ + width: 200px; /*must have*/ + height: 200px; /*must have*/ +}/* + * jQuery UI Progressbar 1.8.7 + * + * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Progressbar#theming + */ +.ui-progressbar { height: 12px; text-align: left; background: #FFF url(progress_bar.gif) 0 -14px repeat-x; } +.ui-progressbar .ui-progressbar-value {margin: -1px; height:100%; background: url(progress_bar.gif) 0 0 repeat-x; } + +/* Extra Input Field Styling */ +.ui-form textarea, .ui-form input:not([type="submit"]):not([type="button"]):not([type="checkbox"]):not([type="radio"]):not([type="file"]):not([type="range"]) { + padding: 3px; + -webkit-border-radius: 2px; + -moz-border-radius: 2px; + border-radius: 2px; + border: 1px solid #cecece; + outline: none; + -webkit-box-shadow: 0 1px 3px rgba(0,0,0,0.1) inset, 0 1px 0 rgba(255,255,255,0.2); + -moz-box-shadow: 0 1px 3px rgba(0,0,0,0.1) inset, 0 1px 0 rgba(255,255,255,0.2); + box-shadow: 0 1px 3px rgba(0,0,0,0.1) inset, 0 1px 0 rgba(255,255,255,0.2); + -webkit-transition: all 250ms ease-in-out; + -moz-transition: all 250ms ease-in-out; + -o-transition: all 250ms ease-in-out; + transition: all 250ms ease-in-out; +} +.ui-form textarea:hover, .ui-form input:not([type="submit"]):not([type="button"]):not([type="checkbox"]):not([type="radio"]):not([type="file"]):not([type="range"]):hover { + border: 1px solid #bdbdbd; + -webkit-box-shadow: 0 1px 3px rgba(0,0,0,0.2) inset, 0 1px 0 rgba(255,255,255,0.2); + -moz-box-shadow: 0 1px 3px rgba(0,0,0,0.2) inset, 0 1px 0 rgba(255,255,255,0.2); + box-shadow: 0 1px 3px rgba(0,0,0,0.2) inset, 0 1px 0 rgba(255,255,255,0.2); +} +.ui-form textarea:focus, .ui-form input:not([type="submit"]):not([type="button"]):not([type="checkbox"]):not([type="radio"]):not([type="file"]):not([type="range"]):focus { + border: 1px solid #95bdd4; + -webkit-box-shadow: 0 2px 3px rgba(161,202,226,0.5) inset, 0 1px 0 rgba(255,255,255,0.2); + -moz-box-shadow: 0 2px 3px rgba(161,202,226,0.5) inset, 0 1px 0 rgba(255,255,255,0.2); + box-shadow: 0 2px 3px rgba(161,202,226,0.5) inset, 0 1px 0 rgba(255,255,255,0.2); +} From 93401ef9887b297343d5bf33b4fbf39f13090894 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 17 Aug 2012 09:26:59 +0300 Subject: [PATCH 06/46] Few css improvements --- app/views/commits/_commit_box.html.haml | 6 +++--- app/views/issues/_form.html.haml | 12 ++++++------ app/views/issues/index.html.haml | 5 +++-- app/views/milestones/_form.html.haml | 6 +++--- 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/app/views/commits/_commit_box.html.haml b/app/views/commits/_commit_box.html.haml index 52f03ba7..506f4e09 100644 --- a/app/views/commits/_commit_box.html.haml +++ b/app/views/commits/_commit_box.html.haml @@ -5,10 +5,10 @@ %span.btn.disabled.grouped %i.icon-comment = @notes_count - = link_to patch_project_commit_path(@project, @commit.id), class: "btn small grouped" do + = link_to patch_project_commit_path(@project, @commit.id), class: "btn small grouped" do %i.icon-download-alt - Get Patch - = link_to tree_project_ref_path(@project, @commit.id), class: "browse-button primary grouped" do + Get Patch + = link_to tree_project_ref_path(@project, @commit.id), class: "browse-button primary grouped" do %strong Browse Code » %h3.commit-title.page_title = gfm @commit.title diff --git a/app/views/issues/_form.html.haml b/app/views/issues/_form.html.haml index 6139f3d4..1b67eabd 100644 --- a/app/views/issues/_form.html.haml +++ b/app/views/issues/_form.html.haml @@ -1,5 +1,5 @@ %div.issue-form-holder - %h3= @issue.new_record? ? "New Issue" : "Edit Issue ##{@issue.id}" + %h3.page_title= @issue.new_record? ? "New Issue" : "Edit Issue ##{@issue.id}" = form_for [@project, @issue], remote: request.xhr? do |f| -if @issue.errors.any? .alert-message.block-message.error @@ -9,26 +9,26 @@ .issue_form_box .issue_title .clearfix - = f.label :title do + = f.label :title do %strong= "Subject *" .input = f.text_field :title, maxlength: 255, class: "xxlarge" .issue_middle_block .issue_assignee - = f.label :assignee_id do + = f.label :assignee_id do %i.icon-user Assign to .input= f.select(:assignee_id, @project.users.all.collect {|p| [ p.name, p.id ] }, { include_blank: "Select a user" }) .issue_milestone - = f.label :milestone_id do + = f.label :milestone_id do %i.icon-time Milestone .input= f.select(:milestone_id, @project.milestones.active.all.collect {|p| [ p.title, p.id ] }, { include_blank: "Select milestone" }) .issue_description .clearfix - = f.label :label_list do - %i.icon-tag + = f.label :label_list do + %i.icon-tag Labels .input = f.text_field :label_list, maxlength: 2000, class: "xxlarge" diff --git a/app/views/issues/index.html.haml b/app/views/issues/index.html.haml index 8876d24d..a6836fd4 100644 --- a/app/views/issues/index.html.haml +++ b/app/views/issues/index.html.haml @@ -7,6 +7,7 @@ .span5 - if can? current_user, :write_issue, @project = link_to new_project_issue_path(@project), class: "right btn small", title: "New Issue", remote: true do + %i.icon-plus New Issue = form_tag search_project_issues_path(@project), method: :get, remote: true, id: "issue_search_form", class: :right do = hidden_field_tag :project_id, @project.id, { id: 'project_id' } @@ -21,7 +22,7 @@ .issues_bulk_update.hide - = form_tag bulk_update_project_issues_path(@project), method: :post do + = form_tag bulk_update_project_issues_path(@project), method: :post do %span.update_issues_text Update selected issues with   .left = select_tag('update[status]', options_for_select(['open', 'closed']), prompt: "Status") @@ -53,7 +54,7 @@ = select_tag(:milestone_id, options_from_collection_for_select([unassigned_filter] + @project.milestones.order("id desc").all, "id", "title", params[:milestone_id]), prompt: "Milestone") = hidden_field_tag :f, params[:f] .clearfix - + %ul#issues-table.unstyled.issues_table = render "issues" diff --git a/app/views/milestones/_form.html.haml b/app/views/milestones/_form.html.haml index daae58fe..1cd08ac3 100644 --- a/app/views/milestones/_form.html.haml +++ b/app/views/milestones/_form.html.haml @@ -1,4 +1,4 @@ -%h3= @milestone.new_record? ? "New Milestone" : "Edit Milestone ##{@milestone.id}" +%h3.page_title= @milestone.new_record? ? "New Milestone" : "Edit Milestone ##{@milestone.id}" .back_link = link_to project_milestones_path(@project) do ← To milestones @@ -17,12 +17,12 @@ = f.label :title, "Title", class: "control-label" .controls = f.text_field :title, maxlength: 255, class: "input-xlarge" - %p.help-block Required + %p.hint Required .control-group = f.label :description, "Description", class: "control-label" .controls = f.text_area :description, maxlength: 2000, class: "input-xlarge", rows: 10 - %p.help-block Markdown is enabled. + %p.hint Markdown is enabled. .span6 .control-group = f.label :due_date, "Due Date", class: "control-label" From 3f9749dd86874c4b6dcbdad7d3cd0dc7406a20b8 Mon Sep 17 00:00:00 2001 From: randx Date: Sat, 18 Aug 2012 23:53:58 +0300 Subject: [PATCH 07/46] Fix branch selector. Few css fixed also --- app/assets/stylesheets/sections/login.scss | 1 + app/assets/stylesheets/sections/notes.scss | 2 +- app/assets/stylesheets/themes/ui_modern.scss | 7 +++++-- app/views/notes/_show.html.haml | 2 +- app/views/refs/_tree.html.haml | 2 -- app/views/refs/_tree_commit.html.haml | 2 +- 6 files changed, 9 insertions(+), 7 deletions(-) diff --git a/app/assets/stylesheets/sections/login.scss b/app/assets/stylesheets/sections/login.scss index 3726d9f0..5b8763cf 100644 --- a/app/assets/stylesheets/sections/login.scss +++ b/app/assets/stylesheets/sections/login.scss @@ -27,6 +27,7 @@ body.login-page{ -moz-border-radius-topright: 5px; border-top-left-radius: 5px; border-top-right-radius: 5px; + margin-bottom:0px; } .login-box input.text.bottom{ diff --git a/app/assets/stylesheets/sections/notes.scss b/app/assets/stylesheets/sections/notes.scss index b498277d..c846e6a7 100644 --- a/app/assets/stylesheets/sections/notes.scss +++ b/app/assets/stylesheets/sections/notes.scss @@ -48,7 +48,7 @@ p { color:$style_color; } .note-author { color: $style_color;} - .note-title { margin-left:50px; padding-top: 5px;} + .note-title { padding-top: 5px;} .avatar { margin-top:3px; } diff --git a/app/assets/stylesheets/themes/ui_modern.scss b/app/assets/stylesheets/themes/ui_modern.scss index 29c857e5..3fd9bb5e 100644 --- a/app/assets/stylesheets/themes/ui_modern.scss +++ b/app/assets/stylesheets/themes/ui_modern.scss @@ -37,6 +37,7 @@ * */ .app_logo { + width:160px; a { h1 { opacity: 0.7; @@ -56,7 +57,7 @@ .separator { width: 1px; height: 40px; - margin: 0 9px; + margin: 0 10px; overflow: hidden; background: #222; border-left: 1px solid #333; @@ -73,6 +74,7 @@ background:none; margin-left:8px; font-size: 13px; + font-weight:bold; line-height: 19px; color:#ccc; &:hover { @@ -81,6 +83,7 @@ border: none; box-shadow:none; text-shadow: 0 -1px 0 #000000; + border-left: 1px solid #333; } } @@ -115,7 +118,7 @@ .project_name { line-height:34px; font-size:22px; - color:#fff; + color:#ccc; text-shadow: 0 1px 1px #111; } diff --git a/app/views/notes/_show.html.haml b/app/views/notes/_show.html.haml index bdb00546..3412e4eb 100644 --- a/app/views/notes/_show.html.haml +++ b/app/views/notes/_show.html.haml @@ -1,5 +1,5 @@ %li{id: dom_id(note), class: "note"} - = image_tag gravatar_icon(note.author.email), class: "avatar" + = image_tag gravatar_icon(note.author.email), class: "avatar s32" %div.note-author %strong= note.author_name = link_to "##{dom_id(note)}", name: dom_id(note) do diff --git a/app/views/refs/_tree.html.haml b/app/views/refs/_tree.html.haml index 6f8175da..c231c407 100644 --- a/app/views/refs/_tree.html.haml +++ b/app/views/refs/_tree.html.haml @@ -51,8 +51,6 @@ :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}"); diff --git a/app/views/refs/_tree_commit.html.haml b/app/views/refs/_tree_commit.html.haml index a5681aa2..1bcf1a7e 100644 --- a/app/views/refs/_tree_commit.html.haml +++ b/app/views/refs/_tree_commit.html.haml @@ -1,3 +1,3 @@ - if tm - %strong= link_to "[#{tm.user_name}]", project_team_member_path(@project, tm) + = link_to "[#{tm.user_name}]", project_team_member_path(@project, tm) = link_to_gfm truncate(content_commit.title, length: tm ? 30 : 50), project_commit_path(@project, content_commit.id), class: "tree-commit-link" From 7530fa9decae2bb3d8622e1a40a080deb831af9f Mon Sep 17 00:00:00 2001 From: randx Date: Sun, 19 Aug 2012 00:08:20 +0300 Subject: [PATCH 08/46] Restored margin for text in notes --- app/assets/stylesheets/sections/notes.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/stylesheets/sections/notes.scss b/app/assets/stylesheets/sections/notes.scss index c846e6a7..30587ef5 100644 --- a/app/assets/stylesheets/sections/notes.scss +++ b/app/assets/stylesheets/sections/notes.scss @@ -48,7 +48,7 @@ p { color:$style_color; } .note-author { color: $style_color;} - .note-title { padding-top: 5px;} + .note-title { margin-left:45px; padding-top: 5px;} .avatar { margin-top:3px; } From 8eaead6a01739fee37d7b29b78decc2e8a4b89a3 Mon Sep 17 00:00:00 2001 From: randx Date: Sun, 19 Aug 2012 00:45:46 +0300 Subject: [PATCH 09/46] Text message for mergeing MR --- app/assets/javascripts/merge_requests.js | 1 + app/assets/stylesheets/sections/merge_requests.scss | 5 +++++ app/views/merge_requests/_show.html.haml | 3 ++- app/views/merge_requests/show/_mr_accept.html.haml | 3 +++ 4 files changed, 11 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/merge_requests.js b/app/assets/javascripts/merge_requests.js index 4b155192..0ab6f6e2 100644 --- a/app/assets/javascripts/merge_requests.js +++ b/app/assets/javascripts/merge_requests.js @@ -112,6 +112,7 @@ var MergeRequest = { already_cannot_be_merged: function(){ $(".automerge_widget").hide(); + $(".merge_in_progress").hide(); $(".automerge_widget.already_cannot_be_merged").show(); } } diff --git a/app/assets/stylesheets/sections/merge_requests.scss b/app/assets/stylesheets/sections/merge_requests.scss index 34f43acf..ec84a64e 100644 --- a/app/assets/stylesheets/sections/merge_requests.scss +++ b/app/assets/stylesheets/sections/merge_requests.scss @@ -94,3 +94,8 @@ li.merge_request { padding-bottom: 2px; } } + +.merge_in_progress { + @extend .padded; + @extend .append-bottom-10; +} diff --git a/app/views/merge_requests/_show.html.haml b/app/views/merge_requests/_show.html.haml index 072bf259..f1b3fa9f 100644 --- a/app/views/merge_requests/_show.html.haml +++ b/app/views/merge_requests/_show.html.haml @@ -33,7 +33,8 @@ }); $(".edit_merge_request").live("ajax:beforeSend", function() { - $(this).replaceWith('#{image_tag "ajax_loader.gif"}'); + $('.can_be_merged').hide(); + $('.merge_in_progress').show(); }) }) diff --git a/app/views/merge_requests/show/_mr_accept.html.haml b/app/views/merge_requests/show/_mr_accept.html.haml index efd47af0..f2422885 100644 --- a/app/views/merge_requests/show/_mr_accept.html.haml +++ b/app/views/merge_requests/show/_mr_accept.html.haml @@ -40,3 +40,6 @@ .alert.alert-info %strong This merge request already can not be merged. Try to reload page. + .merge_in_progress.hide + %span.cgray Merge is in progress. Please wait. Page will be automatically reloaded.   + = image_tag "ajax_loader.gif" From 05c86fb0fc5b9e90889a96f9de301756ce3d5552 Mon Sep 17 00:00:00 2001 From: randx Date: Sun, 19 Aug 2012 10:20:42 +0300 Subject: [PATCH 10/46] Specify charlock holmes version in installation docs --- doc/installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/installation.md b/doc/installation.md index 524c8e86..5611c3b3 100644 --- a/doc/installation.md +++ b/doc/installation.md @@ -134,7 +134,7 @@ Permissions: # 4. Install gitlab and configuration. Check status configuration. - sudo gem install charlock_holmes + sudo gem install charlock_holmes --version '0.6.8' sudo pip install pygments sudo gem install bundler cd /home/gitlab From d4059ac966e26c4f384c53ca14319c56619fef78 Mon Sep 17 00:00:00 2001 From: randx Date: Sun, 19 Aug 2012 10:58:10 +0300 Subject: [PATCH 11/46] Move graph_commit under gitlab module --- app/controllers/projects_controller.rb | 4 +- lib/gitlab/graph_commit.rb | 183 +++++++++++++++++++++++++ lib/graph_commit.rb | 181 ------------------------ 3 files changed, 185 insertions(+), 183 deletions(-) create mode 100644 lib/gitlab/graph_commit.rb delete mode 100644 lib/graph_commit.rb diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index b596a5a6..bd7f811e 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -1,4 +1,4 @@ -require File.join(Rails.root, 'lib', 'graph_commit') +require Rails.root.join('lib', 'gitlab', 'graph_commit') class ProjectsController < ApplicationController before_filter :project, except: [:index, :new, :create] @@ -78,7 +78,7 @@ class ProjectsController < ApplicationController end def graph - @days_json, @commits_json = GraphCommit.to_graph(project) + @days_json, @commits_json = Gitlab::GraphCommit.to_graph(project) end def destroy diff --git a/lib/gitlab/graph_commit.rb b/lib/gitlab/graph_commit.rb new file mode 100644 index 00000000..b9859d79 --- /dev/null +++ b/lib/gitlab/graph_commit.rb @@ -0,0 +1,183 @@ +require "grit" + +module Gitlab + class GraphCommit + attr_accessor :time, :space + attr_accessor :refs + + def self.to_graph(project) + @repo = project.repo + commits = Grit::Commit.find_all(@repo, nil, {max_count: 650}) + + ref_cache = {} + + commits.map! {|c| GraphCommit.new(Commit.new(c))} + commits.each { |commit| commit.add_refs(ref_cache, @repo) } + + days = GraphCommit.index_commits(commits) + @days_json = days.compact.collect{|d| [d.day, d.strftime("%b")] }.to_json + @commits_json = commits.map(&:to_graph_hash).to_json + + return @days_json, @commits_json + end + + # Method is adding time and space on the + # list of commits. As well as returns date list + # corelated with time set on commits. + # + # @param [Array] comits to index + # + # @return [Array] list of commit dates corelated with time on commits + def self.index_commits(commits) + days, heads = [], [] + map = {} + + commits.reverse.each_with_index do |c,i| + c.time = i + days[i] = c.committed_date + map[c.id] = c + heads += c.refs unless c.refs.nil? + end + + heads.select!{|h| h.is_a? Grit::Head or h.is_a? Grit::Remote} + # sort heads so the master is top and current branches are closer + heads.sort! do |a,b| + if a.name == "master" + -1 + elsif b.name == "master" + 1 + else + b.commit.committed_date <=> a.commit.committed_date + end + end + + @_reserved = {} + days.each_index do |i| + @_reserved[i] = [] + end + + heads.each do |h| + if map.include? h.commit.id then + place_chain(map[h.commit.id], map) + end + end + days + end + + # Add space mark on commit and its parents + # + # @param [GraphCommit] the commit object. + # @param [Hash] map of commits + def self.place_chain(commit, map, parent_time = nil) + leaves = take_left_leaves(commit, map) + if leaves.empty? then + return + end + space = find_free_space(leaves.last.time..leaves.first.time) + leaves.each{|l| l.space = space} + # and mark it as reserved + min_time = leaves.last.time + parents = leaves.last.parents.collect + parents.each do |p| + if map.include? p.id then + parent = map[p.id] + if parent.time < min_time then + min_time = parent.time + end + end + end + if parent_time.nil? then + max_time = leaves.first.time + else + max_time = parent_time - 1 + end + mark_reserved(min_time..max_time, space) + # Visit branching chains + leaves.each do |l| + parents = l.parents.collect + .select{|p| map.include? p.id and map[p.id].space == 0} + for p in parents + place_chain(map[p.id], map, l.time) + end + end + end + + def self.mark_reserved(time_range, space) + for day in time_range + @_reserved[day].push(space) + end + end + + def self.find_free_space(time_range) + reserved = [] + for day in time_range + reserved += @_reserved[day] + end + space = 1 + while reserved.include? space do + space += 1 + end + space + end + + # Takes most left subtree branch of commits + # which don't have space mark yet. + # + # @param [GraphCommit] the commit object. + # @param [Hash] map of commits + # + # @return [Array] list of branch commits + def self.take_left_leaves(commit, map) + leaves = [] + leaves.push(commit) if commit.space == 0 + while true + parent = commit.parents.collect + .select{|p| map.include? p.id and map[p.id].space == 0} + if parent.count == 0 then + return leaves + else + commit = map[parent.first.id] + leaves.push(commit) + end + end + end + + + def initialize(commit) + @_commit = commit + @time = -1 + @space = 0 + end + + def method_missing(m, *args, &block) + @_commit.send(m, *args, &block) + end + + def to_graph_hash + h = {} + h[:parents] = self.parents.collect do |p| + [p.id,0,0] + end + h[:author] = Gitlab::Encode.utf8(author.name) + h[:time] = time + h[:space] = space + h[:refs] = refs.collect{|r|r.name}.join(" ") unless refs.nil? + h[:id] = sha + h[:date] = date + h[:message] = Gitlab::Encode.utf8(message) + h[:login] = author.email + h + end + + def add_refs(ref_cache, repo) + if ref_cache.empty? + repo.refs.each do |ref| + ref_cache[ref.commit.id] ||= [] + ref_cache[ref.commit.id] << ref + end + end + @refs = ref_cache[@_commit.id] if ref_cache.include?(@_commit.id) + @refs ||= [] + end + end +end diff --git a/lib/graph_commit.rb b/lib/graph_commit.rb deleted file mode 100644 index e08a8fad..00000000 --- a/lib/graph_commit.rb +++ /dev/null @@ -1,181 +0,0 @@ -require "grit" - -class GraphCommit - attr_accessor :time, :space - attr_accessor :refs - - def self.to_graph(project) - @repo = project.repo - commits = Grit::Commit.find_all(@repo, nil, {max_count: 650}) - - ref_cache = {} - - commits.map! {|c| GraphCommit.new(Commit.new(c))} - commits.each { |commit| commit.add_refs(ref_cache, @repo) } - - days = GraphCommit.index_commits(commits) - @days_json = days.compact.collect{|d| [d.day, d.strftime("%b")] }.to_json - @commits_json = commits.map(&:to_graph_hash).to_json - - return @days_json, @commits_json - end - - # Method is adding time and space on the - # list of commits. As well as returns date list - # corelated with time set on commits. - # - # @param [Array] comits to index - # - # @return [Array] list of commit dates corelated with time on commits - def self.index_commits(commits) - days, heads = [], [] - map = {} - - commits.reverse.each_with_index do |c,i| - c.time = i - days[i] = c.committed_date - map[c.id] = c - heads += c.refs unless c.refs.nil? - end - - heads.select!{|h| h.is_a? Grit::Head or h.is_a? Grit::Remote} - # sort heads so the master is top and current branches are closer - heads.sort! do |a,b| - if a.name == "master" - -1 - elsif b.name == "master" - 1 - else - b.commit.committed_date <=> a.commit.committed_date - end - end - - @_reserved = {} - days.each_index do |i| - @_reserved[i] = [] - end - - heads.each do |h| - if map.include? h.commit.id then - place_chain(map[h.commit.id], map) - end - end - days - end - - # Add space mark on commit and its parents - # - # @param [GraphCommit] the commit object. - # @param [Hash] map of commits - def self.place_chain(commit, map, parent_time = nil) - leaves = take_left_leaves(commit, map) - if leaves.empty? then - return - end - space = find_free_space(leaves.last.time..leaves.first.time) - leaves.each{|l| l.space = space} - # and mark it as reserved - min_time = leaves.last.time - parents = leaves.last.parents.collect - parents.each do |p| - if map.include? p.id then - parent = map[p.id] - if parent.time < min_time then - min_time = parent.time - end - end - end - if parent_time.nil? then - max_time = leaves.first.time - else - max_time = parent_time - 1 - end - mark_reserved(min_time..max_time, space) - # Visit branching chains - leaves.each do |l| - parents = l.parents.collect - .select{|p| map.include? p.id and map[p.id].space == 0} - for p in parents - place_chain(map[p.id], map, l.time) - end - end - end - - def self.mark_reserved(time_range, space) - for day in time_range - @_reserved[day].push(space) - end - end - - def self.find_free_space(time_range) - reserved = [] - for day in time_range - reserved += @_reserved[day] - end - space = 1 - while reserved.include? space do - space += 1 - end - space - end - - # Takes most left subtree branch of commits - # which don't have space mark yet. - # - # @param [GraphCommit] the commit object. - # @param [Hash] map of commits - # - # @return [Array] list of branch commits - def self.take_left_leaves(commit, map) - leaves = [] - leaves.push(commit) if commit.space == 0 - while true - parent = commit.parents.collect - .select{|p| map.include? p.id and map[p.id].space == 0} - if parent.count == 0 then - return leaves - else - commit = map[parent.first.id] - leaves.push(commit) - end - end - end - - - def initialize(commit) - @_commit = commit - @time = -1 - @space = 0 - end - - def method_missing(m, *args, &block) - @_commit.send(m, *args, &block) - end - - def to_graph_hash - h = {} - h[:parents] = self.parents.collect do |p| - [p.id,0,0] - end - h[:author] = Gitlab::Encode.utf8(author.name) - h[:time] = time - h[:space] = space - h[:refs] = refs.collect{|r|r.name}.join(" ") unless refs.nil? - h[:id] = sha - h[:date] = date - h[:message] = Gitlab::Encode.utf8(message) - h[:login] = author.email - h - end - - def add_refs(ref_cache, repo) - if ref_cache.empty? - repo.refs.each do |ref| - ref_cache[ref.commit.id] ||= [] - ref_cache[ref.commit.id] << ref - end - end - @refs = ref_cache[@_commit.id] if ref_cache.include?(@_commit.id) - @refs ||= [] - end -end From d656cb74f7e1becdb6ed686a7d36da48ab521030 Mon Sep 17 00:00:00 2001 From: randx Date: Sun, 19 Aug 2012 11:36:37 +0300 Subject: [PATCH 12/46] Headless gem added --- Gemfile | 1 + Gemfile.lock | 2 ++ features/support/env.rb | 5 +++++ spec/spec_helper.rb | 6 ++++++ 4 files changed, 14 insertions(+) diff --git a/Gemfile b/Gemfile index 26e4e195..e8b0b244 100644 --- a/Gemfile +++ b/Gemfile @@ -101,6 +101,7 @@ group :development, :test do gem "rspec-rails" gem "capybara" gem "capybara-webkit" + gem "headless" gem "autotest" gem "autotest-rails" gem "pry" diff --git a/Gemfile.lock b/Gemfile.lock index 57ad1935..b23bc47c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -186,6 +186,7 @@ GEM railties (~> 3.0) hashery (1.4.0) hashie (1.2.0) + headless (0.3.1) hike (1.2.1) httparty (0.8.3) multi_json (~> 1.0) @@ -398,6 +399,7 @@ DEPENDENCIES grape (~> 0.2.1) grit! haml-rails + headless httparty jquery-rails (= 2.0.2) jquery-ui-rails (= 0.5.0) diff --git a/features/support/env.rb b/features/support/env.rb index b47349c8..496f23f9 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -39,3 +39,8 @@ rescue NameError end Cucumber::Rails::Database.javascript_strategy = :truncation + +require 'headless' + +headless = Headless.new +headless.start diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index f87c9a50..5c0bb618 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -12,6 +12,7 @@ require 'webmock/rspec' require 'factories' require 'monkeypatch' require 'email_spec' +require 'headless' # Requires supporting ruby files with custom matchers and macros, etc, # in spec/support/ and its subdirectories. @@ -30,6 +31,11 @@ RSpec.configure do |config| # instead of true. config.use_transactional_fixtures = false + config.before :all do + headless = Headless.new + headless.start + end + config.before :each, type: :integration do DeviseSessionMock.disable end From 8e3a2def8af612689ddc830a3eab9e463b8bc205 Mon Sep 17 00:00:00 2001 From: randx Date: Sun, 19 Aug 2012 11:44:48 +0300 Subject: [PATCH 13/46] Project /lib cleanup --- lib/color.rb | 31 ------------------------------- lib/tasks/dev/repo.rake | 26 -------------------------- lib/tasks/dev/tests.rake | 10 ---------- lib/tasks/dev/user.sh | 7 ------- 4 files changed, 74 deletions(-) delete mode 100644 lib/color.rb delete mode 100644 lib/tasks/dev/repo.rake delete mode 100644 lib/tasks/dev/tests.rake delete mode 100755 lib/tasks/dev/user.sh diff --git a/lib/color.rb b/lib/color.rb deleted file mode 100644 index 4723804e..00000000 --- a/lib/color.rb +++ /dev/null @@ -1,31 +0,0 @@ -module Color - extend self - - def colorize(text, color_code) - "\033[#{color_code}#{text}\033[0m" - end - - def red(text) - colorize(text, "31m") - end - - def green(text) - colorize(text, "32m") - end - - def yellow(text) - colorize(text, "93m") - end - - def command(string) - `#{string}` - if $?.to_i > 0 - puts red " == #{string} - FAIL" - puts red " == Error during configure" - exit - else - puts green " == #{string} - OK" - end - end -end - diff --git a/lib/tasks/dev/repo.rake b/lib/tasks/dev/repo.rake deleted file mode 100644 index 7b389a55..00000000 --- a/lib/tasks/dev/repo.rake +++ /dev/null @@ -1,26 +0,0 @@ -namespace :dev do - desc "Prepare for development (run dev_user.sh first)" - task :repos => :environment do - key = `sudo -u gitlabdev -H cat /home/gitlabdev/.ssh/id_rsa.pub` - raise "\n *** Run ./lib/tasks/dev/user.sh first *** \n" if key.empty? - Key.create(:user_id => User.first, :key => key, :title => "gitlabdev") - - puts "\n *** Clone diaspora from github" - `sudo -u gitlabdev -H sh -c "cd /home/gitlabdev; git clone git://github.com/diaspora/diaspora.git /home/gitlabdev/diaspora"` - - puts "\n *** Push diaspora source to gitlab" - `sudo -u gitlabdev -H sh -c "cd /home/gitlabdev/diaspora; git remote add local git@localhost:diaspora.git; git push local master; git push local --tags; git checkout -b api origin/api; git push local api; git checkout -b heroku origin/heroku; git push local heroku"` - - puts "\n *** Clone rails from github" - `sudo -u gitlabdev -H sh -c "cd /home/gitlabdev; git clone git://github.com/rails/rails.git /home/gitlabdev/rails"` - - puts "\n *** Push rails source to gitlab" - `sudo -u gitlabdev -H sh -c "cd /home/gitlabdev/rails; git remote add local git@localhost:ruby_on_rails.git; git push local master; git push local --tags"` - - puts "\n *** Clone rubinius from github" - `sudo -u gitlabdev -H sh -c "cd /home/gitlabdev; git clone git://github.com/rubinius/rubinius.git /home/gitlabdev/rubinius"` - - puts "\n *** Push rubinius source to gitlab" - `sudo -u gitlabdev -H sh -c "cd /home/gitlabdev/rubinius; git remote add local git@localhost:rubinius.git; git push local master; git push local --tags"` - end -end diff --git a/lib/tasks/dev/tests.rake b/lib/tasks/dev/tests.rake deleted file mode 100644 index d5c0e75e..00000000 --- a/lib/tasks/dev/tests.rake +++ /dev/null @@ -1,10 +0,0 @@ -namespace :dev do - desc "DEV | Run cucumber and rspec" - task :tests do - ["cucumber", "rspec spec"].each do |cmd| - puts "Starting to run #{cmd}..." - system("export DISPLAY=:99.0 && bundle exec #{cmd}") - raise "#{cmd} failed!" unless $?.exitstatus == 0 - end - end -end diff --git a/lib/tasks/dev/user.sh b/lib/tasks/dev/user.sh deleted file mode 100755 index d6b20df2..00000000 --- a/lib/tasks/dev/user.sh +++ /dev/null @@ -1,7 +0,0 @@ -sudo adduser \ - --gecos 'gitlab dev user' \ - --disabled-password \ - --home /home/gitlabdev \ - gitlabdev - -sudo -i -u gitlabdev -H sh -c "ssh-keygen -t rsa" From ccdea8b80dc3b7642c5d99e8f5dc8e80ed1e19c2 Mon Sep 17 00:00:00 2001 From: randx Date: Sun, 19 Aug 2012 12:14:36 +0300 Subject: [PATCH 14/46] Fix dashboard random test fail --- features/step_definitions/dashboard_steps.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/features/step_definitions/dashboard_steps.rb b/features/step_definitions/dashboard_steps.rb index bd9d69b8..90c3a69c 100644 --- a/features/step_definitions/dashboard_steps.rb +++ b/features/step_definitions/dashboard_steps.rb @@ -93,11 +93,11 @@ end Given /^I have assigned issues$/ do project1 = Factory :project, :path => "project1", - :code => "TEST1" + :code => "gitlabhq_1" project2 = Factory :project, :path => "project2", - :code => "TEST2" + :code => "gitlabhq_2" project1.add_access(@user, :read, :write) project2.add_access(@user, :read, :write) @@ -116,11 +116,11 @@ end Given /^I have authored merge requests$/ do project1 = Factory :project, :path => "project1", - :code => "TEST1" + :code => "gitlabhq_1" project2 = Factory :project, :path => "project2", - :code => "TEST2" + :code => "gitlabhq_2" project1.add_access(@user, :read, :write) project2.add_access(@user, :read, :write) From 335b3ed19791d2bf5aea9c95f46af925ebc80412 Mon Sep 17 00:00:00 2001 From: Jakub Jirutka Date: Mon, 20 Aug 2012 12:58:03 +0200 Subject: [PATCH 15/46] fix condition in find_for_ldap_auth --- app/models/user.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/user.rb b/app/models/user.rb index 1b53bda2..b0538cb7 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -91,7 +91,7 @@ class User < ActiveRecord::Base provider = auth.provider name = auth.info.name.force_encoding("utf-8") email = auth.info.email.downcase unless auth.info.email.nil? - raise OmniAuth::Error, "LDAP accounts must provide an uid and email address" if uid.nil? and email.nil? + raise OmniAuth::Error, "LDAP accounts must provide an uid and email address" if uid.nil? or email.nil? if @user = User.find_by_extern_uid_and_provider(uid, provider) @user From b2b88b2ff2a3044efa259d06c630dc903d0851bd Mon Sep 17 00:00:00 2001 From: randx Date: Mon, 20 Aug 2012 22:51:37 +0300 Subject: [PATCH 16/46] Added font for head panel. Major restyle for header --- .../fonts/korolev-medium-compressed.otf | Bin 0 -> 28796 bytes app/assets/images/logo_dark.png | Bin 0 -> 2858 bytes app/assets/images/logo_white.png | Bin 0 -> 1681 bytes app/assets/stylesheets/main.scss | 2 ++ app/assets/stylesheets/sections/header.scss | 20 ++++++----- app/assets/stylesheets/themes/ui_basic.scss | 32 ++++++++++++++++++ app/assets/stylesheets/themes/ui_mars.scss | 12 ++----- app/assets/stylesheets/themes/ui_modern.scss | 28 ++++++--------- app/views/layouts/_head.html.haml | 1 - config/application.rb | 3 ++ 10 files changed, 63 insertions(+), 35 deletions(-) create mode 100644 app/assets/fonts/korolev-medium-compressed.otf create mode 100644 app/assets/images/logo_dark.png create mode 100644 app/assets/images/logo_white.png diff --git a/app/assets/fonts/korolev-medium-compressed.otf b/app/assets/fonts/korolev-medium-compressed.otf new file mode 100644 index 0000000000000000000000000000000000000000..e3817cec85765374e2853696aca7c889a849fcd0 GIT binary patch literal 28796 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 t)T=0a0bfFm-Tl69gRGvPn_IvC Date: Mon, 20 Aug 2012 23:04:53 -0400 Subject: [PATCH 17/46] Remove some duplication in the Notify mailer --- app/mailers/notify.rb | 44 ++++++++++++++++++++++++++++--------- spec/mailers/notify_spec.rb | 6 ++--- 2 files changed, 37 insertions(+), 13 deletions(-) diff --git a/app/mailers/notify.rb b/app/mailers/notify.rb index 9563fdbc..c3b2e490 100644 --- a/app/mailers/notify.rb +++ b/app/mailers/notify.rb @@ -12,20 +12,20 @@ class Notify < ActionMailer::Base def new_user_email(user_id, password) @user = User.find(user_id) @password = password - mail(to: @user.email, subject: "gitlab | Account was created for you") + mail(to: @user.email, subject: subject("Account was created for you")) end def new_issue_email(issue_id) @issue = Issue.find(issue_id) @project = @issue.project - mail(to: @issue.assignee_email, subject: "gitlab | new issue ##{@issue.id} | #{@issue.title} | #{@project.name}") + mail(to: @issue.assignee_email, subject: subject("new issue ##{@issue.id}", @issue.title)) end def note_wall_email(recipient_id, note_id) recipient = User.find(recipient_id) @note = Note.find(note_id) @project = @note.project - mail(to: recipient.email, subject: "gitlab | #{@project.name}") + mail(to: recipient.email, subject: subject) end def note_commit_email(recipient_id, note_id) @@ -34,7 +34,7 @@ class Notify < ActionMailer::Base @commit = @note.target @commit = CommitDecorator.decorate(@commit) @project = @note.project - mail(to: recipient.email, subject: "gitlab | note for commit #{@commit.short_id} | #{@commit.title} | #{@project.name}") + mail(to: recipient.email, subject: subject("note for commit #{@commit.short_id}", @commit.title)) end def note_merge_request_email(recipient_id, note_id) @@ -42,7 +42,7 @@ class Notify < ActionMailer::Base @note = Note.find(note_id) @merge_request = @note.noteable @project = @note.project - mail(to: recipient.email, subject: "gitlab | note for merge request !#{@merge_request.id} | #{@project.name}") + mail(to: recipient.email, subject: subject("note for merge request !#{@merge_request.id}")) end def note_issue_email(recipient_id, note_id) @@ -50,7 +50,7 @@ class Notify < ActionMailer::Base @note = Note.find(note_id) @issue = @note.noteable @project = @note.project - mail(to: recipient.email, subject: "gitlab | note for issue ##{@issue.id} | #{@project.name}") + mail(to: recipient.email, subject: subject("note for issue ##{@issue.id}")) end def note_wiki_email(recipient_id, note_id) @@ -58,13 +58,13 @@ class Notify < ActionMailer::Base @note = Note.find(note_id) @wiki = @note.noteable @project = @note.project - mail(to: recipient.email, subject: "gitlab | note for wiki | #{@project.name}") + mail(to: recipient.email, subject: subject("note for wiki")) end def new_merge_request_email(merge_request_id) @merge_request = MergeRequest.find(merge_request_id) @project = @merge_request.project - mail(to: @merge_request.assignee_email, subject: "gitlab | new merge request !#{@merge_request.id} | #{@merge_request.title} | #{@project.name}") + mail(to: @merge_request.assignee_email, subject: subject("new merge request !#{@merge_request.id}", @merge_request.title)) end def reassigned_merge_request_email(recipient_id, merge_request_id, previous_assignee_id) @@ -72,7 +72,7 @@ class Notify < ActionMailer::Base @merge_request = MergeRequest.find(merge_request_id) @previous_assignee ||= User.find(previous_assignee_id) @project = @merge_request.project - mail(to: recipient.email, subject: "gitlab | changed merge request !#{@merge_request.id} | #{@merge_request.title} | #{@project.name}") + mail(to: recipient.email, subject: subject("changed merge request !#{@merge_request.id}", @merge_request.title)) end def reassigned_issue_email(recipient_id, issue_id, previous_assignee_id) @@ -80,6 +80,30 @@ class Notify < ActionMailer::Base @issue = Issue.find(issue_id) @previous_assignee ||= User.find(previous_assignee_id) @project = @issue.project - mail(to: recipient.email, subject: "gitlab | changed issue ##{@issue.id} | #{@issue.title} | #{@project.name}") + mail(to: recipient.email, subject: subject("changed issue ##{@issue.id}", @issue.title)) + end + + private + + # Formats arguments into a String suitable for use as an email subject + # + # extra - Extra Strings to be inserted into the subject + # + # Examples + # + # >> subject('Lorem ipsum') + # => "gitlab | Lorem ipsum" + # + # # Automatically inserts Project name when @project is set + # >> @project = Project.last + # => # + # >> subject('Lorem ipsum') + # => "gitlab | Lorem ipsum | Ruby on Rails" + # + # # Accepts multiple arguments + # >> subject('Lorem ipsum', 'Dolor sit amet') + # => "gitlab | Lorem ipsum | Dolor sit amet" + def subject(*extra) + "gitlab | " << extra.join(' | ') << (@project ? " | #{@project.name}" : "") end end diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb index 27af1e38..93427ebf 100644 --- a/spec/mailers/notify_spec.rb +++ b/spec/mailers/notify_spec.rb @@ -24,7 +24,7 @@ describe Notify do end it 'has the correct subject' do - should have_subject /Account was created for you/ + should have_subject /^gitlab \| Account was created for you$/ end it 'contains the new user\'s login name' do @@ -60,7 +60,7 @@ describe Notify do it_behaves_like 'an assignee email' it 'has the correct subject' do - should have_subject /new issue ##{issue.id}/ + should have_subject /new issue ##{issue.id} \| #{issue.title} \| #{project.name}/ end it 'contains a link to the new issue' do @@ -76,7 +76,7 @@ describe Notify do it_behaves_like 'a multiple recipients email' it 'has the correct subject' do - should have_subject /changed issue/ + should have_subject /changed issue ##{issue.id} \| #{issue.title}/ end it 'contains the name of the previous assignee' do From c5b13cc9897d9b7175722589e6f0c3b66ded143d Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Mon, 20 Aug 2012 23:18:57 -0400 Subject: [PATCH 18/46] Remove more duplication in the Notify mailer --- app/mailers/notify.rb | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/app/mailers/notify.rb b/app/mailers/notify.rb index c3b2e490..d0571b7b 100644 --- a/app/mailers/notify.rb +++ b/app/mailers/notify.rb @@ -22,43 +22,38 @@ class Notify < ActionMailer::Base end def note_wall_email(recipient_id, note_id) - recipient = User.find(recipient_id) @note = Note.find(note_id) @project = @note.project - mail(to: recipient.email, subject: subject) + mail(to: recipient(recipient_id), subject: subject) end def note_commit_email(recipient_id, note_id) - recipient = User.find(recipient_id) @note = Note.find(note_id) @commit = @note.target @commit = CommitDecorator.decorate(@commit) @project = @note.project - mail(to: recipient.email, subject: subject("note for commit #{@commit.short_id}", @commit.title)) + mail(to: recipient(recipient_id), subject: subject("note for commit #{@commit.short_id}", @commit.title)) end def note_merge_request_email(recipient_id, note_id) - recipient = User.find(recipient_id) @note = Note.find(note_id) @merge_request = @note.noteable @project = @note.project - mail(to: recipient.email, subject: subject("note for merge request !#{@merge_request.id}")) + mail(to: recipient(recipient_id), subject: subject("note for merge request !#{@merge_request.id}")) end def note_issue_email(recipient_id, note_id) - recipient = User.find(recipient_id) @note = Note.find(note_id) @issue = @note.noteable @project = @note.project - mail(to: recipient.email, subject: subject("note for issue ##{@issue.id}")) + mail(to: recipient(recipient_id), subject: subject("note for issue ##{@issue.id}")) end def note_wiki_email(recipient_id, note_id) - recipient = User.find(recipient_id) @note = Note.find(note_id) @wiki = @note.noteable @project = @note.project - mail(to: recipient.email, subject: subject("note for wiki")) + mail(to: recipient(recipient_id), subject: subject("note for wiki")) end def new_merge_request_email(merge_request_id) @@ -68,23 +63,32 @@ class Notify < ActionMailer::Base end def reassigned_merge_request_email(recipient_id, merge_request_id, previous_assignee_id) - recipient = User.find(recipient_id) @merge_request = MergeRequest.find(merge_request_id) @previous_assignee ||= User.find(previous_assignee_id) @project = @merge_request.project - mail(to: recipient.email, subject: subject("changed merge request !#{@merge_request.id}", @merge_request.title)) + mail(to: recipient(recipient_id), subject: subject("changed merge request !#{@merge_request.id}", @merge_request.title)) end def reassigned_issue_email(recipient_id, issue_id, previous_assignee_id) - recipient = User.find(recipient_id) @issue = Issue.find(issue_id) @previous_assignee ||= User.find(previous_assignee_id) @project = @issue.project - mail(to: recipient.email, subject: subject("changed issue ##{@issue.id}", @issue.title)) + mail(to: recipient(recipient_id), subject: subject("changed issue ##{@issue.id}", @issue.title)) end private + # Look up a User by their ID and return their email address + # + # recipient_id - User ID + # + # Returns a String containing the User's email address. + def recipient(recipient_id) + if recipient = User.find(recipient_id) + recipient.email + end + end + # Formats arguments into a String suitable for use as an email subject # # extra - Extra Strings to be inserted into the subject From 2e7ca8c866179e78866cba1659dee45185911f5a Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 21 Aug 2012 08:20:11 +0300 Subject: [PATCH 19/46] Show only tm events related to this project --- app/assets/stylesheets/sections/projects.scss | 21 ++++++++++--------- app/controllers/team_members_controller.rb | 1 + app/views/team_members/show.html.haml | 2 +- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/app/assets/stylesheets/sections/projects.scss b/app/assets/stylesheets/sections/projects.scss index 8c79e45e..1269ac8a 100644 --- a/app/assets/stylesheets/sections/projects.scss +++ b/app/assets/stylesheets/sections/projects.scss @@ -1,17 +1,18 @@ -.projects { +.projects { @extend .row; .activities { } - .side { + .side { @extend .span4; @extend .right; - .projects_box { - h5 { + .projects_box { + h5 { color:$style_color; font-size:16px; text-shadow: 0 1px 1px #fff; + padding: 2px 10px; } @extend .leftbar; @extend .ui-box; @@ -19,19 +20,19 @@ } } -.new_project, -.edit_project { - .project_name_holder { +.new_project, +.edit_project { + .project_name_holder { input, - label { + label { font-size:16px; line-height:20px; padding:8px; } - label { + label { color:#888; } - .btn { + .btn { padding:6px; margin-left:10px; } diff --git a/app/controllers/team_members_controller.rb b/app/controllers/team_members_controller.rb index 0cc58c3e..0846f096 100644 --- a/app/controllers/team_members_controller.rb +++ b/app/controllers/team_members_controller.rb @@ -9,6 +9,7 @@ class TeamMembersController < ApplicationController def show @team_member = project.users_projects.find(params[:id]) + @events = @team_member.user.recent_events.where(:project_id => @project.id).limit(7) end def new diff --git a/app/views/team_members/show.html.haml b/app/views/team_members/show.html.haml index d7e09bce..6cb357cd 100644 --- a/app/views/team_members/show.html.haml +++ b/app/views/team_members/show.html.haml @@ -51,7 +51,7 @@ = form_for(@team_member, as: :team_member, url: project_team_member_path(@project, @team_member)) do |f| = f.select :project_access, options_for_select(Project.access_options, @team_member.project_access), {}, class: "project-access-select", disabled: !allow_admin %hr - = render user.recent_events.limit(5) + = render @events :javascript $(function(){ $('.repo-access-select, .project-access-select').live("change", function() { From c625293b995cf21a1b551cecaac3cb742fcb2e66 Mon Sep 17 00:00:00 2001 From: randx Date: Tue, 21 Aug 2012 20:14:06 +0300 Subject: [PATCH 20/46] Handle post-receive files via gitolite, not gitlab --- app/roles/repository.rb | 18 --------------- app/views/errors/gitolite.html.haml | 2 -- lib/{post-receive-hook => hooks/post-receive} | 0 lib/tasks/gitlab/setup.rake | 7 +++++- lib/tasks/gitlab/status.rake | 6 ----- lib/tasks/gitlab/update_hooks.rake | 19 --------------- lib/tasks/gitlab/write_hook.rake | 23 +++++++++++++++++++ 7 files changed, 29 insertions(+), 46 deletions(-) rename lib/{post-receive-hook => hooks/post-receive} (100%) delete mode 100644 lib/tasks/gitlab/update_hooks.rake create mode 100644 lib/tasks/gitlab/write_hook.rake diff --git a/app/roles/repository.rb b/app/roles/repository.rb index 8d5b018d..7f1d6f84 100644 --- a/app/roles/repository.rb +++ b/app/roles/repository.rb @@ -30,26 +30,10 @@ module Repository Commit.commits_between(repo, from, to) end - def write_hooks - %w(post-receive).each do |hook| - write_hook(hook, File.read(File.join(Rails.root, 'lib', "#{hook}-hook"))) - end - end - def satellite @satellite ||= Gitlab::Satellite.new(self) end - def write_hook(name, content) - hook_file = File.join(path_to_repo, 'hooks', name) - - File.open(hook_file, 'w') do |f| - f.write(content) - end - - File.chmod(0775, hook_file) - end - def has_post_receive_file? hook_file = File.join(path_to_repo, 'hooks', 'post-receive') File.exists?(hook_file) @@ -73,8 +57,6 @@ module Repository def update_repository Gitlab::GitHost.system.update_project(path, self) - - write_hooks if File.exists?(path_to_repo) end def destroy_repository diff --git a/app/views/errors/gitolite.html.haml b/app/views/errors/gitolite.html.haml index 4788c2e5..50268b1a 100644 --- a/app/views/errors/gitolite.html.haml +++ b/app/views/errors/gitolite.html.haml @@ -23,5 +23,3 @@ = preserve do sudo chmod -R 770 /home/git/repositories/ sudo chown -R git:git /home/git/repositories/ - sudo chown gitlab:gitlab /home/git/repositories/**/hooks/post-receive - diff --git a/lib/post-receive-hook b/lib/hooks/post-receive similarity index 100% rename from lib/post-receive-hook rename to lib/hooks/post-receive diff --git a/lib/tasks/gitlab/setup.rake b/lib/tasks/gitlab/setup.rake index d60e73e9..21ce5d70 100644 --- a/lib/tasks/gitlab/setup.rake +++ b/lib/tasks/gitlab/setup.rake @@ -1,7 +1,12 @@ namespace :gitlab do namespace :app do desc "GITLAB | Setup production application" - task :setup => ['db:setup', 'db:seed_fu', 'gitlab:app:enable_automerge'] + task :setup => [ + 'db:setup', + 'db:seed_fu', + 'gitlab:gitolite:write_hooks', + 'gitlab:app:enable_automerge' + ] end end diff --git a/lib/tasks/gitlab/status.rake b/lib/tasks/gitlab/status.rake index bc4e86ea..a16b1512 100644 --- a/lib/tasks/gitlab/status.rake +++ b/lib/tasks/gitlab/status.rake @@ -67,12 +67,6 @@ namespace :gitlab do next end - - unless File.owned?(hook_file) - puts "post-receive file is not owner by gitlab".red - next - end - puts "post-reveice file ok".green end end diff --git a/lib/tasks/gitlab/update_hooks.rake b/lib/tasks/gitlab/update_hooks.rake deleted file mode 100644 index 44e1617e..00000000 --- a/lib/tasks/gitlab/update_hooks.rake +++ /dev/null @@ -1,19 +0,0 @@ -namespace :gitlab do - namespace :gitolite do - desc "GITLAB | Rewrite hooks for repos" - task :update_hooks => :environment do - puts "Starting Projects" - Project.find_each(:batch_size => 100) do |project| - begin - if project.commit - project.write_hooks - print ".".green - end - rescue Exception => e - print e.message.red - end - end - puts "\nDone with projects" - end - end -end diff --git a/lib/tasks/gitlab/write_hook.rake b/lib/tasks/gitlab/write_hook.rake new file mode 100644 index 00000000..098331b8 --- /dev/null +++ b/lib/tasks/gitlab/write_hook.rake @@ -0,0 +1,23 @@ +namespace :gitlab do + namespace :gitolite do + desc "GITLAB | Write GITLAB hook for gitolite" + task :write_hooks => :environment do + gitolite_hooks_path = File.join("/home", Gitlab.config.ssh_user, "share", "gitolite", "hooks", "common") + gitlab_hooks_path = Rails.root.join("lib", "hooks") + + gitlab_hook_files = ['post-receive'] + + gitlab_hook_files.each do |file_name| + source = File.join(gitlab_hooks_path, file_name) + dest = File.join(gitolite_hooks_path, file_name) + + puts "sudo -u root cp #{source} #{dest}".yellow + `sudo -u root cp #{source} #{dest}` + + puts "sudo -u root chown git:git #{dest}".yellow + `sudo -u root chown git:git #{dest}` + end + end + end +end + From 64f3682feb90c3eb8dbd4f0cd16ad6057b302a6c Mon Sep 17 00:00:00 2001 From: randx Date: Tue, 21 Aug 2012 20:24:04 +0300 Subject: [PATCH 21/46] project should not respond to write_hooks any more --- spec/models/project_spec.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 5bba4ff6..af193295 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -40,7 +40,6 @@ describe Project do it { should respond_to(:commits_with_refs) } it { should respond_to(:commits_since) } it { should respond_to(:commits_between) } - it { should respond_to(:write_hooks) } it { should respond_to(:satellite) } it { should respond_to(:update_repository) } it { should respond_to(:destroy_repository) } From 7fa6a23416e08fe6ac832e60e78bd6fe61f21955 Mon Sep 17 00:00:00 2001 From: randx Date: Tue, 21 Aug 2012 20:53:33 +0300 Subject: [PATCH 22/46] Better fonts for all code like wiki etc --- app/assets/stylesheets/gitlab_bootstrap.scss | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/assets/stylesheets/gitlab_bootstrap.scss b/app/assets/stylesheets/gitlab_bootstrap.scss index 550046d0..e3f3e7af 100644 --- a/app/assets/stylesheets/gitlab_bootstrap.scss +++ b/app/assets/stylesheets/gitlab_bootstrap.scss @@ -1,6 +1,11 @@ body { margin-bottom:20px; } + +pre { + font-family:'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono','lucida console',monospace; +} + a { outline: none; color: $link_color; From f088eaa972ae3bc3beddb85adb98d185ea8a2ddc Mon Sep 17 00:00:00 2001 From: randx Date: Tue, 21 Aug 2012 21:26:56 +0300 Subject: [PATCH 23/46] Refactoring & minor css changes --- CHANGELOG | 5 +++++ app/assets/stylesheets/gitlab_bootstrap.scss | 6 +++++- app/assets/stylesheets/themes/ui_mars.scss | 8 ++++++++ app/models/project.rb | 2 +- app/roles/{project_push.rb => push_observer.rb} | 2 +- app/views/team_members/_show.html.haml | 2 +- 6 files changed, 21 insertions(+), 4 deletions(-) rename app/roles/{project_push.rb => push_observer.rb} (99%) diff --git a/CHANGELOG b/CHANGELOG index 80b68b0a..3a916766 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -3,6 +3,11 @@ v 2.8.0 - Bulk issues update - Issues API - Cucumber coverage increased + - Post-receive files fixed + - UI improved + - Application cleanup + - more cucumber + - capybara-webkit + headless v 2.7.0 - Issue Labels diff --git a/app/assets/stylesheets/gitlab_bootstrap.scss b/app/assets/stylesheets/gitlab_bootstrap.scss index e3f3e7af..8bc63e25 100644 --- a/app/assets/stylesheets/gitlab_bootstrap.scss +++ b/app/assets/stylesheets/gitlab_bootstrap.scss @@ -330,16 +330,20 @@ img.avatar { float:left; margin-right:15px; width:40px; - border:2px solid #ddd; + border:1px solid #ddd; + padding:1px; &.s16 { width:16px; + height:16px; } &.s24 { width:24px; + height:24px; } &.s32 { width:32px; + height:32px; } } diff --git a/app/assets/stylesheets/themes/ui_mars.scss b/app/assets/stylesheets/themes/ui_mars.scss index 2808ad32..c630f388 100644 --- a/app/assets/stylesheets/themes/ui_mars.scss +++ b/app/assets/stylesheets/themes/ui_mars.scss @@ -20,6 +20,10 @@ .fbtn { .btn { + i { + position: relative; + top: 1px; + } margin-left:8px; background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #595D63), to(#31363E)); background-image: -webkit-linear-gradient(#595D63 6.6%, #31363E); @@ -32,6 +36,10 @@ background-image: -moz-linear-gradient(#595D63 6.6%, #202227); background-image: -o-linear-gradient(#595D63 6.6%, #202227); background-position:0 0; + color:#fff; + i { + @extend .icon-white; + } } border: 1px solid #31363E; diff --git a/app/models/project.rb b/app/models/project.rb index 714953c6..3fe11916 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -2,7 +2,7 @@ require "grit" class Project < ActiveRecord::Base include Repository - include ProjectPush + include PushObserver include Authority include Team diff --git a/app/roles/project_push.rb b/app/roles/push_observer.rb similarity index 99% rename from app/roles/project_push.rb rename to app/roles/push_observer.rb index 02025384..1067404d 100644 --- a/app/roles/project_push.rb +++ b/app/roles/push_observer.rb @@ -1,4 +1,4 @@ -module ProjectPush +module PushObserver def observe_push(oldrev, newrev, ref, user) data = post_receive_data(oldrev, newrev, ref, user) diff --git a/app/views/team_members/_show.html.haml b/app/views/team_members/_show.html.haml index f47554c1..2dc4fb65 100644 --- a/app/views/team_members/_show.html.haml +++ b/app/views/team_members/_show.html.haml @@ -9,7 +9,7 @@ %span.label Blocked = link_to project_team_member_path(@project, member), title: user.name, class: "dark" do - = image_tag gravatar_icon(user.email, 40), class: "avatar" + = image_tag gravatar_icon(user.email, 40), class: "avatar s32" = link_to project_team_member_path(@project, member), title: user.name, class: "dark" do %strong= truncate(user.name, lenght: 40) %br From 3095483e650ae4fc79ff5012117877e5734b75ee Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 21 Aug 2012 22:20:09 -0400 Subject: [PATCH 24/46] Speed up the "Project Network Graph" cucumber feature This change involves stubbing out the call to `Grit::Commit.find_all` and limiting the number of commits to 10 vs. the standard of 650 used by `Gitlab::GraphCommit.to_graph`. Prior to this change, this single feature required almost 3 minutes of running time and over 2 GB of memory on my machine. Now it takes less than 3 seconds. --- features/projects/network.feature | 4 +--- features/step_definitions/project/projects_steps.rb | 10 ++++++++-- features/support/env.rb | 2 ++ 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/features/projects/network.feature b/features/projects/network.feature index 9655184c..61c05eb3 100644 --- a/features/projects/network.feature +++ b/features/projects/network.feature @@ -4,9 +4,7 @@ Feature: Project Network Graph Background: Given I signin as a user And I own project "Shop" - And I visit project "Shop" network page + And I visit project "Shop" network page Scenario: I should see project network Then page should have network graph - - diff --git a/features/step_definitions/project/projects_steps.rb b/features/step_definitions/project/projects_steps.rb index c9af346e..1a336ed2 100644 --- a/features/step_definitions/project/projects_steps.rb +++ b/features/step_definitions/project/projects_steps.rb @@ -57,6 +57,11 @@ end Given /^I visit project "(.*?)" network page$/ do |arg1| project = Project.find_by_name(arg1) + + # Stub out find_all to speed this up (10 commits vs. 650) + commits = Grit::Commit.find_all(project.repo, nil, {max_count: 10}) + Grit::Commit.stub(:find_all).and_return(commits) + visit graph_project_path(project) end @@ -67,8 +72,9 @@ end Given /^page should have network graph$/ do page.should have_content "Project Network Graph" within ".graph" do - page.should have_content "stable" - page.should have_content "notes_refacto..." + page.should have_content "master" + page.should have_content "github" + page.should have_content "scss_refactor..." end end diff --git a/features/support/env.rb b/features/support/env.rb index 496f23f9..78d829c1 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -44,3 +44,5 @@ require 'headless' headless = Headless.new headless.start + +require 'cucumber/rspec/doubles' From 6baf9c441d7051276b5bfdbe43499d74d3acc65d Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 21 Aug 2012 22:48:15 -0400 Subject: [PATCH 25/46] Fix cucumber failure that only happened on Travis --- features/step_definitions/project/projects_steps.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/features/step_definitions/project/projects_steps.rb b/features/step_definitions/project/projects_steps.rb index 1a336ed2..3ff08d58 100644 --- a/features/step_definitions/project/projects_steps.rb +++ b/features/step_definitions/project/projects_steps.rb @@ -73,7 +73,6 @@ Given /^page should have network graph$/ do page.should have_content "Project Network Graph" within ".graph" do page.should have_content "master" - page.should have_content "github" page.should have_content "scss_refactor..." end end From d1daeba1736ba145fe525ce08a91f29495a3abf1 Mon Sep 17 00:00:00 2001 From: randx Date: Tue, 21 Aug 2012 22:44:49 +0300 Subject: [PATCH 26/46] Updated app:status & docs with hooks. Removed write_hooks from app:setup --- doc/installation.md | 7 ++++++- lib/tasks/gitlab/setup.rake | 1 - lib/tasks/gitlab/status.rake | 14 ++++++++++++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/doc/installation.md b/doc/installation.md index 5611c3b3..1d32e1b7 100644 --- a/doc/installation.md +++ b/doc/installation.md @@ -119,7 +119,6 @@ Permissions: sudo chmod -R g+rwX /home/git/repositories/ sudo chown -R git:git /home/git/repositories/ - sudo chown gitlab:gitlab /home/git/repositories/**/hooks/post-receive #### CHECK: Logout & login again to apply git group to your user @@ -177,6 +176,11 @@ Permissions: #### Setup DB sudo -u gitlab bundle exec rake gitlab:app:setup RAILS_ENV=production + +#### Setup gitlab hooks + + sudo cp ./lib/hooks/post-receive /home/git/share/gitolite/hooks/common/post-receive + sudo chown git:git /home/git/share/gitolite/hooks/common/post-receive Checking status: @@ -196,6 +200,7 @@ Checking status: Resolving deltas: 100% (174/174), done. Can clone gitolite-admin?............YES UMASK for .gitolite.rc is 0007? ............YES + /home/git/share/gitolite/hooks/common/post-receive exists? ............YES If you got all YES - congrats! You can go to next step. diff --git a/lib/tasks/gitlab/setup.rake b/lib/tasks/gitlab/setup.rake index 21ce5d70..49c86461 100644 --- a/lib/tasks/gitlab/setup.rake +++ b/lib/tasks/gitlab/setup.rake @@ -4,7 +4,6 @@ namespace :gitlab do task :setup => [ 'db:setup', 'db:seed_fu', - 'gitlab:gitolite:write_hooks', 'gitlab:app:enable_automerge' ] end diff --git a/lib/tasks/gitlab/status.rake b/lib/tasks/gitlab/status.rake index a16b1512..02d27d4b 100644 --- a/lib/tasks/gitlab/status.rake +++ b/lib/tasks/gitlab/status.rake @@ -56,6 +56,20 @@ namespace :gitlab do return end + gitolite_hooks_path = File.join("/home", Gitlab.config.ssh_user, "share", "gitolite", "hooks", "common") + gitlab_hook_files = ['post-receive'] + gitlab_hook_files.each do |file_name| + dest = File.join(gitolite_hooks_path, file_name) + print "#{dest} exists? ............" + if File.exists?(dest) + puts "YES".green + else + puts "NO".red + return + end + end + + if Project.count > 0 puts "Validating projects repositories:".yellow Project.find_each(:batch_size => 100) do |project| From 9ec4c2d214b045f3fb207edd67055c4ccaebd381 Mon Sep 17 00:00:00 2001 From: Sytse Sijbrandij Date: Tue, 21 Aug 2012 23:07:11 +0200 Subject: [PATCH 27/46] Show only the commits that are newer in the merge request. --- app/models/merge_request.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 47966d66..542817b0 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -88,8 +88,11 @@ class MergeRequest < ActiveRecord::Base end 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) rescue [] + # Only show what is new in the source branch compared to the target branch, not the other way around. + # The linex below with merge_base is equivalent to diff with three dots (git diff branch1...branch2) + # From the git documentation: "git diff A...B" is equivalent to "git diff $(git-merge-base A B) B" + common_commit = project.repo.git.native(:merge_base, {}, [target_branch, source_branch]).strip + diffs = project.repo.diff(common_commit, source_branch) end def last_commit From 5c7ed6fa26b47ac71ff6ba04720d85df6d74b200 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 22 Aug 2012 01:15:26 +0300 Subject: [PATCH 28/46] Up to 2.8 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 3f80e494..834f2629 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.8.0pre +2.8.0 From aa50408ecb5c7355d7cac86f7a59f02ae087714c Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 22 Aug 2012 03:56:53 +0300 Subject: [PATCH 29/46] 1. Better message if no ssh key 2. SSH Keys Help page --- app/assets/stylesheets/common.scss | 8 +++ app/assets/stylesheets/gitlab_bootstrap.scss | 5 ++ app/views/dashboard/index.html.haml | 11 +-- app/views/help/index.html.haml | 3 + app/views/help/permissions.html.haml | 4 +- app/views/help/ssh.html.haml | 25 +++++++ app/views/help/system_hooks.html.haml | 4 +- app/views/help/web_hooks.html.haml | 8 +-- app/views/help/workflow.html.haml | 13 ++-- app/views/keys/_form.html.haml | 8 ++- app/views/keys/new.html.haml | 2 +- .../show/_how_to_merge.html.haml | 11 ++- app/views/projects/empty.html.haml | 67 +++++++------------ app/views/shared/_no_ssh.html.haml | 8 +++ config/routes.rb | 1 + 15 files changed, 104 insertions(+), 74 deletions(-) create mode 100644 app/views/help/ssh.html.haml create mode 100644 app/views/shared/_no_ssh.html.haml diff --git a/app/assets/stylesheets/common.scss b/app/assets/stylesheets/common.scss index 6103d05c..3c160358 100644 --- a/app/assets/stylesheets/common.scss +++ b/app/assets/stylesheets/common.scss @@ -735,3 +735,11 @@ li.note { font-size: 12px; } } + +.error_message { + @extend .cred; + border-bottom: 1px solid #D21; + padding-bottom:20px; + text-align:center; + margin-bottom:10px; +} diff --git a/app/assets/stylesheets/gitlab_bootstrap.scss b/app/assets/stylesheets/gitlab_bootstrap.scss index 8bc63e25..03beeaef 100644 --- a/app/assets/stylesheets/gitlab_bootstrap.scss +++ b/app/assets/stylesheets/gitlab_bootstrap.scss @@ -4,6 +4,11 @@ body { pre { font-family:'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono','lucida console',monospace; + + &.dark { + background: #333; + color:#f5f5f5; + } } a { diff --git a/app/views/dashboard/index.html.haml b/app/views/dashboard/index.html.haml index 9fea5acb..ba7d019c 100644 --- a/app/views/dashboard/index.html.haml +++ b/app/views/dashboard/index.html.haml @@ -1,14 +1,7 @@ - if @projects.any? .projects .activities.span8 - - if current_user.require_ssh_key? - .alert.alert-error.padded - %span - You wont be able to pull/push project code unless you - %strong - = link_to new_key_path, class: "vlink" do - add new key - to your profile + = render 'shared/no_ssh' - if @events.any? .content_list= render @events - else @@ -57,5 +50,5 @@ If you will be added to project - it will be displayed here -:javascript +:javascript $(function(){ Pager.init(20); }); diff --git a/app/views/help/index.html.haml b/app/views/help/index.html.haml index 66f7c722..02549577 100644 --- a/app/views/help/index.html.haml +++ b/app/views/help/index.html.haml @@ -31,3 +31,6 @@ %li %h5= link_to "Gitlab Markdown", help_markdown_path + + %li + %h5= link_to "SSH keys", help_ssh_path diff --git a/app/views/help/permissions.html.haml b/app/views/help/permissions.html.haml index 7511d15d..f9287fa0 100644 --- a/app/views/help/permissions.html.haml +++ b/app/views/help/permissions.html.haml @@ -1,6 +1,6 @@ -%h3 Permissions +%h3.page_title Permissions .back_link - = link_to help_path do + = link_to help_path do ← to index %hr diff --git a/app/views/help/ssh.html.haml b/app/views/help/ssh.html.haml new file mode 100644 index 00000000..6a581204 --- /dev/null +++ b/app/views/help/ssh.html.haml @@ -0,0 +1,25 @@ +%h3.page_title SSH Keys +.back_link + = link_to help_path do + ← to index +%hr + +%p.slead + SSH key allows you to establish a secure connection between your computer and Gitlab + +%p.slead + To generate a new SSH key just open your terminal and use code below. + +%pre.dark + ssh-keygen -t rsa -C "#{current_user.email}" + + \# Creates a new ssh key using the provided email + \# Generating public/private rsa key pair... + +%p.slead + Next just use code below to dump your public key and add to GITLAB SSH Keys + +%pre.dark + cat ~/.ssh/id_rsa.pub + + \# ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC6eNtGpNGwstc.... diff --git a/app/views/help/system_hooks.html.haml b/app/views/help/system_hooks.html.haml index 2088208a..9fc8cbab 100644 --- a/app/views/help/system_hooks.html.haml +++ b/app/views/help/system_hooks.html.haml @@ -1,10 +1,10 @@ %h3 System hooks .back_link - = link_to :back do + = link_to :back do ← back %hr -%p.slead +%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. diff --git a/app/views/help/web_hooks.html.haml b/app/views/help/web_hooks.html.haml index 3acea62c..263eadf6 100644 --- a/app/views/help/web_hooks.html.haml +++ b/app/views/help/web_hooks.html.haml @@ -1,11 +1,11 @@ -%h3 Web hooks +%h3.page_title Web hooks .back_link - = link_to help_path do + = link_to help_path do ← to index %hr -%p.slead - Every Gitlab project can trigger a web server whenever the repo is pushed to. +%p.slead + Every Gitlab project can trigger a web server whenever the repo is pushed to. %br Web Hooks can be used to update an external issue tracker, trigger CI builds, update a backup mirror, or even deploy to your production server. %br diff --git a/app/views/help/workflow.html.haml b/app/views/help/workflow.html.haml index 7db8133b..a3fe3b01 100644 --- a/app/views/help/workflow.html.haml +++ b/app/views/help/workflow.html.haml @@ -1,7 +1,6 @@ -- bash_lexer = Pygments::Lexer[:bash] -%h3 Workflow +%h3.page_title Workflow .back_link - = link_to help_path do + = link_to help_path do ← to index %hr @@ -9,25 +8,25 @@ %li %p Clone project .bash - %pre + %pre.dark git clone git@example.com:project-name.git %li %p Create branch with your feature .bash - %pre + %pre.dark git checkout -b $feature_name %li %p Write code. Commit changes .bash - %pre + %pre.dark git commit -am "My feature is ready" %li %p Push your branch to gitlabhq .bash - %pre + %pre.dark git push origin $feature_name %li diff --git a/app/views/keys/_form.html.haml b/app/views/keys/_form.html.haml index ee2eafdd..9c6e229b 100644 --- a/app/views/keys/_form.html.haml +++ b/app/views/keys/_form.html.haml @@ -11,7 +11,13 @@ .input= f.text_field :title .clearfix = f.label :key - .input= f.text_area :key, class: [:xxlarge, :thin_area] + .input + = f.text_area :key, class: [:xxlarge, :thin_area] + %p.hint + Paste your public key here. Read more about how generate it + = link_to "here", help_ssh_path + + .actions = f.submit 'Save', class: "primary btn" = link_to "Cancel", keys_path, class: "btn" diff --git a/app/views/keys/new.html.haml b/app/views/keys/new.html.haml index 02e782b9..fff38058 100644 --- a/app/views/keys/new.html.haml +++ b/app/views/keys/new.html.haml @@ -1,4 +1,4 @@ -%h3.page_title New key +%h3.page_title Add an SSH Key %hr = render 'form' diff --git a/app/views/merge_requests/show/_how_to_merge.html.haml b/app/views/merge_requests/show/_how_to_merge.html.haml index c21f2727..69881d43 100644 --- a/app/views/merge_requests/show/_how_to_merge.html.haml +++ b/app/views/merge_requests/show/_how_to_merge.html.haml @@ -3,13 +3,12 @@ %a.close{href: "#"} × %h3 How To Merge .modal-body - %pre + %pre.dark = preserve do - :erb - git checkout <%= @merge_request.target_branch %> - git fetch origin - git merge origin/<%= @merge_request.source_branch %> - git push origin <%= @merge_request.target_branch %> + git checkout #{@merge_request.target_branch} + git fetch origin + git merge origin/#{@merge_request.source_branch} + git push origin #{@merge_request.target_branch} :javascript diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml index b8d0dad9..907d5ef4 100644 --- a/app/views/projects/empty.html.haml +++ b/app/views/projects/empty.html.haml @@ -1,47 +1,30 @@ -- if current_user.require_ssh_key? - .alert-message.block-message.error - %ul - %li You have no ssh keys added to your profile. - %li You wont be able to pull/push repository. - %li Visit profile → keys and add public key of every machine you want to use for work with gitlabhq. - -.alert-message.block-message.error - %ul.unstyled.alert_holder - %li You should push repository to proceed. - %li After push you will be able to browse code, commits etc. - -- bash_lexer = Pygments::Lexer[:bash] += render 'shared/no_ssh' %div.git-empty - %h3 Git global setup: - - setup_str = ["git config --global user.name \"#{current_user.name}\"", - "git config --global user.email \"#{current_user.email}\""].join("\n") - = preserve do - = raw bash_lexer.highlight(setup_str, lexer: 'bash', options: {encoding: 'utf-8'}) + %h4 Git global setup: + %pre.dark + = preserve do + git config --global user.name "#{current_user.name}" + git config --global user.email "#{current_user.email}" - %br - %br - %h3 Create Repository - - repo_setup_str = ["mkdir #{@project.path}", - "cd #{@project.path}", - "git init", - "touch README", - "git add README", - "git commit -m 'first commit'", - "git remote add origin #{@project.url_to_repo}", - "git push -u origin master"].join("\n") + %h4.prepend-top-20 Create Repository + %pre.dark + = preserve do + mkdir #{@project.path} + cd #{@project.path} + git init + touch README + git add README + git commit -m 'first commit' + git remote add origin #{@project.url_to_repo} + git push -u origin master - = preserve do - = raw bash_lexer.highlight(repo_setup_str) - - %br - %br - %h3 Existing Git Repo? - - exist_repo_setup_str = ["cd existing_git_repo", - "git remote add origin #{@project.url_to_repo}", - "git push -u origin master"].join("\n") - = preserve do - = raw bash_lexer.highlight(exist_repo_setup_str) + %h4.prepend-top-20 Existing Git Repo? + %pre.dark + = preserve do + cd existing_git_repo + git remote add origin #{@project.url_to_repo} + git push -u origin master - if can? current_user, :admin_project, @project - .alert-message.block-message.error.prepend-top-20 - = link_to 'Remove project', @project, confirm: 'Are you sure?', method: :delete, class: "btn danger" + .prepend-top-20 + = link_to 'Remove project', @project, confirm: 'Are you sure?', method: :delete, class: "btn danger right" diff --git a/app/views/shared/_no_ssh.html.haml b/app/views/shared/_no_ssh.html.haml new file mode 100644 index 00000000..b6ab666b --- /dev/null +++ b/app/views/shared/_no_ssh.html.haml @@ -0,0 +1,8 @@ +- if current_user.require_ssh_key? + %h6.error_message + %span + You wont be able to pull/push project code unless you + %strong + = link_to new_key_path, class: "vlink" do + add SSH key + to your profile diff --git a/config/routes.rb b/config/routes.rb index 04e13bc4..97594d57 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -30,6 +30,7 @@ Gitlab::Application.routes.draw do get 'help/web_hooks' => 'help#web_hooks' get 'help/system_hooks' => 'help#system_hooks' get 'help/markdown' => 'help#markdown' + get 'help/ssh' => 'help#ssh' # # Admin Area From 6428d4dd63d112e3d1b2c3fa2f751651318a96d4 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 23 Aug 2012 08:15:30 +0300 Subject: [PATCH 30/46] gitlab_meta gem added --- Gemfile | 1 + Gemfile.lock | 2 ++ 2 files changed, 3 insertions(+) diff --git a/Gemfile b/Gemfile index e8b0b244..c3a01cb5 100644 --- a/Gemfile +++ b/Gemfile @@ -75,6 +75,7 @@ gem 'settingslogic' # Misc gem "foreman" gem "git" +gem "gitlab_meta" group :assets do gem "sass-rails", "3.2.5" diff --git a/Gemfile.lock b/Gemfile.lock index b23bc47c..b8bc95c6 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -172,6 +172,7 @@ GEM gherkin (2.11.0) json (>= 1.4.6) git (1.2.5) + gitlab_meta (2.8.1) grape (0.2.1) hashie (~> 1.2) multi_json @@ -394,6 +395,7 @@ DEPENDENCIES ffaker foreman git + gitlab_meta gitolite! grack! grape (~> 0.2.1) From a94b2d1ccfa17f92d2d17f20a23ab86e4456291e Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 23 Aug 2012 08:28:00 +0300 Subject: [PATCH 31/46] Fix meta gem version --- Gemfile | 2 +- Gemfile.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile b/Gemfile index c3a01cb5..f256eccb 100644 --- a/Gemfile +++ b/Gemfile @@ -75,7 +75,7 @@ gem 'settingslogic' # Misc gem "foreman" gem "git" -gem "gitlab_meta" +gem "gitlab_meta", '2.8' group :assets do gem "sass-rails", "3.2.5" diff --git a/Gemfile.lock b/Gemfile.lock index b8bc95c6..b2a00118 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -172,7 +172,7 @@ GEM gherkin (2.11.0) json (>= 1.4.6) git (1.2.5) - gitlab_meta (2.8.1) + gitlab_meta (2.8) grape (0.2.1) hashie (~> 1.2) multi_json @@ -395,7 +395,7 @@ DEPENDENCIES ffaker foreman git - gitlab_meta + gitlab_meta (= 2.8) gitolite! grack! grape (~> 0.2.1) From ed2b53cd1c34c421b23208eeb502a141a6829f9d Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 23 Aug 2012 09:17:18 +0300 Subject: [PATCH 32/46] Up to 2.8.1 --- CHANGELOG | 5 +++++ VERSION | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 3a916766..6868c07b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,8 @@ +v 2.8.1 + - ability to disable gravatars + - improved MR diff logic + - ssh key help page + v 2.8.0 - Gitlab Flavored Markdown - Bulk issues update diff --git a/VERSION b/VERSION index 834f2629..dbe59006 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.8.0 +2.8.1 From db4c3e58bca67bf773d9fe3e73acd6b687498334 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Thu, 23 Aug 2012 07:24:25 -0400 Subject: [PATCH 33/46] Don't run SimpleCov on Travis --- features/support/env.rb | 6 ++++-- spec/spec_helper.rb | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/features/support/env.rb b/features/support/env.rb index 78d829c1..b69a5fe7 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -1,5 +1,7 @@ -require 'simplecov' -SimpleCov.start 'rails' +unless ENV['CI'] + require 'simplecov' + SimpleCov.start 'rails' +end require 'cucumber/rails' require 'webmock/cucumber' diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 5c0bb618..cd931475 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,5 +1,7 @@ -require 'simplecov' -SimpleCov.start 'rails' +unless ENV['CI'] + require 'simplecov' + SimpleCov.start 'rails' +end # This file is copied to spec/ when you run 'rails generate rspec:install' ENV["RAILS_ENV"] ||= 'test' From 32ae7fb616673a003ba24dcde1ded3bad8ff37d1 Mon Sep 17 00:00:00 2001 From: Riyad Preukschas Date: Thu, 23 Aug 2012 20:10:06 +0200 Subject: [PATCH 34/46] Improve GFM code documentation --- app/helpers/gitlab_markdown_helper.rb | 21 +++++++++++++++++++-- lib/gitlab/markdown.rb | 19 ++++++++++++++----- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/app/helpers/gitlab_markdown_helper.rb b/app/helpers/gitlab_markdown_helper.rb index 24bc3e85..88e3473b 100644 --- a/app/helpers/gitlab_markdown_helper.rb +++ b/app/helpers/gitlab_markdown_helper.rb @@ -1,9 +1,18 @@ module GitlabMarkdownHelper + # Replaces references (i.e. @abc, #123, !456, ...) in the text with links to + # the appropriate items in Gitlab. + # + # text - the source text + # html_options - extra options for the reference links as given to link_to + # + # note: reference links will only be generated if @project is set + # + # see Gitlab::Markdown for details on the supported syntax def gfm(text, html_options = {}) return text if text.nil? return text if @project.nil? - # Extract pre blocks + # Extract pre blocks so they are not altered # from http://github.github.com/github-flavored-markdown/ extractions = {} text.gsub!(%r{
    .*?
    |.*?}m) do |match| @@ -25,7 +34,15 @@ module GitlabMarkdownHelper text.html_safe end - # circumvents nesting links, which will behave bad in browsers + # Use this in places where you would normally use link_to(gfm(...), ...). + # + # It solves a problem occurring with nested links (i.e. + # "outer text gfm ref more outer text"). This will not be + # interpreted as intended. Browsers will parse something like + # "outer text gfm ref more outer text" (notice the last part is + # not linked any more). link_to_gfm corrects that. It wraps all parts to + # explicitly produce the correct linking behavior (i.e. + # "outer text gfm ref more outer text"). def link_to_gfm(body, url, html_options = {}) gfm_body = gfm(body, html_options) diff --git a/lib/gitlab/markdown.rb b/lib/gitlab/markdown.rb index d3daed91..75fa835d 100644 --- a/lib/gitlab/markdown.rb +++ b/lib/gitlab/markdown.rb @@ -1,5 +1,14 @@ module Gitlab - # Custom parsing for Gitlab-flavored Markdown + # Custom parser for Gitlab-flavored Markdown + # + # It replaces references in the text with links to the appropriate items in Gitlab. + # + # Supported reference formats are: + # * @foo for team members + # * #123 for issues + # * !123 for merge requests + # * $123 for snippets + # * 123456 for commits # # Examples # @@ -67,25 +76,25 @@ module Gitlab def reference_user(identifier) if user = @project.users.where(name: identifier).first member = @project.users_projects.where(user_id: user).first - link_to("@#{user.name}", project_team_member_path(@project, member), html_options.merge(class: "gfm gfm-team_member #{html_options[:class]}")) if member + link_to("@#{identifier}", project_team_member_path(@project, member), html_options.merge(class: "gfm gfm-team_member #{html_options[:class]}")) if member end end def reference_issue(identifier) if issue = @project.issues.where(id: identifier).first - link_to("##{issue.id}", project_issue_path(@project, issue), html_options.merge(title: "Issue: #{issue.title}", class: "gfm gfm-issue #{html_options[:class]}")) + link_to("##{identifier}", project_issue_path(@project, issue), html_options.merge(title: "Issue: #{issue.title}", class: "gfm gfm-issue #{html_options[:class]}")) end end def reference_merge_request(identifier) if merge_request = @project.merge_requests.where(id: identifier).first - link_to("!#{merge_request.id}", project_merge_request_path(@project, merge_request), html_options.merge(title: "Merge Request: #{merge_request.title}", class: "gfm gfm-merge_request #{html_options[:class]}")) + link_to("!#{identifier}", project_merge_request_path(@project, merge_request), html_options.merge(title: "Merge Request: #{merge_request.title}", class: "gfm gfm-merge_request #{html_options[:class]}")) end end def reference_snippet(identifier) if snippet = @project.snippets.where(id: identifier).first - link_to("$#{snippet.id}", project_snippet_path(@project, snippet), html_options.merge(title: "Snippet: #{snippet.title}", class: "gfm gfm-snippet #{html_options[:class]}")) + link_to("$#{identifier}", project_snippet_path(@project, snippet), html_options.merge(title: "Snippet: #{snippet.title}", class: "gfm gfm-snippet #{html_options[:class]}")) end end From e2f19befbcc2c0b347507171ef94047426aba5b0 Mon Sep 17 00:00:00 2001 From: Cyril Date: Thu, 23 Aug 2012 23:04:43 +0200 Subject: [PATCH 35/46] fix inline forms --- app/assets/stylesheets/sections/projects.scss | 1 + app/views/admin/hooks/index.html.haml | 2 +- app/views/admin/projects/index.html.haml | 2 +- app/views/admin/users/index.html.haml | 2 +- app/views/hooks/index.html.haml | 2 +- app/views/search/show.html.haml | 2 +- 6 files changed, 6 insertions(+), 5 deletions(-) diff --git a/app/assets/stylesheets/sections/projects.scss b/app/assets/stylesheets/sections/projects.scss index 1269ac8a..0866b43f 100644 --- a/app/assets/stylesheets/sections/projects.scss +++ b/app/assets/stylesheets/sections/projects.scss @@ -35,6 +35,7 @@ .btn { padding:6px; margin-left:10px; + margin-bottom:8px; } } } diff --git a/app/views/admin/hooks/index.html.haml b/app/views/admin/hooks/index.html.haml index d34acffe..43288424 100644 --- a/app/views/admin/hooks/index.html.haml +++ b/app/views/admin/hooks/index.html.haml @@ -5,7 +5,7 @@ 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| += form_for @hook, as: :hook, url: admin_hooks_path, html: { class: 'form-inline' } do |f| -if @hook.errors.any? .alert-message.block-message.error - @hook.errors.full_messages.each do |msg| diff --git a/app/views/admin/projects/index.html.haml b/app/views/admin/projects/index.html.haml index 4512bb7e..882b2ab5 100644 --- a/app/views/admin/projects/index.html.haml +++ b/app/views/admin/projects/index.html.haml @@ -2,7 +2,7 @@ Projects = link_to 'New Project', new_admin_project_path, class: "btn small right" %br -= form_tag admin_projects_path, method: :get do += form_tag admin_projects_path, method: :get, class: 'form-inline' do = text_field_tag :name, params[:name], class: "xlarge" = submit_tag "Search", class: "btn submit primary" diff --git a/app/views/admin/users/index.html.haml b/app/views/admin/users/index.html.haml index 5edca312..3d027217 100644 --- a/app/views/admin/users/index.html.haml +++ b/app/views/admin/users/index.html.haml @@ -3,7 +3,7 @@ = link_to 'New User', new_admin_user_path, class: "btn small right" %br -= form_tag admin_users_path, method: :get do += form_tag admin_users_path, method: :get, class: 'form-inline' do = text_field_tag :name, params[:name], class: "xlarge" = submit_tag "Search", class: "btn submit primary" %ul.nav.nav-pills diff --git a/app/views/hooks/index.html.haml b/app/views/hooks/index.html.haml index 4e15dc50..3d2a381e 100644 --- a/app/views/hooks/index.html.haml +++ b/app/views/hooks/index.html.haml @@ -8,7 +8,7 @@ Read more about web hooks %strong #{link_to "here", help_web_hooks_path, class: "vlink"} -= form_for [@project, @hook], as: :hook, url: project_hooks_path(@project) do |f| += form_for [@project, @hook], as: :hook, url: project_hooks_path(@project), html: { class: 'form-inline' } do |f| -if @hook.errors.any? .alert-message.block-message.error - @hook.errors.full_messages.each do |msg| diff --git a/app/views/search/show.html.haml b/app/views/search/show.html.haml index ed9da1f0..9a0b4789 100644 --- a/app/views/search/show.html.haml +++ b/app/views/search/show.html.haml @@ -1,4 +1,4 @@ -= form_tag search_path, method: :get do |f| += form_tag search_path, method: :get, class: 'form-inline' do |f| .padded = label_tag :search do %strong Looking for From 2c128f3874eabf642ba78775e169177129f10a97 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Thu, 23 Aug 2012 19:20:28 -0400 Subject: [PATCH 36/46] Remove rails-footnotes gem and its related files --- .rails_footnotes | 3 --- Gemfile | 1 - Gemfile.lock | 3 --- config/initializers/rails_footnotes.rb | 3 --- 4 files changed, 10 deletions(-) delete mode 100644 .rails_footnotes delete mode 100644 config/initializers/rails_footnotes.rb diff --git a/.rails_footnotes b/.rails_footnotes deleted file mode 100644 index 1019a70a..00000000 --- a/.rails_footnotes +++ /dev/null @@ -1,3 +0,0 @@ -#this code temporarily disables notes for all controllers -# Footnotes::Filter.notes = [] - diff --git a/Gemfile b/Gemfile index f256eccb..214c06a9 100644 --- a/Gemfile +++ b/Gemfile @@ -93,7 +93,6 @@ end group :development do gem "letter_opener" - gem "rails-footnotes" gem "annotate", :git => "https://github.com/ctran/annotate_models.git" gem 'rack-mini-profiler' end diff --git a/Gemfile.lock b/Gemfile.lock index b2a00118..c2d60d79 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -259,8 +259,6 @@ GEM activesupport (= 3.2.8) bundler (~> 1.0) railties (= 3.2.8) - rails-footnotes (3.7.8) - rails (>= 3.0.0) railties (3.2.8) actionpack (= 3.2.8) activesupport (= 3.2.8) @@ -417,7 +415,6 @@ DEPENDENCIES pygments.rb! rack-mini-profiler rails (= 3.2.8) - rails-footnotes raphael-rails (= 1.5.2) redcarpet (~> 2.1.1) resque (~> 1.20.0) diff --git a/config/initializers/rails_footnotes.rb b/config/initializers/rails_footnotes.rb deleted file mode 100644 index afe6f3ad..00000000 --- a/config/initializers/rails_footnotes.rb +++ /dev/null @@ -1,3 +0,0 @@ -#if defined?(Footnotes) && Rails.env.development? - #Footnotes.run! # first of all -#end From 4adac4deba315c182820a1fe820fe0d297ae21ab Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Thu, 23 Aug 2012 19:21:20 -0400 Subject: [PATCH 37/46] Move gitlab_meta gem to :production group --- Gemfile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 214c06a9..73daebec 100644 --- a/Gemfile +++ b/Gemfile @@ -75,7 +75,6 @@ gem 'settingslogic' # Misc gem "foreman" gem "git" -gem "gitlab_meta", '2.8' group :assets do gem "sass-rails", "3.2.5" @@ -120,3 +119,7 @@ group :test do gem 'resque_spec' gem "webmock" end + +group :production do + gem "gitlab_meta", '2.8' +end From af2e2e29b9b537f6bae752b74280ca7a3819d468 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Thu, 23 Aug 2012 19:21:55 -0400 Subject: [PATCH 38/46] Remove unused minitest and turn gems --- Gemfile | 2 -- Gemfile.lock | 6 ------ 2 files changed, 8 deletions(-) diff --git a/Gemfile b/Gemfile index 73daebec..4629383e 100644 --- a/Gemfile +++ b/Gemfile @@ -111,8 +111,6 @@ end group :test do gem 'cucumber-rails', :require => false - gem 'minitest', ">= 2.10" - gem "turn", :require => false gem "simplecov", :require => false gem "shoulda-matchers" gem 'email_spec' diff --git a/Gemfile.lock b/Gemfile.lock index c2d60d79..88da1cd9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -99,7 +99,6 @@ GEM acts-as-taggable-on (2.3.1) rails (~> 3.0) addressable (2.2.8) - ansi (1.4.2) arel (3.0.2) autotest (4.4.6) ZenTest (>= 4.4.1) @@ -219,7 +218,6 @@ GEM treetop (~> 1.4.8) method_source (0.7.1) mime-types (1.19) - minitest (3.1.0) modernizr (2.5.3) sprockets (~> 2.0) multi_json (1.3.6) @@ -348,8 +346,6 @@ GEM treetop (1.4.10) polyglot polyglot (>= 0.3.1) - turn (0.9.5) - ansi tzinfo (0.3.33) uglifier (1.0.3) execjs (>= 0.3.0) @@ -407,7 +403,6 @@ DEPENDENCIES launchy letter_opener linguist (~> 1.0.0)! - minitest (>= 2.10) modernizr (= 2.5.3) mysql2 omniauth-ldap! @@ -431,7 +426,6 @@ DEPENDENCIES stamp therubyracer thin - turn uglifier (= 1.0.3) unicorn webmock From f9ab136abdedb1ea1085e4e0a881371d6ad0dffb Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Thu, 23 Aug 2012 17:01:30 -0700 Subject: [PATCH 39/46] Misspelled Draper :) --- app/decorators/application_decorator.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/decorators/application_decorator.rb b/app/decorators/application_decorator.rb index 7bc88648..3023699e 100644 --- a/app/decorators/application_decorator.rb +++ b/app/decorators/application_decorator.rb @@ -1,4 +1,4 @@ -class ApplicationDecorator < Drapper::Base +class ApplicationDecorator < Draper::Base # Lazy Helpers # PRO: Call Rails helpers without the h. proxy # ex: number_to_currency(model.price) From 4d9197efa819730de6ed4b927dc2c34270be1ec6 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Thu, 23 Aug 2012 17:11:37 -0700 Subject: [PATCH 40/46] Fix Draper spelling in Gemfile --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index f256eccb..2d448ced 100644 --- a/Gemfile +++ b/Gemfile @@ -54,7 +54,7 @@ gem "unicorn" gem "acts-as-taggable-on", "2.3.1" # Decorators -gem "drapper" +gem "draper" # Background jobs gem "resque", "~> 1.20.0" From b962bcd4a7fbe017430f4f0ea84074088fc8c8b1 Mon Sep 17 00:00:00 2001 From: randx Date: Wed, 22 Aug 2012 01:25:11 +0300 Subject: [PATCH 41/46] draper Gemfile.lock --- Gemfile.lock | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 88da1cd9..656bede4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -155,7 +155,9 @@ GEM railties (~> 3.1) warden (~> 1.2.1) diff-lcs (1.1.3) - drapper (0.8.4) + draper (0.17.0) + actionpack (~> 3.2) + activesupport (~> 3.2) email_spec (1.2.1) mail (~> 2.2) rspec (~> 2.0) @@ -384,7 +386,7 @@ DEPENDENCIES cucumber-rails database_cleaner devise (~> 2.1.0) - drapper + draper email_spec ffaker foreman From 4381084af341684240b1a671d368511afcf5774a Mon Sep 17 00:00:00 2001 From: randx Date: Fri, 24 Aug 2012 11:48:30 +0300 Subject: [PATCH 42/46] Trying to get rid of random dashboard/issues cucumber fail --- features/step_definitions/dashboard_steps.rb | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/features/step_definitions/dashboard_steps.rb b/features/step_definitions/dashboard_steps.rb index 90c3a69c..1eec7619 100644 --- a/features/step_definitions/dashboard_steps.rb +++ b/features/step_definitions/dashboard_steps.rb @@ -91,26 +91,18 @@ Then /^I should see my merge requests$/ do end Given /^I have assigned issues$/ do - project1 = Factory :project, - :path => "project1", - :code => "gitlabhq_1" - - project2 = Factory :project, - :path => "project2", - :code => "gitlabhq_2" - - project1.add_access(@user, :read, :write) - project2.add_access(@user, :read, :write) + project = Factory :project + project.add_access(@user, :read, :write) issue1 = Factory :issue, :author => @user, :assignee => @user, - :project => project1 + :project => project issue2 = Factory :issue, :author => @user, :assignee => @user, - :project => project2 + :project => project end Given /^I have authored merge requests$/ do From a502f67c0b358cc6b391df0c5dca48375c21fcad Mon Sep 17 00:00:00 2001 From: randx Date: Fri, 24 Aug 2012 12:03:48 +0300 Subject: [PATCH 43/46] Up to 2.8.2 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index dbe59006..1817afea 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.8.1 +2.8.2 From 14bd9c9228c50d2a9c5914394603d7ff2b2aa145 Mon Sep 17 00:00:00 2001 From: randx Date: Fri, 24 Aug 2012 12:17:05 +0300 Subject: [PATCH 44/46] Fix dashboard issues atom feed rspec --- spec/requests/atom/dashboard_issues_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/requests/atom/dashboard_issues_spec.rb b/spec/requests/atom/dashboard_issues_spec.rb index 9b4ffc0e..1d208c70 100644 --- a/spec/requests/atom/dashboard_issues_spec.rb +++ b/spec/requests/atom/dashboard_issues_spec.rb @@ -7,11 +7,11 @@ describe "User Issues Dashboard" do login_as :user @project1 = Factory :project, - path: "project1", + path: "gitlabhq_0", code: "TEST1" @project2 = Factory :project, - path: "project2", + path: "gitlabhq_1", code: "TEST2" @project1.add_access(@user, :read, :write) From 3dd7703b8083d44c64a03cef6b72d161aed04239 Mon Sep 17 00:00:00 2001 From: randx Date: Fri, 24 Aug 2012 13:05:40 +0300 Subject: [PATCH 45/46] Feature: Labels page. Index page --- app/controllers/labels_controller.rb | 25 +++++++++++++++++++ app/views/issues/_head.html.haml | 3 +++ app/views/labels/_label.html.haml | 4 +++ app/views/labels/index.html.haml | 14 +++++++++++ config/routes.rb | 2 ++ features/projects/issues/labels.feature | 13 ++++++++++ .../project/project_issues_steps.rb | 21 +++++++++++++++- 7 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 app/controllers/labels_controller.rb create mode 100644 app/views/labels/_label.html.haml create mode 100644 app/views/labels/index.html.haml create mode 100644 features/projects/issues/labels.feature diff --git a/app/controllers/labels_controller.rb b/app/controllers/labels_controller.rb new file mode 100644 index 00000000..f52fc2d8 --- /dev/null +++ b/app/controllers/labels_controller.rb @@ -0,0 +1,25 @@ +class LabelsController < ApplicationController + before_filter :authenticate_user! + before_filter :project + before_filter :module_enabled + + layout "project" + + # Authorize + before_filter :add_project_abilities + + # Allow read any issue + before_filter :authorize_read_issue! + + respond_to :js, :html + + def index + @labels = Issue.tag_counts_on(:labels) + end + + protected + + def module_enabled + return render_404 unless @project.issues_enabled + end +end diff --git a/app/views/issues/_head.html.haml b/app/views/issues/_head.html.haml index 1f6e7d7f..8ebe3e05 100644 --- a/app/views/issues/_head.html.haml +++ b/app/views/issues/_head.html.haml @@ -5,6 +5,9 @@ %li{class: "#{'active' if current_page?(project_milestones_path(@project))}"} = link_to project_milestones_path(@project), class: "tab" do Milestones + %li{class: "#{'active' if current_page?(project_labels_path(@project))}"} + = link_to project_labels_path(@project), class: "tab" do + Labels %li.right %span.rss-icon = link_to project_issues_path(@project, :atom, { private_token: current_user.private_token }) do diff --git a/app/views/labels/_label.html.haml b/app/views/labels/_label.html.haml new file mode 100644 index 00000000..32158c20 --- /dev/null +++ b/app/views/labels/_label.html.haml @@ -0,0 +1,4 @@ +%li.wll + %strong= label.name + .right + %span= pluralize label.count, 'issue' diff --git a/app/views/labels/index.html.haml b/app/views/labels/index.html.haml new file mode 100644 index 00000000..4e41d375 --- /dev/null +++ b/app/views/labels/index.html.haml @@ -0,0 +1,14 @@ += render "issues/head" + +%h3.page_title + Labels +%br +%div.ui-box + %ul.unstyled.labels-table + - @labels.each do |label| + = render 'label', label: label + + - unless @labels.present? + %li + %h3.nothing_here_message Nothing to show here + diff --git a/config/routes.rb b/config/routes.rb index 97594d57..f895478f 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -197,7 +197,9 @@ Gitlab::Application.routes.draw do end resources :team_members resources :milestones + resources :labels, :only => [:index] resources :issues do + collection do post :sort post :bulk_update diff --git a/features/projects/issues/labels.feature b/features/projects/issues/labels.feature new file mode 100644 index 00000000..5a20bfd6 --- /dev/null +++ b/features/projects/issues/labels.feature @@ -0,0 +1,13 @@ +Feature: Labels + Background: + Given I signin as a user + And I own project "Shop" + And project "Shop" have issues tags: + | name | + | bug | + | feature | + Given I visit project "Shop" labels page + + Scenario: I should see active milestones + Then I should see label "bug" + And I should see label "feature" diff --git a/features/step_definitions/project/project_issues_steps.rb b/features/step_definitions/project/project_issues_steps.rb index 00a1721f..27de03d5 100644 --- a/features/step_definitions/project/project_issues_steps.rb +++ b/features/step_definitions/project/project_issues_steps.rb @@ -33,6 +33,25 @@ Given /^I visit issue page "(.*?)"$/ do |arg1| end Given /^I submit new issue "(.*?)"$/ do |arg1| - fill_in "issue_title", :with => arg1 + fill_in "issue_title", with: arg1 click_button "Submit new issue" end + +Given /^project "(.*?)" have issues tags:$/ do |arg1, table| + project = Project.find_by_name(arg1) + table.hashes.each do |hash| + Factory :issue, + project: project, + label_list: [hash[:name]] + end +end + +Given /^I visit project "(.*?)" labels page$/ do |arg1| + visit project_labels_path(Project.find_by_name(arg1)) +end + +Then /^I should see label "(.*?)"$/ do |arg1| + within ".labels-table" do + page.should have_content arg1 + end +end From 6d4ae75f544d9819954865c105414a722344336a Mon Sep 17 00:00:00 2001 From: randx Date: Fri, 24 Aug 2012 13:17:19 +0300 Subject: [PATCH 46/46] Final fix for dashboard cucumber feature --- features/step_definitions/dashboard_steps.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/features/step_definitions/dashboard_steps.rb b/features/step_definitions/dashboard_steps.rb index 1eec7619..d910ec90 100644 --- a/features/step_definitions/dashboard_steps.rb +++ b/features/step_definitions/dashboard_steps.rb @@ -107,11 +107,11 @@ end Given /^I have authored merge requests$/ do project1 = Factory :project, - :path => "project1", + :path => "gitlabhq_1", :code => "gitlabhq_1" project2 = Factory :project, - :path => "project2", + :path => "gitlabhq_2", :code => "gitlabhq_2" project1.add_access(@user, :read, :write)