diff --git a/Gemfile b/Gemfile index 3bbfec2b..ee383ce3 100644 --- a/Gemfile +++ b/Gemfile @@ -1,7 +1,11 @@ source 'https://rubygems.org' # as web framework -gem 'rails', '~> 4.1' +gem 'rails', '~> 4.2', '>= 4.2.4' + +# A set of Rails responders to dry up your application +# required for respond_with and the class-level respond_to methods in rails 4.2 +gem 'responders', '~> 2.0' # as the database for Active Record gem 'mysql2' @@ -124,6 +128,9 @@ gem 'acts_as_list' # for switch checkboxes gem 'bootstrap-switch-rails', '~> 3.0.0' +# responsible for comparing HTML doms and asserting that DOM elements are present in Rails applications +gem 'rails-dom-testing' + # Use guard and spring for testing in development group :development do # rspec Guard rules @@ -139,6 +146,8 @@ group :development do gem 'letter_opener' # mina is a blazing fast deployment system gem 'mina' + # debugging tool for your Ruby on Rails applications + gem 'web-console', '~> 2.0' end # Use rspec and capybara as testing framework diff --git a/Gemfile.lock b/Gemfile.lock index 6a948871..fe4c0061 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,434 +1,492 @@ GIT remote: git://github.com/stefanpenner/country_select.git - revision: 585e9538a01ae9878e92f12a98261a09842ed015 + revision: 2023cc8f0c07a6c98a3f1b03cccb065ca26b050d specs: - country_select (2.1.0) - countries (~> 0.9, >= 0.9.3) + country_select (2.4.0) + countries (~> 1.1.0) + sort_alphabetical (~> 1.0) GEM remote: https://rubygems.org/ specs: - actionmailer (4.1.8) - actionpack (= 4.1.8) - actionview (= 4.1.8) + actionmailer (4.2.4) + actionpack (= 4.2.4) + actionview (= 4.2.4) + activejob (= 4.2.4) mail (~> 2.5, >= 2.5.4) - actionpack (4.1.8) - actionview (= 4.1.8) - activesupport (= 4.1.8) - rack (~> 1.5.2) + rails-dom-testing (~> 1.0, >= 1.0.5) + actionpack (4.2.4) + actionview (= 4.2.4) + activesupport (= 4.2.4) + rack (~> 1.6) rack-test (~> 0.6.2) - actionview (4.1.8) - activesupport (= 4.1.8) + rails-dom-testing (~> 1.0, >= 1.0.5) + rails-html-sanitizer (~> 1.0, >= 1.0.2) + actionview (4.2.4) + activesupport (= 4.2.4) builder (~> 3.1) erubis (~> 2.7.0) - active_model_serializers (0.8.1) - activemodel (>= 3.0) - activemodel (4.1.8) - activesupport (= 4.1.8) + rails-dom-testing (~> 1.0, >= 1.0.5) + rails-html-sanitizer (~> 1.0, >= 1.0.2) + active_model_serializers (0.9.3) + activemodel (>= 3.2) + activejob (4.2.4) + activesupport (= 4.2.4) + globalid (>= 0.3.0) + activemodel (4.2.4) + activesupport (= 4.2.4) builder (~> 3.1) - activerecord (4.1.8) - activemodel (= 4.1.8) - activesupport (= 4.1.8) - arel (~> 5.0.0) - activesupport (4.1.8) - i18n (~> 0.6, >= 0.6.9) + activerecord (4.2.4) + activemodel (= 4.2.4) + activesupport (= 4.2.4) + arel (~> 6.0) + activesupport (4.2.4) + i18n (~> 0.7) json (~> 1.7, >= 1.7.7) minitest (~> 5.1) - thread_safe (~> 0.1) + thread_safe (~> 0.3, >= 0.3.4) tzinfo (~> 1.1) - activeuuid (0.5.0) + activeuuid (0.6.1) activerecord (>= 3.1) uuidtools - acts_as_commentable_with_threading (1.2.0) + acts_as_commentable_with_threading (2.0.0) + activerecord (>= 4.0) + activesupport (>= 4.0) + awesome_nested_set (>= 3.0) + acts_as_list (0.7.2) activerecord (>= 3.0) - activesupport (>= 3.0) - awesome_nested_set (>= 2.0) - acts_as_list (0.4.0) - activerecord (>= 3.0) - addressable (2.3.6) - ahoy_matey (1.0.0) + addressable (2.3.8) + ahoy_matey (1.2.1) addressable browser (>= 0.4.0) + errbase geocoder - referer-parser + rails + referer-parser (>= 0.3.0) request_store user_agent_parser uuidtools - arel (5.0.1.20140414130214) - ast (2.0.0) - autoprefixer-rails (5.1.9) + arel (6.0.3) + ast (2.1.0) + astrolabe (1.3.1) + parser (~> 2.2) + autoprefixer-rails (6.0.3) execjs json - awesome_nested_set (3.0.0.rc.5) + awesome_nested_set (3.0.2) activerecord (>= 4.0.0, < 5) axlsx (2.0.1) htmlentities (~> 4.3.1) nokogiri (>= 1.4.1) rubyzip (~> 1.0.0) - axlsx_rails (0.2.0) + axlsx_rails (0.4.0) axlsx (>= 2.0.1) rails (>= 3.1) - bcrypt (3.1.7) + bcrypt (3.1.10) + binding_of_caller (0.7.2) + debug_inspector (>= 0.0.1) bootstrap-sass (3.3.4.1) autoprefixer-rails (>= 5.0.0.1) sass (>= 3.2.19) bootstrap-switch-rails (3.0.2) bootstrap3-datetimepicker-rails (3.0.3) momentjs-rails (>= 2.8.1) - browser (0.6.0) + browser (1.0.1) builder (3.2.2) - byebug (3.1.2) - columnize (~> 0.8) - debugger-linecache (~> 1.2) - cancancan (1.8.4) - capybara (2.2.1) + byebug (6.0.2) + cancancan (1.13.1) + capybara (2.5.0) mime-types (>= 1.16) nokogiri (>= 1.3.3) rack (>= 1.0.0) rack-test (>= 0.5.4) xpath (~> 2.0) - celluloid (0.15.2) - timers (~> 1.1.0) - celluloid-io (0.15.0) - celluloid (>= 0.15.0) - nio4r (>= 0.5.0) - chart-js-rails (0.0.6) + chart-js-rails (0.0.9) railties (> 3.1) chronic (0.10.2) climate_control (0.0.3) activesupport (>= 3.0) cliver (0.3.2) - cocaine (0.5.4) + cocaine (0.5.7) climate_control (>= 0.0.3, < 1.0) cocoon (1.2.6) coderay (1.1.0) - coffee-rails (4.0.1) + coffee-rails (4.1.0) coffee-script (>= 2.2.0) railties (>= 4.0.0, < 5.0) - coffee-script (2.2.0) + coffee-script (2.4.1) coffee-script-source execjs - coffee-script-source (1.7.0) - columnize (0.8.9) - countries (0.9.3) + coffee-script-source (1.9.1.1) + countries (1.1.0) currencies (~> 0.4.2) - coveralls (0.7.0) - multi_json (~> 1.3) - rest-client - simplecov (>= 0.7) - term-ansicolor - thor + i18n_data (~> 0.7.0) + coveralls (0.8.3) + json (~> 1.8) + rest-client (>= 1.6.8, < 2) + simplecov (~> 0.10.0) + term-ansicolor (~> 1.3) + thor (~> 0.19.1) currencies (0.4.2) - d3_rails (3.4.6) + d3_rails (3.5.6) railties (>= 3.1.0) - daemons (1.1.9) - database_cleaner (1.3.0) - debugger-linecache (1.2.0) - delayed_job (4.0.2) - activesupport (>= 3.0, < 4.2) - delayed_job_active_record (4.0.1) - activerecord (>= 3.0, < 4.2) - delayed_job (>= 3.0, < 4.1) - devise (3.2.4) + daemons (1.2.3) + database_cleaner (1.5.1) + debug_inspector (0.0.2) + delayed_job (4.1.1) + activesupport (>= 3.0, < 5.0) + delayed_job_active_record (4.1.0) + activerecord (>= 3.0, < 5) + delayed_job (>= 3.0, < 5) + devise (3.5.2) bcrypt (~> 3.0) orm_adapter (~> 0.1) railties (>= 3.2.6, < 5) + responders thread_safe (~> 0.1) warden (~> 1.2.3) devise_ichain_authenticatable (0.3.1) devise (>= 2.2) diff-lcs (1.2.5) - docile (1.1.3) + docile (1.1.5) + domain_name (0.5.25) + unf (>= 0.0.5, < 1.0.0) + errbase (0.0.3) erubis (2.7.0) - execjs (2.5.2) - factory_girl (4.4.0) + execjs (2.6.0) + factory_girl (4.5.0) activesupport (>= 3.0.0) - factory_girl_rails (4.4.1) - factory_girl (~> 4.4.0) + factory_girl_rails (4.5.0) + factory_girl (~> 4.5.0) railties (>= 3.0.0) - faraday (0.9.0) + faraday (0.9.2) multipart-post (>= 1.2, < 3) - ffi (1.9.3) - font-awesome-rails (4.1.0.0) + ffi (1.9.10) + font-awesome-rails (4.4.0.0) railties (>= 3.2, < 5.0) - formatador (0.2.4) - formtastic (2.3.0.rc3) + formatador (0.2.5) + formtastic (2.3.1) actionpack (>= 3.0) formtastic-bootstrap (3.0.0) formtastic (>= 2.2) - geocoder (1.2.2) + geocoder (1.2.11) + globalid (0.3.6) + activesupport (>= 4.1.0) gravtastic (3.2.6) - guard (2.6.0) + guard (2.13.0) formatador (>= 0.2.4) - listen (~> 2.7) + listen (>= 2.7, <= 4.0) lumberjack (~> 1.0) + nenv (~> 0.1) + notiffany (~> 0.0) pry (>= 0.9.12) + shellany (~> 0.0) thor (>= 0.18.1) - guard-rspec (4.2.8) + guard-rspec (4.2.10) guard (~> 2.1) rspec (>= 2.14, < 4.0) - haml (4.0.5) + haml (4.0.7) tilt - haml-rails (0.5.3) + haml-rails (0.9.0) actionpack (>= 4.0.1) activesupport (>= 4.0.1) - haml (>= 3.1, < 5.0) + haml (>= 4.0.6, < 5.0) + html2haml (>= 1.0.1) railties (>= 4.0.1) - hashie (2.1.1) - hike (1.2.3) + hashie (3.4.2) hoptoad_notifier (2.4.11) activesupport builder - htmlentities (4.3.1) - i18n (0.6.11) - inversion (0.12.3) - loggability (~> 0.4) + html2haml (2.0.0) + erubis (~> 2.7.0) + haml (~> 4.0.0) + nokogiri (~> 1.6.0) + ruby_parser (~> 3.5) + htmlentities (4.3.4) + http-cookie (1.0.2) + domain_name (~> 0.5) + i18n (0.7.0) + i18n_data (0.7.0) + inversion (0.18.0) + loggability (~> 0.11) jquery-datatables-rails (2.2.3) jquery-rails sass-rails - jquery-rails (3.1.2) - railties (>= 3.0, < 5.0) + jquery-rails (4.0.5) + rails-dom-testing (~> 1.0) + railties (>= 4.2.0) thor (>= 0.14, < 2.0) - json (1.8.1) - jwt (1.0.0) - launchy (2.4.2) + json (1.8.3) + jwt (1.5.1) + launchy (2.4.3) addressable (~> 2.3) leaflet-rails (0.7.4) - letter_opener (1.2.0) + letter_opener (1.4.1) launchy (~> 2.2) - listen (2.7.2) - celluloid (>= 0.15.2) - celluloid-io (>= 0.15.0) + listen (3.0.3) rb-fsevent (>= 0.9.3) rb-inotify (>= 0.9) loggability (0.11.0) - lumberjack (1.0.5) + loofah (2.0.3) + nokogiri (>= 1.5.9) + lumberjack (1.0.9) mail (2.6.3) mime-types (>= 1.16, < 3) method_source (0.8.2) - mime-types (2.4.3) - mina (0.3.0) - open4 + mime-types (2.6.2) + mimemagic (0.3.0) + mina (0.3.7) + open4 (~> 1.3.4) rake - mini_portile (0.6.0) - minitest (5.4.3) - momentjs-rails (2.8.1) + mini_portile (0.6.2) + minitest (5.8.1) + momentjs-rails (2.10.6) railties (>= 3.1) - monetize (0.3.0) - money (~> 6.1.0.beta1) - money (6.1.1) - i18n (~> 0.6.4) - money-rails (0.12.0) + monetize (1.1.0) + money (~> 6.5.0) + money (6.5.1) + i18n (>= 0.6.4, <= 0.7.0) + money-rails (1.4.1) activesupport (>= 3.0) - monetize (~> 0.3.0) - money (~> 6.1.1) + monetize (~> 1.1.0) + money (~> 6.5.0) railties (>= 3.0) - multi_json (1.10.1) + multi_json (1.11.2) multi_xml (0.5.5) multipart-post (2.0.0) - mysql2 (0.3.16) + mysql2 (0.4.1) + nenv (0.2.0) netrc (0.10.3) - nio4r (1.1.0) - nokogiri (1.6.2.1) - mini_portile (= 0.6.0) - oauth2 (0.9.4) + nokogiri (1.6.6.2) + mini_portile (~> 0.6.0) + notiffany (0.0.8) + nenv (~> 0.1) + shellany (~> 0.0) + oauth2 (1.0.0) faraday (>= 0.8, < 0.10) jwt (~> 1.0) multi_json (~> 1.3) multi_xml (~> 0.5) rack (~> 1.2) - omniauth (1.2.1) - hashie (>= 1.2, < 3) + omniauth (1.2.2) + hashie (>= 1.2, < 4) rack (~> 1.0) - omniauth-facebook (1.6.0) - omniauth-oauth2 (~> 1.1) + omniauth-facebook (2.0.1) + omniauth-oauth2 (~> 1.2) omniauth-github (1.1.2) omniauth (~> 1.0) omniauth-oauth2 (~> 1.1) - omniauth-google-oauth2 (0.2.4) - omniauth (~> 1.0) - omniauth-oauth2 (~> 1.1) - omniauth-oauth2 (1.1.2) - faraday (>= 0.8, < 0.10) + omniauth-google-oauth2 (0.2.8) + addressable (~> 2.3) + jwt (~> 1.0) multi_json (~> 1.3) - oauth2 (~> 0.9.3) + omniauth (>= 1.1.1) + omniauth-oauth2 (>= 1.1.1) + omniauth-oauth2 (1.4.0) + oauth2 (~> 1.0) omniauth (~> 1.2) omniauth-openid (1.0.1) omniauth (~> 1.0) rack-openid (~> 1.3.1) - open4 (1.3.3) + open4 (1.3.4) orm_adapter (0.5.0) - paper_trail (3.0.1) - activerecord (>= 3.0, < 5.0) - activesupport (>= 3.0, < 5.0) - paperclip (4.1.1) - activemodel (>= 3.0.0) - activesupport (>= 3.0.0) - cocaine (~> 0.5.3) + paper_trail (4.0.0) + activerecord (>= 3.0, < 6.0) + activesupport (>= 3.0, < 6.0) + request_store (~> 1.1) + paperclip (4.3.1) + activemodel (>= 3.2.0) + activesupport (>= 3.2.0) + cocaine (~> 0.5.5) mime-types - parser (2.2.0.pre.3) + mimemagic (= 0.3.0) + parser (2.2.3.0) ast (>= 1.1, < 3.0) - slop (~> 3.4, >= 3.4.5) - pdf-core (0.2.5) - phantomjs (1.9.7.1) + pdf-core (0.6.0) + phantomjs (1.9.8.0) piwik_analytics (1.0.2) actionpack activesupport rails (>= 3.0.0) - poltergeist (1.5.1) + poltergeist (1.7.0) capybara (~> 2.1) cliver (~> 0.3.1) multi_json (~> 1.0) websocket-driver (>= 0.2.0) - powerpack (0.0.9) - prawn (1.0.0) - pdf-core (~> 0.2.2) - ttfunk (~> 1.1.1) + powerpack (0.1.1) + prawn (2.0.2) + pdf-core (~> 0.6.0) + ttfunk (~> 1.4.0) prawn_rails (0.0.11) prawn (>= 0.11.1) railties (>= 3.0.0) - protected_attributes (1.0.7) + protected_attributes (1.1.3) activemodel (>= 4.0.1, < 5.0) - pry (0.9.12.6) - coderay (~> 1.0) - method_source (~> 0.8) + pry (0.10.3) + coderay (~> 1.1.0) + method_source (~> 0.8.1) slop (~> 3.4) - quiet_assets (1.0.2) + quiet_assets (1.1.0) railties (>= 3.1, < 5.0) - rack (1.5.2) + rack (1.6.4) rack-openid (1.3.1) rack (>= 1.1.0) ruby-openid (>= 2.1.8) - rack-test (0.6.2) + rack-test (0.6.3) rack (>= 1.0) - rails (4.1.8) - actionmailer (= 4.1.8) - actionpack (= 4.1.8) - actionview (= 4.1.8) - activemodel (= 4.1.8) - activerecord (= 4.1.8) - activesupport (= 4.1.8) + rails (4.2.4) + actionmailer (= 4.2.4) + actionpack (= 4.2.4) + actionview (= 4.2.4) + activejob (= 4.2.4) + activemodel (= 4.2.4) + activerecord (= 4.2.4) + activesupport (= 4.2.4) bundler (>= 1.3.0, < 2.0) - railties (= 4.1.8) - sprockets-rails (~> 2.0) + railties (= 4.2.4) + sprockets-rails + rails-deprecated_sanitizer (1.0.3) + activesupport (>= 4.2.0.alpha) + rails-dom-testing (1.0.7) + activesupport (>= 4.2.0.beta, < 5.0) + nokogiri (~> 1.6.0) + rails-deprecated_sanitizer (>= 1.0.1) + rails-html-sanitizer (1.0.2) + loofah (~> 2.0) rails-observers (0.1.2) activemodel (~> 4.0) - railties (4.1.8) - actionpack (= 4.1.8) - activesupport (= 4.1.8) + railties (4.2.4) + actionpack (= 4.2.4) + activesupport (= 4.2.4) rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) rainbow (2.0.0) - rake (10.3.2) - rb-fsevent (0.9.4) - rb-inotify (0.9.4) + rake (10.4.2) + rb-fsevent (0.9.6) + rb-inotify (0.9.5) ffi (>= 0.5.0) - rdoc (4.1.1) - json (~> 1.4) + rdoc (4.2.0) rdoc-generator-fivefish (0.1.0) inversion (~> 0.12) loggability (~> 0.6) rdoc (~> 4.0) yajl-ruby (~> 1.1) - redcarpet (3.2.3) - referer-parser (0.2.1) - request_store (1.0.6) - rest-client (1.7.3) + redcarpet (3.3.3) + referer-parser (0.3.0) + request_store (1.2.0) + responders (2.1.0) + railties (>= 4.2.0, < 5) + rest-client (1.8.0) + http-cookie (>= 1.0.2, < 2.0) mime-types (>= 1.16, < 3.0) netrc (~> 0.7) - rolify (3.4.0) - rspec (3.0.0) - rspec-core (~> 3.0.0) - rspec-expectations (~> 3.0.0) - rspec-mocks (~> 3.0.0) - rspec-activemodel-mocks (1.0.1) + rolify (4.1.1) + rspec (3.3.0) + rspec-core (~> 3.3.0) + rspec-expectations (~> 3.3.0) + rspec-mocks (~> 3.3.0) + rspec-activemodel-mocks (1.0.2) activemodel (>= 3.0) activesupport (>= 3.0) rspec-mocks (>= 2.99, < 4.0) - rspec-core (3.0.2) - rspec-support (~> 3.0.0) - rspec-expectations (3.0.2) + rspec-core (3.3.2) + rspec-support (~> 3.3.0) + rspec-expectations (3.3.1) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.0.0) - rspec-mocks (3.0.2) - rspec-support (~> 3.0.0) - rspec-rails (3.0.0) - actionpack (>= 3.0) - activesupport (>= 3.0) - railties (>= 3.0) - rspec-core (~> 3.0.0) - rspec-expectations (~> 3.0.0) - rspec-mocks (~> 3.0.0) - rspec-support (~> 3.0.0) - rspec-support (3.0.2) - rubocop (0.24.1) - json (>= 1.7.7, < 2) - parser (>= 2.2.0.pre.3, < 3.0) - powerpack (~> 0.0.6) + rspec-support (~> 3.3.0) + rspec-mocks (3.3.2) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.3.0) + rspec-rails (3.3.3) + actionpack (>= 3.0, < 4.3) + activesupport (>= 3.0, < 4.3) + railties (>= 3.0, < 4.3) + rspec-core (~> 3.3.0) + rspec-expectations (~> 3.3.0) + rspec-mocks (~> 3.3.0) + rspec-support (~> 3.3.0) + rspec-support (3.3.0) + rubocop (0.34.2) + astrolabe (~> 1.3) + parser (>= 2.2.2.5, < 3.0) + powerpack (~> 0.1) rainbow (>= 1.99.1, < 3.0) ruby-progressbar (~> 1.4) - ruby-openid (2.5.0) - ruby-progressbar (1.5.1) + ruby-openid (2.7.0) + ruby-progressbar (1.7.5) + ruby_parser (3.7.1) + sexp_processor (~> 4.1) rubyzip (1.0.0) - sass (3.2.19) - sass-rails (4.0.4) + sass (3.4.19) + sass-rails (5.0.4) railties (>= 4.0.0, < 5.0) - sass (~> 3.2.2) - sprockets (~> 2.8, < 2.12) - sprockets-rails (~> 2.0) + sass (~> 3.1) + sprockets (>= 2.8, < 4.0) + sprockets-rails (>= 2.0, < 4.0) + tilt (>= 1.1, < 3) + sexp_processor (4.6.0) + shellany (0.0.1) shoulda (3.5.0) shoulda-context (~> 1.0, >= 1.0.1) shoulda-matchers (>= 1.4.1, < 3.0) shoulda-context (1.2.1) - shoulda-matchers (2.6.1) + shoulda-matchers (2.8.0) activesupport (>= 3.0.0) - simplecov (0.8.2) + simplecov (0.10.0) docile (~> 1.1.0) - multi_json - simplecov-html (~> 0.8.0) - simplecov-html (0.8.0) + json (~> 1.8) + simplecov-html (~> 0.10.0) + simplecov-html (0.10.0) slop (3.6.0) - spring (1.1.2) - spring-commands-rspec (1.0.2) + sort_alphabetical (1.0.2) + unicode_utils (>= 1.2.2) + spring (1.4.0) + spring-commands-rspec (1.0.4) spring (>= 0.9.1) - sprockets (2.11.3) - hike (~> 1.2) - multi_json (~> 1.0) - rack (~> 1.0) - tilt (~> 1.1, != 1.3.0) - sprockets-rails (2.2.0) + sprockets (3.4.0) + rack (> 1, < 3) + sprockets-rails (2.3.3) actionpack (>= 3.0) activesupport (>= 3.0) sprockets (>= 2.8, < 4.0) - sqlite3 (1.3.9) - term-ansicolor (1.3.0) + sqlite3 (1.3.11) + term-ansicolor (1.3.2) tins (~> 1.0) thor (0.19.1) - thread_safe (0.3.4) - tilt (1.4.1) - timecop (0.7.1) - timers (1.1.0) - tins (1.1.0) - transitions (0.1.12) - ttfunk (1.1.1) - turbolinks (2.2.2) + thread_safe (0.3.5) + tilt (2.0.1) + timecop (0.8.0) + tins (1.6.0) + transitions (0.2.1) + ttfunk (1.4.0) + turbolinks (2.5.3) coffee-rails tzinfo (1.2.2) thread_safe (~> 0.1) - uglifier (2.5.0) + uglifier (2.7.2) execjs (>= 0.3.0) json (>= 1.8.0) - user_agent_parser (2.1.5) - uuidtools (2.1.4) + unf (0.1.4) + unf_ext + unf_ext (0.0.7.1) + unicode_utils (1.4.0) + user_agent_parser (2.3.0) + uuidtools (2.1.5) warden (1.2.3) rack (>= 1.0) - websocket-driver (0.3.3) - whenever (0.9.2) - activesupport (>= 2.3.4) + web-console (2.2.1) + activemodel (>= 4.0) + binding_of_caller (>= 0.7.2) + railties (>= 4.0) + sprockets-rails (>= 2.0, < 4.0) + websocket-driver (0.6.2) + websocket-extensions (>= 0.1.0) + websocket-extensions (0.1.2) + whenever (0.9.4) chronic (>= 0.6.3) xpath (2.0.0) nokogiri (~> 1.3) - yajl-ruby (1.2.0) + yajl-ruby (1.2.1) PLATFORMS ruby @@ -487,10 +545,12 @@ DEPENDENCIES prawn_rails protected_attributes quiet_assets - rails (~> 4.1) + rails (~> 4.2, >= 4.2.4) + rails-dom-testing rails-observers rdoc-generator-fivefish redcarpet + responders (~> 2.0) rolify rspec-activemodel-mocks rspec-rails @@ -503,4 +563,8 @@ DEPENDENCIES transitions turbolinks uglifier (>= 1.3.0) + web-console (~> 2.0) whenever + +BUNDLED WITH + 1.10.6 diff --git a/app/assets/stylesheets/osem.css.scss b/app/assets/stylesheets/osem.css.scss index 83a921a6..2f44ba19 100644 --- a/app/assets/stylesheets/osem.css.scss +++ b/app/assets/stylesheets/osem.css.scss @@ -1,13 +1,13 @@ html { - position: relative; - min-height: 100%; + position: relative; + min-height: 100%; } body { - /* Margin bottom by 2 times the footer height */ - margin-bottom: 60px; - /* Margin bottom by navbar height */ - padding-top: 60px; + /* Margin bottom by 2 times the footer height */ + margin-bottom: 60px; + /* Margin bottom by navbar height */ + padding-top: 60px; } #content { @@ -15,23 +15,23 @@ body { } #footer { - position: absolute; - bottom: 0; - width: 100%; - /* Set the fixed height of the footer here */ - height: 60px; - background-color: #f5f5f5; - .container { - padding: 15px; - } + position: absolute; + bottom: 0; + width: 100%; + /* Set the fixed height of the footer here */ + height: 60px; + background-color: #f5f5f5; + .container { + padding: 15px; + } } .nav-tabs { - margin-bottom: 15px; + margin-bottom: 15px; } .img-center { - margin: 0 auto; + margin: 0 auto; } /* centered columns styles */ @@ -55,7 +55,9 @@ fieldset { margin: 20px 0 20px 0; } -.bootstrap-switch { height: 1.7em } +.bootstrap-switch { + height: 1.7em +} .comment-reply{ padding-top: 40px; @@ -71,4 +73,14 @@ p.comment-body { #account-already { font-size: 0.6em; -} \ No newline at end of file +} + +/* comments views pane: use padding-bottom before next comment box */ +.panel.panel-default .panel-body .notifications { + padding-bottom: 20px; +} + +/* comments views pane: use padding-bottom after each comment */ +.panel.panel-default .panel-body .notifications p { + padding-bottom: 20px; +} diff --git a/app/controllers/admin/comments_controller.rb b/app/controllers/admin/comments_controller.rb new file mode 100644 index 00000000..6e82635c --- /dev/null +++ b/app/controllers/admin/comments_controller.rb @@ -0,0 +1,30 @@ +module Admin + class CommentsController < Admin::BaseController + load_and_authorize_resource :conference, find_by: :short_title + load_and_authorize_resource + + + def index + # All available comments, grouped and sorted + @comments = grouped_comments(accessible_ordered_comments) + + # Grouped, sorted, available comments, posted since current_user last login + @unread_comments = grouped_comments(accessible_ordered_comments.find_since_last_login(current_user)) + + # Grouped, sorted, available comments, posted by current_user + @posted_comments = grouped_comments(accessible_ordered_comments.find_comments_by_user(current_user)) + end + + private + + # Returning all available comments, ordered by created_at: :desc and by event.title + def accessible_ordered_comments + Comment.accessible_by(current_ability).joins('INNER JOIN events ON commentable_id = events.id').order('events.title', 'comments.created_at DESC') + end + +# Grouping all comments by conference, and by event. It returns {:conference => {:event => [{comment_2}, {comment_1 }]}} + def grouped_comments(remarks) + remarks.group_by{ |comment| comment.commentable.conference }.map {|conference, comments| [conference, comments.group_by{|comment| comment.commentable}]}.to_h + end + end +end diff --git a/app/controllers/admin/conference_controller.rb b/app/controllers/admin/conference_controller.rb index 89ad9970..25f60e12 100644 --- a/app/controllers/admin/conference_controller.rb +++ b/app/controllers/admin/conference_controller.rb @@ -27,7 +27,6 @@ module Admin @recent_users = User.limit(5).order(created_at: :desc) @recent_events = Event.limit(5).order(created_at: :desc) @recent_registrations = Registration.limit(5).order(created_at: :desc) - @top_submitter = Conference.get_top_submitter @submissions = {} diff --git a/app/controllers/admin/events_controller.rb b/app/controllers/admin/events_controller.rb index 019d0116..2334531d 100644 --- a/app/controllers/admin/events_controller.rb +++ b/app/controllers/admin/events_controller.rb @@ -179,12 +179,12 @@ module Admin def update_state(transition, notice, mail = false, subject = false, send_mail = false) alert = @event.update_state(transition, mail, subject, send_mail, params[:send_mail].blank?) - if !alert.blank? - flash[:error] = error - return redirect_back_or_to(admin_conference_events_path(conference_id: @conference.short_title)) && return - else + if alert.blank? flash[:notice] = notice redirect_back_or_to(admin_conference_events_path(conference_id: @conference.short_title)) && return + else + flash[:error] = alert + return redirect_back_or_to(admin_conference_events_path(conference_id: @conference.short_title)) && return end end end diff --git a/app/controllers/admin/registrations_controller.rb b/app/controllers/admin/registrations_controller.rb index 9bafb828..70abe4a1 100644 --- a/app/controllers/admin/registrations_controller.rb +++ b/app/controllers/admin/registrations_controller.rb @@ -17,9 +17,9 @@ module Admin @registration.update_attributes(registration_params) if @registration.save redirect_to admin_conference_registrations_path(@conference.short_title), - notice: 'Successfully updated registration!' + notice: "Successfully updated registration for #{@registration.user.email}!" else - flash[:error] = "An error prohibited the Registration for #{@conference.title}: "\ + flash[:error] = "An error prohibited the Registration for #{@registration.user.email}: "\ "#{@registration.errors.full_messages.join('. ')}." render :edit end @@ -56,7 +56,7 @@ module Admin permit( :conference_id, :arrival, :departure, :volunteer, - vchoice_ids: [], qanswer_ids: [], + vchoice_ids: [], qanswer_ids: [], event_ids: [], qanswers_attributes: [], user_attributes: [ :id, :name, :tshirt, :mobile, :volunteer_experience, :languages, diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 7e9f7652..4c72b3dc 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -278,4 +278,8 @@ module ApplicationHelper new_user_registration_path end end + + def unread_notifications(user) + Comment.accessible_by(current_ability).find_since_last_login(user) + end end diff --git a/app/mailers/mailbot.rb b/app/mailers/mailbot.rb index 9cf89b88..e8118382 100644 --- a/app/mailers/mailbot.rb +++ b/app/mailers/mailbot.rb @@ -80,6 +80,22 @@ class Mailbot < ActionMailer::Base end end + def send_notification_email_for_comment(comment) + @comment = comment + @event = @comment.commentable + @conference = @event.conference + recipients = User.comment_notifiable(@conference) # with scope + recipients.each do |user| + @user = user + mail(to: @user.email, + from: @conference.contact.email, + reply_to: @conference.contact.email, + template_path: 'admin/emails', + template_name: 'comment_template', + subject: "New comment has been posted for #{@event.title}") + end + end + def build_email(conference, to, subject, body) mail(to: to, from: conference.contact.email, diff --git a/app/models/ability.rb b/app/models/ability.rb index 0f89057e..bd30a8fd 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -88,12 +88,13 @@ class Ability # Abilities from not_signed_in and signed_in are also inherited signed_in(user) - signed_in_with_organizer_role(user) - signed_in_with_cfp_role(user) - signed_in_with_info_desk_role(user) - signed_in_with_volunteers_coordinator_role(user) + signed_in_with_organizer_role(user) if user.has_role? :organizer, :any + signed_in_with_cfp_role(user) if user.has_role? :cfp, :any + signed_in_with_info_desk_role(user) if user.has_role? :info_desk, :any + signed_in_with_volunteers_coordinator_role(user) if user.has_role? :volunteer_coordinator, :any # for users with any role + can :access, Admin can [:show], Conference can :index, Commercial, commercialable_type: 'Conference' cannot [:edit, :update, :destroy], Question, global: true @@ -137,6 +138,8 @@ class Ability can :manage, Sponsor, conference_id: conf_ids_for_organizer can :manage, SponsorshipLevel, conference_id: conf_ids_for_organizer can :manage, Ticket, conference_id: conf_ids_for_organizer + can :index, Comment, commentable_type: 'Event', + commentable_id: Event.where(conference_id: conf_ids_for_organizer).pluck(:id) end def signed_in_with_cfp_role(user) @@ -155,6 +158,8 @@ class Ability can :manage, CallForPaper, conference_id: conf_ids_for_cfp can :manage, Commercial, commercialable_type: 'Event', commercialable_id: Event.where(conference_id: conf_ids_for_cfp).pluck(:id) + can :index, Comment, commentable_type: 'Event', + commentable_id: Event.where(conference_id: conf_ids_for_cfp).pluck(:id) end def signed_in_with_info_desk_role(user) diff --git a/app/models/comment.rb b/app/models/comment.rb index 6196856e..f668afb3 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -3,6 +3,7 @@ class Comment < ActiveRecord::Base attr_accessible :commentable, :body, :user_id validates_presence_of :body validates_presence_of :user + after_create :send_notification # NOTE: install the acts_as_votable plugin if you # want user to vote on the quality of comments. @@ -40,9 +41,18 @@ class Comment < ActiveRecord::Base where(commentable_type: commentable_str.to_s, commentable_id: commentable_id).order('created_at DESC') } + scope :find_since_last_login, lambda { |user| + where(created_at: (user.last_sign_in_at..Time.now)).order(created_at: :desc) + } # Helper class method to look up a commentable object # given the commentable class name and id def self.find_commentable(commentable_str, commentable_id) commentable_str.constantize.find(commentable_id) end + + private + + def send_notification + Mailbot.delay.send_notification_email_for_comment(self) + end end diff --git a/app/models/conference.rb b/app/models/conference.rb index 94e23149..3c6f20b2 100644 --- a/app/models/conference.rb +++ b/app/models/conference.rb @@ -114,17 +114,17 @@ class Conference < ActiveRecord::Base before_create :add_color def date_range_string - startstr = "Unknown - " - endstr = "Unknown" + startstr = 'Unknown - ' + endstr = 'Unknown' if start_date.month == end_date.month && start_date.year == end_date.year - startstr = start_date.strftime("%B %d - ") - endstr = end_date.strftime("%d, %Y") + startstr = start_date.strftime('%B %d - ') + endstr = end_date.strftime('%d, %Y') elsif start_date.month != end_date.month && start_date.year == end_date.year - startstr = start_date.strftime("%B %d - ") - endstr = end_date.strftime("%B %d, %Y") + startstr = start_date.strftime('%B %d - ') + endstr = end_date.strftime('%B %d, %Y') else - startstr = start_date.strftime("%B %d, %Y - ") - endstr = end_date.strftime("%B %d, %Y") + startstr = start_date.strftime('%B %d, %Y - ') + endstr = end_date.strftime('%B %d, %Y') end result = startstr + endstr diff --git a/app/models/email_settings.rb b/app/models/email_settings.rb index 11a7b628..5642b4da 100644 --- a/app/models/email_settings.rb +++ b/app/models/email_settings.rb @@ -49,7 +49,7 @@ class EmailSettings < ActiveRecord::Base h['registration_end_date'] = conference.registration_period.end_date end - if !event.nil? + if event h['eventtitle'] = event.title h['proposalslink'] = Rails.application.routes.url_helpers.conference_proposal_index_url( conference.short_title, host: CONFIG['url_for_emails']) @@ -67,6 +67,8 @@ class EmailSettings < ActiveRecord::Base parse_template(conf_update_template, values) end + private + def parse_template(text, values) values.each do |key, value| if value.kind_of?(Date) diff --git a/app/models/user.rb b/app/models/user.rb index 6214d07a..568a1c89 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -11,6 +11,9 @@ class User < ActiveRecord::Base before_create :setup_role + # add scope + scope :comment_notifiable, ->(conference) {joins(:roles).where('roles.name IN (?)', [:organizer, :cfp]).where('roles.resource_type = ? AND roles.resource_id = ?', 'Conference', conference.id)} + # Include default devise modules. Others available are: # :token_authenticatable, :confirmable, # :lockable, :timeoutable and :omniauthable diff --git a/app/views/admin/comments/_all_comments.html.haml b/app/views/admin/comments/_all_comments.html.haml new file mode 100644 index 00000000..02ad15e8 --- /dev/null +++ b/app/views/admin/comments/_all_comments.html.haml @@ -0,0 +1,12 @@ +- @comments.each do |conference, events| + .panel.panel-default + .panel-heading + %h4.title.panel-title= conference.title + .panel-body + - events.each do |event, comments| + .notifications + %h4.title= link_to event.title, admin_conference_event_path(event.conference.short_title, event) + %hr + - comments.each do |comment| + %h5.strong Posted by: #{comment.user.name} | Created at: #{comment.created_at} + %p= comment.body diff --git a/app/views/admin/comments/_posted_comments.html.haml b/app/views/admin/comments/_posted_comments.html.haml new file mode 100644 index 00000000..89c90519 --- /dev/null +++ b/app/views/admin/comments/_posted_comments.html.haml @@ -0,0 +1,12 @@ +- @posted_comments.each do |conference, events| + .panel.panel-default + .panel-heading + %h4.title.panel-title= conference.title + .panel-body + - events.each do |event, comments| + .notifications + %h4.title= link_to event.title, admin_conference_event_path(event.conference.short_title, event) + %hr + - comments.each do |comment| + %h5.strong Created at: #{comment.created_at} + %p= comment.body diff --git a/app/views/admin/comments/_unread_comments.html.haml b/app/views/admin/comments/_unread_comments.html.haml new file mode 100644 index 00000000..9291aa83 --- /dev/null +++ b/app/views/admin/comments/_unread_comments.html.haml @@ -0,0 +1,12 @@ +- @unread_comments.each do |conference, events| + .panel.panel-default + .panel-heading + %h4.title.panel-title= conference.title + .panel-body + - events.each do |event, comments| + .notifications + %h4.title= link_to event.title, admin_conference_event_path(event.conference.short_title, event) + %hr + - comments.each do |comment| + %h5.strong Posted by: #{comment.user.name} | Created at: #{comment.created_at} + %p= comment.body diff --git a/app/views/admin/comments/index.html.haml b/app/views/admin/comments/index.html.haml new file mode 100644 index 00000000..9a141c85 --- /dev/null +++ b/app/views/admin/comments/index.html.haml @@ -0,0 +1,24 @@ +%h1 Comments +%br +.row + .col-md-12 + %ul.nav.nav-tabs#commentsTable + %li.active + %a{:href=>"#unread_comments", "data-toggle"=>"tab"} + %span.fa.fa-comment + Unread + %li + %a{:href=>"#posted_comments", "data-toggle"=>"tab"} + %span.fa.fa-pencil + Posted + %li + %a{:href=>"#all_comments", "data-toggle"=>"tab"} + %span.fa.fa-comments-o + All + .tab-content + .tab-pane.active#unread_comments + = render partial: 'unread_comments' + .tab-pane#posted_comments + = render partial: 'posted_comments' + .tab-pane#all_comments + = render partial: 'all_comments' diff --git a/app/views/admin/emails/comment_template.text.erb b/app/views/admin/emails/comment_template.text.erb new file mode 100644 index 00000000..0ebd8ad4 --- /dev/null +++ b/app/views/admin/emails/comment_template.text.erb @@ -0,0 +1,10 @@ +Dear <%= @user.name %>, + +User <%= @comment.user.name %> posted a new comment for event <%= @event.title %> of <%= @conference.short_title %> . + +"<%= @comment.body %>" + +To reply to this comment, please go to <%= h( admin_conference_event_url(@conference.short_title, @event)) %> + +Best wishes, +<%= @conference.short_title %> Team diff --git a/app/views/admin/event_types/index.html.haml b/app/views/admin/event_types/index.html.haml index fddc2dd3..e5513eff 100644 --- a/app/views/admin/event_types/index.html.haml +++ b/app/views/admin/event_types/index.html.haml @@ -9,6 +9,7 @@ %table.table.table-hover#event_types %thead %th Title + %th Description %th Length %th Abstract Length %th Color @@ -18,6 +19,8 @@ %tr %td = event_type.title + %td + = event_type.description %td = event_type.length Minutes diff --git a/app/views/admin/events/index.html.haml b/app/views/admin/events/index.html.haml index 1f591268..10a99f6d 100644 --- a/app/views/admin/events/index.html.haml +++ b/app/views/admin/events/index.html.haml @@ -54,6 +54,14 @@ = javascript_tag "$('label[avgrate=true]').prevAll().andSelf().addClass('bright');" - else = label_tag "label_rating", "", :class => "avgrating" + %br + - voted = event.voted?(event, current_user) + - if voted + %span.label.label-success + Your rating: #{voted.rating} + - else + %span.label.label-danger + Not rated - else 0/#{@conference.call_for_paper.rating} %br diff --git a/app/views/admin/registrations/_questions.html.haml b/app/views/admin/registrations/_questions.html.haml index f82a1213..473a3976 100644 --- a/app/views/admin/registrations/_questions.html.haml +++ b/app/views/admin/registrations/_questions.html.haml @@ -4,6 +4,6 @@ = q.title %b A: - - registration.qanswers.where(:question_id => q.id).each do |qa| + - registration.qanswers.where(question_id: q.id).each do |qa| = qa.answer.title - %br \ No newline at end of file + %br diff --git a/app/views/admin/registrations/edit.html.haml b/app/views/admin/registrations/edit.html.haml index caa84108..694da7a5 100644 --- a/app/views/admin/registrations/edit.html.haml +++ b/app/views/admin/registrations/edit.html.haml @@ -2,25 +2,17 @@ .col-md-12 .page-header %h1 - Edit registration of #{@user.username} for #{@conference.short_title} + Registration for #{@user.username} .row - .col-md-8 - = semantic_form_for(@registration, :url => admin_conference_registration_path(@conference.short_title, @registration)) do |f| + .col-md-6 + = semantic_form_for(@registration, url: admin_conference_registration_path(@conference.short_title, @registration)) do |f| = f.inputs 'Personal Information' do = f.fields_for :user do |u| = u.input :name, as: :string = u.input :nickname, as: :string = u.input :affiliation, placeholder: 'Company/User Group/nothing', as: :string = f.inputs 'Registration Information' do - - @conference.questions.each do |q| - %h5 - = "Q: #{q.title}" - - if q.question_type.id == 1 || q.question_type.id == 2 # yes/no or single choice - = f.input :qanswers, :collection => q.qanswers, :as => :select, :input_html => { :multiple => false }, :label => false, :include_blank => "Please make your choice", - :member_label => Proc.new {|a| a.answer.title} - - if q.question_type.id == 3 # multiple choice - = f.input :qanswers, :collection => q.qanswers, :as => :check_boxes, :label => false, - :member_label => Proc.new {|a| a.answer.title} + = render partial: 'conference_registrations/registration_info', locals: { f: f } -# Not necessary - if @conference.social_events.count > 0 @@ -28,4 +20,4 @@ %br Yes, I'll be attending... %br = f.input :social_events, as: :check_boxes, label: false, collection: @conference.social_events - = f.action :submit, button_html: { value: 'Edit Registration', class: 'btn btn-primary' } + = f.action :submit, button_html: { class: 'btn btn-primary' } diff --git a/app/views/admin/registrations/index.html.haml b/app/views/admin/registrations/index.html.haml index ae48c678..4e3a843e 100644 --- a/app/views/admin/registrations/index.html.haml +++ b/app/views/admin/registrations/index.html.haml @@ -6,8 +6,8 @@ = "(#{@registrations.length})" if @registrations .btn-group.pull-right - if can? :read, Registration - = link_to "Export PDF", admin_conference_registrations_path(@conference.short_title, :format => :pdf), :class => "btn btn-default" - = link_to "Export XLS", {:format => :xlsx}, :class => "btn btn-default" + = link_to 'Export PDF', admin_conference_registrations_path(@conference.short_title, format: :pdf), class: 'btn btn-success' + = link_to 'Export XLS', {format: :xlsx}, class: 'btn btn-success' %p.text-muted All the people who registered to your event %table.table.table-hover.datatable#registrations @@ -32,12 +32,12 @@ = registration.email %td - if registration.arrival - = registration.arrival.strftime("%d %b %H:%M") + = registration.arrival.strftime('%d %b %H:%M') - else n/a %td - if registration.departure - = registration.departure.strftime("%d %b %H:%M") + = registration.departure.strftime('%d %b %H:%M') - else n/a -if @conference.questions.any? @@ -47,7 +47,7 @@ = check_box_tag "#{@conference.short_title}_#{registration.id}", registration.id, registration.attended, class: 'switch-checkbox', method: :patch, url: toggle_attendance_admin_conference_registration_path(@conference.short_title, id: registration.id)+"?attended=", - data: { size: "small", + data: { size: 'small', on_color: 'success', off_color: 'warning', on_text: 'Present', @@ -61,7 +61,7 @@ .questions{class: "question#{index}", style: 'display:none;'} = render partial: 'questions', locals: { registration: registration } -.modal.fade{ id: "questions", 'role' => 'dialog', 'aria-hidden' => 'true' } +.modal.fade{ id: 'questions', 'role' => 'dialog', 'aria-hidden' => 'true' } .modal-dialog .modal-content .modal-header diff --git a/app/views/conference/index.html.haml b/app/views/conference/index.html.haml index 79ea924d..b94f5075 100644 --- a/app/views/conference/index.html.haml +++ b/app/views/conference/index.html.haml @@ -4,19 +4,19 @@ .page-header %h2 Upcoming Conferences - @current.each do |conference| - = render :partial => "conference_details", :locals => {:conference => conference} + = render partial: 'conference_details', locals: { conference: conference } -if @antiquated and @antiquated.any? .row .col-md-12 %p.text-right - %button{:type=>"button", :class=>"btn btn-link btn-sm", "data-toggle"=>"collapse", "data-target"=>"#antiquated", "aria-expanded"=>"true", "aria-controls"=>"antiquated"} + %button{ type: 'button', class: 'btn btn-link btn-sm', 'data-toggle' => 'collapse', 'data-target' => '#antiquated', 'aria-expanded' => 'true', 'aria-controls' => 'antiquated'} Older conferences = "(#{@antiquated.count})" %i.fa.fa-chevron-right - %i.fa.fa-chevron-down{:style => 'display: none'} + %i.fa.fa-chevron-down{ style: 'display: none' } #antiquated.collapse - @antiquated.each do |conference| - = render :partial => "conference_details", :locals => {:conference => conference} + = render partial: 'conference_details', locals: { conference: conference} -content_for :script_body do :javascript diff --git a/app/views/conference_registrations/_form.html.haml b/app/views/conference_registrations/_form.html.haml index f3b282f1..7678abef 100644 --- a/app/views/conference_registrations/_form.html.haml +++ b/app/views/conference_registrations/_form.html.haml @@ -21,14 +21,7 @@ = semantic_form_for(@registration, url: conference_conference_registrations_path(@conference.short_title)) do |f| = render partial: 'devise/shared/sign_up_form_embedded' - - if @conference.questions.any? - = render partial: 'questions', locals: { f: f } - - if @conference.events.workshops.any? - =f.inputs 'Register to Workshops' do - = f.input :events, as: :check_boxes, label: false, collection: @conference.events.workshops - = f.inputs 'Your Travel Info' do - = f.input :arrival, as: :string, label: 'Your arrival time', input_html: { value: (f.object.arrival.to_formatted_s(:db_without_seconds) unless f.object.arrival.nil?), id: 'registration-arrival-datepicker', readonly: 'readonly' } - = f.input :departure, as: :string, label: 'Your departure time', input_html: { value: (f.object.departure.to_formatted_s(:db_without_seconds) unless f.object.departure.nil?), id: 'registration-departure-datepicker', readonly: 'readonly' } + = render partial: 'registration_info', locals: { f: f } .row .col-md-12 %p.pull-right diff --git a/app/views/conference_registrations/_questions.html.haml b/app/views/conference_registrations/_questions.html.haml index 2443b7dc..56ee7836 100644 --- a/app/views/conference_registrations/_questions.html.haml +++ b/app/views/conference_registrations/_questions.html.haml @@ -5,4 +5,4 @@ :member_label => Proc.new {|a| a.answer.title} - if q.question_type.id == 3 # multiple choice = f.input :qanswers, :collection => q.qanswers, :as => :check_boxes, label: q.title, - :member_label => Proc.new {|a| a.answer.title} \ No newline at end of file + :member_label => Proc.new {|a| a.answer.title} diff --git a/app/views/conference_registrations/_registration_info.html.haml b/app/views/conference_registrations/_registration_info.html.haml new file mode 100644 index 00000000..9653862d --- /dev/null +++ b/app/views/conference_registrations/_registration_info.html.haml @@ -0,0 +1,8 @@ +- if @conference.questions.any? + = render partial: 'conference_registrations/questions', locals: { f: f } +- if @conference.events.workshops.any? + =f.inputs 'Pre-registration required for the following:' do + = f.input :events, as: :check_boxes, label: false, collection: @conference.events.workshops += f.inputs 'Your Travel Info' do + = f.input :arrival, as: :string, label: 'Your arrival time', input_html: { value: (f.object.arrival.to_formatted_s(:db_without_seconds) unless f.object.arrival.nil?), id: 'registration-arrival-datepicker', readonly: 'readonly' } + = f.input :departure, as: :string, label: 'Your departure time', input_html: { value: (f.object.departure.to_formatted_s(:db_without_seconds) unless f.object.departure.nil?), id: 'registration-departure-datepicker', readonly: 'readonly' } diff --git a/app/views/layouts/_admin_sidebar.html.haml b/app/views/layouts/_admin_sidebar.html.haml index 9fc01035..016c77a2 100644 --- a/app/views/layouts/_admin_sidebar.html.haml +++ b/app/views/layouts/_admin_sidebar.html.haml @@ -29,9 +29,14 @@ Dashboard - if can? :show, @conference %li{:class=> "#{active_nav_li(edit_admin_conference_path(@conference.short_title))}"} - = link_to(edit_admin_conference_path(@conference.short_title)) do - %span.fa.fa-home - Basics + - if can? :edit, @conference + = link_to(edit_admin_conference_path(@conference.short_title)) do + %span.fa.fa-home + Basics + - else + %a + %span.fa.fa-home + Basics %ul - if can? :update, Contact.new(conference_id: @conference.id) %li{:class=> "#{active_nav_li(edit_admin_conference_contact_path(@conference.short_title))}"} diff --git a/app/views/layouts/_navigation.html.haml b/app/views/layouts/_navigation.html.haml index ea837d2c..cf2b3b44 100644 --- a/app/views/layouts/_navigation.html.haml +++ b/app/views/layouts/_navigation.html.haml @@ -13,17 +13,34 @@ %ul.nav.navbar-nav#splash-nav = content_for :splash_nav -if user_signed_in? - %ul.nav.navbar-nav.navbar-right - %li.dropdown - %a.dropdown-toggle{"data-toggle" => "dropdown", :href => "#", id: "current-user-detail"} - - if not current_user.name.blank? - #{current_user.name} - -else - #{current_user.email} - = image_tag(current_user.gravatar_url(size: '18'), title: "Yo #{current_user.name}!", :alt => '') - %b.caret - %ul.dropdown-menu - = render 'layouts/user_menu' + .btn-group.pull-right + %ul.nav.navbar-nav.navbar-right + %li.dropdown + %a.dropdown-toggle{"data-toggle" => "dropdown", :href => "#", id: "current-user-detail"} + - if not current_user.name.blank? + #{current_user.name} + -else + #{current_user.email} + = image_tag(current_user.gravatar_url(size: '18'), title: "Yo #{current_user.name}!", :alt => '') + %b.caret + %ul.dropdown-menu + = render 'layouts/user_menu' + - if can? :index, Comment + %ul.nav.navbar-nav.navbar-right + %li.dropdown + %a.dropdown-toggle{"data-toggle" => "dropdown", :href => "#"} + - if unread_notifications(current_user) + Notifications (#{unread_notifications(current_user).length}) + %span.fa.fa-comment + %b.caret + %ul.dropdown-menu + - if unread_notifications(current_user).length > 0 + %li.dropdown-header Last 5 Comments for: + - unread_notifications(current_user).limit(5).group_by{ |comment| comment.commentable}.each do |event, comments| + %li= link_to("#{event.title}(#{comments.count})", admin_conference_event_path(event.conference.short_title, event.id)) + %li.divider + %li= link_to "See all unread Comments (#{unread_notifications(current_user).length})", admin_comments_path + %li= link_to 'See all Comments', admin_comments_path(anchor: 'all_comments') - else %ul.nav.navbar-nav.navbar-right - if CONFIG['authentication']['ichain']['enabled'] diff --git a/app/views/layouts/_unread_comment.html.haml b/app/views/layouts/_unread_comment.html.haml new file mode 100644 index 00000000..d337df5c --- /dev/null +++ b/app/views/layouts/_unread_comment.html.haml @@ -0,0 +1,2 @@ +%li= link_to("New comment for: #{unread_comment.commentable.title}", admin_conference_event_path(unread_comment.commentable.conference.short_title, unread_comment.commentable_id)) + diff --git a/app/views/layouts/_user_menu.html.haml b/app/views/layouts/_user_menu.html.haml index fe349b3b..1764d9d0 100644 --- a/app/views/layouts/_user_menu.html.haml +++ b/app/views/layouts/_user_menu.html.haml @@ -21,14 +21,15 @@ = link_to(destroy_user_session_path, :method=>'delete') do %span.fa.fa-minus Sign out -- if can? :manage, Conference +- if can? :access, Admin %li.divider %li - = link_to(admin_conference_index_path()) do - - if Conference.any? + - if Conference.any? + = link_to(admin_conference_index_path()) do %span.fa.fa-home - Administration - - else + Administration + - if can? :create, Conference + =link_to(new_admin_conference_path) do %span.fa.fa-plus Create Conference -if @conference and @conference.id and can? :show, @conference diff --git a/app/views/proposal/_form.html.haml b/app/views/proposal/_form.html.haml index f7eb3f3b..c0bc8dc8 100644 --- a/app/views/proposal/_form.html.haml +++ b/app/views/proposal/_form.html.haml @@ -4,9 +4,9 @@ .tabbable %ul.nav.nav-tabs %li.active - = link_to "Proposal", "#proposal-content", "data-toggle"=>"tab" + = link_to 'Proposal', '#proposal-content', 'data-toggle' => 'tab' %li - = link_to 'Commercials', '#commercials-content', 'data-toggle'=>'tab' + = link_to 'Commercials', '#commercials-content', 'data-toggle' => 'tab' .tab-content #proposal-content.tab-pane.active = render 'proposal/proposal_form' @@ -29,7 +29,7 @@ = link_to 'Edit', edit_conference_proposal_commercial_path(@conference.short_title, @event.id, commercial.id), class: 'btn btn-primary' - if can? :destroy, commercial = link_to 'Delete', conference_proposal_commercial_path(@conference.short_title, @event.id, commercial.id), - :method => :delete, :data => { :confirm => 'Are you sure?' }, class: 'btn btn-danger' + method: :delete, data: { confirm: 'Are you sure?' }, class: 'btn btn-danger' - if can? :create, @event.commercials.new %hr = link_to 'Add Commercial', new_conference_proposal_commercial_path(@conference.short_title, @event.id), class: 'btn btn-primary' diff --git a/app/views/proposal/_proposal_form.html.haml b/app/views/proposal/_proposal_form.html.haml index 8b748139..83b34a5f 100644 --- a/app/views/proposal/_proposal_form.html.haml +++ b/app/views/proposal/_proposal_form.html.haml @@ -43,11 +43,11 @@ = f.input :is_highlight %p.text-right - = link_to '#description', "data-toggle"=>"collapse" do + = link_to '#description', 'data-toggle' => 'collapse' do Do you require something special? .collapse#description = f.input :description, input_html: { rows: 5 }, label: 'Requirements', placeholder: 'Eg. Whiteboard, printer, or something like that.' %p.text-right - = f.action :submit, :as => :button, :button_html => {:class => "btn btn-success"}, label: 'Update Proposal' + = f.submit 'Update Proposal', class: 'btn btn-success' diff --git a/app/views/proposal/new.html.haml b/app/views/proposal/new.html.haml index 1d91c157..e4b4fd99 100644 --- a/app/views/proposal/new.html.haml +++ b/app/views/proposal/new.html.haml @@ -11,11 +11,11 @@ - if !current_user %legend %span - =link_to('#signup', role: 'tab', "aria-controls" => "home", "data-toggle" => "tab") do + =link_to('#signup', role: 'tab', 'aria-controls' => 'home', 'data-toggle' => 'tab') do = CONFIG['name'] Account %span.pull-right#account-already - =link_to('#signin', role: 'tab', "aria-controls" => "home", "data-toggle" => "tab") do + =link_to('#signin', role: 'tab', 'aria-controls' => 'home', 'data-toggle' => 'tab') do Already have an account? .tab-content .tab-pane.active{role: 'tabpanel', id: 'signup'} @@ -25,7 +25,7 @@ = render partial: 'devise/shared/sign_up_form_embedded' = f.inputs name: 'Proposal Information' do - = f.input :title, as: :string, required: true + = f.input :title, as: :string, required: true, input_html: { required: true } = f.input :event_type_id, as: :select, collection: @conference.event_types.map {|type| ["#{type.title} - #{show_time(type.length)}", type.id, data: { min_words: type.minimum_abstract_length, max_words: type.maximum_abstract_length }]}, @@ -38,7 +38,7 @@ :javascript $("##{@conference.event_types.first.id}-help").collapse('show'); - = f.input :abstract, input_html: { rows: 5 }, + = f.input :abstract, input_html: { rows: 5, required: true }, required: true, hint: link_to('Tips to improve your presentations', 'http://blog.hubspot.com/blog/tabid/6307/bid/5975/10-Rules-to-Instantly-Improve-Your-Presentations.aspx') %p @@ -55,12 +55,13 @@ = f.input :require_registration, label: 'Require participants to register to your event' %p.text-right - = link_to '#description', "data-toggle"=>"collapse", id: 'description_link' do + = link_to '#description', 'data-toggle' => 'collapse', id: 'description_link' do Do you require something special? .collapse#description = f.input :description, input_html: { rows: 5 }, label: 'Requirements', placeholder: 'Eg. Whiteboard, printer, or something like that.' %p.text-right - = f.action :submit, :as => :button, :button_html => {:class => "btn btn-success"}, label: 'Create Proposal' + = f.submit 'Create Proposal', class: 'btn btn-success' + .tab-pane{role: 'tabpanel', id: 'signin'} = render partial: 'devise/shared/sign_in_form_embedded' diff --git a/bin/bundle b/bin/bundle new file mode 100755 index 00000000..66e9889e --- /dev/null +++ b/bin/bundle @@ -0,0 +1,3 @@ +#!/usr/bin/env ruby +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) +load Gem.bin_path('bundler', 'bundle') diff --git a/bin/rails b/bin/rails index f8da2cff..5191e692 100755 --- a/bin/rails +++ b/bin/rails @@ -1,6 +1,4 @@ #!/usr/bin/env ruby -# This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application. - -APP_PATH = File.expand_path('../../config/application', __FILE__) -require File.expand_path('../../config/boot', __FILE__) +APP_PATH = File.expand_path('../../config/application', __FILE__) +require_relative '../config/boot' require 'rails/commands' diff --git a/bin/rake b/bin/rake new file mode 100755 index 00000000..17240489 --- /dev/null +++ b/bin/rake @@ -0,0 +1,4 @@ +#!/usr/bin/env ruby +require_relative '../config/boot' +require 'rake' +Rake.application.run diff --git a/bin/setup b/bin/setup new file mode 100755 index 00000000..acdb2c13 --- /dev/null +++ b/bin/setup @@ -0,0 +1,29 @@ +#!/usr/bin/env ruby +require 'pathname' + +# path to your application root. +APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) + +Dir.chdir APP_ROOT do + # This script is a starting point to setup your application. + # Add necessary setup steps to this file: + + puts "== Installing dependencies ==" + system "gem install bundler --conservative" + system "bundle check || bundle install" + + # puts "\n== Copying sample files ==" + # unless File.exist?("config/database.yml") + # system "cp config/database.yml.sample config/database.yml" + # end + + puts "\n== Preparing database ==" + system "bin/rake db:setup" + + puts "\n== Removing old logs and tempfiles ==" + system "rm -f log/*" + system "rm -rf tmp/cache" + + puts "\n== Restarting application server ==" + system "touch tmp/restart.txt" +end diff --git a/config/application.rb b/config/application.rb index 43872169..860ba195 100644 --- a/config/application.rb +++ b/config/application.rb @@ -37,6 +37,9 @@ module Osem # Configure the default encoding used in templates for Ruby 1.9. config.encoding = 'utf-8' + # Do not swallow errors in after_commit/after_rollback callbacks. + config.active_record.raise_in_transactional_callbacks = true + # Configure sensitive parameters which will be filtered from the log file. config.filter_parameters += [:password] diff --git a/config/boot.rb b/config/boot.rb index 5e5f0c1f..6b750f00 100644 --- a/config/boot.rb +++ b/config/boot.rb @@ -1,4 +1,3 @@ -# Set up gems listed in the Gemfile. ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) -require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE']) +require 'bundler/setup' # Set up gems listed in the Gemfile. diff --git a/config/environments/production.rb b/config/environments/production.rb index 69c7f9de..e1bf1bf9 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -35,8 +35,9 @@ Osem::Application.configure do # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. # config.force_ssl = true - # See everything in the log (default is :info) - # config.log_level = :debug + # See everything in the log. Set to `:info` to match the current default, or set to `:debug` to opt-into + # the future default. + config.log_level = :info # Prepend all log lines with the following tags # config.log_tags = [ :subdomain, :uuid ] diff --git a/config/environments/test.rb b/config/environments/test.rb index 05845254..09080c8d 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -35,6 +35,9 @@ Osem::Application.configure do # Print deprecation notices to the stderr config.active_support.deprecation = :stderr + # Randomize/sort the order test cases are executed. + config.active_support.test_order = :sorted # or `:random` + # Set the detault url for action mailer config.action_mailer.default_url_options = { host: 'localhost:3000' } diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb new file mode 100644 index 00000000..01ef3e66 --- /dev/null +++ b/config/initializers/assets.rb @@ -0,0 +1,11 @@ +# Be sure to restart your server when you modify this file. + +# Version of your assets, change this if you want to expire all your assets. +Rails.application.config.assets.version = '1.0' + +# Add additional assets to the asset load path +# Rails.application.config.assets.paths << Emoji.images_path + +# Precompile additional assets. +# application.js, application.css, and all non-JS/CSS in app/assets folder are already added. +# Rails.application.config.assets.precompile += %w( search.js ) diff --git a/config/initializers/cookies_serializer.rb b/config/initializers/cookies_serializer.rb new file mode 100644 index 00000000..ac5f8b66 --- /dev/null +++ b/config/initializers/cookies_serializer.rb @@ -0,0 +1,3 @@ +# Be sure to restart your server when you modify this file. + +Rails.application.config.action_dispatch.cookies_serializer = :marshal diff --git a/config/initializers/filter_parameter_logging.rb b/config/initializers/filter_parameter_logging.rb new file mode 100644 index 00000000..4a994e1e --- /dev/null +++ b/config/initializers/filter_parameter_logging.rb @@ -0,0 +1,4 @@ +# Be sure to restart your server when you modify this file. + +# Configure sensitive parameters which will be filtered from the log file. +Rails.application.config.filter_parameters += [:password] diff --git a/config/initializers/wrap_parameters.rb b/config/initializers/wrap_parameters.rb index 999df201..33725e95 100644 --- a/config/initializers/wrap_parameters.rb +++ b/config/initializers/wrap_parameters.rb @@ -1,14 +1,14 @@ # Be sure to restart your server when you modify this file. -# + # This file contains settings for ActionController::ParamsWrapper which # is enabled by default. # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. ActiveSupport.on_load(:action_controller) do - wrap_parameters format: [:json] + wrap_parameters format: [:json] if respond_to?(:wrap_parameters) end -# Disable root element in JSON by default. -ActiveSupport.on_load(:active_record) do - self.include_root_in_json = false -end +# To enable root element in JSON for ActiveRecord objects. +# ActiveSupport.on_load(:active_record) do +# self.include_root_in_json = true +# end diff --git a/config/routes.rb b/config/routes.rb index 5f8a59b6..05766f76 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -15,6 +15,7 @@ Osem::Application.routes.draw do namespace :admin do resources :users resources :people + resources :comments, only: [:index] resources :conference do member do get :roles diff --git a/db/migrate/20150929142405_change_postalcode_format_in_venues.rb b/db/migrate/20150929142405_change_postalcode_format_in_venues.rb new file mode 100644 index 00000000..010f9349 --- /dev/null +++ b/db/migrate/20150929142405_change_postalcode_format_in_venues.rb @@ -0,0 +1,9 @@ +class ChangePostalcodeFormatInVenues < ActiveRecord::Migration + def up + change_column :venues, :postalcode, :string + end + + def down + raise ActiveRecord::IrreversibleMigration.new('Cannot reverse migration.') + end +end diff --git a/db/schema.rb b/db/schema.rb index 086a703f..0426bdbf 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20150415121038) do +ActiveRecord::Schema.define(version: 20150929142405) do create_table "ahoy_events", force: true do |t| t.uuid "visit_id" @@ -515,7 +515,7 @@ ActiveRecord::Schema.define(version: 20150415121038) do t.integer "photo_file_size" t.datetime "photo_updated_at" t.string "street" - t.integer "postalcode" + t.string "postalcode" t.string "city" t.string "country" t.string "latitude" diff --git a/spec/controllers/admin/comments_controller_spec.rb b/spec/controllers/admin/comments_controller_spec.rb new file mode 100644 index 00000000..ac75b189 --- /dev/null +++ b/spec/controllers/admin/comments_controller_spec.rb @@ -0,0 +1,59 @@ +require 'spec_helper' + +describe Admin::CommentsController, type: :controller do + + # It is necessary to use bang version of let to build roles before user + let(:conference) { create(:conference) } + let!(:first_user) { create(:user) } + let!(:organizer_role) { create(:role, name: 'organizer', resource: conference) } + let(:organizer) { create(:user, role_ids: organizer_role.id, last_sign_in_at: Time.now - 1.day) } + let(:participant) { create(:user) } + let(:event) { create(:event, conference: conference) } + let(:comment) { create(:comment, commentable_type: 'Event', commentable_id: event.id) } + + context 'not logged in user' do + describe 'GET #index' do + it 'renders the :index template' do + comment + get :index + expect(response).to redirect_to(user_session_path) + end + end + end + + context 'logged in as admin, organizer or cfp' do + before :each do + sign_in(organizer) + comment + end + describe 'GET #index' do + it 'populates a hash with comments' do + get :index + expect(assigns(:comments)).to be_a(Hash) + # assigns(:comments).first returns an array of first pair key-value from hash. + # Calling again 'first' returns the key, meaning the Conference object. + expect(assigns(:comments).first.first.title).to eq(comment.commentable.conference.title) + end + it 'has status 200: OK' do + get :index + expect(response).to have_http_status(:ok) + end + it 'renders the :index template' do + get :index + expect(response).to render_template(:index) + end + end + end + + context 'logged in with any other role or normal user' do + describe 'GET#index' do + it 'requires organizer privileges' do + sign_in(participant) + comment + get :index + expect(response).to redirect_to(root_path) + expect(flash[:alert]).to match('You are not authorized to access this area!') + end + end + end +end diff --git a/spec/factories/comments.rb b/spec/factories/comments.rb new file mode 100644 index 00000000..a424e23b --- /dev/null +++ b/spec/factories/comments.rb @@ -0,0 +1,9 @@ +# Read about factories at https://github.com/thoughtbot/factory_girl + +FactoryGirl.define do + factory :comment do + body 'Most interresting comment ever, created by a girl.' + user + association :commentable, factory: :event + end +end diff --git a/spec/factories/events.rb b/spec/factories/events.rb index 537dfecb..6dc89b22 100644 --- a/spec/factories/events.rb +++ b/spec/factories/events.rb @@ -47,6 +47,7 @@ FactoryGirl.define do event.difficulty_level = build(:difficulty_level, conference: event.conference) event.track = build(:track, conference: event.conference) event.room = build(:room, conference: event.conference) + event.comment_threads << build(:comment, commentable: event) end end diff --git a/spec/features/ability_spec.rb b/spec/features/ability_spec.rb index 53f21818..c7abb168 100644 --- a/spec/features/ability_spec.rb +++ b/spec/features/ability_spec.rb @@ -105,7 +105,8 @@ feature 'Has correct abilities' do visit admin_conference_path(conference2.short_title) expect(page).to have_selector('li.nav-header.nav-header-bigger a', text: 'Dashboard') - expect(page).to have_link('Basics', href: "/admin/conference/#{conference2.short_title}/edit") + expect(page).to_not have_link('Basics', href: "/admin/conference/#{conference2.short_title}/edit") + expect(page).to have_text('Basics') expect(page).to_not have_link('Contact', href: "/admin/conference/#{conference2.short_title}/contact/edit") expect(page).to have_link('Commercials', href: "/admin/conference/#{conference2.short_title}/commercials") expect(page).to have_link('Events', href: "/admin/conference/#{conference2.short_title}/events") @@ -176,7 +177,8 @@ feature 'Has correct abilities' do visit admin_conference_path(conference3.short_title) expect(page).to have_selector('li.nav-header.nav-header-bigger a', text: 'Dashboard') - expect(page).to have_link('Basics', href: "/admin/conference/#{conference3.short_title}/edit") + expect(page).to_not have_link('Basics', href: "/admin/conference/#{conference2.short_title}/edit") + expect(page).to have_text('Basics') expect(page).to_not have_link('Contact', href: "/admin/conference/#{conference3.short_title}/contact/edit") expect(page).to have_link('Commercials', href: "/admin/conference/#{conference3.short_title}/commercials") expect(page).to_not have_link('Events', href: "/admin/conference/#{conference3.short_title}/events") diff --git a/spec/models/ability_spec.rb b/spec/models/ability_spec.rb index b3859748..73812dc1 100644 --- a/spec/models/ability_spec.rb +++ b/spec/models/ability_spec.rb @@ -141,6 +141,8 @@ describe 'User' do it{ should_not be_able_to(:manage, other_event.difficulty_level) } it{ should be_able_to(:manage, event.commercials.first) } it{ should_not be_able_to(:manage, other_event.commercials.first) } + it{ should be_able_to(:index, event.comment_threads.first) } + it{ should_not be_able_to(:index, other_event.comment_threads.first) } end context 'when user has the role cfp' do @@ -198,6 +200,8 @@ describe 'User' do it{ should_not be_able_to(:manage, other_event.difficulty_level) } it{ should be_able_to(:manage, event.commercials.first) } it{ should_not be_able_to(:manage, other_event.commercials.first) } + it{ should be_able_to(:index, event.comment_threads.first) } + it{ should_not be_able_to(:index, other_event.comment_threads.first) } end context 'when user has the role info_desk' do @@ -255,6 +259,8 @@ describe 'User' do it{ should_not be_able_to(:manage, other_event.difficulty_level) } it{ should_not be_able_to(:manage, event.commercials.first) } it{ should_not be_able_to(:manage, other_event.commercials.first) } + it{ should_not be_able_to(:index, event.comment_threads.first) } + it{ should_not be_able_to(:index, other_event.comment_threads.first) } end context 'when user has the role volunteers_coordinator' do @@ -312,6 +318,8 @@ describe 'User' do it{ should_not be_able_to(:manage, other_event.difficulty_level) } it{ should_not be_able_to(:manage, event.commercials.first) } it{ should_not be_able_to(:manage, other_event.commercials.first) } + it{ should_not be_able_to(:index, event.comment_threads.first) } + it{ should_not be_able_to(:index, other_event.comment_threads.first) } it 'should be_able to :manage Vposition' it 'should be_able to :manage Vday' end