From aaeb77d9698049374a0a4ba640a27173ccd01dd8 Mon Sep 17 00:00:00 2001 From: Eloy Gomez Date: Sat, 8 Aug 2015 12:05:55 +0200 Subject: [PATCH 01/90] Add gem ahoy_matey and some dependencies --- Gemfile | 5 +- Gemfile.lock | 23 ++++++++ app/assets/javascripts/application.js | 1 + app/models/ahoy/event.rb | 8 +++ config/initializers/ahoy.rb | 3 + db/migrate/20150804144230_create_visits.rb | 56 +++++++++++++++++++ .../20150804144231_create_ahoy_events.rb | 20 +++++++ db/schema.rb | 54 ++++++++++++++++-- 8 files changed, 163 insertions(+), 7 deletions(-) create mode 100644 app/models/ahoy/event.rb create mode 100644 config/initializers/ahoy.rb create mode 100644 db/migrate/20150804144230_create_visits.rb create mode 100644 db/migrate/20150804144231_create_ahoy_events.rb diff --git a/Gemfile b/Gemfile index 4c37d5ea1..ee555ec92 100644 --- a/Gemfile +++ b/Gemfile @@ -32,6 +32,10 @@ gem 'acts_as_votable' gem "recaptcha", require: "recaptcha/rails" gem 'ckeditor' +gem 'ahoy_matey' # stats +gem "chartkick" # charts +gem 'groupdate' # group temporary data + group :development, :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console gem 'byebug' @@ -62,4 +66,3 @@ end group :test do gem 'email_spec' end - diff --git a/Gemfile.lock b/Gemfile.lock index 62be9aa71..c005c2b21 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -44,12 +44,23 @@ GEM awesome_nested_set (>= 3.0) acts_as_votable (0.10.0) addressable (2.3.8) + ahoy_matey (1.2.0) + addressable + browser (>= 0.4.0) + errbase + geocoder + rails + referer-parser (>= 0.3.0) + request_store + user_agent_parser + uuidtools arel (6.0.2) awesome_nested_set (3.0.2) activerecord (>= 4.0.0, < 5) bcrypt (3.1.10) binding_of_caller (0.7.2) debug_inspector (>= 0.0.1) + browser (0.9.1) builder (3.2.2) byebug (5.0.0) columnize (= 0.9.0) @@ -74,6 +85,7 @@ GEM rack (>= 1.0.0) rack-test (>= 0.5.4) xpath (~> 2.0) + chartkick (1.3.2) ckeditor (4.1.2) cocaine orm_adapter (~> 0.5.0) @@ -117,6 +129,7 @@ GEM email_spec (1.6.0) launchy (~> 2.1) mail (~> 2.2) + errbase (0.0.3) erubis (2.7.0) execjs (2.5.2) factory_girl (4.5.0) @@ -127,8 +140,11 @@ GEM foundation-rails (5.5.2.1) railties (>= 3.1.0) sass (>= 3.3.0, < 3.5) + geocoder (1.2.9) globalid (0.3.5) activesupport (>= 4.1.0) + groupdate (2.4.0) + activesupport (>= 3) highline (1.7.3) http-cookie (1.0.2) domain_name (~> 0.5) @@ -209,6 +225,8 @@ GEM rake (10.4.2) rdoc (4.2.0) recaptcha (0.4.0) + referer-parser (0.3.0) + request_store (1.2.0) responders (2.1.0) railties (>= 4.2.0, < 5) rest-client (1.8.0) @@ -276,6 +294,8 @@ GEM unf (0.1.4) unf_ext unf_ext (0.0.7.1) + user_agent_parser (2.2.0) + uuidtools (2.1.5) warden (1.2.3) rack (>= 1.0) web-console (2.2.1) @@ -296,6 +316,7 @@ DEPENDENCIES acts-as-taggable-on acts_as_commentable_with_threading acts_as_votable + ahoy_matey byebug capistrano (= 3.4.0) capistrano-bundler (= 1.1.4) @@ -303,6 +324,7 @@ DEPENDENCIES capistrano-rails (= 1.1.3) capistrano-rvm capybara + chartkick ckeditor coffee-rails (~> 4.1.0) coveralls @@ -311,6 +333,7 @@ DEPENDENCIES email_spec factory_girl_rails foundation-rails + groupdate i18n-tasks jbuilder (~> 2.0) jquery-rails diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 08153c4ec..10fad952a 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -15,6 +15,7 @@ //= require foundation //= require turbolinks //= require ckeditor/init +//= require ahoy //= require app //= require_tree . diff --git a/app/models/ahoy/event.rb b/app/models/ahoy/event.rb new file mode 100644 index 000000000..2aac3c59c --- /dev/null +++ b/app/models/ahoy/event.rb @@ -0,0 +1,8 @@ +module Ahoy + class Event < ActiveRecord::Base + self.table_name = "ahoy_events" + + belongs_to :visit + belongs_to :user + end +end diff --git a/config/initializers/ahoy.rb b/config/initializers/ahoy.rb new file mode 100644 index 000000000..22d326e53 --- /dev/null +++ b/config/initializers/ahoy.rb @@ -0,0 +1,3 @@ +class Ahoy::Store < Ahoy::Stores::ActiveRecordStore + # customize here +end diff --git a/db/migrate/20150804144230_create_visits.rb b/db/migrate/20150804144230_create_visits.rb new file mode 100644 index 000000000..6f5156c0e --- /dev/null +++ b/db/migrate/20150804144230_create_visits.rb @@ -0,0 +1,56 @@ +class CreateVisits < ActiveRecord::Migration + def change + create_table :visits, id: false do |t| + t.uuid :id, default: nil, primary_key: true + t.uuid :visitor_id, default: nil + + # the rest are recommended but optional + # simply remove the columns you don't want + + # standard + t.string :ip + t.text :user_agent + t.text :referrer + t.text :landing_page + + # user + t.integer :user_id + # add t.string :user_type if polymorphic + + # traffic source + t.string :referring_domain + t.string :search_keyword + + # technology + t.string :browser + t.string :os + t.string :device_type + t.integer :screen_height + t.integer :screen_width + + # location + t.string :country + t.string :region + t.string :city + t.string :postal_code + t.decimal :latitude + t.decimal :longitude + + # utm parameters + t.string :utm_source + t.string :utm_medium + t.string :utm_term + t.string :utm_content + t.string :utm_campaign + + # native apps + # t.string :platform + # t.string :app_version + # t.string :os_version + + t.timestamp :started_at + end + + add_index :visits, [:user_id] + end +end diff --git a/db/migrate/20150804144231_create_ahoy_events.rb b/db/migrate/20150804144231_create_ahoy_events.rb new file mode 100644 index 000000000..81fb71f37 --- /dev/null +++ b/db/migrate/20150804144231_create_ahoy_events.rb @@ -0,0 +1,20 @@ +class CreateAhoyEvents < ActiveRecord::Migration + def change + create_table :ahoy_events, id: false do |t| + t.uuid :id, default: nil, primary_key: true + t.uuid :visit_id, default: nil + + # user + t.integer :user_id + # add t.string :user_type if polymorphic + + t.string :name + t.jsonb :properties + t.timestamp :time + end + + add_index :ahoy_events, [:visit_id] + add_index :ahoy_events, [:user_id] + add_index :ahoy_events, [:time] + end +end diff --git a/db/schema.rb b/db/schema.rb index f6947e462..22b60c506 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -16,6 +16,18 @@ ActiveRecord::Schema.define(version: 20150806163142) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" + create_table "ahoy_events", id: :uuid, default: nil, force: :cascade do |t| + t.uuid "visit_id" + t.integer "user_id" + t.string "name" + t.jsonb "properties" + t.datetime "time" + end + + add_index "ahoy_events", ["time"], name: "index_ahoy_events_on_time", using: :btree + add_index "ahoy_events", ["user_id"], name: "index_ahoy_events_on_user_id", using: :btree + add_index "ahoy_events", ["visit_id"], name: "index_ahoy_events_on_visit_id", using: :btree + create_table "comments", force: :cascade do |t| t.integer "commentable_id" t.string "commentable_type" @@ -62,18 +74,18 @@ ActiveRecord::Schema.define(version: 20150806163142) do add_index "tags", ["name"], name: "index_tags_on_name", unique: true, using: :btree create_table "users", force: :cascade do |t| - t.string "email", default: "", null: false - t.string "encrypted_password", default: "", null: false + t.string "email", default: "", null: false + t.string "encrypted_password", default: "", null: false t.string "reset_password_token" t.datetime "reset_password_sent_at" t.datetime "remember_created_at" - t.integer "sign_in_count", default: 0, null: false + t.integer "sign_in_count", default: 0, null: false t.datetime "current_sign_in_at" t.datetime "last_sign_in_at" t.string "current_sign_in_ip" t.string "last_sign_in_ip" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false t.string "first_name" t.string "last_name" t.string "confirmation_token" @@ -81,7 +93,7 @@ ActiveRecord::Schema.define(version: 20150806163142) do t.datetime "confirmation_sent_at" t.string "unconfirmed_email" t.string "nickname" - t.boolean "use_nickname", default: false, null: false + t.boolean "use_nickname", default: false, null: false t.boolean "email_on_debate_comment", default: false t.boolean "email_on_comment_reply", default: false end @@ -90,6 +102,36 @@ ActiveRecord::Schema.define(version: 20150806163142) do add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree + create_table "visits", id: :uuid, default: nil, force: :cascade do |t| + t.uuid "visitor_id" + t.string "ip" + t.text "user_agent" + t.text "referrer" + t.text "landing_page" + t.integer "user_id" + t.string "referring_domain" + t.string "search_keyword" + t.string "browser" + t.string "os" + t.string "device_type" + t.integer "screen_height" + t.integer "screen_width" + t.string "country" + t.string "region" + t.string "city" + t.string "postal_code" + t.decimal "latitude" + t.decimal "longitude" + t.string "utm_source" + t.string "utm_medium" + t.string "utm_term" + t.string "utm_content" + t.string "utm_campaign" + t.datetime "started_at" + end + + add_index "visits", ["user_id"], name: "index_visits_on_user_id", using: :btree + create_table "votes", force: :cascade do |t| t.integer "votable_id" t.string "votable_type" From 42d6e499157e628c846f1ebcc4a45e42dd9e6a39 Mon Sep 17 00:00:00 2001 From: Eloy Gomez Date: Sat, 8 Aug 2015 12:23:28 +0200 Subject: [PATCH 02/90] Save visitor ip --- config/initializers/ahoy.rb | 8 +++++++- db/migrate/20150808100936_add_ip_to_ahoy_event.rb | 5 +++++ db/schema.rb | 3 ++- 3 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 db/migrate/20150808100936_add_ip_to_ahoy_event.rb diff --git a/config/initializers/ahoy.rb b/config/initializers/ahoy.rb index 22d326e53..9a1215c68 100644 --- a/config/initializers/ahoy.rb +++ b/config/initializers/ahoy.rb @@ -1,3 +1,9 @@ class Ahoy::Store < Ahoy::Stores::ActiveRecordStore - # customize here + + # Track user IP + def track_event(name, properties, options) + super do |event| + event.ip = request.ip + end + end end diff --git a/db/migrate/20150808100936_add_ip_to_ahoy_event.rb b/db/migrate/20150808100936_add_ip_to_ahoy_event.rb new file mode 100644 index 000000000..2c6d9d9b2 --- /dev/null +++ b/db/migrate/20150808100936_add_ip_to_ahoy_event.rb @@ -0,0 +1,5 @@ +class AddIpToAhoyEvent < ActiveRecord::Migration + def change + add_column :ahoy_events, :ip, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index 22b60c506..7846bfd1e 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: 20150806163142) do +ActiveRecord::Schema.define(version: 20150808100936) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -22,6 +22,7 @@ ActiveRecord::Schema.define(version: 20150806163142) do t.string "name" t.jsonb "properties" t.datetime "time" + t.string "ip" end add_index "ahoy_events", ["time"], name: "index_ahoy_events_on_time", using: :btree From a83a787090e62574c8a1984223f2a5095b0aec44 Mon Sep 17 00:00:00 2001 From: Eloy Gomez Date: Sat, 8 Aug 2015 12:56:41 +0200 Subject: [PATCH 03/90] Add model visit --- app/models/visit.rb | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 app/models/visit.rb diff --git a/app/models/visit.rb b/app/models/visit.rb new file mode 100644 index 000000000..6bb47fed0 --- /dev/null +++ b/app/models/visit.rb @@ -0,0 +1,4 @@ +class Visit < ActiveRecord::Base + has_many :ahoy_events, class_name: "Ahoy::Event" + belongs_to :user +end From d16b2bef2800ee51d43a1422292c21e36ec34190 Mon Sep 17 00:00:00 2001 From: Eloy Gomez Date: Sat, 8 Aug 2015 12:58:02 +0200 Subject: [PATCH 04/90] Include devise helpers in controller specs --- spec/spec_helper.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 640f9e96c..a1915bc34 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,6 +1,8 @@ require 'factory_girl_rails' require 'database_cleaner' require "email_spec" +require 'devise' + Dir["./spec/support/**/*.rb"].sort.each { |f| require f} RSpec.configure do |config| @@ -8,6 +10,7 @@ RSpec.configure do |config| config.filter_run :focus config.run_all_when_everything_filtered = true + config.include Devise::TestHelpers, :type => :controller config.include FactoryGirl::Syntax::Methods config.include(EmailSpec::Helpers) config.include(EmailSpec::Matchers) From b23ca8deb1e37bebbd480672af20fbba8ca380bb Mon Sep 17 00:00:00 2001 From: Eloy Gomez Date: Sat, 8 Aug 2015 13:08:48 +0200 Subject: [PATCH 05/90] Create event on debate creation --- app/controllers/debates_controller.rb | 1 + spec/controllers/debates_controller_spec.rb | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 spec/controllers/debates_controller_spec.rb diff --git a/app/controllers/debates_controller.rb b/app/controllers/debates_controller.rb index a395f9c5e..b8ab7792c 100644 --- a/app/controllers/debates_controller.rb +++ b/app/controllers/debates_controller.rb @@ -30,6 +30,7 @@ class DebatesController < ApplicationController @debate = Debate.new(debate_params) @debate.author = current_user if verify_captcha?(@debate) and @debate.save + ahoy.track :debate_created, debate_id: @debate.id redirect_to @debate, notice: t('flash.actions.create.notice', resource_name: 'Debate') else render :new diff --git a/spec/controllers/debates_controller_spec.rb b/spec/controllers/debates_controller_spec.rb new file mode 100644 index 000000000..1a24c17d5 --- /dev/null +++ b/spec/controllers/debates_controller_spec.rb @@ -0,0 +1,18 @@ +require 'rails_helper' + +describe DebatesController do + + # create + #---------------------------------------------------------------------- + + describe 'POST create' do + let(:user) { create :user } + + it 'should create an ahoy event' do + sign_in user + post :create, debate: { title: 'foo', description: 'foo bar', terms_of_service: 1 } + expect(Ahoy::Event.where(name: :debate_created).count).to eq 1 + expect(Ahoy::Event.last.properties['debate_id']).to eq Debate.last.id + end + end +end From 02f19aa4b11bd8a03a6d49c4bd068931d38c0197 Mon Sep 17 00:00:00 2001 From: Eloy Gomez Date: Sat, 8 Aug 2015 13:09:22 +0200 Subject: [PATCH 06/90] Add visit_id to debate --- app/models/debate.rb | 6 ++++++ db/migrate/20150808102442_add_visit_id_to_debate.rb | 5 +++++ db/schema.rb | 3 ++- 3 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20150808102442_add_visit_id_to_debate.rb diff --git a/app/models/debate.rb b/app/models/debate.rb index 28181101a..208d10277 100644 --- a/app/models/debate.rb +++ b/app/models/debate.rb @@ -18,6 +18,12 @@ class Debate < ActiveRecord::Base before_validation :sanitize_description before_validation :sanitize_tag_list + # ahoy setup + #---------------------------------------------------------------------- + + visitable # Ahoy will automatically assign visit_id on create + + def likes get_likes.size end diff --git a/db/migrate/20150808102442_add_visit_id_to_debate.rb b/db/migrate/20150808102442_add_visit_id_to_debate.rb new file mode 100644 index 000000000..4d09f2cd0 --- /dev/null +++ b/db/migrate/20150808102442_add_visit_id_to_debate.rb @@ -0,0 +1,5 @@ +class AddVisitIdToDebate < ActiveRecord::Migration + def change + add_column :debates, :visit_id, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index 7846bfd1e..b60f74eae 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: 20150808100936) do +ActiveRecord::Schema.define(version: 20150808102442) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -52,6 +52,7 @@ ActiveRecord::Schema.define(version: 20150808100936) do t.integer "author_id" t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.string "visit_id" end create_table "taggings", force: :cascade do |t| From fe2080aff9a79bd7081ddb9051f19ceddd5ce03f Mon Sep 17 00:00:00 2001 From: Eloy Gomez Date: Sat, 8 Aug 2015 14:33:44 +0200 Subject: [PATCH 07/90] Display some stats --- app/controllers/api/api_controller.rb | 4 ++ app/controllers/api/stats_controller.rb | 10 +++++ app/controllers/stats_controller.rb | 5 +++ app/views/layouts/application.html.erb | 3 +- app/views/stats/show.html.erb | 9 ++++ config/routes.rb | 5 +++ spec/controllers/api/stats_controller_spec.rb | 44 +++++++++++++++++++ spec/factories.rb | 7 ++- 8 files changed, 85 insertions(+), 2 deletions(-) create mode 100644 app/controllers/api/api_controller.rb create mode 100644 app/controllers/api/stats_controller.rb create mode 100644 app/controllers/stats_controller.rb create mode 100644 app/views/stats/show.html.erb create mode 100644 spec/controllers/api/stats_controller_spec.rb diff --git a/app/controllers/api/api_controller.rb b/app/controllers/api/api_controller.rb new file mode 100644 index 000000000..0534444c0 --- /dev/null +++ b/app/controllers/api/api_controller.rb @@ -0,0 +1,4 @@ +class Api::ApiController < ApplicationController + before_action :authenticate_user! + protect_from_forgery with: :null_session +end diff --git a/app/controllers/api/stats_controller.rb b/app/controllers/api/stats_controller.rb new file mode 100644 index 000000000..26fb373fb --- /dev/null +++ b/app/controllers/api/stats_controller.rb @@ -0,0 +1,10 @@ +class Api::StatsController < Api::ApiController + def show + event_type = params[:event] + unless event_type.present? + return render json: {}, status: :bad_request + end + + render json: Ahoy::Event.where(name: event_type).group_by_day(:time).count + end +end diff --git a/app/controllers/stats_controller.rb b/app/controllers/stats_controller.rb new file mode 100644 index 000000000..c33f6843e --- /dev/null +++ b/app/controllers/stats_controller.rb @@ -0,0 +1,5 @@ +class StatsController < ApplicationController + def show + @event_types = Ahoy::Event.select(:name).uniq.pluck(:name) + end +end diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 36a3c44cc..5c6324e31 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -8,6 +8,7 @@ <%= stylesheet_link_tag "application" %> <%= javascript_include_tag "vendor/modernizr" %> <%= javascript_include_tag "application", 'data-turbolinks-track' => true %> + <%= javascript_include_tag "//www.google.com/jsapi", "chartkick" %> <%= csrf_meta_tags %> @@ -27,4 +28,4 @@ <%= render 'layouts/footer' %> - \ No newline at end of file + diff --git a/app/views/stats/show.html.erb b/app/views/stats/show.html.erb new file mode 100644 index 000000000..0671b15ad --- /dev/null +++ b/app/views/stats/show.html.erb @@ -0,0 +1,9 @@ +

Stats

+ +

Visits

+<%= line_chart Visit.group_by_day(:started_at).count %> + +<% @event_types.each do |event_type| %> +

<%= event_type.titleize %>

+ <%= line_chart api_stats_path(event: event_type) %> +<% end %> diff --git a/config/routes.rb b/config/routes.rb index 73a77f60e..6cb8dcba6 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -21,6 +21,11 @@ Rails.application.routes.draw do end resource :account, controller: "account", only: [:show, :update] + resource :stats, only: [:show] + + namespace :api do + resource :stats, only: [:show] + end # Example of regular route: # get 'products/:id' => 'catalog#view' diff --git a/spec/controllers/api/stats_controller_spec.rb b/spec/controllers/api/stats_controller_spec.rb new file mode 100644 index 000000000..e2f4cd02b --- /dev/null +++ b/spec/controllers/api/stats_controller_spec.rb @@ -0,0 +1,44 @@ +require 'rails_helper' + +describe Api::StatsController do + + # GET index + #---------------------------------------------------------------------- + + describe 'GET index' do + let(:user) { create :user } + + context 'event not present' do + it 'should respond with bad_request' do + sign_in user + get :show + expect(response.status).to eq 400 + end + end + + context 'event present' do + it 'should return event counts' do + time_1 = DateTime.yesterday.to_datetime + time_2 = DateTime.now + + event_1 = create :ahoy_event, name: 'foo', time: time_1 + event_2 = create :ahoy_event, name: 'foo', time: time_1 + event_3 = create :ahoy_event, name: 'foo', time: time_2 + event_4 = create :ahoy_event, name: 'bar' + + sign_in user + get :show, event: 'foo' + + expect(response).to be_ok + + data = JSON.parse(response.body) + dates = data.keys + + expect(DateTime.parse dates.first).to eq time_1.utc.beginning_of_day + expect(DateTime.parse dates.last).to eq time_2.utc.beginning_of_day + expect(data[dates.first]).to eq 2 + expect(data[dates.last]).to eq 1 + end + end + end +end diff --git a/spec/factories.rb b/spec/factories.rb index 8e1a2ea6c..18b65b7f3 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -31,4 +31,9 @@ FactoryGirl.define do debate end -end \ No newline at end of file + factory :ahoy_event, :class => Ahoy::Event do + id { SecureRandom.uuid } + time DateTime.now + sequence(:name) {|n| "Event #{n} type"} + end +end From 7f8b3abace1bad7ff2fa2e2c50eb28066da19414 Mon Sep 17 00:00:00 2001 From: Eloy Gomez Date: Sat, 8 Aug 2015 15:04:47 +0200 Subject: [PATCH 08/90] Use Postgres 9.4 with travis --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index c1d4bc930..cb294ec9c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,6 @@ language: ruby +addons: + postgresql: "9.4" rvm: - "2.2.2" cache: bundler From cfd923138c4cdaa648a46d5edbe4bbaef0ddba91 Mon Sep 17 00:00:00 2001 From: Eloy Gomez Date: Mon, 10 Aug 2015 15:39:14 +0200 Subject: [PATCH 09/90] Add minimum PostgreSQL version. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 66c1853dc..0af5e03f7 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Las herramientas utilizadas para el frontend no están cerradas aún. Los estilo ## Configuración para desarrollo y tests -Prerequisitos: tener instalado git, Ruby 2.2.2, la gema `bundler`, y una librería moderna de PostgreSQL. +Prerequisitos: tener instalado git, Ruby 2.2.2, la gema `bundler`, y PostgreSQL 9.4 o superior. ``` cd participacion From 3551b55bdddb9ab6b966d63e25fe96918a3d2811 Mon Sep 17 00:00:00 2001 From: kikito Date: Tue, 11 Aug 2015 18:57:16 +0200 Subject: [PATCH 10/90] add migration for User organization fields --- ...0150810155304_add_organization_to_users.rb | 7 +++++++ db/schema.rb | 21 +++++++++++-------- 2 files changed, 19 insertions(+), 9 deletions(-) create mode 100644 db/migrate/20150810155304_add_organization_to_users.rb diff --git a/db/migrate/20150810155304_add_organization_to_users.rb b/db/migrate/20150810155304_add_organization_to_users.rb new file mode 100644 index 000000000..b5c38400c --- /dev/null +++ b/db/migrate/20150810155304_add_organization_to_users.rb @@ -0,0 +1,7 @@ +class AddOrganizationToUsers < ActiveRecord::Migration + def change + add_column :users, :organization_name, :string, limit: 80 + add_column :users, :organization_verified_at, :datetime + add_column :users, :phone_number, :string, limit: 30 + end +end diff --git a/db/schema.rb b/db/schema.rb index 2d97d8a60..343294168 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: 20150807140346) do +ActiveRecord::Schema.define(version: 20150810155304) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -74,18 +74,18 @@ ActiveRecord::Schema.define(version: 20150807140346) do add_index "tags", ["name"], name: "index_tags_on_name", unique: true, using: :btree create_table "users", force: :cascade do |t| - t.string "email", default: "", null: false - t.string "encrypted_password", default: "", null: false + t.string "email", default: "", null: false + t.string "encrypted_password", default: "", null: false t.string "reset_password_token" t.datetime "reset_password_sent_at" t.datetime "remember_created_at" - t.integer "sign_in_count", default: 0, null: false + t.integer "sign_in_count", default: 0, null: false t.datetime "current_sign_in_at" t.datetime "last_sign_in_at" t.string "current_sign_in_ip" t.string "last_sign_in_ip" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false t.string "first_name" t.string "last_name" t.string "confirmation_token" @@ -93,9 +93,12 @@ ActiveRecord::Schema.define(version: 20150807140346) do t.datetime "confirmation_sent_at" t.string "unconfirmed_email" t.string "nickname" - t.boolean "use_nickname", default: false, null: false - t.boolean "email_on_debate_comment", default: false - t.boolean "email_on_comment_reply", default: false + t.boolean "use_nickname", default: false, null: false + t.boolean "email_on_debate_comment", default: false + t.boolean "email_on_comment_reply", default: false + t.string "organization_name", limit: 80 + t.datetime "organization_verified_at" + t.string "phone_number", limit: 30 end add_index "users", ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true, using: :btree From 83e3dd1c6fa738618294388cc6e8c9ec48c3541c Mon Sep 17 00:00:00 2001 From: kikito Date: Tue, 11 Aug 2015 19:15:00 +0200 Subject: [PATCH 11/90] Implements User#organization? and User#verified_organization? --- app/models/user.rb | 8 ++++++++ spec/models/user_spec.rb | 20 ++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/app/models/user.rb b/app/models/user.rb index 4eb832546..c6e477815 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -27,4 +27,12 @@ class User < ActiveRecord::Base def moderator? @is_moderator ||= Moderator.where(user_id: id).exists? end + + def organization? + organization_name.present? + end + + def verified_organization? + organization_verified_at.present? + end end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 4a8fe8fd7..b57595660 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -111,4 +111,24 @@ describe User do end end + describe "organization?" do + it "is false when organization_name is blank" do + expect(subject.organization?).to be false + end + it "is true when organization_name exists" do + subject.organization_name = "Anonymous" + expect(subject.organization?).to be true + end + end + + describe "verified_organization?" do + it "is false when organization_verified_at? is blank" do + expect(subject.verified_organization?).to be false + end + it "is true when organization_verified_at? exists" do + subject.organization_verified_at = Time.now + expect(subject.verified_organization?).to be true + end + end + end From 9ee1e5dbc54fe3e56073104da529ae6b78472297 Mon Sep 17 00:00:00 2001 From: kikito Date: Tue, 11 Aug 2015 19:34:18 +0200 Subject: [PATCH 12/90] Adds Abilities for Organizations --- app/models/ability.rb | 14 +++++++++++--- spec/models/ability_spec.rb | 25 +++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/app/models/ability.rb b/app/models/ability.rb index c1fc24ae3..3314bc321 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -8,14 +8,22 @@ class Ability if user # logged-in users can [:read, :update], User, id: user.id - can [:read, :create, :vote], Debate + can :read, Debate can :update, Debate do |debate| debate.editable_by?(user) end - can [:create, :vote], Comment + unless user.organization? + can :vote, Debate + can :vote, Comment + end - if user.moderator? or user.administrator? + if !user.organization? || user.verified_organization? + can :create, Comment + can :create, Debate + end + + if user.moderator? || user.administrator? elsif user.administrator? diff --git a/spec/models/ability_spec.rb b/spec/models/ability_spec.rb index 3954ec600..352f8085c 100644 --- a/spec/models/ability_spec.rb +++ b/spec/models/ability_spec.rb @@ -45,6 +45,31 @@ describe Ability do end end + describe "Organization" do + let(:user) { create(:user, organization_name: "Organization") } + + it { should be_able_to(:show, user) } + it { should be_able_to(:edit, user) } + + it { should be_able_to(:index, Debate) } + it { should be_able_to(:show, debate) } + + it { should_not be_able_to(:vote, debate) } + it { should_not be_able_to(:vote, Comment) } + + describe "Not verified" do + it { should_not be_able_to(:create, Comment) } + it { should_not be_able_to(:create, Debate) } + end + + describe "Verified" do + before(:each) { user.organization_verified_at = Time.now } + + it { should be_able_to(:create, Comment) } + it { should be_able_to(:create, Debate) } + end + end + describe "Moderator" do let(:user) { create(:user) } before { create(:moderator, user: user) } From 523e69fb02b4ee614478cdeeda945a248226df63 Mon Sep 17 00:00:00 2001 From: kikito Date: Tue, 11 Aug 2015 19:48:11 +0200 Subject: [PATCH 13/90] Adds validations for organizations --- app/models/user.rb | 20 ++++++++++++++++---- spec/models/user_spec.rb | 12 ++++++++++++ 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/app/models/user.rb b/app/models/user.rb index c6e477815..6ced65533 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -4,12 +4,14 @@ class User < ActiveRecord::Base acts_as_voter - validates :first_name, presence: true, unless: :use_nickname? - validates :last_name, presence: true, unless: :use_nickname? - validates :nickname, presence: true, if: :use_nickname? + validates :first_name, presence: true, if: :use_first_name? + validates :last_name, presence: true, if: :use_last_name? + validates :nickname, presence: true, if: :use_nickname? def name - use_nickname? ? nickname : "#{first_name} #{last_name}" + return nickname if use_nickname? + return organization_name if organization? + "#{first_name} #{last_name}" end def votes_on_debates(debates_ids = []) @@ -35,4 +37,14 @@ class User < ActiveRecord::Base def verified_organization? organization_verified_at.present? end + + private + def use_first_name? + !use_nickname? && !organization? + end + + def use_last_name? + use_first_name? + end + end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index b57595660..58c2c3cba 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -119,6 +119,18 @@ describe User do subject.organization_name = "Anonymous" expect(subject.organization?).to be true end + + it "deactivates the validation of first_name and last_name " do + subject.first_name = nil + subject.last_name = nil + subject.organization_name = "The A Team" + expect(subject).to be_valid + end + + it "calculates the name using the organization name" do + subject.organization_name = "The A Team" + expect(subject.name).to eq("The A Team") + end end describe "verified_organization?" do From 5cef152c37fa6991e513fafbdb0be7f736045b94 Mon Sep 17 00:00:00 2001 From: kikito Date: Wed, 12 Aug 2015 13:47:47 +0200 Subject: [PATCH 14/90] Adds organization rejection & tests --- app/models/user.rb | 8 ++++- ...2_add_organization_rejected_at_to_users.rb | 5 ++++ db/schema.rb | 12 +++++++- spec/models/user_spec.rb | 30 +++++++++++++++++++ 4 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 db/migrate/20150812104712_add_organization_rejected_at_to_users.rb diff --git a/app/models/user.rb b/app/models/user.rb index 6ced65533..0bee83446 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -35,7 +35,13 @@ class User < ActiveRecord::Base end def verified_organization? - organization_verified_at.present? + organization_verified_at.present? && + (organization_rejected_at.blank? || organization_rejected_at < organization_verified_at) + end + + def rejected_organization? + organization_rejected_at.present? && + (organization_verified_at.blank? || organization_verified_at < organization_rejected_at) end private diff --git a/db/migrate/20150812104712_add_organization_rejected_at_to_users.rb b/db/migrate/20150812104712_add_organization_rejected_at_to_users.rb new file mode 100644 index 000000000..a097c7b34 --- /dev/null +++ b/db/migrate/20150812104712_add_organization_rejected_at_to_users.rb @@ -0,0 +1,5 @@ +class AddOrganizationRejectedAtToUsers < ActiveRecord::Migration + def change + add_column :users, :organization_rejected_at, :datetime + end +end diff --git a/db/schema.rb b/db/schema.rb index 343294168..79860f41f 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: 20150810155304) do +ActiveRecord::Schema.define(version: 20150812104712) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -53,6 +53,15 @@ ActiveRecord::Schema.define(version: 20150810155304) do add_index "moderators", ["user_id"], name: "index_moderators_on_user_id", using: :btree + create_table "simple_captcha_data", force: :cascade do |t| + t.string "key", limit: 40 + t.string "value", limit: 6 + t.datetime "created_at" + t.datetime "updated_at" + end + + add_index "simple_captcha_data", ["key"], name: "idx_key", using: :btree + create_table "taggings", force: :cascade do |t| t.integer "tag_id" t.integer "taggable_id" @@ -99,6 +108,7 @@ ActiveRecord::Schema.define(version: 20150810155304) do t.string "organization_name", limit: 80 t.datetime "organization_verified_at" t.string "phone_number", limit: 30 + t.datetime "organization_rejected_at" end add_index "users", ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true, using: :btree diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 58c2c3cba..c258e2bd1 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -141,6 +141,36 @@ describe User do subject.organization_verified_at = Time.now expect(subject.verified_organization?).to be true end + it "is false when the organization was verified and then rejected" do + subject.organization_verified_at = Time.now + subject.organization_rejected_at = Time.now + 1 + expect(subject.verified_organization?).to be false + end + it "is true when the organization was rejected and then verified" do + subject.organization_rejected_at = Time.now + subject.organization_verified_at = Time.now + 1 + expect(subject.verified_organization?).to be true + end + end + + describe "rejected_organization?" do + it "is false when organization_rejected_at? is blank" do + expect(subject.rejected_organization?).to be false + end + it "is true when organization_rejected_at? exists" do + subject.organization_rejected_at = Time.now + expect(subject.rejected_organization?).to be true + end + it "is true when the organization was verified and then rejected" do + subject.organization_verified_at = Time.now + subject.organization_rejected_at = Time.now + 1 + expect(subject.rejected_organization?).to be true + end + it "is false when the organization was rejected and then verified" do + subject.organization_rejected_at = Time.now + subject.organization_verified_at = Time.now + 1 + expect(subject.rejected_organization?).to be false + end end end From 450b0098c6efc9f8b311677ae6f5153c18f2a48b Mon Sep 17 00:00:00 2001 From: kikito Date: Wed, 12 Aug 2015 17:48:25 +0200 Subject: [PATCH 15/90] Adds abilities for verifying/rejecting organizations --- app/models/ability.rb | 7 +++++++ spec/models/ability_spec.rb | 14 ++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/app/models/ability.rb b/app/models/ability.rb index 3314bc321..d1e47095f 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -24,6 +24,13 @@ class Ability end if user.moderator? || user.administrator? + can :verify_organization, User do |u| + !u.verified_organization? + end + + can :reject_organization, User do |u| + !u.rejected_organization? + end elsif user.administrator? diff --git a/spec/models/ability_spec.rb b/spec/models/ability_spec.rb index 352f8085c..896794310 100644 --- a/spec/models/ability_spec.rb +++ b/spec/models/ability_spec.rb @@ -78,6 +78,20 @@ describe Ability do it { should be_able_to(:show, debate) } it { should be_able_to(:vote, debate) } + describe "organizations" do + let(:pending_organization) { create(:user, organization_name: 'org') } + let(:rejected_organization) { create(:user, organization_name: 'org', organization_rejected_at: Time.now)} + let(:verified_organization) { create(:user, organization_name: 'org', organization_verified_at: Time.now)} + + it { should be_able_to( :verify_organization, pending_organization) } + it { should be_able_to( :reject_organization, pending_organization) } + + it { should_not be_able_to(:verify_organization, verified_organization) } + it { should be_able_to( :reject_organization, verified_organization) } + + it { should_not be_able_to(:reject_organization, rejected_organization) } + it { should be_able_to( :verify_organization, rejected_organization) } + end end describe "Administrator" do From 674e2c5b8cf52bebbfd4115f794db96982cfd86c Mon Sep 17 00:00:00 2001 From: kikito Date: Wed, 12 Aug 2015 17:49:01 +0200 Subject: [PATCH 16/90] Adds User.organizations scope --- app/models/user.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/models/user.rb b/app/models/user.rb index 0bee83446..04acd124c 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -8,6 +8,8 @@ class User < ActiveRecord::Base validates :last_name, presence: true, if: :use_last_name? validates :nickname, presence: true, if: :use_nickname? + scope :organizations, -> { where("users.organization_name IS NOT NULL AND users.organization_name <> ''") } + def name return nickname if use_nickname? return organization_name if organization? From cb301c44266cfdde3caf8e5241478a10aff99bd3 Mon Sep 17 00:00:00 2001 From: kikito Date: Wed, 12 Aug 2015 18:24:57 +0200 Subject: [PATCH 17/90] adds organization factory and refactors ability_spec --- spec/factories.rb | 9 +++++++++ spec/models/ability_spec.rb | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/spec/factories.rb b/spec/factories.rb index fc76685d0..582c3e0a9 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -8,6 +8,13 @@ FactoryGirl.define do confirmed_at { Time.now } end + factory :organization, class: User do + organization_name 'org' + sequence(:email) { |n| "org#{n}@madrid.es" } + password 'pleaseverifyme' + confirmed_at { Time.now } + end + factory :debate do sequence(:title) {|n| "Debate #{n} title"} description 'Debate description' @@ -39,4 +46,6 @@ FactoryGirl.define do user end + + end diff --git a/spec/models/ability_spec.rb b/spec/models/ability_spec.rb index 896794310..952f1cf1f 100644 --- a/spec/models/ability_spec.rb +++ b/spec/models/ability_spec.rb @@ -46,7 +46,7 @@ describe Ability do end describe "Organization" do - let(:user) { create(:user, organization_name: "Organization") } + let(:user) { create(:organization) } it { should be_able_to(:show, user) } it { should be_able_to(:edit, user) } From a59ef10e446b2a247ad8aa50e0c86e756ec5ad92 Mon Sep 17 00:00:00 2001 From: kikito Date: Wed, 12 Aug 2015 18:26:12 +0200 Subject: [PATCH 18/90] adds organization controller, with views and tests --- .../moderation/organizations_controller.rb | 25 ++++++++ .../moderation/organizations/index.html.erb | 31 ++++++++++ config/locales/moderation.en.yml | 9 ++- config/locales/moderation.es.yml | 10 +++- config/routes.rb | 6 ++ .../features/moderation/organizations_spec.rb | 57 +++++++++++++++++++ 6 files changed, 136 insertions(+), 2 deletions(-) create mode 100644 app/controllers/moderation/organizations_controller.rb create mode 100644 app/views/moderation/organizations/index.html.erb create mode 100644 spec/features/moderation/organizations_spec.rb diff --git a/app/controllers/moderation/organizations_controller.rb b/app/controllers/moderation/organizations_controller.rb new file mode 100644 index 000000000..4788d3aa2 --- /dev/null +++ b/app/controllers/moderation/organizations_controller.rb @@ -0,0 +1,25 @@ +class Moderation::OrganizationsController < Moderation::BaseController + + before_filter :load_organizations, only: :index + load_and_authorize_resource class: 'User' + + def index + end + + def verify_organization + @organization.update(organization_verified_at: Time.now) + redirect_to action: :index + end + + def reject_organization + @organization.update(organization_rejected_at: Time.now) + redirect_to action: :index + end + + private + + def load_organizations + @organizations = User.organizations.order(:organization_name, :email) + end + +end diff --git a/app/views/moderation/organizations/index.html.erb b/app/views/moderation/organizations/index.html.erb new file mode 100644 index 000000000..3f1d833b6 --- /dev/null +++ b/app/views/moderation/organizations/index.html.erb @@ -0,0 +1,31 @@ +

<%= t('moderation.organizations.index.title') %>

+ + +<% @organizations.each do |organization| %> + + + + + <% if organization.verified_organization? %> + + <% end %> + <% if can? :verify_organization, organization %> + + <% end %> + <% if organization.rejected_organization? %> + + <% end %> + <% if can? :reject_organization, organization %> + + <% end %> + +<% end %> +
<%= organization.organization_name %><%= organization.email %><%= organization.phone_number %><%= t('moderation.organizations.index.verified') %><%= link_to t('moderation.organizations.index.verify'), + verify_organization_moderation_organization_path(organization), + method: :put + %> + <%= t('moderation.organizations.index.rejected') %><%= link_to t('moderation.organizations.index.reject'), + reject_organization_moderation_organization_path(organization), + method: :put + %> +
diff --git a/config/locales/moderation.en.yml b/config/locales/moderation.en.yml index f6cf170e6..c3a765104 100644 --- a/config/locales/moderation.en.yml +++ b/config/locales/moderation.en.yml @@ -2,4 +2,11 @@ en: moderation: dashboard: index: - title: Moderation \ No newline at end of file + title: Moderation + organizations: + index: + title: Organizations + verify: Verify + reject: Reject + verified: Verified + rejected: Rejected diff --git a/config/locales/moderation.es.yml b/config/locales/moderation.es.yml index d54710977..958bd7c75 100644 --- a/config/locales/moderation.es.yml +++ b/config/locales/moderation.es.yml @@ -2,4 +2,12 @@ es: moderation: dashboard: index: - title: Moderación \ No newline at end of file + title: Moderación + organizations: + index: + title: Organizaciones + verify: Verificar + reject: Rechazar + verified: Verificado + rejected: Rechazado + diff --git a/config/routes.rb b/config/routes.rb index 74179ba2a..87eceda55 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -27,6 +27,12 @@ Rails.application.routes.draw do namespace :moderation do root to: "dashboard#index" + resources :organizations, only: :index do + member do + put :verify_organization + put :reject_organization + end + end end # Example of regular route: diff --git a/spec/features/moderation/organizations_spec.rb b/spec/features/moderation/organizations_spec.rb new file mode 100644 index 000000000..9c07a2b9c --- /dev/null +++ b/spec/features/moderation/organizations_spec.rb @@ -0,0 +1,57 @@ +require 'rails_helper' + +feature 'Moderations::Organizations' do + + + background do + moderator = create(:user) + create(:moderator, user: moderator) + + login_as(moderator) + end + + scenario "pending organizations have links to verify and reject" do + organization = create(:organization) + + visit moderation_organizations_path + expect(page).to have_selector(:link_or_button, 'Verify') + expect(page).to have_selector(:link_or_button, 'Reject') + + click_on 'Verify' + expect(current_path).to eq(moderation_organizations_path) + expect(page).to have_content ('Verified') + + expect(organization.reload.verified_organization?).to eq(true) + end + + scenario "verified organizations have link to reject" do + organization = create(:organization, organization_verified_at: Time.now) + + visit moderation_organizations_path + expect(page).to have_content ('Verified') + expect(page).to_not have_selector(:link_or_button, 'Verify') + expect(page).to have_selector(:link_or_button, 'Reject') + + click_on 'Reject' + expect(current_path).to eq(moderation_organizations_path) + expect(page).to have_content ('Rejected') + + expect(organization.reload.rejected_organization?).to eq(true) + end + + scenario "rejected organizations have link to verify" do + organization = create(:organization, organization_rejected_at: Time.now) + + visit moderation_organizations_path + expect(page).to have_content ('Rejected') + expect(page).to have_selector(:link_or_button, 'Verify') + expect(page).to_not have_selector(:link_or_button, 'Reject') + + click_on 'Verify' + expect(current_path).to eq(moderation_organizations_path) + expect(page).to have_content ('Verified') + + expect(organization.reload.verified_organization?).to eq(true) + end + +end From 91804b5f7c4e191f84847f330740b4748370fb0a Mon Sep 17 00:00:00 2001 From: kikito Date: Thu, 13 Aug 2015 18:18:49 +0200 Subject: [PATCH 19/90] replaces organization migrations with a new table --- ...0150810155304_add_organization_to_users.rb | 7 ---- ...2_add_organization_rejected_at_to_users.rb | 5 --- .../20150813142213_create_organizations.rb | 10 ++++++ ...0150813161654_add_phone_number_to_users.rb | 5 +++ db/schema.rb | 36 +++++++++++-------- 5 files changed, 37 insertions(+), 26 deletions(-) delete mode 100644 db/migrate/20150810155304_add_organization_to_users.rb delete mode 100644 db/migrate/20150812104712_add_organization_rejected_at_to_users.rb create mode 100644 db/migrate/20150813142213_create_organizations.rb create mode 100644 db/migrate/20150813161654_add_phone_number_to_users.rb diff --git a/db/migrate/20150810155304_add_organization_to_users.rb b/db/migrate/20150810155304_add_organization_to_users.rb deleted file mode 100644 index b5c38400c..000000000 --- a/db/migrate/20150810155304_add_organization_to_users.rb +++ /dev/null @@ -1,7 +0,0 @@ -class AddOrganizationToUsers < ActiveRecord::Migration - def change - add_column :users, :organization_name, :string, limit: 80 - add_column :users, :organization_verified_at, :datetime - add_column :users, :phone_number, :string, limit: 30 - end -end diff --git a/db/migrate/20150812104712_add_organization_rejected_at_to_users.rb b/db/migrate/20150812104712_add_organization_rejected_at_to_users.rb deleted file mode 100644 index a097c7b34..000000000 --- a/db/migrate/20150812104712_add_organization_rejected_at_to_users.rb +++ /dev/null @@ -1,5 +0,0 @@ -class AddOrganizationRejectedAtToUsers < ActiveRecord::Migration - def change - add_column :users, :organization_rejected_at, :datetime - end -end diff --git a/db/migrate/20150813142213_create_organizations.rb b/db/migrate/20150813142213_create_organizations.rb new file mode 100644 index 000000000..a0974df7f --- /dev/null +++ b/db/migrate/20150813142213_create_organizations.rb @@ -0,0 +1,10 @@ +class CreateOrganizations < ActiveRecord::Migration + def change + create_table :organizations do |t| + t.belongs_to :user, index: true, foreign_key: true + t.string :name, limit: 80 + t.datetime :verified_at + t.datetime :rejected_at + end + end +end diff --git a/db/migrate/20150813161654_add_phone_number_to_users.rb b/db/migrate/20150813161654_add_phone_number_to_users.rb new file mode 100644 index 000000000..070b99586 --- /dev/null +++ b/db/migrate/20150813161654_add_phone_number_to_users.rb @@ -0,0 +1,5 @@ +class AddPhoneNumberToUsers < ActiveRecord::Migration + def change + add_column :users, :phone_number, :string, limit: 30 + end +end diff --git a/db/schema.rb b/db/schema.rb index 79860f41f..2c4e7e639 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: 20150812104712) do +ActiveRecord::Schema.define(version: 20150813161654) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -28,12 +28,13 @@ ActiveRecord::Schema.define(version: 20150812104712) do t.string "title" t.text "body" t.string "subject" - t.integer "user_id", null: false + t.integer "user_id", null: false t.integer "parent_id" t.integer "lft" t.integer "rgt" t.datetime "created_at" t.datetime "updated_at" + t.integer "children_count", default: 0 end add_index "comments", ["commentable_id", "commentable_type"], name: "index_comments_on_commentable_id_and_commentable_type", using: :btree @@ -53,6 +54,15 @@ ActiveRecord::Schema.define(version: 20150812104712) do add_index "moderators", ["user_id"], name: "index_moderators_on_user_id", using: :btree + create_table "organizations", force: :cascade do |t| + t.integer "user_id" + t.string "name", limit: 80 + t.datetime "verified_at" + t.datetime "rejected_at" + end + + add_index "organizations", ["user_id"], name: "index_organizations_on_user_id", using: :btree + create_table "simple_captcha_data", force: :cascade do |t| t.string "key", limit: 40 t.string "value", limit: 6 @@ -83,18 +93,18 @@ ActiveRecord::Schema.define(version: 20150812104712) do add_index "tags", ["name"], name: "index_tags_on_name", unique: true, using: :btree create_table "users", force: :cascade do |t| - t.string "email", default: "", null: false - t.string "encrypted_password", default: "", null: false + t.string "email", default: "", null: false + t.string "encrypted_password", default: "", null: false t.string "reset_password_token" t.datetime "reset_password_sent_at" t.datetime "remember_created_at" - t.integer "sign_in_count", default: 0, null: false + t.integer "sign_in_count", default: 0, null: false t.datetime "current_sign_in_at" t.datetime "last_sign_in_at" t.string "current_sign_in_ip" t.string "last_sign_in_ip" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false t.string "first_name" t.string "last_name" t.string "confirmation_token" @@ -102,13 +112,10 @@ ActiveRecord::Schema.define(version: 20150812104712) do t.datetime "confirmation_sent_at" t.string "unconfirmed_email" t.string "nickname" - t.boolean "use_nickname", default: false, null: false - t.boolean "email_on_debate_comment", default: false - t.boolean "email_on_comment_reply", default: false - t.string "organization_name", limit: 80 - t.datetime "organization_verified_at" - t.string "phone_number", limit: 30 - t.datetime "organization_rejected_at" + t.boolean "use_nickname", default: false, null: false + t.boolean "email_on_debate_comment", default: false + t.boolean "email_on_comment_reply", default: false + t.string "phone_number", limit: 30 end add_index "users", ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true, using: :btree @@ -132,4 +139,5 @@ ActiveRecord::Schema.define(version: 20150812104712) do add_foreign_key "administrators", "users" add_foreign_key "moderators", "users" + add_foreign_key "organizations", "users" end From 19db7f9ddaaac066a524e61114825375c893f285 Mon Sep 17 00:00:00 2001 From: kikito Date: Thu, 13 Aug 2015 20:02:29 +0200 Subject: [PATCH 20/90] Updates factories to use the new organization table structure --- spec/factories.rb | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/spec/factories.rb b/spec/factories.rb index 582c3e0a9..6e7c25413 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -8,13 +8,6 @@ FactoryGirl.define do confirmed_at { Time.now } end - factory :organization, class: User do - organization_name 'org' - sequence(:email) { |n| "org#{n}@madrid.es" } - password 'pleaseverifyme' - confirmed_at { Time.now } - end - factory :debate do sequence(:title) {|n| "Debate #{n} title"} description 'Debate description' @@ -46,6 +39,17 @@ FactoryGirl.define do user end + factory :organization do + user + sequence(:name) { |n| "org#{n}" } + end + factory :verified_organization, parent: :organization do + verified_at { Time.now} + end + + factory :rejected_organization, parent: :organization do + rejected_at { Time.now} + end end From b4b69d89e7445dcada46faee327949446692afb7 Mon Sep 17 00:00:00 2001 From: kikito Date: Thu, 13 Aug 2015 20:03:38 +0200 Subject: [PATCH 21/90] Updates abilities for the new organisations Note that unverified organisations can now create debates and comments - that is a change over the initial request --- app/models/ability.rb | 17 ++++++----------- spec/models/ability_spec.rb | 38 +++++++++++++++---------------------- 2 files changed, 21 insertions(+), 34 deletions(-) diff --git a/app/models/ability.rb b/app/models/ability.rb index d1e47095f..0d1a518bf 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -13,24 +13,19 @@ class Ability debate.editable_by?(user) end + can :create, Comment + can :create, Debate + unless user.organization? can :vote, Debate can :vote, Comment end - if !user.organization? || user.verified_organization? - can :create, Comment - can :create, Debate - end - if user.moderator? || user.administrator? - can :verify_organization, User do |u| - !u.verified_organization? - end - can :reject_organization, User do |u| - !u.rejected_organization? - end + can :read, Organization + can(:verify, Organization){ |o| !o.verified? } + can(:reject, Organization){ |o| !o.rejected? } elsif user.administrator? diff --git a/spec/models/ability_spec.rb b/spec/models/ability_spec.rb index 952f1cf1f..0cc6edcc8 100644 --- a/spec/models/ability_spec.rb +++ b/spec/models/ability_spec.rb @@ -46,28 +46,18 @@ describe Ability do end describe "Organization" do - let(:user) { create(:organization) } + let(:user) { create(:user) } + before(:each) { create(:organization, user: user) } it { should be_able_to(:show, user) } it { should be_able_to(:edit, user) } it { should be_able_to(:index, Debate) } it { should be_able_to(:show, debate) } - it { should_not be_able_to(:vote, debate) } + + it { should be_able_to(:create, Comment) } it { should_not be_able_to(:vote, Comment) } - - describe "Not verified" do - it { should_not be_able_to(:create, Comment) } - it { should_not be_able_to(:create, Debate) } - end - - describe "Verified" do - before(:each) { user.organization_verified_at = Time.now } - - it { should be_able_to(:create, Comment) } - it { should be_able_to(:create, Debate) } - end end describe "Moderator" do @@ -78,19 +68,21 @@ describe Ability do it { should be_able_to(:show, debate) } it { should be_able_to(:vote, debate) } + it { should be_able_to(:read, Organization) } + describe "organizations" do - let(:pending_organization) { create(:user, organization_name: 'org') } - let(:rejected_organization) { create(:user, organization_name: 'org', organization_rejected_at: Time.now)} - let(:verified_organization) { create(:user, organization_name: 'org', organization_verified_at: Time.now)} + let(:pending_organization) { create(:organization) } + let(:rejected_organization) { create(:rejected_organization) } + let(:verified_organization) { create(:verified_organization) } - it { should be_able_to( :verify_organization, pending_organization) } - it { should be_able_to( :reject_organization, pending_organization) } + it { should be_able_to( :verify, pending_organization) } + it { should be_able_to( :reject, pending_organization) } - it { should_not be_able_to(:verify_organization, verified_organization) } - it { should be_able_to( :reject_organization, verified_organization) } + it { should_not be_able_to(:verify, verified_organization) } + it { should be_able_to( :reject, verified_organization) } - it { should_not be_able_to(:reject_organization, rejected_organization) } - it { should be_able_to( :verify_organization, rejected_organization) } + it { should_not be_able_to(:reject, rejected_organization) } + it { should be_able_to( :verify, rejected_organization) } end end From bdf462c16b35f6543682fbee564d66956e382e4e Mon Sep 17 00:00:00 2001 From: kikito Date: Thu, 13 Aug 2015 20:04:21 +0200 Subject: [PATCH 22/90] Adds new Organization model --- app/models/organization.rb | 26 ++++++++++++++++++ spec/models/organization_spec.rb | 46 ++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 app/models/organization.rb create mode 100644 spec/models/organization_spec.rb diff --git a/app/models/organization.rb b/app/models/organization.rb new file mode 100644 index 000000000..8bcb377b6 --- /dev/null +++ b/app/models/organization.rb @@ -0,0 +1,26 @@ +class Organization < ActiveRecord::Base + + belongs_to :user + + delegate :email, :phone_number, to: :user + + def verify + update(verified_at: Time.now) + end + + def reject + update(rejected_at: Time.now) + end + + def verified? + verified_at.present? && + (rejected_at.blank? || rejected_at < verified_at) + end + + def rejected? + rejected_at.present? && + (verified_at.blank? || verified_at < rejected_at) + end + + +end diff --git a/spec/models/organization_spec.rb b/spec/models/organization_spec.rb new file mode 100644 index 000000000..6fad93d5b --- /dev/null +++ b/spec/models/organization_spec.rb @@ -0,0 +1,46 @@ +require 'rails_helper' + +describe Organization do + + subject { create(:organization) } + + describe "verified?" do + it "is false when verified_at? is blank" do + expect(subject.verified?).to be false + end + it "is true when verified_at? exists" do + subject.verified_at = Time.now + expect(subject.verified?).to be true + end + it "is false when the organization was verified and then rejected" do + subject.verified_at = Time.now + subject.rejected_at = Time.now + 1 + expect(subject.verified?).to be false + end + it "is true when the organization was rejected and then verified" do + subject.rejected_at = Time.now + subject.verified_at = Time.now + 1 + expect(subject.verified?).to be true + end + end + + describe "rejected?" do + it "is false when rejected_at? is blank" do + expect(subject.rejected?).to be false + end + it "is true when rejected_at? exists" do + subject.rejected_at = Time.now + expect(subject.rejected?).to be true + end + it "is true when the organization was verified and then rejected" do + subject.verified_at = Time.now + subject.rejected_at = Time.now + 1 + expect(subject.rejected?).to be true + end + it "is false when the organization was rejected and then verified" do + subject.rejected_at = Time.now + subject.verified_at = Time.now + 1 + expect(subject.rejected?).to be false + end + end +end From 976c88177da4ef6fc25151c0608705e2e7aa4712 Mon Sep 17 00:00:00 2001 From: kikito Date: Thu, 13 Aug 2015 20:04:57 +0200 Subject: [PATCH 23/90] Updates User model, moving org stuff to Organisation model --- app/models/user.rb | 26 +++++++--------- spec/models/user_spec.rb | 66 +++++++++------------------------------- 2 files changed, 25 insertions(+), 67 deletions(-) diff --git a/app/models/user.rb b/app/models/user.rb index 04acd124c..9fb1f65c4 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -8,11 +8,17 @@ class User < ActiveRecord::Base validates :last_name, presence: true, if: :use_last_name? validates :nickname, presence: true, if: :use_nickname? - scope :organizations, -> { where("users.organization_name IS NOT NULL AND users.organization_name <> ''") } + has_one :administrator + has_one :moderator + has_one :organization + + scope :administrators, -> { joins(:administrators) } + scope :moderators, -> { joins(:moderator) } + scope :organizations, -> { joins(:organization) } def name return nickname if use_nickname? - return organization_name if organization? + return organization.name if organization? "#{first_name} #{last_name}" end @@ -25,25 +31,15 @@ class User < ActiveRecord::Base end def administrator? - @is_administrator ||= Administrator.where(user_id: id).exists? + administrator.present? end def moderator? - @is_moderator ||= Moderator.where(user_id: id).exists? + moderator.present? end def organization? - organization_name.present? - end - - def verified_organization? - organization_verified_at.present? && - (organization_rejected_at.blank? || organization_rejected_at < organization_verified_at) - end - - def rejected_organization? - organization_rejected_at.present? && - (organization_verified_at.blank? || organization_verified_at < organization_rejected_at) + organization.present? end private diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index c258e2bd1..b357d2818 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -112,64 +112,26 @@ describe User do end describe "organization?" do - it "is false when organization_name is blank" do + it "is false when the user is not an organization" do expect(subject.organization?).to be false end - it "is true when organization_name exists" do - subject.organization_name = "Anonymous" - expect(subject.organization?).to be true - end - it "deactivates the validation of first_name and last_name " do - subject.first_name = nil - subject.last_name = nil - subject.organization_name = "The A Team" - expect(subject).to be_valid - end + describe 'when it is an organization' do + before(:each) { create(:organization, user: subject) } - it "calculates the name using the organization name" do - subject.organization_name = "The A Team" - expect(subject.name).to eq("The A Team") - end - end + it "is true when the user is an organization" do + expect(subject.organization?).to be true + end - describe "verified_organization?" do - it "is false when organization_verified_at? is blank" do - expect(subject.verified_organization?).to be false - end - it "is true when organization_verified_at? exists" do - subject.organization_verified_at = Time.now - expect(subject.verified_organization?).to be true - end - it "is false when the organization was verified and then rejected" do - subject.organization_verified_at = Time.now - subject.organization_rejected_at = Time.now + 1 - expect(subject.verified_organization?).to be false - end - it "is true when the organization was rejected and then verified" do - subject.organization_rejected_at = Time.now - subject.organization_verified_at = Time.now + 1 - expect(subject.verified_organization?).to be true - end - end + it "deactivates the validation of first_name and last_name" do + subject.first_name = nil + subject.last_name = nil + expect(subject).to be_valid + end - describe "rejected_organization?" do - it "is false when organization_rejected_at? is blank" do - expect(subject.rejected_organization?).to be false - end - it "is true when organization_rejected_at? exists" do - subject.organization_rejected_at = Time.now - expect(subject.rejected_organization?).to be true - end - it "is true when the organization was verified and then rejected" do - subject.organization_verified_at = Time.now - subject.organization_rejected_at = Time.now + 1 - expect(subject.rejected_organization?).to be true - end - it "is false when the organization was rejected and then verified" do - subject.organization_rejected_at = Time.now - subject.organization_verified_at = Time.now + 1 - expect(subject.rejected_organization?).to be false + it "calculates the name using the organization name" do + expect(subject.name).to eq(subject.organization.name) + end end end From 4e5d53f2f17271d86ae6946cd0284683233868dc Mon Sep 17 00:00:00 2001 From: kikito Date: Thu, 13 Aug 2015 20:05:32 +0200 Subject: [PATCH 24/90] Updates the Moderation Organisation controller --- .../moderation/organizations_controller.rb | 18 ++++++------------ .../moderation/organizations/index.html.erb | 16 ++++++++-------- config/routes.rb | 4 ++-- spec/features/moderation/organizations_spec.rb | 10 +++++----- 4 files changed, 21 insertions(+), 27 deletions(-) diff --git a/app/controllers/moderation/organizations_controller.rb b/app/controllers/moderation/organizations_controller.rb index 4788d3aa2..1528d0ea2 100644 --- a/app/controllers/moderation/organizations_controller.rb +++ b/app/controllers/moderation/organizations_controller.rb @@ -1,25 +1,19 @@ class Moderation::OrganizationsController < Moderation::BaseController - before_filter :load_organizations, only: :index - load_and_authorize_resource class: 'User' + load_and_authorize_resource def index + @organizations = @organizations.includes(:user).order(:name, 'users.email') end - def verify_organization - @organization.update(organization_verified_at: Time.now) + def verify + @organization.verify redirect_to action: :index end - def reject_organization - @organization.update(organization_rejected_at: Time.now) + def reject + @organization.reject redirect_to action: :index end - private - - def load_organizations - @organizations = User.organizations.order(:organization_name, :email) - end - end diff --git a/app/views/moderation/organizations/index.html.erb b/app/views/moderation/organizations/index.html.erb index 3f1d833b6..06549a9b3 100644 --- a/app/views/moderation/organizations/index.html.erb +++ b/app/views/moderation/organizations/index.html.erb @@ -1,27 +1,27 @@

<%= t('moderation.organizations.index.title') %>

-<% @organizations.each do |organization| %> + <% @organizations.each do |organization| %> - + - <% if organization.verified_organization? %> + <% if organization.verified? %> <% end %> - <% if can? :verify_organization, organization %> + <% if can? :verify, organization %> <% end %> - <% if organization.rejected_organization? %> + <% if organization.rejected? %> <% end %> - <% if can? :reject_organization, organization %> + <% if can? :reject, organization %> diff --git a/config/routes.rb b/config/routes.rb index 87eceda55..fa1f6624b 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -29,8 +29,8 @@ Rails.application.routes.draw do root to: "dashboard#index" resources :organizations, only: :index do member do - put :verify_organization - put :reject_organization + put :verify + put :reject end end end diff --git a/spec/features/moderation/organizations_spec.rb b/spec/features/moderation/organizations_spec.rb index 9c07a2b9c..22cf8d2db 100644 --- a/spec/features/moderation/organizations_spec.rb +++ b/spec/features/moderation/organizations_spec.rb @@ -21,11 +21,11 @@ feature 'Moderations::Organizations' do expect(current_path).to eq(moderation_organizations_path) expect(page).to have_content ('Verified') - expect(organization.reload.verified_organization?).to eq(true) + expect(organization.reload.verified?).to eq(true) end scenario "verified organizations have link to reject" do - organization = create(:organization, organization_verified_at: Time.now) + organization = create(:verified_organization) visit moderation_organizations_path expect(page).to have_content ('Verified') @@ -36,11 +36,11 @@ feature 'Moderations::Organizations' do expect(current_path).to eq(moderation_organizations_path) expect(page).to have_content ('Rejected') - expect(organization.reload.rejected_organization?).to eq(true) + expect(organization.reload.rejected?).to eq(true) end scenario "rejected organizations have link to verify" do - organization = create(:organization, organization_rejected_at: Time.now) + organization = create(:rejected_organization) visit moderation_organizations_path expect(page).to have_content ('Rejected') @@ -51,7 +51,7 @@ feature 'Moderations::Organizations' do expect(current_path).to eq(moderation_organizations_path) expect(page).to have_content ('Verified') - expect(organization.reload.verified_organization?).to eq(true) + expect(organization.reload.verified?).to eq(true) end end From 60a1cc22676d3c4dd981f8ab876f5d219684a5d0 Mon Sep 17 00:00:00 2001 From: kikito Date: Fri, 14 Aug 2015 20:24:19 +0200 Subject: [PATCH 25/90] Adds User.is_organization and User.organization_name --- app/models/user.rb | 18 ++++++++++++++---- spec/models/user_spec.rb | 30 ++++++++++++++++++++++++------ 2 files changed, 38 insertions(+), 10 deletions(-) diff --git a/app/models/user.rb b/app/models/user.rb index 9fb1f65c4..3b14879ca 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -4,9 +4,10 @@ class User < ActiveRecord::Base acts_as_voter - validates :first_name, presence: true, if: :use_first_name? - validates :last_name, presence: true, if: :use_last_name? - validates :nickname, presence: true, if: :use_nickname? + validates :first_name, presence: true, if: :use_first_name? + validates :last_name, presence: true, if: :use_last_name? + validates :nickname, presence: true, if: :use_nickname? + validates :organization_name, presence: true, if: :is_organization has_one :administrator has_one :moderator @@ -16,6 +17,11 @@ class User < ActiveRecord::Base scope :moderators, -> { joins(:moderator) } scope :organizations, -> { joins(:organization) } + attr_accessor :organization_name + attr_accessor :is_organization + + after_save :create_associated_organization + def name return nickname if use_nickname? return organization.name if organization? @@ -44,11 +50,15 @@ class User < ActiveRecord::Base private def use_first_name? - !use_nickname? && !organization? + !is_organization && !use_nickname? end def use_last_name? use_first_name? end + def create_associated_organization + create_organization(name: organization_name) if is_organization + end + end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index b357d2818..ce9fb5989 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -123,16 +123,34 @@ describe User do expect(subject.organization?).to be true end - it "deactivates the validation of first_name and last_name" do - subject.first_name = nil - subject.last_name = nil - expect(subject).to be_valid - end - it "calculates the name using the organization name" do expect(subject.name).to eq(subject.organization.name) end end end + describe "is_organization" do + before(:each) { subject.is_organization = true } + + it "deactivates the validation of first_name and last_name, and activates the validation of organization_name" do + subject.first_name = nil + subject.last_name = nil + subject.organization_name = nil + expect(subject).to_not be_valid + + subject.organization_name = 'org' + expect(subject).to be_valid + end + + it "triggers the creation of an associated organization using organization_name" do + expect(subject.organization).to_not be + + subject.is_organization = true + subject.organization_name = 'org' + subject.save + + expect(subject.organization).to be + end + end + end From 9343952d524f1ccbc87656052cae10c5d0f6f3de Mon Sep 17 00:00:00 2001 From: kikito Date: Sun, 16 Aug 2015 23:35:56 +0200 Subject: [PATCH 26/90] Adds User.organization_attributes --- app/models/organization.rb | 3 ++- app/models/user.rb | 20 +++++++++----------- spec/models/user_spec.rb | 28 +++++++++++----------------- 3 files changed, 22 insertions(+), 29 deletions(-) diff --git a/app/models/organization.rb b/app/models/organization.rb index 8bcb377b6..29bf01c2c 100644 --- a/app/models/organization.rb +++ b/app/models/organization.rb @@ -2,6 +2,8 @@ class Organization < ActiveRecord::Base belongs_to :user + validates :name, presence: true + delegate :email, :phone_number, to: :user def verify @@ -22,5 +24,4 @@ class Organization < ActiveRecord::Base (verified_at.blank? || verified_at < rejected_at) end - end diff --git a/app/models/user.rb b/app/models/user.rb index 3b14879ca..ef1afc5ee 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -4,15 +4,18 @@ class User < ActiveRecord::Base acts_as_voter - validates :first_name, presence: true, if: :use_first_name? - validates :last_name, presence: true, if: :use_last_name? - validates :nickname, presence: true, if: :use_nickname? - validates :organization_name, presence: true, if: :is_organization - has_one :administrator has_one :moderator has_one :organization + validates :first_name, presence: true, if: :use_first_name? + validates :last_name, presence: true, if: :use_last_name? + validates :nickname, presence: true, if: :use_nickname? + + validates_associated :organization, message: false + + accepts_nested_attributes_for :organization + scope :administrators, -> { joins(:administrators) } scope :moderators, -> { joins(:moderator) } scope :organizations, -> { joins(:organization) } @@ -50,15 +53,10 @@ class User < ActiveRecord::Base private def use_first_name? - !is_organization && !use_nickname? + !organization? && !use_nickname? end def use_last_name? use_first_name? end - - def create_associated_organization - create_organization(name: organization_name) if is_organization - end - end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index ce9fb5989..7d79898c0 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -129,27 +129,21 @@ describe User do end end - describe "is_organization" do - before(:each) { subject.is_organization = true } + describe "organization_attributes" do + before(:each) { subject.organization_attributes = {name: 'org'} } - it "deactivates the validation of first_name and last_name, and activates the validation of organization_name" do - subject.first_name = nil - subject.last_name = nil - subject.organization_name = nil - expect(subject).to_not be_valid - - subject.organization_name = 'org' - expect(subject).to be_valid + it "triggers the creation of an associated organization" do + expect(subject.organization).to be + expect(subject.organization.name).to eq('org') end - it "triggers the creation of an associated organization using organization_name" do - expect(subject.organization).to_not be + it "deactivates the validation of first_name and last_name, and activates the validation of organization" do + subject.first_name = nil + subject.last_name = nil + expect(subject).to be_valid - subject.is_organization = true - subject.organization_name = 'org' - subject.save - - expect(subject.organization).to be + subject.organization.name= nil + expect(subject).to_not be_valid end end From 1f2f318e83b1dee529b2ab14331b0e7e7dfe3892 Mon Sep 17 00:00:00 2001 From: kikito Date: Sun, 16 Aug 2015 23:36:33 +0200 Subject: [PATCH 27/90] Turns on devise scoped views That way we can have users/registrations & organizations/registrations --- config/initializers/devise.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index 667c5f1b8..3be902f18 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -211,7 +211,7 @@ Devise.setup do |config| # Turn scoped views on. Before rendering "sessions/new", it will first check for # "users/sessions/new". It's turned off by default because it's slower if you # are using only default views. - # config.scoped_views = false + config.scoped_views = true # Configure the default scope given to Warden. By default it's the first # devise role declared in your routes (usually :user). From de73eac2d0e821158e7954676efdde95a0ecfb37 Mon Sep 17 00:00:00 2001 From: kikito Date: Sun, 16 Aug 2015 23:41:05 +0200 Subject: [PATCH 28/90] Moves the registrations controller to users/registrations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is necessary because rails gets confused when it has a a “root” registration_controller and another non-root one. The non-root one uses the views from the root. --- .../{ => users}/registrations_controller.rb | 9 ++-- app/views/devise/registrations/edit.html.erb | 45 ------------------ app/views/users/registrations/edit.html.erb | 45 ++++++++++++++++++ .../registrations/new.html.erb | 32 ++++++------- config/locales/devise_views.en.yml | 47 ++++++++++--------- config/locales/devise_views.es.yml | 47 ++++++++++--------- config/routes.rb | 2 +- 7 files changed, 114 insertions(+), 113 deletions(-) rename app/controllers/{ => users}/registrations_controller.rb (51%) delete mode 100644 app/views/devise/registrations/edit.html.erb create mode 100644 app/views/users/registrations/edit.html.erb rename app/views/{devise => users}/registrations/new.html.erb (68%) diff --git a/app/controllers/registrations_controller.rb b/app/controllers/users/registrations_controller.rb similarity index 51% rename from app/controllers/registrations_controller.rb rename to app/controllers/users/registrations_controller.rb index 4991cdf70..174b12aa2 100644 --- a/app/controllers/registrations_controller.rb +++ b/app/controllers/users/registrations_controller.rb @@ -1,4 +1,4 @@ -class RegistrationsController < Devise::RegistrationsController +class Users::RegistrationsController < Devise::RegistrationsController include RecaptchaHelper def create @@ -11,11 +11,10 @@ class RegistrationsController < Devise::RegistrationsController end end - private - def sign_up_params - params.require(:user).permit(:first_name, :last_name, :email, :password, :password_confirmation, :use_nickname, :nickname) - end + def sign_up_params + params.require(:user).permit(:first_name, :last_name, :email, :password, :password_confirmation, :use_nickname, :nickname) + end end diff --git a/app/views/devise/registrations/edit.html.erb b/app/views/devise/registrations/edit.html.erb deleted file mode 100644 index b670b6776..000000000 --- a/app/views/devise/registrations/edit.html.erb +++ /dev/null @@ -1,45 +0,0 @@ -
- -
\ No newline at end of file diff --git a/app/views/users/registrations/edit.html.erb b/app/views/users/registrations/edit.html.erb new file mode 100644 index 000000000..b17b8f2e1 --- /dev/null +++ b/app/views/users/registrations/edit.html.erb @@ -0,0 +1,45 @@ +
+ +
diff --git a/app/views/devise/registrations/new.html.erb b/app/views/users/registrations/new.html.erb similarity index 68% rename from app/views/devise/registrations/new.html.erb rename to app/views/users/registrations/new.html.erb index c7cd839a2..893054f8e 100644 --- a/app/views/devise/registrations/new.html.erb +++ b/app/views/users/registrations/new.html.erb @@ -2,60 +2,60 @@
-

<%= t("devise_views.registrations.new.title") %>

+

<%= t("devise_views.users.registrations.new.title") %>

<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %> <%= devise_error_messages! %>
- <%= f.label :first_name, t("devise_views.registrations.new.first_name_label") %> - <%= f.text_field :first_name, autofocus: true, placeholder: t("devise_views.registrations.new.first_name_label") %> + <%= f.label :first_name, t("devise_views.users.registrations.new.first_name_label") %> + <%= f.text_field :first_name, autofocus: true, placeholder: t("devise_views.users.registrations.new.first_name_label") %>
- <%= f.label :last_name, t("devise_views.registrations.new.last_name_label") %> - <%= f.text_field :last_name, placeholder: t("devise_views.registrations.new.last_name_label") %> + <%= f.label :last_name, t("devise_views.users.registrations.new.last_name_label") %> + <%= f.text_field :last_name, placeholder: t("devise_views.users.registrations.new.last_name_label") %>
<%= f.check_box :use_nickname %> - <%= t("devise_views.registrations.new.use_nickname_label") %> + <%= t("devise_views.users.registrations.new.use_nickname_label") %>
- <%= f.label :nickname, t("devise_views.registrations.new.nickname_label") %> - <%= f.text_field :nickname, placeholder: t("devise_views.registrations.new.nickname_label") %> + <%= f.label :nickname, t("devise_views.users.registrations.new.nickname_label") %> + <%= f.text_field :nickname, placeholder: t("devise_views.users.registrations.new.nickname_label") %>
- <%= f.label :email, t("devise_views.registrations.new.email_label") %> - <%= f.email_field :email, placeholder: t("devise_views.registrations.new.email_label") %> + <%= f.label :email, t("devise_views.users.registrations.new.email_label") %> + <%= f.email_field :email, placeholder: t("devise_views.users.registrations.new.email_label") %>
- <%= f.label :password, t("devise_views.registrations.new.password_label"), class: "inline-block" %> + <%= f.label :password, t("devise_views.users.registrations.new.password_label"), class: "inline-block" %> <% if @minimum_password_length %> - <%= t("devise_views.registrations.new.min_length", min: @minimum_password_length) %> + <%= t("devise_views.users.registrations.new.min_length", min: @minimum_password_length) %> <% end %> - <%= f.password_field :password, autocomplete: "off", placeholder: t("devise_views.registrations.new.password_label") %> + <%= f.password_field :password, autocomplete: "off", placeholder: t("devise_views.users.registrations.new.password_label") %>
- <%= f.label :password_confirmation, t("devise_views.registrations.new.password_confirmation_label") %> - <%= f.password_field :password_confirmation, autocomplete: "off", placeholder: t("devise_views.registrations.new.password_confirmation_label") %> + <%= f.label :password_confirmation, t("devise_views.users.registrations.new.password_confirmation_label") %> + <%= f.password_field :password_confirmation, autocomplete: "off", placeholder: t("devise_views.users.registrations.new.password_confirmation_label") %>
@@ -63,7 +63,7 @@
- <%= f.submit t("devise_views.registrations.new.submit"), class: "button radius expand" %> + <%= f.submit t("devise_views.users.registrations.new.submit"), class: "button radius expand" %>
<% end %> diff --git a/config/locales/devise_views.en.yml b/config/locales/devise_views.en.yml index ae778d00f..3560ecdc5 100644 --- a/config/locales/devise_views.en.yml +++ b/config/locales/devise_views.en.yml @@ -36,29 +36,30 @@ en: title: "Forgot your password?" email_label: "Email" send_submit: "Send me reset password instructions" - registrations: - edit: - edit: "Edit" - email_label: "Email" - waiting_for: "Currently waiting confirmation for:" - leave_blank: "Leave blank if you don't want to change it" - password_label: "New password" - password_confirmation_label: "Confirm new password" - current_password_label: "Current password" - need_current: "We need your current password to confirm your changes" - update_submit: "Update" - back_link: "Back" - new: - title: "Sign up" - first_name_label: "First name" - last_name_label: "Last name" - nickname_label: "Nickname" - use_nickname_label: "Use nickname" - email_label: "Email" - password_label: "Password" - min_length: "(%{min} characters minimum)" - password_confirmation_label: "Confirm password" - submit: "Sign up" + users: + registrations: + edit: + edit: "Edit" + email_label: "Email" + waiting_for: "Currently waiting confirmation for:" + leave_blank: "Leave blank if you don't want to change it" + password_label: "New password" + password_confirmation_label: "Confirm new password" + current_password_label: "Current password" + need_current: "We need your current password to confirm your changes" + update_submit: "Update" + back_link: "Back" + new: + title: "Sign up" + first_name_label: "First name" + last_name_label: "Last name" + nickname_label: "Nickname" + use_nickname_label: "Use nickname" + email_label: "Email" + password_label: "Password" + min_length: "(%{min} characters minimum)" + password_confirmation_label: "Confirm password" + submit: "Sign up" sessions: new: title: "Log in" diff --git a/config/locales/devise_views.es.yml b/config/locales/devise_views.es.yml index 647dd3237..50da3f345 100644 --- a/config/locales/devise_views.es.yml +++ b/config/locales/devise_views.es.yml @@ -36,29 +36,30 @@ es: title: "¿Has olvidado tu contraseña?" email_label: "Email" send_submit: "Recibir instrucciones para recuperar mi contraseña" - registrations: - edit: - edit: "Editar" - email_label: "Email" - waiting_for: "Esperando confirmación de:" - leave_blank: "Dejar en blanco si no deseas cambiarla" - password_label: "Contraseña nueva" - password_confirmation_label: "Confirmar contraseña nueva" - current_password_label: "Contraseña actual" - need_current: "Necesitamos tu contraseña actual para confirmar los cambios" - update_submit: "Actualizar" - back_link: "Atrás" - new: - title: "Registrarse" - first_name_label: "Nombre" - last_name_label: "Apellidos" - nickname_label: "Pseudónimo" - use_nickname_label: "Usar pseudónimo" - email_label: "Email" - password_label: "Contraseña" - min_length: "(mínimo %{min} caracteres)" - password_confirmation_label: "Confirmar contraseña" - submit: "Registrarse" + users: + registrations: + edit: + edit: "Editar" + email_label: "Email" + waiting_for: "Esperando confirmación de:" + leave_blank: "Dejar en blanco si no deseas cambiarla" + password_label: "Contraseña nueva" + password_confirmation_label: "Confirmar contraseña nueva" + current_password_label: "Contraseña actual" + need_current: "Necesitamos tu contraseña actual para confirmar los cambios" + update_submit: "Actualizar" + back_link: "Atrás" + new: + title: "Registrarse" + first_name_label: "Nombre" + last_name_label: "Apellidos" + nickname_label: "Pseudónimo" + use_nickname_label: "Usar pseudónimo" + email_label: "Email" + password_label: "Contraseña" + min_length: "(mínimo %{min} caracteres)" + password_confirmation_label: "Confirmar contraseña" + submit: "Registrarse" sessions: new: title: "Entrar" diff --git a/config/routes.rb b/config/routes.rb index fa1f6624b..286b8237a 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,5 +1,5 @@ Rails.application.routes.draw do - devise_for :users, controllers: { registrations: 'registrations' } + devise_for :users, controllers: { registrations: 'users/registrations' } # The priority is based upon order of creation: first created -> highest priority. # See how all your routes lay out with "rake routes". From 9af1f2f4a9b4acdb8bd17d72ef3385e6bc978859 Mon Sep 17 00:00:00 2001 From: kikito Date: Mon, 17 Aug 2015 01:00:46 +0200 Subject: [PATCH 29/90] Adds registration controller for organizations --- .../organizations/registrations_controller.rb | 29 +++++++++ .../organizations/registrations/new.html.erb | 64 +++++++++++++++++++ config/locales/activerecord.en.yml | 5 +- config/locales/activerecord.es.yml | 5 +- config/locales/devise_views.en.yml | 12 ++++ config/locales/devise_views.es.yml | 12 ++++ config/routes.rb | 5 ++ spec/features/organizations_spec.rb | 32 ++++++++++ 8 files changed, 162 insertions(+), 2 deletions(-) create mode 100644 app/controllers/organizations/registrations_controller.rb create mode 100644 app/views/organizations/registrations/new.html.erb create mode 100644 spec/features/organizations_spec.rb diff --git a/app/controllers/organizations/registrations_controller.rb b/app/controllers/organizations/registrations_controller.rb new file mode 100644 index 000000000..2751bc988 --- /dev/null +++ b/app/controllers/organizations/registrations_controller.rb @@ -0,0 +1,29 @@ +class Organizations::RegistrationsController < Devise::RegistrationsController + include RecaptchaHelper + + def new + super do |user| + user.build_organization + end + end + + def create + if verify_captcha?(resource) + super do |user| + # Removes unuseful "organization is invalid" error message + user.errors.messages.delete(:organization) + end + else + build_resource(sign_up_params) + flash.now[:alert] = t('recaptcha.errors.verification_failed') + respond_with resource + end + end + + private + + def sign_up_params + params.require(:user).permit(:email, :password, :phone_number, :password_confirmation, organization_attributes: [:name]) + end + +end diff --git a/app/views/organizations/registrations/new.html.erb b/app/views/organizations/registrations/new.html.erb new file mode 100644 index 000000000..f8ca107be --- /dev/null +++ b/app/views/organizations/registrations/new.html.erb @@ -0,0 +1,64 @@ +
+
+
+
+

<%= t("devise_views.organizations.registrations.new.title") %>

+ + <%= form_for(resource, as: :user, url: organization_registration_path) do |f| %> + + <%= devise_error_messages! %> + + <%= f.fields_for :organization do |fo| %> +
+
+ <%= fo.label :organization_name, t("devise_views.organizations.registrations.new.organization_name_label") %> + <%= fo.text_field :name, autofocus: true, placeholder: t("devise_views.organizations.registrations.new.organization_name_label") %> +
+
+ <% end %> + +
+
+ <%= f.label :email, t("devise_views.organizations.registrations.new.email_label") %> + <%= f.email_field :email, placeholder: t("devise_views.organizations.registrations.new.email_label") %> +
+
+ +
+
+ <%= f.label :phone_number, t("devise_views.organizations.registrations.new.phone_number_label") %> + <%= f.text_field :phone_number, placeholder: t("devise_views.organizations.registrations.new.phone_number_label") %> +
+
+ +
+
+ <%= f.label :password, t("devise_views.organizations.registrations.new.password_label"), class: "inline-block" %> + <% if @minimum_password_length %> + <%= t("devise_views.organizations.registrations.new.min_length", min: @minimum_password_length) %> + <% end %> + <%= f.password_field :password, autocomplete: "off", placeholder: t("devise_views.organizations.registrations.new.password_label") %> +
+
+ +
+
+ <%= f.label :password_confirmation, t("devise_views.organizations.registrations.new.password_confirmation_label") %> + <%= f.password_field :password_confirmation, autocomplete: "off", placeholder: t("devise_views.organizations.registrations.new.password_confirmation_label") %> +
+
+ + <%= render 'shared/captcha', resource: resource %> + +
+
+ <%= f.submit t("devise_views.organizations.registrations.new.submit"), class: "button radius expand" %> +
+
+ <% end %> + + <%= render "devise/shared/links" %> +
+
+
+
diff --git a/config/locales/activerecord.en.yml b/config/locales/activerecord.en.yml index 3edd65527..e62df2a65 100644 --- a/config/locales/activerecord.en.yml +++ b/config/locales/activerecord.en.yml @@ -5,6 +5,7 @@ en: debate: Debate user: User vote: Vote + organization: Organization attributes: comment: body: Comment @@ -19,4 +20,6 @@ en: first_name: "First name" last_name: "Last name" nickname: Nickname - password: Password \ No newline at end of file + password: Password + organization: + name: Organization name diff --git a/config/locales/activerecord.es.yml b/config/locales/activerecord.es.yml index 6bab3edb6..36af7e1b3 100644 --- a/config/locales/activerecord.es.yml +++ b/config/locales/activerecord.es.yml @@ -5,6 +5,7 @@ es: debate: Debate user: Usuario vote: Voto + organization: Organización attributes: comment: body: Comentario @@ -19,4 +20,6 @@ es: first_name: Nombre last_name: Apellidos nickname: Pseudónimo - password: Contraseña \ No newline at end of file + password: Contraseña + organization: + name: Nombre de organización diff --git a/config/locales/devise_views.en.yml b/config/locales/devise_views.en.yml index 3560ecdc5..a2f2fe1e1 100644 --- a/config/locales/devise_views.en.yml +++ b/config/locales/devise_views.en.yml @@ -60,6 +60,17 @@ en: min_length: "(%{min} characters minimum)" password_confirmation_label: "Confirm password" submit: "Sign up" + organizations: + registrations: + new: + title: "Sign up as organization" + organization_name_label: "Organization name" + email_label: "Email" + password_label: "Password" + phone_number_label: "Phone number" + min_length: "(%{min} characters minimum)" + password_confirmation_label: "Confirm password" + submit: "Sign up" sessions: new: title: "Log in" @@ -77,6 +88,7 @@ en: login: "Log in" signup: "Sign up" signin_with_provider: "Sign in with %{provider}" + organization_signup: "Sign up as an organization" new_password: "Forgot your password?" new_confirmation: "Didn't receive confirmation instructions?" new_unlock: "Didn't receive unlock instructions?" diff --git a/config/locales/devise_views.es.yml b/config/locales/devise_views.es.yml index 50da3f345..0f920ecf2 100644 --- a/config/locales/devise_views.es.yml +++ b/config/locales/devise_views.es.yml @@ -60,6 +60,17 @@ es: min_length: "(mínimo %{min} caracteres)" password_confirmation_label: "Confirmar contraseña" submit: "Registrarse" + organizations: + registrations: + new: + title: "Registrarse como organización" + organization_name_label: "Nombre de la organización" + email_label: "Email" + password_label: "Contraseña" + phone_number_label: "Teléfono" + min_length: "(mínimo %{min} caracteres)" + password_confirmation_label: "Confirmar contraseña" + submit: "Registrarse" sessions: new: title: "Entrar" @@ -76,6 +87,7 @@ es: links: login: "Entrar" signup: "Registrarse" + organization_signup: "Registro para organizaciones" signin_with_provider: "Entrar con %{provider}" new_password: "¿Olvidaste tu contraseña?" new_confirmation: "¿No has recibido instrucciones para confirmar tu cuenta?" diff --git a/config/routes.rb b/config/routes.rb index 286b8237a..ba2fc77c4 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,5 +1,10 @@ Rails.application.routes.draw do devise_for :users, controllers: { registrations: 'users/registrations' } + devise_for :organizations, class_name: 'User', + controllers: { + registrations: 'organizations/registrations', + sessions: 'devise/sessions' + } # The priority is based upon order of creation: first created -> highest priority. # See how all your routes lay out with "rake routes". diff --git a/spec/features/organizations_spec.rb b/spec/features/organizations_spec.rb new file mode 100644 index 000000000..4bee2cef5 --- /dev/null +++ b/spec/features/organizations_spec.rb @@ -0,0 +1,32 @@ +require 'rails_helper' + +feature 'Organizations' do + + scenario 'Organizations can be created' do + user = User.organizations.where(email: 'green@peace.com').first + expect(user).to_not be + + visit new_organization_registration_path + + fill_in 'user_organization_attributes_name', with: 'Greenpeace' + fill_in 'user_email', with: 'green@peace.com' + fill_in 'user_password', with: 'greenpeace' + fill_in 'user_password_confirmation', with: 'greenpeace' + + click_button 'Sign up' + + user = User.organizations.where(email: 'green@peace.com').first + expect(user).to be + expect(user).to be_organization + expect(user.organization).to_not be_verified + end + + scenario "Organization fields are validated" do + visit new_organization_registration_path + click_button 'Sign up' + + expect(page).to have_content "Email can't be blank" + expect(page).to have_content "Password can't be blank" + expect(page).to have_content "Organization name can't be blank" + end +end From e84873d875c9e63f2ce60ca38e03c4eda56a360e Mon Sep 17 00:00:00 2001 From: kikito Date: Mon, 17 Aug 2015 01:01:16 +0200 Subject: [PATCH 30/90] Modifies devise shared link to include organisations sometimes --- app/views/devise/shared/_links.html.erb | 13 ++++++++++--- spec/features/organizations_spec.rb | 13 +++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/app/views/devise/shared/_links.html.erb b/app/views/devise/shared/_links.html.erb index d7a77614e..0fd80f429 100644 --- a/app/views/devise/shared/_links.html.erb +++ b/app/views/devise/shared/_links.html.erb @@ -3,10 +3,16 @@ <%= link_to t("devise_views.shared.links.login"), new_session_path(resource_name) %>
<% end -%> - <%- if devise_mapping.registerable? && controller_name != 'registrations' %> - <%= link_to t("devise_views.shared.links.signup"), new_registration_path(resource_name) %>
+ <%- if devise_mapping.registerable? && + controller_name != 'registrations' || + controller_path != 'users/registrations' %> + <%= link_to t("devise_views.shared.links.signup"), new_user_registration_path %>
<% end -%> + <%- if devise_mapping.registerable? && controller_name == 'registrations' && controller_path != 'organizations/registrations' %> + <%= link_to t("devise_views.shared.links.organization_signup"), new_organization_registration_path %>
+ <% end -%> + <%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %> <%= link_to t("devise_views.shared.links.new_password"), new_password_path(resource_name) %>
<% end -%> @@ -24,4 +30,5 @@ <%= link_to t("devise_views.shared.links.signin_with_provider", provider: provider.to_s.titleize), omniauth_authorize_path(resource_name, provider) %>
<% end -%> <% end -%> -
\ No newline at end of file + +
diff --git a/spec/features/organizations_spec.rb b/spec/features/organizations_spec.rb index 4bee2cef5..d0144ecb5 100644 --- a/spec/features/organizations_spec.rb +++ b/spec/features/organizations_spec.rb @@ -29,4 +29,17 @@ feature 'Organizations' do expect(page).to have_content "Password can't be blank" expect(page).to have_content "Organization name can't be blank" end + + scenario 'Shared links' do + visit new_user_registration_path + expect(page).to have_link "Sign up as an organization" + + visit new_organization_registration_path + expect(page).to have_link "Sign up" + + visit new_user_session_path + + expect(page).to have_link "Sign up" + expect(page).to_not have_link "Sign up as an organization" + end end From 543f71889126e96fc99564affc10808bbbf280c7 Mon Sep 17 00:00:00 2001 From: kikito Date: Mon, 17 Aug 2015 01:12:14 +0200 Subject: [PATCH 31/90] Adds phone_number to the regular user registration / account views --- app/controllers/account_controller.rb | 2 +- app/controllers/users/registrations_controller.rb | 2 +- app/views/account/show.html.erb | 5 +++++ app/views/users/registrations/new.html.erb | 7 +++++++ config/locales/devise_views.en.yml | 1 + config/locales/devise_views.es.yml | 1 + config/locales/en.yml | 1 + config/locales/es.yml | 1 + 8 files changed, 18 insertions(+), 2 deletions(-) diff --git a/app/controllers/account_controller.rb b/app/controllers/account_controller.rb index eb0837fee..3c81da291 100644 --- a/app/controllers/account_controller.rb +++ b/app/controllers/account_controller.rb @@ -21,7 +21,7 @@ class AccountController < ApplicationController end def account_params - params.require(:account).permit(:first_name, :last_name, :nickname, :use_nickname, :email_on_debate_comment, :email_on_comment_reply) + params.require(:account).permit(:first_name, :last_name, :phone_number, :nickname, :use_nickname, :email_on_debate_comment, :email_on_comment_reply) end end diff --git a/app/controllers/users/registrations_controller.rb b/app/controllers/users/registrations_controller.rb index 174b12aa2..916b5128b 100644 --- a/app/controllers/users/registrations_controller.rb +++ b/app/controllers/users/registrations_controller.rb @@ -14,7 +14,7 @@ class Users::RegistrationsController < Devise::RegistrationsController private def sign_up_params - params.require(:user).permit(:first_name, :last_name, :email, :password, :password_confirmation, :use_nickname, :nickname) + params.require(:user).permit(:first_name, :last_name, :email, :phone_number, :password, :password_confirmation, :use_nickname, :nickname) end end diff --git a/app/views/account/show.html.erb b/app/views/account/show.html.erb index 185af445b..aae6ae28a 100644 --- a/app/views/account/show.html.erb +++ b/app/views/account/show.html.erb @@ -35,6 +35,11 @@ <% end %>
+
+ <%= f.label :last_name, t("account.show.phone_number_label") %> + <%= f.text_field :phone_number, placeholder: t("account.show.phone_number_label") %> +
+
<%= f.label :email_on_debate_comment do %> <%= f.check_box :email_on_debate_comment %> diff --git a/app/views/users/registrations/new.html.erb b/app/views/users/registrations/new.html.erb index 893054f8e..c41028fe6 100644 --- a/app/views/users/registrations/new.html.erb +++ b/app/views/users/registrations/new.html.erb @@ -42,6 +42,13 @@
+
+
+ <%= f.label :phone_number, t("devise_views.users.registrations.new.phone_number_label") %> + <%= f.text_field :phone_number, placeholder: t("devise_views.users.registrations.new.phone_number_label") %> +
+
+
<%= f.label :password, t("devise_views.users.registrations.new.password_label"), class: "inline-block" %> diff --git a/config/locales/devise_views.en.yml b/config/locales/devise_views.en.yml index a2f2fe1e1..6895790cb 100644 --- a/config/locales/devise_views.en.yml +++ b/config/locales/devise_views.en.yml @@ -56,6 +56,7 @@ en: nickname_label: "Nickname" use_nickname_label: "Use nickname" email_label: "Email" + phone_number_label: "Phone number" password_label: "Password" min_length: "(%{min} characters minimum)" password_confirmation_label: "Confirm password" diff --git a/config/locales/devise_views.es.yml b/config/locales/devise_views.es.yml index 0f920ecf2..700040e12 100644 --- a/config/locales/devise_views.es.yml +++ b/config/locales/devise_views.es.yml @@ -56,6 +56,7 @@ es: nickname_label: "Pseudónimo" use_nickname_label: "Usar pseudónimo" email_label: "Email" + phone_number_label: "Teléfono" password_label: "Contraseña" min_length: "(mínimo %{min} caracteres)" password_confirmation_label: "Confirmar contraseña" diff --git a/config/locales/en.yml b/config/locales/en.yml index 29bc4df24..480a50a56 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -70,6 +70,7 @@ en: change_credentials_link: "Change my credentials" first_name_label: "First Name" last_name_label: "Last Name" + phone_number_label: "Phone number" use_nickname_label: "Use nickname" nickname_label: "Nickname" recaptcha: diff --git a/config/locales/es.yml b/config/locales/es.yml index 16af9d324..2ee0d07b6 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -70,6 +70,7 @@ es: email_on_comment_reply_label: "Recibir un email cuando alguien contesta a mis comentarios" first_name_label: "Nombre" last_name_label: "Apellidos" + phone_number_label: "Teléfono" use_nickname_label: "Usar pseudónimo" nickname_label: "Pseudónimo" recaptcha: From cd0a64ea3e09c1203f72aaed247b0e5cda889d35 Mon Sep 17 00:00:00 2001 From: rgarcia Date: Mon, 17 Aug 2015 03:46:00 +0200 Subject: [PATCH 32/90] adds paranoia gem [#136] --- Gemfile | 1 + Gemfile.lock | 3 +++ 2 files changed, 4 insertions(+) diff --git a/Gemfile b/Gemfile index a4e9b0e20..aeca43f64 100644 --- a/Gemfile +++ b/Gemfile @@ -34,6 +34,7 @@ gem 'cancancan' gem 'social-share-button' gem 'initialjs-rails' gem 'unicorn' +gem 'paranoia' group :development, :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console diff --git a/Gemfile.lock b/Gemfile.lock index 2bcab8ad0..92b372edc 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -179,6 +179,8 @@ GEM nokogiri (1.6.6.2) mini_portile (~> 0.6.0) orm_adapter (0.5.0) + paranoia (2.1.3) + activerecord (~> 4.0) pg (0.18.2) poltergeist (1.6.0) capybara (~> 2.1) @@ -331,6 +333,7 @@ DEPENDENCIES kaminari launchy letter_opener_web (~> 1.2.0) + paranoia pg poltergeist quiet_assets From 42b441a866833f23636eb12852cfaaf1a48c3a69 Mon Sep 17 00:00:00 2001 From: kikito Date: Mon, 17 Aug 2015 12:29:42 +0200 Subject: [PATCH 33/90] fixes specs by using the correct capthca helper instead of deactivating it --- config/initializers/simple_captcha.rb | 2 +- spec/features/organizations_spec.rb | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/config/initializers/simple_captcha.rb b/config/initializers/simple_captcha.rb index 25d3a1342..2ac5826c3 100644 --- a/config/initializers/simple_captcha.rb +++ b/config/initializers/simple_captcha.rb @@ -1,4 +1,4 @@ -SimpleCaptcha.always_pass = Rails.env.test? +SimpleCaptcha.always_pass = false SimpleCaptcha.setup do |sc| # default: 100x28 diff --git a/spec/features/organizations_spec.rb b/spec/features/organizations_spec.rb index a79264d65..00ec6c6f9 100644 --- a/spec/features/organizations_spec.rb +++ b/spec/features/organizations_spec.rb @@ -12,6 +12,7 @@ feature 'Organizations' do fill_in 'user_email', with: 'green@peace.com' fill_in 'user_password', with: 'greenpeace' fill_in 'user_password_confirmation', with: 'greenpeace' + fill_in 'user_captcha', with: correct_captcha_text click_button 'Sign up' From 9f1f64cc3a77965f4092975323320ce4c6778209 Mon Sep 17 00:00:00 2001 From: Alberto Garcia Cabeza Date: Mon, 17 Aug 2015 12:31:05 +0200 Subject: [PATCH 34/90] Adds sub navigation menu --- app/assets/stylesheets/participacion.scss | 31 ++++++++++++++++++++++- app/views/layouts/_header.html.erb | 15 +++++++++++ config/locales/en.yml | 4 +++ config/locales/es.yml | 4 +++ 4 files changed, 53 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/participacion.scss b/app/assets/stylesheets/participacion.scss index 6e5ad3d7b..710fb8c6d 100644 --- a/app/assets/stylesheets/participacion.scss +++ b/app/assets/stylesheets/participacion.scss @@ -134,7 +134,7 @@ header { min-height: rem-calc(480); &.results { - min-height: rem-calc(192); + min-height: rem-calc(216); } h1 { @@ -276,6 +276,35 @@ header { } } +.subnavigation { + background: white; + border-bottom: 1px solid white; + clear: both; + + a { + color: $link; + font-size: rem-calc(14); + font-weight: bold; + + &.active { + color: $text; + + &:after { + bottom: -17px; + left: 50%; + border: solid transparent; + content: " "; + height: 0; + width: 0; + position: absolute; + border-top-color: #fff; + border-width: 8px; + margin-left: -8px; + } + } + } +} + // 05. Footer // - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/views/layouts/_header.html.erb b/app/views/layouts/_header.html.erb index b88dcded4..3572b9690 100644 --- a/app/views/layouts/_header.html.erb +++ b/app/views/layouts/_header.html.erb @@ -35,6 +35,21 @@ <%= render 'devise/menu/login_items' %> <%= render 'shared/admin_login_items' %> + +
diff --git a/config/locales/en.yml b/config/locales/en.yml index f1d7ba646..a6b1115e5 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -15,6 +15,10 @@ en: language: Site language administration: Administration moderation: Moderation + welcome: Welcome + news: News + debates: Debates + initiatives: Initiatives footer: copyright: "Ayuntamiento de Madrid, 2015. All rights reserved" form: diff --git a/config/locales/es.yml b/config/locales/es.yml index 45cfac0d5..50b709375 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -15,6 +15,10 @@ es: language: Idioma de la página administration: Administar moderation: Moderar + welcome: Portada + news: Novedades + debates: Debates + initiatives: Iniciativas footer: copyright: "Ayuntamiento de Madrid, %{year}. Todos los derechos reservados" form: From ae21209920fc5b20566e0b0a6260e906f65ac6b4 Mon Sep 17 00:00:00 2001 From: kikito Date: Mon, 17 Aug 2015 13:20:52 +0200 Subject: [PATCH 35/90] Moves the layout to admin::BaseController and moderation::BaseController --- app/controllers/admin/base_controller.rb | 4 +++- app/controllers/admin/tags_controller.rb | 1 - app/controllers/moderation/base_controller.rb | 2 ++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/app/controllers/admin/base_controller.rb b/app/controllers/admin/base_controller.rb index f4322efe7..81ec67996 100644 --- a/app/controllers/admin/base_controller.rb +++ b/app/controllers/admin/base_controller.rb @@ -1,4 +1,6 @@ class Admin::BaseController < ApplicationController + layout 'admin' + before_action :authenticate_user! skip_authorization_check @@ -10,4 +12,4 @@ class Admin::BaseController < ApplicationController raise CanCan::AccessDenied unless current_user.try(:administrator?) end -end \ No newline at end of file +end diff --git a/app/controllers/admin/tags_controller.rb b/app/controllers/admin/tags_controller.rb index ecacdbdb2..03cfeb64f 100644 --- a/app/controllers/admin/tags_controller.rb +++ b/app/controllers/admin/tags_controller.rb @@ -1,5 +1,4 @@ class Admin::TagsController < Admin::BaseController - layout 'admin' before_action :find_tag, only: [:update, :destroy] respond_to :html, :js diff --git a/app/controllers/moderation/base_controller.rb b/app/controllers/moderation/base_controller.rb index 0ee155e03..f2a794526 100644 --- a/app/controllers/moderation/base_controller.rb +++ b/app/controllers/moderation/base_controller.rb @@ -1,4 +1,6 @@ class Moderation::BaseController < ApplicationController + layout 'admin' + before_action :authenticate_user! skip_authorization_check From 3096a738922d99db635e9b102bb23a6ec83b3fdc Mon Sep 17 00:00:00 2001 From: kikito Date: Mon, 17 Aug 2015 13:22:38 +0200 Subject: [PATCH 36/90] Moves organization verification to Admin from Moderation --- .../organizations_controller.rb | 2 +- app/views/admin/organizations/index.html.erb | 34 +++++++++++++++++++ .../moderation/organizations/index.html.erb | 31 ----------------- config/locales/admin.en.yml | 7 ++++ config/locales/admin.es.yml | 11 ++++++ config/locales/moderation.en.yml | 8 +---- config/locales/moderation.es.yml | 8 +---- config/routes.rb | 12 +++---- .../organizations_spec.rb | 18 +++++----- 9 files changed, 70 insertions(+), 61 deletions(-) rename app/controllers/{moderation => admin}/organizations_controller.rb (80%) create mode 100644 app/views/admin/organizations/index.html.erb delete mode 100644 app/views/moderation/organizations/index.html.erb rename spec/features/{moderation => admin}/organizations_spec.rb (76%) diff --git a/app/controllers/moderation/organizations_controller.rb b/app/controllers/admin/organizations_controller.rb similarity index 80% rename from app/controllers/moderation/organizations_controller.rb rename to app/controllers/admin/organizations_controller.rb index 1528d0ea2..6f88f8d90 100644 --- a/app/controllers/moderation/organizations_controller.rb +++ b/app/controllers/admin/organizations_controller.rb @@ -1,4 +1,4 @@ -class Moderation::OrganizationsController < Moderation::BaseController +class Admin::OrganizationsController < Admin::BaseController load_and_authorize_resource diff --git a/app/views/admin/organizations/index.html.erb b/app/views/admin/organizations/index.html.erb new file mode 100644 index 000000000..d09f810d5 --- /dev/null +++ b/app/views/admin/organizations/index.html.erb @@ -0,0 +1,34 @@ +
+ +

<%= t('admin.organizations.index.title') %>

+ +
<%= organization.organization_name %><%= organization.name %> <%= organization.email %> <%= organization.phone_number %><%= t('moderation.organizations.index.verified') %><%= link_to t('moderation.organizations.index.verify'), - verify_organization_moderation_organization_path(organization), + verify_moderation_organization_path(organization), method: :put %> <%= t('moderation.organizations.index.rejected') %><%= link_to t('moderation.organizations.index.reject'), - reject_organization_moderation_organization_path(organization), + reject_moderation_organization_path(organization), method: :put %>
+ <% @organizations.each do |organization| %> + + + + + <% if organization.verified? %> + + <% end %> + <% if can? :verify, organization %> + + <% end %> + <% if organization.rejected? %> + + <% end %> + <% if can? :reject, organization %> + + <% end %> + + <% end %> +
<%= organization.name %><%= organization.email %><%= organization.phone_number %><%= t('admin.organizations.index.verified') %><%= link_to t('admin.organizations.index.verify'), + verify_admin_organization_path(organization), + method: :put + %> + <%= t('admin.organizations.index.rejected') %><%= link_to t('admin.organizations.index.reject'), + reject_admin_organization_path(organization), + method: :put + %> +
+ diff --git a/app/views/moderation/organizations/index.html.erb b/app/views/moderation/organizations/index.html.erb deleted file mode 100644 index 06549a9b3..000000000 --- a/app/views/moderation/organizations/index.html.erb +++ /dev/null @@ -1,31 +0,0 @@ -

<%= t('moderation.organizations.index.title') %>

- - - <% @organizations.each do |organization| %> - - - - - <% if organization.verified? %> - - <% end %> - <% if can? :verify, organization %> - - <% end %> - <% if organization.rejected? %> - - <% end %> - <% if can? :reject, organization %> - - <% end %> - -<% end %> -
<%= organization.name %><%= organization.email %><%= organization.phone_number %><%= t('moderation.organizations.index.verified') %><%= link_to t('moderation.organizations.index.verify'), - verify_moderation_organization_path(organization), - method: :put - %> - <%= t('moderation.organizations.index.rejected') %><%= link_to t('moderation.organizations.index.reject'), - reject_moderation_organization_path(organization), - method: :put - %> -
diff --git a/config/locales/admin.en.yml b/config/locales/admin.en.yml index a5b469e1b..50f59cb81 100644 --- a/config/locales/admin.en.yml +++ b/config/locales/admin.en.yml @@ -11,3 +11,10 @@ en: name: placeholder: 'Write a topic' destroy: Delete Tag + organizations: + index: + title: Organizations + verify: Verify + reject: Reject + verified: Verified + rejected: Rejected diff --git a/config/locales/admin.es.yml b/config/locales/admin.es.yml index fecb7c5f4..74290ce6c 100644 --- a/config/locales/admin.es.yml +++ b/config/locales/admin.es.yml @@ -3,6 +3,17 @@ es: dashboard: index: title: Administración + organizations: + index: + title: Organizaciones + verify: Verificar + reject: Rechazar + verified: Verificado + rejected: Rechazado + filter: Filtro + filter_all: Todas + filter_verified: Verificadas + filter_rejected: Rechazadas tags: index: title: 'Temas de debate' diff --git a/config/locales/moderation.en.yml b/config/locales/moderation.en.yml index c3a765104..cf53051ed 100644 --- a/config/locales/moderation.en.yml +++ b/config/locales/moderation.en.yml @@ -3,10 +3,4 @@ en: dashboard: index: title: Moderation - organizations: - index: - title: Organizations - verify: Verify - reject: Reject - verified: Verified - rejected: Rejected + diff --git a/config/locales/moderation.es.yml b/config/locales/moderation.es.yml index 958bd7c75..a921f0827 100644 --- a/config/locales/moderation.es.yml +++ b/config/locales/moderation.es.yml @@ -3,11 +3,5 @@ es: dashboard: index: title: Moderación - organizations: - index: - title: Organizaciones - verify: Verificar - reject: Rechazar - verified: Verificado - rejected: Rechazado + diff --git a/config/routes.rb b/config/routes.rb index 7ccbe2bb0..34fa752e1 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -28,18 +28,18 @@ Rails.application.routes.draw do namespace :admin do root to: "dashboard#index" - - resources :tags, only: [:index, :create, :update, :destroy] - end - - namespace :moderation do - root to: "dashboard#index" resources :organizations, only: :index do member do put :verify put :reject end end + + resources :tags, only: [:index, :create, :update, :destroy] + end + + namespace :moderation do + root to: "dashboard#index" end # Example of regular route: diff --git a/spec/features/moderation/organizations_spec.rb b/spec/features/admin/organizations_spec.rb similarity index 76% rename from spec/features/moderation/organizations_spec.rb rename to spec/features/admin/organizations_spec.rb index 22cf8d2db..626eb9f9e 100644 --- a/spec/features/moderation/organizations_spec.rb +++ b/spec/features/admin/organizations_spec.rb @@ -4,21 +4,21 @@ feature 'Moderations::Organizations' do background do - moderator = create(:user) - create(:moderator, user: moderator) + administrator = create(:user) + create(:administrator, user: administrator) - login_as(moderator) + login_as(administrator) end scenario "pending organizations have links to verify and reject" do organization = create(:organization) - visit moderation_organizations_path + visit admin_organizations_path expect(page).to have_selector(:link_or_button, 'Verify') expect(page).to have_selector(:link_or_button, 'Reject') click_on 'Verify' - expect(current_path).to eq(moderation_organizations_path) + expect(current_path).to eq(admin_organizations_path) expect(page).to have_content ('Verified') expect(organization.reload.verified?).to eq(true) @@ -27,13 +27,13 @@ feature 'Moderations::Organizations' do scenario "verified organizations have link to reject" do organization = create(:verified_organization) - visit moderation_organizations_path + visit admin_organizations_path expect(page).to have_content ('Verified') expect(page).to_not have_selector(:link_or_button, 'Verify') expect(page).to have_selector(:link_or_button, 'Reject') click_on 'Reject' - expect(current_path).to eq(moderation_organizations_path) + expect(current_path).to eq(admin_organizations_path) expect(page).to have_content ('Rejected') expect(organization.reload.rejected?).to eq(true) @@ -42,13 +42,13 @@ feature 'Moderations::Organizations' do scenario "rejected organizations have link to verify" do organization = create(:rejected_organization) - visit moderation_organizations_path + visit admin_organizations_path expect(page).to have_content ('Rejected') expect(page).to have_selector(:link_or_button, 'Verify') expect(page).to_not have_selector(:link_or_button, 'Reject') click_on 'Verify' - expect(current_path).to eq(moderation_organizations_path) + expect(current_path).to eq(admin_organizations_path) expect(page).to have_content ('Verified') expect(organization.reload.verified?).to eq(true) From 264f6318c336d135deee8f8f83d256fcb036b74c Mon Sep 17 00:00:00 2001 From: kikito Date: Mon, 17 Aug 2015 13:23:18 +0200 Subject: [PATCH 37/90] Adds i18n & ul for admin menus --- app/views/admin/_menu.html.erb | 5 ++++- config/locales/admin.en.yml | 3 +++ config/locales/admin.es.yml | 3 +++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/app/views/admin/_menu.html.erb b/app/views/admin/_menu.html.erb index 79540a5d0..007dd966f 100644 --- a/app/views/admin/_menu.html.erb +++ b/app/views/admin/_menu.html.erb @@ -1,3 +1,6 @@
- <%= link_to t('admin.tags.index.title'), admin_tags_path %> +
    +
  • <%= link_to t('admin.menu.tags'), admin_tags_path %>
  • +
  • <%= link_to t('admin.menu.organizations'), admin_organizations_path %>
  • +
diff --git a/config/locales/admin.en.yml b/config/locales/admin.en.yml index 50f59cb81..d9a40ff41 100644 --- a/config/locales/admin.en.yml +++ b/config/locales/admin.en.yml @@ -1,5 +1,8 @@ en: admin: + menu: + tags: Tags + organizations: Organizations dashboard: index: title: Administration diff --git a/config/locales/admin.es.yml b/config/locales/admin.es.yml index 74290ce6c..bb1576045 100644 --- a/config/locales/admin.es.yml +++ b/config/locales/admin.es.yml @@ -1,5 +1,8 @@ es: admin: + menu: + tags: Temas de debate + organizations: Organizaciones dashboard: index: title: Administración From f31ed2940df79e566d852a7e834ccd11dc5ba778 Mon Sep 17 00:00:00 2001 From: rgarcia Date: Mon, 17 Aug 2015 13:39:31 +0200 Subject: [PATCH 38/90] adds moderation to comments and debates [#136] --- app/assets/javascripts/comments.js.coffee | 5 +- .../javascripts/moderator_comment.js.coffee | 7 +++ .../javascripts/moderator_debates.js.coffee | 8 +++ app/assets/stylesheets/debates.scss | 5 ++ app/controllers/admin/base_controller.rb | 1 + app/controllers/admin/comments_controller.rb | 13 +++++ app/controllers/admin/debates_controller.rb | 16 +++++ app/controllers/comments_controller.rb | 2 + app/controllers/debates_controller.rb | 4 +- .../moderation/comments_controller.rb | 8 +++ .../moderation/debates_controller.rb | 7 +++ app/helpers/abilities_helper.rb | 7 +++ app/models/ability.rb | 7 ++- app/models/comment.rb | 3 + app/models/debate.rb | 2 + app/views/admin/_menu.html.erb | 8 ++- app/views/admin/comments/index.html.erb | 14 +++++ app/views/admin/debates/index.html.erb | 13 +++++ app/views/admin/debates/show.html.erb | 13 +++++ app/views/comments/_actions.html.erb | 5 ++ app/views/comments/_comment.html.erb | 58 ++++++++++++------- app/views/comments/_form.html.erb | 2 - app/views/comments/create.js.erb | 7 ++- app/views/debates/_actions.html.erb | 2 + app/views/debates/show.html.erb | 17 ++++-- app/views/moderation/comments/hide.js.erb | 3 + app/views/moderation/debates/hide.js.erb | 3 + config/locales/admin.en.yml | 19 ++++++ config/locales/admin.es.yml | 18 ++++++ config/routes.rb | 24 ++++++-- ...0150810201448_add_hidden_at_to_comments.rb | 6 ++ ...20150811145628_add_hidden_at_to_debates.rb | 6 ++ db/schema.rb | 6 +- lib/acts_as_paranoid_aliases.rb | 33 +++++++++++ spec/models/ability_spec.rb | 8 +++ 35 files changed, 315 insertions(+), 45 deletions(-) create mode 100644 app/assets/javascripts/moderator_comment.js.coffee create mode 100644 app/assets/javascripts/moderator_debates.js.coffee create mode 100644 app/controllers/admin/comments_controller.rb create mode 100644 app/controllers/admin/debates_controller.rb create mode 100644 app/controllers/moderation/comments_controller.rb create mode 100644 app/controllers/moderation/debates_controller.rb create mode 100644 app/helpers/abilities_helper.rb create mode 100644 app/views/admin/comments/index.html.erb create mode 100644 app/views/admin/debates/index.html.erb create mode 100644 app/views/admin/debates/show.html.erb create mode 100644 app/views/comments/_actions.html.erb create mode 100644 app/views/debates/_actions.html.erb create mode 100644 app/views/moderation/comments/hide.js.erb create mode 100644 app/views/moderation/debates/hide.js.erb create mode 100644 db/migrate/20150810201448_add_hidden_at_to_comments.rb create mode 100644 db/migrate/20150811145628_add_hidden_at_to_debates.rb create mode 100644 lib/acts_as_paranoid_aliases.rb diff --git a/app/assets/javascripts/comments.js.coffee b/app/assets/javascripts/comments.js.coffee index 377fa4d1a..8a9ebe0b1 100644 --- a/app/assets/javascripts/comments.js.coffee +++ b/app/assets/javascripts/comments.js.coffee @@ -1,8 +1,11 @@ App.Comments = - add_response: (parent_id, response_html) -> + add_comment: (parent_id, response_html) -> $(response_html).insertAfter($("#js-comment-form-#{parent_id}")) + add_reply: (parent_id, response_html) -> + $("##{parent_id} .comment-children:first").prepend($(response_html)) + display_error: (field_with_errors, error_html) -> $(error_html).insertAfter($("#{field_with_errors}")) diff --git a/app/assets/javascripts/moderator_comment.js.coffee b/app/assets/javascripts/moderator_comment.js.coffee new file mode 100644 index 000000000..7cc680169 --- /dev/null +++ b/app/assets/javascripts/moderator_comment.js.coffee @@ -0,0 +1,7 @@ +App.ModeratorComments = + + add_class_faded: (id) -> + $("##{id} .comment-body:first").addClass("faded") + + hide_moderator_actions: (id) -> + $("##{id} #moderator-comment-actions:first").hide() \ No newline at end of file diff --git a/app/assets/javascripts/moderator_debates.js.coffee b/app/assets/javascripts/moderator_debates.js.coffee new file mode 100644 index 000000000..802504ec2 --- /dev/null +++ b/app/assets/javascripts/moderator_debates.js.coffee @@ -0,0 +1,8 @@ +App.ModeratorDebates = + + add_class_faded: (id) -> + $("##{id}").addClass("faded") + $("#comments").addClass("faded") + + hide_moderator_actions: (id) -> + $("##{id} #moderator-debate-actions:first").hide() \ No newline at end of file diff --git a/app/assets/stylesheets/debates.scss b/app/assets/stylesheets/debates.scss index f242e8757..afc99b75a 100644 --- a/app/assets/stylesheets/debates.scss +++ b/app/assets/stylesheets/debates.scss @@ -571,4 +571,9 @@ } } } + +} + +.faded { + opacity: 0.4; } diff --git a/app/controllers/admin/base_controller.rb b/app/controllers/admin/base_controller.rb index f4322efe7..f011be7d9 100644 --- a/app/controllers/admin/base_controller.rb +++ b/app/controllers/admin/base_controller.rb @@ -1,4 +1,5 @@ class Admin::BaseController < ApplicationController + layout 'admin' before_action :authenticate_user! skip_authorization_check diff --git a/app/controllers/admin/comments_controller.rb b/app/controllers/admin/comments_controller.rb new file mode 100644 index 000000000..4c385b5d4 --- /dev/null +++ b/app/controllers/admin/comments_controller.rb @@ -0,0 +1,13 @@ +class Admin::CommentsController < Admin::BaseController + + def index + @comments = Comment.only_hidden + end + + def restore + @comment = Comment.with_hidden.find(params[:id]) + @comment.restore + redirect_to admin_comments_path, notice: t('admin.comments.restore.success') + end + +end \ No newline at end of file diff --git a/app/controllers/admin/debates_controller.rb b/app/controllers/admin/debates_controller.rb new file mode 100644 index 000000000..a37744f71 --- /dev/null +++ b/app/controllers/admin/debates_controller.rb @@ -0,0 +1,16 @@ +class Admin::DebatesController < Admin::BaseController + + def index + @debates = Debate.only_hidden + end + + def show + @debate = Debate.with_hidden.find(params[:id]) + end + + def restore + @debate = Debate.with_hidden.find(params[:id]) + @debate.restore + redirect_to admin_debates_path, notice: t('admin.debates.restore.success') + end +end \ No newline at end of file diff --git a/app/controllers/comments_controller.rb b/app/controllers/comments_controller.rb index 71de112a9..a10d80da9 100644 --- a/app/controllers/comments_controller.rb +++ b/app/controllers/comments_controller.rb @@ -2,6 +2,7 @@ class CommentsController < ApplicationController before_action :authenticate_user! before_action :build_comment, only: :create before_action :parent, only: :create + load_and_authorize_resource respond_to :html, :js @@ -50,4 +51,5 @@ class CommentsController < ApplicationController def email_on_comment_reply? reply? && parent.author.email_on_comment_reply? end + end diff --git a/app/controllers/debates_controller.rb b/app/controllers/debates_controller.rb index 0816245c7..eeb82825a 100644 --- a/app/controllers/debates_controller.rb +++ b/app/controllers/debates_controller.rb @@ -1,6 +1,8 @@ class DebatesController < ApplicationController before_action :authenticate_user!, except: [:index, :show] + load_and_authorize_resource + respond_to :html, :js def index @debates = Debate.search(params) @@ -9,6 +11,7 @@ class DebatesController < ApplicationController def show set_debate_votes(@debate) + @comments = @debate.root_comments.with_hidden.recent end def new @@ -46,7 +49,6 @@ class DebatesController < ApplicationController set_debate_votes(@debate) end - private def debate_params diff --git a/app/controllers/moderation/comments_controller.rb b/app/controllers/moderation/comments_controller.rb new file mode 100644 index 000000000..c250fc460 --- /dev/null +++ b/app/controllers/moderation/comments_controller.rb @@ -0,0 +1,8 @@ +class Moderation::CommentsController < Moderation::BaseController + + def hide + @comment = Comment.find(params[:id]) + @comment.hide + end + +end \ No newline at end of file diff --git a/app/controllers/moderation/debates_controller.rb b/app/controllers/moderation/debates_controller.rb new file mode 100644 index 000000000..cb5599092 --- /dev/null +++ b/app/controllers/moderation/debates_controller.rb @@ -0,0 +1,7 @@ +class Moderation::DebatesController < Moderation::BaseController + + def hide + @debate = Debate.find(params[:id]) + @debate.hide + end +end \ No newline at end of file diff --git a/app/helpers/abilities_helper.rb b/app/helpers/abilities_helper.rb new file mode 100644 index 000000000..462af0479 --- /dev/null +++ b/app/helpers/abilities_helper.rb @@ -0,0 +1,7 @@ +module AbilitiesHelper + + def moderator? + current_user.try(:moderator?) + end + +end \ No newline at end of file diff --git a/app/models/ability.rb b/app/models/ability.rb index c1fc24ae3..62947b52c 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -15,10 +15,13 @@ class Ability can [:create, :vote], Comment - if user.moderator? or user.administrator? + if user.moderator? + can [:hide], Comment + can [:hide], Debate elsif user.administrator? - + can [:restore], Comment + can [:restore], Debate end end end diff --git a/app/models/comment.rb b/app/models/comment.rb index b8182b04f..f8a43c222 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -1,5 +1,8 @@ class Comment < ActiveRecord::Base + include ActsAsParanoidAliases acts_as_nested_set scope: [:commentable_id, :commentable_type], counter_cache: :children_count + + acts_as_paranoid column: :hidden_at acts_as_votable validates :body, presence: true diff --git a/app/models/debate.rb b/app/models/debate.rb index b0d022c51..e029ffdd4 100644 --- a/app/models/debate.rb +++ b/app/models/debate.rb @@ -1,5 +1,6 @@ require 'numeric' class Debate < ActiveRecord::Base + include ActsAsParanoidAliases default_scope { order('created_at DESC') } apply_simple_captcha @@ -8,6 +9,7 @@ class Debate < ActiveRecord::Base acts_as_votable acts_as_commentable acts_as_taggable + acts_as_paranoid column: :hidden_at belongs_to :author, class_name: 'User', foreign_key: 'author_id' diff --git a/app/views/admin/_menu.html.erb b/app/views/admin/_menu.html.erb index 79540a5d0..8ea2b27af 100644 --- a/app/views/admin/_menu.html.erb +++ b/app/views/admin/_menu.html.erb @@ -1,3 +1,5 @@ -
- <%= link_to t('admin.tags.index.title'), admin_tags_path %> -
+
    +
  • <%= link_to t('admin.menu.debate_topics'), admin_tags_path %>
  • +
  • <%= link_to t('admin.menu.hidden_debates'), admin_debates_path %>
  • +
  • <%= link_to t('admin.menu.hidden_comments'), admin_comments_path %>
  • +
diff --git a/app/views/admin/comments/index.html.erb b/app/views/admin/comments/index.html.erb new file mode 100644 index 000000000..1555fda0c --- /dev/null +++ b/app/views/admin/comments/index.html.erb @@ -0,0 +1,14 @@ +
+

<%= t("admin.comments.index.title") %>

+ +
    + <% @comments.each do |comment| %> +
  • + <%= comment.body %> + + <%= link_to t("admin.actions.restore"), restore_admin_comment_path(comment), + method: :put, data: { confirm: t("admin.actions.confirm") } %> +
  • + <% end %> +
+
diff --git a/app/views/admin/debates/index.html.erb b/app/views/admin/debates/index.html.erb new file mode 100644 index 000000000..ce07205b3 --- /dev/null +++ b/app/views/admin/debates/index.html.erb @@ -0,0 +1,13 @@ +
+

<%= t("admin.debates.index.title") %>

+ +
    + <% @debates.each do |debate| %> + <%= link_to admin_debate_path(debate) do %> +
  • + <%= link_to debate.title, admin_debate_path(debate) %> +
  • + <% end %> + <% end %> +
+
\ No newline at end of file diff --git a/app/views/admin/debates/show.html.erb b/app/views/admin/debates/show.html.erb new file mode 100644 index 000000000..aaa22eec9 --- /dev/null +++ b/app/views/admin/debates/show.html.erb @@ -0,0 +1,13 @@ +
+

<%= t("admin.debates.index.title") %>

+ +
+
<%= @debate.title %>
+
<%= @debate.description %>
+ +
+ <%= link_to t('admin.actions.restore'), restore_admin_debate_path(@debate), + method: :put, data: { confirm: t('admin.actions.confirm') } %> +
+
+
\ No newline at end of file diff --git a/app/views/comments/_actions.html.erb b/app/views/comments/_actions.html.erb new file mode 100644 index 000000000..ecc9bc9c0 --- /dev/null +++ b/app/views/comments/_actions.html.erb @@ -0,0 +1,5 @@ + +  |  + <%= link_to t("admin.actions.hide").capitalize, hide_moderation_comment_path(comment), + method: :put, remote: true, data: { confirm: t('admin.actions.confirm') } %> + \ No newline at end of file diff --git a/app/views/comments/_comment.html.erb b/app/views/comments/_comment.html.erb index af5a41897..1de4bfd7b 100644 --- a/app/views/comments/_comment.html.erb +++ b/app/views/comments/_comment.html.erb @@ -1,30 +1,44 @@
-
+
- <%= avatar_image(comment.user, size: 32, class: 'left') %> + <% if comment.hidden? %> + This comment has been deleted + <% else %> -
- - <%= comment.user.name %> • <%= time_ago_in_words(comment.created_at) %> - -

<%= comment.body %>

+ <%= avatar_image(comment.user, size: 32, class: 'left') %> - - <%= render 'comments/votes', comment: comment %> - +
+ + <%= comment.user.name %> • <%= time_ago_in_words(comment.created_at) %> + +

<%= comment.body %>

-

- <%= t("debates.comment.responses", count: comment.children_count) %> - <% if user_signed_in? %> -  |  - <%= render 'comments/form', {parent: comment, toggeable: true} %> - <% end %> -

-
+ + <%= render 'comments/votes', comment: comment %> + -
- <%= render comment.children.reorder('id DESC, lft') %> -
+

+ <%= t("debates.comment.responses", count: comment.children_count) %> + + <% if user_signed_in? %> +  |  + <%= link_to(comment_link_text(comment), "", + class: "js-add-comment-link", data: {'id': dom_id(comment)}) %> + + <% if moderator? %> + <%= render 'comments/actions', comment: comment %> + <% end %> + + <%= render 'comments/form', {parent: comment, toggeable: true} %> + <% end %> +

+
+ + <% end %> + +
+ <%= render comment.children.with_deleted.reorder('id DESC, lft') %> +
-
+
\ No newline at end of file diff --git a/app/views/comments/_form.html.erb b/app/views/comments/_form.html.erb index da2b3bb61..d7fcb5cdd 100644 --- a/app/views/comments/_form.html.erb +++ b/app/views/comments/_form.html.erb @@ -1,5 +1,3 @@ -<%= link_to(comment_link_text(parent), "", class: "js-add-comment-link", data: {'id': dom_id(parent)}) if toggeable %> -
> <%= form_for [@debate, Comment.new], remote: true do |f| %> <%= f.text_area :body %> diff --git a/app/views/comments/create.js.erb b/app/views/comments/create.js.erb index f2b078195..ed3e9c8f9 100644 --- a/app/views/comments/create.js.erb +++ b/app/views/comments/create.js.erb @@ -1,9 +1,10 @@ -var parent_id = '<%= dom_id(@parent) %>'; +var parent_id = "<%= dom_id(@parent) %>"; +var comment_html = "<%= j(render @comment) %>" <% if @parent.is_a?(Debate) -%> App.Comments.reset_form(parent_id); + App.Comments.add_comment(parent_id, comment_html); <% else -%> App.Comments.reset_and_hide_form(parent_id); + App.Comments.add_reply(parent_id, comment_html); <% end -%> - -App.Comments.add_response(parent_id, "<%= j(render @comment) %>"); diff --git a/app/views/debates/_actions.html.erb b/app/views/debates/_actions.html.erb new file mode 100644 index 000000000..3300a7b1d --- /dev/null +++ b/app/views/debates/_actions.html.erb @@ -0,0 +1,2 @@ +<%= link_to t("admin.actions.hide").capitalize, hide_moderation_debate_path(debate), + method: :put, remote: true, data: { confirm: t('admin.actions.confirm') } %> diff --git a/app/views/debates/show.html.erb b/app/views/debates/show.html.erb index 7396a72a2..29ce06b2b 100644 --- a/app/views/debates/show.html.erb +++ b/app/views/debates/show.html.erb @@ -1,5 +1,5 @@
-
+
 <%= link_to t("debates.show.back_link"), debates_path, class: 'left back' %> @@ -21,12 +21,19 @@  •  <%= l @debate.created_at.to_date %>  •  -  <%= link_to t("debates.show.comments", count: @debate.comment_threads.count), "#comments" %> +   + <%= link_to t("debates.show.comments", count: @debate.comment_threads.count), "#comments" %>
<%= @debate.description %> <%= render 'shared/tags', debate: @debate %> + + <% if moderator? %> +
+ <%= render 'actions', debate: @debate %> +
+ <% end %>
<% end %> - <%= render @debate.root_comments.recent %> + + <%= render @comments %>
diff --git a/app/views/moderation/comments/hide.js.erb b/app/views/moderation/comments/hide.js.erb new file mode 100644 index 000000000..4583aff53 --- /dev/null +++ b/app/views/moderation/comments/hide.js.erb @@ -0,0 +1,3 @@ +var comment_id = '<%= dom_id(@comment) %>'; +App.ModeratorComments.add_class_faded(comment_id); +App.ModeratorComments.hide_moderator_actions(comment_id); \ No newline at end of file diff --git a/app/views/moderation/debates/hide.js.erb b/app/views/moderation/debates/hide.js.erb new file mode 100644 index 000000000..1502feb08 --- /dev/null +++ b/app/views/moderation/debates/hide.js.erb @@ -0,0 +1,3 @@ +var debate_id = '<%= dom_id(@debate) %>'; +App.ModeratorDebates.add_class_faded(debate_id); +App.ModeratorDebates.hide_moderator_actions(debate_id); \ No newline at end of file diff --git a/config/locales/admin.en.yml b/config/locales/admin.en.yml index a5b469e1b..42c6d375c 100644 --- a/config/locales/admin.en.yml +++ b/config/locales/admin.en.yml @@ -3,6 +3,14 @@ en: dashboard: index: title: Administration + menu: + debate_topics: Debate topics + hidden_debates: Hidden debates + hidden_comments: Hidden comments + actions: + hide: Hide + restore: Restore + confirm: 'Are you sure?' tags: index: title: 'Debate topics' @@ -11,3 +19,14 @@ en: name: placeholder: 'Write a topic' destroy: Delete Tag + comments: + index: + title: Hidden comments + restore: + success: The comment has been restored + debates: + index: + title: Hidden debates + restore: + success: The debate has been restored + diff --git a/config/locales/admin.es.yml b/config/locales/admin.es.yml index fecb7c5f4..71062c25b 100644 --- a/config/locales/admin.es.yml +++ b/config/locales/admin.es.yml @@ -3,6 +3,14 @@ es: dashboard: index: title: Administración + menu: + debate_topics: Temas de debate + hidden_debates: Debates ocultos + hidden_comments: Comentarios ocultos + actions: + hide: Ocultar + restore: Permitir + confirm: '¿Estás seguro?' tags: index: title: 'Temas de debate' @@ -11,3 +19,13 @@ es: name: placeholder: 'Escribe el nombre del tema' destroy: Elimina la etiqueta + comments: + index: + title: Comentarios ocultos + restore: + success: El comentario ha sido permitido + debates: + index: + title: Debates ocultos + restore: + success: El debate ha sido permitido diff --git a/config/routes.rb b/config/routes.rb index 5470223e8..27f558e69 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -8,14 +8,10 @@ Rails.application.routes.draw do root 'welcome#index' resources :debates do - member do - post :vote - end + member { post :vote } resources :comments, only: :create, shallow: true do - member do - post :vote - end + member { post :vote } end end @@ -24,11 +20,27 @@ Rails.application.routes.draw do namespace :admin do root to: "dashboard#index" + resources :debates, only: [:index, :show] do + member { put :restore } + end + + resources :comments, only: :index do + member { put :restore } + end + resources :tags, only: [:index, :create, :update, :destroy] end namespace :moderation do root to: "dashboard#index" + + resources :debates, only: [] do + member { put :hide } + end + + resources :comments, only: [:index] do + member { put :hide } + end end # Example of regular route: diff --git a/db/migrate/20150810201448_add_hidden_at_to_comments.rb b/db/migrate/20150810201448_add_hidden_at_to_comments.rb new file mode 100644 index 000000000..55d652087 --- /dev/null +++ b/db/migrate/20150810201448_add_hidden_at_to_comments.rb @@ -0,0 +1,6 @@ +class AddHiddenAtToComments < ActiveRecord::Migration + def change + add_column :comments, :hidden_at, :datetime + add_index :comments, :hidden_at + end +end diff --git a/db/migrate/20150811145628_add_hidden_at_to_debates.rb b/db/migrate/20150811145628_add_hidden_at_to_debates.rb new file mode 100644 index 000000000..5fbb29532 --- /dev/null +++ b/db/migrate/20150811145628_add_hidden_at_to_debates.rb @@ -0,0 +1,6 @@ +class AddHiddenAtToDebates < ActiveRecord::Migration + def change + add_column :debates, :hidden_at, :datetime + add_index :debates, :hidden_at + end +end diff --git a/db/schema.rb b/db/schema.rb index 4e8e2c286..f35d265f6 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -12,7 +12,6 @@ # It's strongly recommended that you check this file into your version control system. ActiveRecord::Schema.define(version: 20150815154430) do - # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -35,9 +34,11 @@ ActiveRecord::Schema.define(version: 20150815154430) do t.datetime "created_at" t.datetime "updated_at" t.integer "children_count", default: 0 + t.datetime "hidden_at" end add_index "comments", ["commentable_id", "commentable_type"], name: "index_comments_on_commentable_id_and_commentable_type", using: :btree + add_index "comments", ["hidden_at"], name: "index_comments_on_hidden_at", using: :btree add_index "comments", ["user_id"], name: "index_comments_on_user_id", using: :btree create_table "debates", force: :cascade do |t| @@ -46,8 +47,11 @@ ActiveRecord::Schema.define(version: 20150815154430) do t.integer "author_id" t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.datetime "hidden_at" end + add_index "debates", ["hidden_at"], name: "index_debates_on_hidden_at", using: :btree + create_table "moderators", force: :cascade do |t| t.integer "user_id" end diff --git a/lib/acts_as_paranoid_aliases.rb b/lib/acts_as_paranoid_aliases.rb new file mode 100644 index 000000000..fac6b685b --- /dev/null +++ b/lib/acts_as_paranoid_aliases.rb @@ -0,0 +1,33 @@ +module ActsAsParanoidAliases + + def self.included(base) + base.extend(ClassMethods) + + def viewable? + not deleted? + end + + def hide + update_attribute(:hidden_at, Time.now) + end + + def hidden? + deleted? + end + end + + module ClassMethods + def with_hidden + with_deleted + end + + def only_hidden + only_deleted + end + end + +end + +module ActsAsParanoid + include ActsAsParanoidAliases +end \ No newline at end of file diff --git a/spec/models/ability_spec.rb b/spec/models/ability_spec.rb index 3954ec600..5486b286e 100644 --- a/spec/models/ability_spec.rb +++ b/spec/models/ability_spec.rb @@ -4,6 +4,7 @@ require 'cancan/matchers' describe Ability do subject(:ability) { Ability.new(user) } let(:debate) { Debate.new } + let(:comment) { create(:comment) } describe "Non-logged in user" do let(:user) { nil } @@ -53,6 +54,11 @@ describe Ability do it { should be_able_to(:show, debate) } it { should be_able_to(:vote, debate) } + it { should be_able_to(:hide, comment) } + it { should be_able_to(:hide, debate) } + + it { should_not be_able_to(:restore, comment) } + it { should_not be_able_to(:restore, debate) } end describe "Administrator" do @@ -63,5 +69,7 @@ describe Ability do it { should be_able_to(:show, debate) } it { should be_able_to(:vote, debate) } + it { should be_able_to(:restore, comment) } + it { should be_able_to(:restore, debate) } end end From d9a31b7158b094baf339a454d3782317c1aa0053 Mon Sep 17 00:00:00 2001 From: rgarcia Date: Mon, 17 Aug 2015 13:39:58 +0200 Subject: [PATCH 39/90] adds specs for moderation [#136] --- spec/factories.rb | 8 +++ spec/features/admin/comments_spec.rb | 28 +++++++++ spec/features/admin/debates_spec.rb | 23 ++++++++ spec/features/comments_spec.rb | 2 +- spec/features/moderation/comments_spec.rb | 72 +++++++++++++++++++++++ spec/features/moderation/debates_spec.rb | 24 ++++++++ 6 files changed, 156 insertions(+), 1 deletion(-) create mode 100644 spec/features/admin/comments_spec.rb create mode 100644 spec/features/admin/debates_spec.rb create mode 100644 spec/features/moderation/comments_spec.rb create mode 100644 spec/features/moderation/debates_spec.rb diff --git a/spec/factories.rb b/spec/factories.rb index 18e3e39de..3a06c4701 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -13,6 +13,10 @@ FactoryGirl.define do description 'Debate description' terms_of_service '1' association :author, factory: :user + + trait :hidden do + hidden_at Time.now + end end factory :vote do @@ -25,6 +29,10 @@ FactoryGirl.define do association :commentable, factory: :debate user body 'Comment body' + + trait :hidden do + hidden_at Time.now + end end factory :administrator do diff --git a/spec/features/admin/comments_spec.rb b/spec/features/admin/comments_spec.rb new file mode 100644 index 000000000..6e4a0d53d --- /dev/null +++ b/spec/features/admin/comments_spec.rb @@ -0,0 +1,28 @@ +require 'rails_helper' + +feature 'Admin comments' do + + scenario 'Restore', :js do + citizen = create(:user) + admin = create(:administrator) + + debate = create(:debate) + comment = create(:comment, :hidden, commentable: debate, body: 'Not really SPAM') + + login_as(admin.user) + visit admin_comments_path + + within("#comment_#{comment.id}") do + first(:link, "Restore").click + end + + expect(page).to have_content 'The comment has been restored' + + login_as(citizen) + visit debate_path(debate) + + expect(page).to have_css('.comment', count: 1) + expect(page).to have_content('Not really SPAM') + end + +end \ No newline at end of file diff --git a/spec/features/admin/debates_spec.rb b/spec/features/admin/debates_spec.rb new file mode 100644 index 000000000..50859816e --- /dev/null +++ b/spec/features/admin/debates_spec.rb @@ -0,0 +1,23 @@ +require 'rails_helper' + +feature 'Admin debates' do + + scenario 'Restore', :js do + citizen = create(:user) + admin = create(:administrator) + + debate = create(:debate, :hidden) + + login_as(admin.user) + visit admin_debate_path(debate) + + click_link 'Restore' + + expect(page).to have_content 'The debate has been restored' + + login_as(citizen) + visit debates_path + + expect(page).to have_css('.debate', count: 1) + end +end diff --git a/spec/features/comments_spec.rb b/spec/features/comments_spec.rb index 27a7ac1cd..b1f311be5 100644 --- a/spec/features/comments_spec.rb +++ b/spec/features/comments_spec.rb @@ -77,7 +77,7 @@ feature 'Comments' do click_button 'Publish reply' end - within("#comment-#{comment.id}") do + within "#comment_#{comment.id}" do expect(page).to have_content 'It will be done next week.' end diff --git a/spec/features/moderation/comments_spec.rb b/spec/features/moderation/comments_spec.rb new file mode 100644 index 000000000..099946d82 --- /dev/null +++ b/spec/features/moderation/comments_spec.rb @@ -0,0 +1,72 @@ +require 'rails_helper' + +feature 'Moderate Comments' do + + scenario 'Hide', :js do + citizen = create(:user) + moderator = create(:moderator) + + debate = create(:debate) + comment = create(:comment, commentable: debate, body: 'SPAM') + + login_as(moderator.user) + visit debate_path(debate) + + within("#comment_#{comment.id}") do + click_link 'Hide' + expect(page).to have_css('.comment .faded') + end + + login_as(citizen) + visit debate_path(debate) + + expect(page).to have_css('.comment', count: 1) + expect(page).to have_content('This comment has been deleted') + expect(page).to_not have_content('SPAM') + end + + scenario 'Children visible', :js do + citizen = create(:user) + moderator = create(:moderator) + + debate = create(:debate) + comment = create(:comment, commentable: debate, body: 'SPAM') + reply = create(:comment, commentable: debate, body: 'Acceptable reply', parent_id: comment.id) + + login_as(moderator.user) + visit debate_path(debate) + + within("#comment_#{comment.id}") do + first(:link, "Hide").click + expect(page).to have_css('.comment .faded') + end + + login_as(citizen) + visit debate_path(debate) + + expect(page).to have_css('.comment', count: 2) + expect(page).to have_content('This comment has been deleted') + expect(page).to_not have_content('SPAM') + + expect(page).to have_content('Acceptable reply') + end + + scenario 'Moderator actions' do + citizen = create(:user) + moderator = create(:moderator) + + debate = create(:debate) + comment = create(:comment, commentable: debate) + + login_as(moderator.user) + visit debate_path(debate) + + expect(page).to have_css("#moderator-comment-actions") + + login_as(citizen) + visit debate_path(debate) + + expect(page).to_not have_css("#moderator-comment-actions") + end + +end \ No newline at end of file diff --git a/spec/features/moderation/debates_spec.rb b/spec/features/moderation/debates_spec.rb new file mode 100644 index 000000000..388044a72 --- /dev/null +++ b/spec/features/moderation/debates_spec.rb @@ -0,0 +1,24 @@ +require 'rails_helper' + +feature 'Moderate debates' do + + scenario 'Hide', :js do + citizen = create(:user) + moderator = create(:moderator) + + debate = create(:debate) + + login_as(moderator.user) + visit debate_path(debate) + + within("#debate_#{debate.id}") do + click_link 'Hide' + end + + login_as(citizen) + visit debates_path + + expect(page).to have_css('.debate', count: 0) + end + +end \ No newline at end of file From fa6fa39cb6866e6122bd1e453337744062948044 Mon Sep 17 00:00:00 2001 From: rgarcia Date: Mon, 17 Aug 2015 13:55:27 +0200 Subject: [PATCH 40/90] removes unused method [#136] --- lib/acts_as_paranoid_aliases.rb | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/acts_as_paranoid_aliases.rb b/lib/acts_as_paranoid_aliases.rb index fac6b685b..066ce3e29 100644 --- a/lib/acts_as_paranoid_aliases.rb +++ b/lib/acts_as_paranoid_aliases.rb @@ -3,10 +3,6 @@ module ActsAsParanoidAliases def self.included(base) base.extend(ClassMethods) - def viewable? - not deleted? - end - def hide update_attribute(:hidden_at, Time.now) end From 74742d5f763abf7dd3877838f5a02260df46ec17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juanjo=20Baz=C3=A1n?= Date: Mon, 17 Aug 2015 14:08:59 +0200 Subject: [PATCH 41/90] adds i18n to deleted comments --- app/views/comments/_comment.html.erb | 2 +- config/locales/en.yml | 1 + config/locales/es.yml | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/views/comments/_comment.html.erb b/app/views/comments/_comment.html.erb index 1de4bfd7b..b81b64915 100644 --- a/app/views/comments/_comment.html.erb +++ b/app/views/comments/_comment.html.erb @@ -2,7 +2,7 @@
<% if comment.hidden? %> - This comment has been deleted + <%= t("debates.comment.deleted") %> <% else %> <%= avatar_image(comment.user, size: 32, class: 'left') %> diff --git a/config/locales/en.yml b/config/locales/en.yml index f1d7ba646..115a4aa57 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -38,6 +38,7 @@ en: one: 1 vote other: "%{count} votes" comment: + deleted: This comment has been deleted responses: zero: No Responses one: 1 Response diff --git a/config/locales/es.yml b/config/locales/es.yml index 45cfac0d5..0203e5d48 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -38,6 +38,7 @@ es: one: 1 voto other: "%{count} votos" comment: + deleted: Este comentario ha sido eliminado responses: zero: Sin respuestas one: 1 Respuesta From 1c435eacbea6cf7d7494d9cd20eb66b9a1e2eb6d Mon Sep 17 00:00:00 2001 From: kikito Date: Mon, 17 Aug 2015 14:25:43 +0200 Subject: [PATCH 42/90] adds filters to admin/organizations --- .../admin/organizations_controller.rb | 17 ++++- app/models/organization.rb | 5 ++ app/views/admin/organizations/index.html.erb | 18 ++++- config/i18n-tasks.yml | 1 + config/locales/admin.en.yml | 21 ++++-- config/locales/admin.es.yml | 8 ++- spec/features/admin/organizations_spec.rb | 71 +++++++++++++++++-- 7 files changed, 120 insertions(+), 21 deletions(-) diff --git a/app/controllers/admin/organizations_controller.rb b/app/controllers/admin/organizations_controller.rb index 6f88f8d90..c7e3c159c 100644 --- a/app/controllers/admin/organizations_controller.rb +++ b/app/controllers/admin/organizations_controller.rb @@ -1,19 +1,32 @@ class Admin::OrganizationsController < Admin::BaseController + before_filter :set_valid_filters + before_filter :parse_filter load_and_authorize_resource def index + @organizations = @organizations.send(@filter) @organizations = @organizations.includes(:user).order(:name, 'users.email') end def verify @organization.verify - redirect_to action: :index + redirect_to action: :index, filter: @filter end def reject @organization.reject - redirect_to action: :index + redirect_to action: :index, filter: @filter end + private + def set_valid_filters + @valid_filters = %w{all pending verified rejected} + end + + def parse_filter + @filter = params[:filter] + @filter = 'all' unless @valid_filters.include?(@filter) + end + end diff --git a/app/models/organization.rb b/app/models/organization.rb index 29bf01c2c..80e1a559a 100644 --- a/app/models/organization.rb +++ b/app/models/organization.rb @@ -3,9 +3,14 @@ class Organization < ActiveRecord::Base belongs_to :user validates :name, presence: true + validates :user_id, presence: true delegate :email, :phone_number, to: :user + scope :pending, -> { where(verified_at: nil, rejected_at: nil) } + scope :verified, -> { where("verified_at is not null and (rejected_at is null or rejected_at < verified_at)") } + scope :rejected, -> { where("rejected_at is not null and (verified_at is null or verified_at < rejected_at)") } + def verify update(verified_at: Time.now) end diff --git a/app/views/admin/organizations/index.html.erb b/app/views/admin/organizations/index.html.erb index d09f810d5..b22dfdbc9 100644 --- a/app/views/admin/organizations/index.html.erb +++ b/app/views/admin/organizations/index.html.erb @@ -2,6 +2,19 @@

<%= t('admin.organizations.index.title') %>

+

+ <%= t('admin.organizations.index.filter') %>: + + <% @valid_filters.each do |filter| %> + <% if @filter == filter %> + <%= t("admin.organizations.index.filters.#{filter}") %> + <% else %> + <%= link_to t("admin.organizations.index.filters.#{filter}"), + admin_organizations_path(filter: filter) %> + <% end %> + <% end %> +

+ <% @organizations.each do |organization| %> @@ -13,7 +26,7 @@ <% end %> <% if can? :verify, organization %> @@ -23,7 +36,7 @@ <% end %> <% if can? :reject, organization %> @@ -31,4 +44,5 @@ <% end %>
<%= link_to t('admin.organizations.index.verify'), - verify_admin_organization_path(organization), + verify_admin_organization_path(organization, filter: @filter), method: :put %> <%= link_to t('admin.organizations.index.reject'), - reject_admin_organization_path(organization), + reject_admin_organization_path(organization, filter: @filter), method: :put %>
+
diff --git a/config/i18n-tasks.yml b/config/i18n-tasks.yml index 5e6eb2899..e53b82334 100644 --- a/config/i18n-tasks.yml +++ b/config/i18n-tasks.yml @@ -94,6 +94,7 @@ ignore_missing: ## Consider these keys used: ignore_unused: - 'activerecord.*' + - 'admin.organizations.index.filter.*' # - '{devise,kaminari,will_paginate}.*' # - 'simple_form.{yes,no}' # - 'simple_form.{placeholders,hints,labels}.*' diff --git a/config/locales/admin.en.yml b/config/locales/admin.en.yml index d9a40ff41..34a25deaf 100644 --- a/config/locales/admin.en.yml +++ b/config/locales/admin.en.yml @@ -6,6 +6,19 @@ en: dashboard: index: title: Administration + organizations: + index: + title: Organizations + verify: Verify + reject: Reject + verified: Verified + rejected: Rejected + filter: Filtro + filters: + all: All + pending: Pending + verified: Verified + rejected: Rejected tags: index: title: 'Debate topics' @@ -14,10 +27,4 @@ en: name: placeholder: 'Write a topic' destroy: Delete Tag - organizations: - index: - title: Organizations - verify: Verify - reject: Reject - verified: Verified - rejected: Rejected + diff --git a/config/locales/admin.es.yml b/config/locales/admin.es.yml index bb1576045..86761f02c 100644 --- a/config/locales/admin.es.yml +++ b/config/locales/admin.es.yml @@ -14,9 +14,11 @@ es: verified: Verificado rejected: Rechazado filter: Filtro - filter_all: Todas - filter_verified: Verificadas - filter_rejected: Rechazadas + filters: + all: Todas + pending: Pendientes + verified: Verificadas + rejected: Rechazadas tags: index: title: 'Temas de debate' diff --git a/spec/features/admin/organizations_spec.rb b/spec/features/admin/organizations_spec.rb index 626eb9f9e..c3ac46d3d 100644 --- a/spec/features/admin/organizations_spec.rb +++ b/spec/features/admin/organizations_spec.rb @@ -14,8 +14,8 @@ feature 'Moderations::Organizations' do organization = create(:organization) visit admin_organizations_path - expect(page).to have_selector(:link_or_button, 'Verify') - expect(page).to have_selector(:link_or_button, 'Reject') + expect(page).to have_link('Verify') + expect(page).to have_link('Reject') click_on 'Verify' expect(current_path).to eq(admin_organizations_path) @@ -29,8 +29,8 @@ feature 'Moderations::Organizations' do visit admin_organizations_path expect(page).to have_content ('Verified') - expect(page).to_not have_selector(:link_or_button, 'Verify') - expect(page).to have_selector(:link_or_button, 'Reject') + expect(page).to_not have_link('Verify') + expect(page).to have_link('Reject') click_on 'Reject' expect(current_path).to eq(admin_organizations_path) @@ -43,9 +43,8 @@ feature 'Moderations::Organizations' do organization = create(:rejected_organization) visit admin_organizations_path - expect(page).to have_content ('Rejected') - expect(page).to have_selector(:link_or_button, 'Verify') - expect(page).to_not have_selector(:link_or_button, 'Reject') + expect(page).to have_link('Verify') + expect(page).to_not have_link('Reject', exact: true) click_on 'Verify' expect(current_path).to eq(admin_organizations_path) @@ -54,4 +53,62 @@ feature 'Moderations::Organizations' do expect(organization.reload.verified?).to eq(true) end + scenario "Current filter is properly highlighted" do + visit admin_organizations_path + expect(page).to_not have_link('All') + expect(page).to have_link('Pending') + expect(page).to have_link('Verified') + expect(page).to have_link('Rejected') + + visit admin_organizations_path(filter: 'all') + expect(page).to_not have_link('All') + expect(page).to have_link('Pending') + expect(page).to have_link('Verified') + expect(page).to have_link('Rejected') + + visit admin_organizations_path(filter: 'pending') + expect(page).to have_link('All') + expect(page).to_not have_link('Pending') + expect(page).to have_link('Verified') + expect(page).to have_link('Rejected') + + visit admin_organizations_path(filter: 'verified') + expect(page).to have_link('All') + expect(page).to have_link('Pending') + expect(page).to_not have_link('Verified') + expect(page).to have_link('Rejected') + + visit admin_organizations_path(filter: 'rejected') + expect(page).to have_link('All') + expect(page).to have_link('Pending') + expect(page).to have_link('Verified') + expect(page).to_not have_link('Rejected') + end + + scenario "Filtering organizations" do + create(:organization, name: "Pending Organization") + create(:rejected_organization, name: "Rejected Organization") + create(:verified_organization, name: "Verified Organization") + + visit admin_organizations_path(filter: 'all') + expect(page).to have_content('Pending Organization') + expect(page).to have_content('Rejected Organization') + expect(page).to have_content('Verified Organization') + + visit admin_organizations_path(filter: 'pending') + expect(page).to have_content('Pending Organization') + expect(page).to_not have_content('Rejected Organization') + expect(page).to_not have_content('Verified Organization') + + visit admin_organizations_path(filter: 'verified') + expect(page).to_not have_content('Pending Organization') + expect(page).to_not have_content('Rejected Organization') + expect(page).to have_content('Verified Organization') + + visit admin_organizations_path(filter: 'rejected') + expect(page).to_not have_content('Pending Organization') + expect(page).to have_content('Rejected Organization') + expect(page).to_not have_content('Verified Organization') + end + end From 43f8db8e3a53727349c65590bc1cc3ebb2fbbcab Mon Sep 17 00:00:00 2001 From: Alberto Garcia Cabeza Date: Mon, 17 Aug 2015 14:53:00 +0200 Subject: [PATCH 43/90] Adds styles for titles and filters --- app/assets/stylesheets/debates.scss | 5 -- app/assets/stylesheets/participacion.scss | 35 +++++++++++- app/views/debates/index.html.erb | 65 ++++++++++++++++++++++- app/views/welcome/index.html.erb | 9 +++- config/locales/en.yml | 10 ++++ config/locales/es.yml | 22 ++++---- 6 files changed, 124 insertions(+), 22 deletions(-) diff --git a/app/assets/stylesheets/debates.scss b/app/assets/stylesheets/debates.scss index f242e8757..76c4405ed 100644 --- a/app/assets/stylesheets/debates.scss +++ b/app/assets/stylesheets/debates.scss @@ -138,10 +138,6 @@ // 02. Index // - - - - - - - - - - - - - - - - - - - - - - - - - -.featured-debates { - margin-top: rem-calc(23); -} - .debate-featured { .panel { @@ -244,7 +240,6 @@ .debates-list { margin-bottom: rem-calc(48); - margin-top: rem-calc(24); } .debate { diff --git a/app/assets/stylesheets/participacion.scss b/app/assets/stylesheets/participacion.scss index 6e5ad3d7b..49ac151fe 100644 --- a/app/assets/stylesheets/participacion.scss +++ b/app/assets/stylesheets/participacion.scss @@ -10,6 +10,7 @@ // 08. Forms // 09. Alerts // 10. User account +// 11. Filters // // 01. Variables @@ -111,7 +112,6 @@ h1, h2, h3, h4, h5, h6 { } .sidebar { - margin-top: rem-calc(24); margin-bottom: rem-calc(48); } @@ -556,3 +556,36 @@ form { } } } + +// 11. Filters +// - - - - - - - - - - - - - - - - - - - - - - - - - + +.filters { + + h2 { + display: inline-block; + font-size: rem-calc(24); + margin: rem-calc(24) 0; + } + + select { + height: auto; + margin: rem-calc(24) rem-calc(6); + min-width: rem-calc(180); + outline: 0; + padding: rem-calc(12); + width: auto; + + optgroup { + font-size: rem-calc(14); + } + + option { + + &:after { + content: "a"; + font-family: "icons"; + } + } + } +} diff --git a/app/views/debates/index.html.erb b/app/views/debates/index.html.erb index 3a3881b76..6fa0db61c 100644 --- a/app/views/debates/index.html.erb +++ b/app/views/debates/index.html.erb @@ -1,6 +1,67 @@
+ + +
+
+

<%= t("debates.index.showing") %>

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

<%= t("debates.index.showing") %>

+ + + +

<%= t("debates.index.tag") %>

+ + + +

(43)

+
+
+ +
-
+
<%= render @debates %>
@@ -10,4 +71,4 @@
-
\ No newline at end of file + diff --git a/app/views/welcome/index.html.erb b/app/views/welcome/index.html.erb index 434499859..ada595c02 100644 --- a/app/views/welcome/index.html.erb +++ b/app/views/welcome/index.html.erb @@ -1,5 +1,10 @@
-
\ No newline at end of file + diff --git a/config/locales/en.yml b/config/locales/en.yml index f1d7ba646..4ae370798 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -27,6 +27,14 @@ en: debates: index: create_debate: Create a debate + showing: You are seeing + tag: with the topic + filter_debates: debates + filter_initiatives: initiatives + filter_debates_and_initiatives: debates and initiatives + filter_news: the newest + filter_votes: the most voted + filter_rated: the best rated debate: debate: Debate comments: @@ -124,3 +132,5 @@ en: default: "You are not authorized to access this page." manage: all: "You are not authorized to %{action} %{subject}." + welcome: + featured_debates: Features debates diff --git a/config/locales/es.yml b/config/locales/es.yml index 45cfac0d5..bc5ee7df1 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -27,6 +27,14 @@ es: debates: index: create_debate: Crea un debate + showing: "Estás viendo" + tag: "con el tema" + filter_debates: debates + filter_initiatives: iniciativas + filter_debates_and_initiatives: debates e iniciativas + filter_news: "más nuevos" + filter_votes: "más votados" + filter_rated: mejor valorados debate: debate: Debate comments: @@ -122,17 +130,7 @@ es: subject: Alguien ha respondido a tu comentario unauthorized: default: "No tienes permiso para acceder a esta página." - index: - all: "No tienes permiso para listar %{subject}" - show: - all: "No tienes permiso para ver %{subject}" - edit: - all: "No tienes permiso para editar %{subject}" - update: - all: "No tienes permiso para modificar %{subject}" - create: - all: "No tienes permiso para crear %{subject}" - delete: - all: "No tienes permiso para borrar %{subject}" manage: all: "No tienes permiso para realizar la acción '%{action}' sobre %{subject}." + welcome: + featured_debates: Debates destacados From 4b59105be58f7292c55f9e349b4ff1b7801bf9d4 Mon Sep 17 00:00:00 2001 From: Alberto Garcia Cabeza Date: Mon, 17 Aug 2015 15:00:59 +0200 Subject: [PATCH 44/90] Adds filters for topics --- app/views/debates/index.html.erb | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/views/debates/index.html.erb b/app/views/debates/index.html.erb index 6fa0db61c..0e3e41d1d 100644 --- a/app/views/debates/index.html.erb +++ b/app/views/debates/index.html.erb @@ -49,6 +49,18 @@ + +

<%= t("debates.index.tag") %>