From 7bf6aa24509b4a98572c7e6459fcde17624d7448 Mon Sep 17 00:00:00 2001 From: Michael Parrish Date: Wed, 24 Feb 2016 10:50:48 -0600 Subject: [PATCH] Whitespace --- Vagrantfile | 10 +- app/controllers/application_controller.rb | 22 +- app/controllers/comments_controller.rb | 12 +- app/controllers/concerns/action_rendering.rb | 10 +- app/controllers/concerns/action_rescuing.rb | 32 +-- app/controllers/concerns/talk_resource.rb | 6 +- app/controllers/conversations_controller.rb | 6 +- app/controllers/moderations_controller.rb | 10 +- app/controllers/notifications_controller.rb | 2 +- app/controllers/searches_controller.rb | 28 +-- .../subscription_preferences_controller.rb | 2 +- app/controllers/tags_controller.rb | 4 +- app/controllers/unsubscribe_controller.rb | 12 +- app/helpers/mailer_helper.rb | 8 +- app/mailers/notification_mailer.rb | 22 +- app/models/announcement.rb | 8 +- app/models/board.rb | 18 +- app/models/collection.rb | 8 +- app/models/comment.rb | 40 ++-- app/models/concerns/boolean_coercion.rb | 2 +- app/models/concerns/comment/mentioning.rb | 18 +- app/models/concerns/comment/publishing.rb | 8 +- app/models/concerns/comment/subscribing.rb | 10 +- app/models/concerns/comment/tagging.rb | 12 +- app/models/concerns/discussion/subscribing.rb | 8 +- app/models/concerns/expirable.rb | 4 +- app/models/concerns/focusable.rb | 10 +- app/models/concerns/hash_changes.rb | 8 +- app/models/concerns/hstore_update.rb | 12 +- app/models/concerns/moderatable.rb | 6 +- app/models/concerns/notifiable.rb | 2 +- app/models/concerns/searchable.rb | 14 +- app/models/concerns/searchable/model.rb | 12 +- app/models/concerns/searchable/querying.rb | 6 +- app/models/concerns/sectioned.rb | 4 +- app/models/concerns/subscribable.rb | 2 +- .../concerns/subscription_categories.rb | 4 +- app/models/conversation.rb | 12 +- app/models/data_request.rb | 18 +- app/models/discussion.rb | 30 +-- app/models/group_mention.rb | 12 +- app/models/mention.rb | 12 +- app/models/message.rb | 22 +- app/models/moderation.rb | 28 +-- app/models/notification.rb | 12 +- app/models/oauth_access_token.rb | 10 +- app/models/role.rb | 4 +- app/models/search.rb | 14 +- app/models/searchable_collection.rb | 2 +- app/models/subscription.rb | 14 +- app/models/subscription_preference.rb | 12 +- app/models/tag.rb | 8 +- app/models/unsubscribe_token.rb | 8 +- app/models/user.rb | 22 +- app/models/user_conversation.rb | 10 +- app/models/user_ip_ban.rb | 2 +- app/policies/announcement_policy.rb | 10 +- app/policies/application_policy.rb | 50 ++-- app/policies/blocked_user_policy.rb | 10 +- app/policies/board_policy.rb | 34 +-- app/policies/comment_policy.rb | 32 +-- app/policies/conversation_policy.rb | 10 +- app/policies/data_request_policy.rb | 10 +- app/policies/discussion_policy.rb | 22 +- app/policies/mention_policy.rb | 16 +- app/policies/message_policy.rb | 10 +- app/policies/moderation_policy.rb | 16 +- app/policies/notification_policy.rb | 10 +- app/policies/role_policy.rb | 10 +- app/policies/subscription_policy.rb | 10 +- .../subscription_preference_policy.rb | 10 +- app/policies/tag_policy.rb | 20 +- app/policies/user_conversation_policy.rb | 10 +- app/policies/user_ip_ban_policy.rb | 10 +- app/policies/user_policy.rb | 10 +- app/schemas/announcement_schema.rb | 8 +- app/schemas/blocked_user_schema.rb | 6 +- app/schemas/board_schema.rb | 12 +- app/schemas/comment_schema.rb | 8 +- app/schemas/concerns/focus_schema.rb | 2 +- app/schemas/conversation_schema.rb | 8 +- app/schemas/data_request_schema.rb | 6 +- app/schemas/discussion_schema.rb | 10 +- app/schemas/message_schema.rb | 6 +- app/schemas/moderation_schema.rb | 14 +- app/schemas/notification_schema.rb | 6 +- app/schemas/role_schema.rb | 8 +- app/schemas/subscription_preference_schema.rb | 6 +- app/schemas/subscription_schema.rb | 6 +- app/schemas/user_ip_ban_schema.rb | 6 +- app/serializers/announcement_serializer.rb | 2 +- app/serializers/blocked_user_serializer.rb | 2 +- app/serializers/board_serializer.rb | 4 +- app/serializers/comment_serializer.rb | 16 +- .../concerns/embedded_attributes.rb | 14 +- .../concerns/moderation_actions.rb | 2 +- app/serializers/concerns/talk_serializer.rb | 28 +-- app/serializers/conversation_serializer.rb | 6 +- app/serializers/discussion_serializer.rb | 8 +- app/serializers/mention_serializer.rb | 2 +- app/serializers/moderation_serializer.rb | 6 +- app/serializers/notification_serializer.rb | 8 +- .../popular_focus_tag_serializer.rb | 4 +- app/serializers/popular_tag_serializer.rb | 4 +- app/serializers/user_serializer.rb | 2 +- app/services/application_service.rb | 2 +- app/services/comment_service.rb | 8 +- app/services/concerns/talk_service.rb | 44 ++-- app/services/conversation_service.rb | 16 +- app/services/discussion_service.rb | 6 +- app/services/message_service.rb | 8 +- app/services/moderation_service.rb | 18 +- app/services/user_ip_ban_service.rb | 2 +- app/views/layouts/application.html.erb | 14 +- app/views/notification_mailer/notify.html.erb | 42 ++-- app/workers/announcement_worker.rb | 4 +- app/workers/board_visibility_worker.rb | 4 +- app/workers/collection_refresh_worker.rb | 6 +- app/workers/comment_export_worker.rb | 12 +- app/workers/comment_publish_worker.rb | 4 +- app/workers/comment_subscription_worker.rb | 4 +- app/workers/concerns/data_export_worker.rb | 24 +- app/workers/concerns/digest_email_worker.rb | 8 +- app/workers/concerns/expiry_worker.rb | 8 +- app/workers/daily_digest_email_worker.rb | 2 +- app/workers/discussion_subscription_worker.rb | 4 +- app/workers/group_mention_worker.rb | 4 +- app/workers/mention_worker.rb | 4 +- app/workers/message_notification_worker.rb | 4 +- app/workers/moderation_notification_worker.rb | 4 +- app/workers/notification_email_worker.rb | 10 +- app/workers/notification_worker.rb | 4 +- app/workers/tag_export_worker.rb | 8 +- app/workers/weekly_digest_email_worker.rb | 2 +- config/application.rb | 14 +- config/environments/development.rb | 4 +- config/environments/production.rb | 4 +- config/environments/staging.rb | 4 +- config/environments/test.rb | 2 +- config/initializers/inflections.rb | 2 +- config/initializers/zoo_stream.rb | 2 +- config/routes.rb | 2 +- db/dev_seeds.rb | 16 +- ...0141118131943_add_external_primary_keys.rb | 2 +- ...18135801_remove_denormalized_attributes.rb | 2 +- .../20141119114610_allow_null_focus_type.rb | 2 +- ...0141203180103_change_moderation_actions.rb | 2 +- ...141203204039_add_section_to_moderations.rb | 2 +- db/migrate/20141219164858_require_section.rb | 2 +- db/migrate/20150116153904_remove_jsonb.rb | 2 +- db/migrate/20150213181551_add_indexes.rb | 20 +- db/migrate/20150226200251_remove_focuses.rb | 2 +- db/migrate/20150304161605_remove_users.rb | 2 +- ...20150327164704_create_searchable_boards.rb | 2 +- ...327201946_create_searchable_discussions.rb | 2 +- ...150327202155_create_searchable_comments.rb | 2 +- ...0150331175344_add_searchable_type_index.rb | 6 +- ...407210341_allow_null_moderation_targets.rb | 2 +- .../20150429150018_create_notifications.rb | 2 +- .../20150429150504_create_announcements.rb | 2 +- .../20150522193855_create_subscriptions.rb | 2 +- ...2195539_create_subscription_preferences.rb | 2 +- ...72525_change_discussion_sticky_position.rb | 2 +- ...bject_default_to_boards_and_discussions.rb | 2 +- .../20150709212510_create_user_ip_bans.rb | 2 +- .../20150729203853_migrate_project_ids.rb | 4 +- ...0150821172624_create_unsubscribe_tokens.rb | 2 +- ...2_add_comment_created_at_to_discussions.rb | 4 +- ...175254_add_comment_created_at_to_boards.rb | 4 +- .../20150930181940_add_board_positions.rb | 4 +- ...38_add_multiple_sections_to_searchables.rb | 2 +- .../20160111193434_drop_unused_indexes.rb | 10 +- lib/front_end.rb | 8 +- lib/invalid_search_type_error.rb | 2 +- lib/markdown_api.rb | 8 +- lib/pundit/not_authorized_error.rb | 4 +- lib/subscription_uniqueness_validator.rb | 6 +- lib/sugar.rb | 8 +- lib/tasks/db.rake | 70 +++--- .../migrate_last_comment_created_at.rake | 2 +- lib/tasks/migrate_section.rake | 6 +- lib/uploader.rb | 14 +- .../announcements_controller_spec.rb | 14 +- .../application_controller_spec.rb | 20 +- .../blocked_users_controller_spec.rb | 12 +- spec/controllers/boards_controller_spec.rb | 8 +- spec/controllers/comments_controller_spec.rb | 58 ++--- .../conversations_controller_spec.rb | 50 ++-- .../data_requests_controller_spec.rb | 10 +- .../discussions_controller_spec.rb | 14 +- spec/controllers/messages_controller_spec.rb | 12 +- .../moderations_controller_spec.rb | 36 +-- .../notifications_controller_spec.rb | 28 +-- spec/controllers/roles_controller_spec.rb | 10 +- spec/controllers/searches_controller_spec.rb | 82 +++---- ...ubscription_preferences_controller_spec.rb | 10 +- .../subscriptions_controller_spec.rb | 12 +- spec/controllers/tags_controller_spec.rb | 18 +- .../unsubscribe_controller_spec.rb | 10 +- .../user_ip_bans_controller_spec.rb | 10 +- spec/controllers/users_controller_spec.rb | 4 +- spec/factories/boards.rb | 8 +- spec/factories/collections.rb | 2 +- spec/factories/comments.rb | 2 +- spec/factories/common.rb | 2 +- spec/factories/conversations.rb | 10 +- spec/factories/data_requests.rb | 4 +- spec/factories/discussions.rb | 4 +- spec/factories/messages.rb | 2 +- spec/factories/moderations.rb | 6 +- spec/factories/oauth_access_tokens.rb | 4 +- spec/factories/roles.rb | 2 +- spec/factories/tags.rb | 6 +- spec/factories/users.rb | 12 +- spec/lib/front_end_spec.rb | 44 ++-- spec/lib/markdown_api_spec.rb | 28 +-- spec/lib/sugar_spec.rb | 34 +-- spec/lib/uploader_spec.rb | 28 +-- spec/mailers/notification_mailer_spec.rb | 124 +++++----- spec/models/announcement_spec.rb | 6 +- spec/models/blocked_user_spec.rb | 8 +- spec/models/board_spec.rb | 30 +-- spec/models/comment_spec.rb | 214 +++++++++--------- spec/models/conversation_spec.rb | 28 +-- spec/models/data_request_spec.rb | 38 ++-- spec/models/discussion_spec.rb | 80 +++---- spec/models/group_mention_spec.rb | 26 +-- spec/models/mention_spec.rb | 12 +- spec/models/message_spec.rb | 54 ++--- spec/models/moderation_spec.rb | 52 ++--- spec/models/notification_spec.rb | 16 +- spec/models/oauth_access_token_spec.rb | 18 +- spec/models/role_spec.rb | 2 +- spec/models/search_spec.rb | 20 +- spec/models/subscription_preference_spec.rb | 32 +-- spec/models/subscription_spec.rb | 30 +-- spec/models/tag_spec.rb | 18 +- spec/models/unsubscribe_token_spec.rb | 18 +- spec/models/user_conversation_spec.rb | 12 +- spec/models/user_ip_ban_spec.rb | 8 +- spec/models/user_spec.rb | 62 ++--- spec/policies/announcement_policy_spec.rb | 8 +- spec/policies/application_policy_spec.rb | 40 ++-- spec/policies/blocked_user_policy_spec.rb | 16 +- spec/policies/board_policy_spec.rb | 62 ++--- spec/policies/comment_policy_spec.rb | 92 ++++---- spec/policies/conversation_policy_spec.rb | 14 +- spec/policies/data_request_policy_spec.rb | 22 +- spec/policies/discussion_policy_spec.rb | 80 +++---- spec/policies/mention_policy_spec.rb | 78 +++---- spec/policies/message_policy_spec.rb | 14 +- spec/policies/moderation_policy_spec.rb | 12 +- spec/policies/notification_policy_spec.rb | 10 +- spec/policies/role_policy_spec.rb | 12 +- spec/policies/subscription_policy_spec.rb | 10 +- .../subscription_preference_policy_spec.rb | 10 +- spec/policies/tag_policy_spec.rb | 84 +++---- .../policies/user_conversation_policy_spec.rb | 14 +- spec/policies/user_ip_ban_policy_spec.rb | 8 +- spec/policies/user_policy_spec.rb | 8 +- spec/schemas/announcement_schema_spec.rb | 12 +- spec/schemas/blocked_user_schema_spec.rb | 8 +- spec/schemas/board_schema_spec.rb | 20 +- spec/schemas/comment_schema_spec.rb | 18 +- spec/schemas/conversation_schema_spec.rb | 10 +- spec/schemas/data_request_schema_spec.rb | 8 +- spec/schemas/discussion_schema_spec.rb | 28 +-- spec/schemas/message_schema_spec.rb | 8 +- spec/schemas/moderation_schema_spec.rb | 24 +- spec/schemas/notification_schema_spec.rb | 8 +- spec/schemas/role_schema_spec.rb | 18 +- .../subscription_preference_schema_spec.rb | 10 +- spec/schemas/subscription_schema_spec.rb | 10 +- spec/schemas/user_ip_ban_schema_spec.rb | 8 +- spec/serializers/comment_serializer_spec.rb | 4 +- .../conversation_serializer_spec.rb | 14 +- .../serializers/discussion_serializer_spec.rb | 8 +- .../serializers/moderation_serializer_spec.rb | 8 +- .../notification_serializer_spec.rb | 2 +- spec/serializers/user_serializer_spec.rb | 2 +- spec/services/announcement_service_spec.rb | 2 +- spec/services/application_service_spec.rb | 2 +- spec/services/blocked_user_service_spec.rb | 2 +- spec/services/board_service_spec.rb | 2 +- spec/services/comment_service_spec.rb | 16 +- spec/services/conversation_service_spec.rb | 22 +- spec/services/data_request_service_spec.rb | 2 +- spec/services/discussion_service_spec.rb | 28 +-- spec/services/message_service_spec.rb | 12 +- spec/services/moderation_service_spec.rb | 24 +- spec/services/role_service_spec.rb | 2 +- spec/services/subscription_service_spec.rb | 2 +- spec/services/user_ip_ban_service_spec.rb | 2 +- spec/spec_helper.rb | 10 +- spec/support/policy_matcher.rb | 8 +- .../shared_context_for_mangled_params.rb | 2 +- .../shared_examples_for_controller_actions.rb | 22 +- ..._examples_for_controller_authenticating.rb | 6 +- ...shared_examples_for_controller_creating.rb | 26 +-- ...hared_examples_for_controller_rendering.rb | 10 +- ...shared_examples_for_controller_rescuing.rb | 48 ++-- ...red_examples_for_controller_restricting.rb | 12 +- ...shared_examples_for_controller_updating.rb | 26 +-- .../shared_examples_for_controllers.rb | 6 +- .../shared_examples_for_data_export_worker.rb | 76 +++---- ...hared_examples_for_digest_email_workers.rb | 8 +- ...shared_examples_for_embedded_attributes.rb | 6 +- spec/support/shared_examples_for_expirable.rb | 10 +- .../shared_examples_for_moderatable.rb | 20 +- ...red_examples_for_moderatable_serializer.rb | 12 +- .../support/shared_examples_for_notifiable.rb | 2 +- .../support/shared_examples_for_searchable.rb | 42 ++-- ...hared_examples_for_searchable_interface.rb | 68 +++--- spec/support/shared_examples_for_sectioned.rb | 12 +- spec/support/shared_examples_for_services.rb | 80 +++---- .../shared_examples_for_subscribable.rb | 4 +- .../shared_examples_for_talk_serializer.rb | 16 +- spec/support/validation_matcher.rb | 12 +- spec/workers/announcement_worker_spec.rb | 4 +- spec/workers/board_visibility_worker_spec.rb | 4 +- .../workers/collection_refresh_worker_spec.rb | 6 +- spec/workers/comment_export_worker_spec.rb | 38 ++-- spec/workers/comment_publish_worker_spec.rb | 6 +- .../comment_subscription_worker_spec.rb | 4 +- .../discussion_subscription_worker_spec.rb | 4 +- spec/workers/group_mention_worker_spec.rb | 4 +- spec/workers/mention_worker_spec.rb | 4 +- .../message_notification_worker_spec.rb | 4 +- .../moderation_notification_worker_spec.rb | 4 +- .../workers/notification_email_worker_spec.rb | 22 +- spec/workers/notification_worker_spec.rb | 12 +- spec/workers/tag_export_worker_spec.rb | 16 +- 332 files changed, 2439 insertions(+), 2439 deletions(-) diff --git a/Vagrantfile b/Vagrantfile index f9ab70e0..95007369 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -7,20 +7,20 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| config.vm.box_url = 'https://cloud-images.ubuntu.com/vagrant/trusty/current/trusty-server-cloudimg-amd64-vagrant-disk1.box' config.vm.network :forwarded_port, guest: 80, host: 3000 # puma config.vm.network :forwarded_port, guest: 5432, host: 5433 # postgres - + config.vm.provision :shell, inline: 'mkdir -p /postgres_data' - + config.vm.provision 'docker' do |d| d.pull_images 'zooniverse/postgresql' - + d.run 'zooniverse/postgresql', args: '--name pg --publish 5432:5432 --env PG_USER="talk" --env DB="talk_staging" --env PASS="talk" -v /postgres_data:/data' - + d.build_image '/vagrant', args: '-t zooniverse/talk' d.run 'talk', image: 'zooniverse/talk', args: '--publish 80:80 --link pg:pg -v /vagrant:/rails_app --env TALK_DB_PASSWORD="talk" --env SECRET_KEY_BASE="79c13497d5c6bf783c544ad058c28469b101c9612ea607b335863573f37fe4201233b7f044ba5b5923d2e1aa709e4a0bb61f4dbb6a9b9c9dbd03ffeb21c9382d" --env RACK_ENV=staging --env RAILS_ENV=staging --env VAGRANT_APP=1' end - + config.vm.provider :virtualbox do |vb| vb.customize ['modifyvm', :id, '--memory', '2048'] vb.customize ['modifyvm', :id, '--cpus', '4'] diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index d47e76af..4be33256 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -2,11 +2,11 @@ class ApplicationController < ActionController::Base include Pundit include ActionRendering include ActionRescuing - + before_action :set_format before_action :enforce_ban, if: ->{ current_user } before_action :enforce_ip_ban - + def root authorize :application, :index? respond_to do |format| @@ -14,7 +14,7 @@ def root format.json{ render json: resources } end end - + def resources { announcements: { href: '/announcements', type: 'announcements' }, @@ -36,21 +36,21 @@ def resources users: { href: '/users', type: 'users' } } end - + def set_format request.format = :json if request.format == :json_api end - + def sinkhole raise ActionController::RoutingError.new 'Not found' end - + def log_event!(label, payload) EventLog.create label: label, user_id: current_user.try(:id), payload: payload rescue nil end - + concerning :Authentication do def bearer_token return @bearer_token if @bearer_token @@ -59,21 +59,21 @@ def bearer_token @bearer_token end end - + concerning :CurrentUser do def current_user return unless bearer_token @current_user ||= User.from_panoptes bearer_token end - + def current_user_ip request.remote_ip end - + def enforce_ban raise Talk::BannedUserError if current_user.banned? end - + def enforce_ip_ban raise Talk::BannedUserError if UserIpBan.banned?(request) end diff --git a/app/controllers/comments_controller.rb b/app/controllers/comments_controller.rb index c3923fe6..be502617 100644 --- a/app/controllers/comments_controller.rb +++ b/app/controllers/comments_controller.rb @@ -1,29 +1,29 @@ class CommentsController < ApplicationController include TalkResource - + before_action :filter_subject_default, on: :index, if: ->{ params.has_key?(:subject_default) && params[:section] } - + def upvote service.upvote render json: serializer_class.resource(service.resource, nil, current_user: current_user) end - + def remove_upvote service.remove_upvote render json: serializer_class.resource(service.resource, nil, current_user: current_user) end - + def destroy comment = Comment.find params[:id] authorize comment comment.soft_destroy render json: { }, status: :no_content end - + protected - + def filter_subject_default board_ids = Board.where(section: params[:section], subject_default: params[:subject_default]).pluck :id params[:board_id] = board_ids.join ',' diff --git a/app/controllers/concerns/action_rendering.rb b/app/controllers/concerns/action_rendering.rb index bec67ecb..597fbe18 100644 --- a/app/controllers/concerns/action_rendering.rb +++ b/app/controllers/concerns/action_rendering.rb @@ -1,28 +1,28 @@ module ActionRendering extend ActiveSupport::Concern - + def index authorize model_class scoped = policy_scope model_class params[:sort] ||= serializer_class.default_sort if serializer_class.default_sort render json: serializer_class.page(params, scoped, current_user: current_user) end - + def show authorize model_class.where(id: resource_ids) render json: serializer_class.resource(params, nil, current_user: current_user) end - + def create service.create render json: serializer_class.resource(service.resource, nil, current_user: current_user) end - + def update service.update render json: serializer_class.resource(service.resource, nil, current_user: current_user) end - + def destroy instance = model_class.find params[:id] authorize instance diff --git a/app/controllers/concerns/action_rescuing.rb b/app/controllers/concerns/action_rescuing.rb index 091a5b45..b493c2ca 100644 --- a/app/controllers/concerns/action_rescuing.rb +++ b/app/controllers/concerns/action_rescuing.rb @@ -1,27 +1,27 @@ module ActionRescuing extend ActiveSupport::Concern - + included do rescue_from ActiveRecord::RecordNotFound, with: :not_found rescue_from ActiveRecord::RecordInvalid, with: :bad_request rescue_from ActiveRecord::RecordNotUnique, with: :conflict rescue_from ActiveRecord::UnknownAttributeError, with: :unprocessable rescue_from ActiveRecord::RecordNotDestroyed, with: :unauthorized - + rescue_from ActionController::UnpermittedParameters, with: :unprocessable rescue_from ActionController::RoutingError, with: :not_found rescue_from ActionController::ParameterMissing, with: :unprocessable - + rescue_from JSON::Schema::ValidationError, with: :unprocessable - + rescue_from Pundit::AuthorizationNotPerformedError, with: :not_implemented rescue_from Pundit::PolicyScopingNotPerformedError, with: :not_implemented rescue_from Pundit::NotDefinedError, with: :not_implemented rescue_from Pundit::NotAuthorizedError, with: :unauthorized - + rescue_from RangeError, with: :bad_request rescue_from RestPack::Serializer::InvalidInclude, with: :bad_request - + rescue_from TalkService::ParameterError, with: :unprocessable rescue_from Talk::InvalidParameterError, with: :unprocessable rescue_from Talk::BannedUserError, with: :forbidden @@ -30,7 +30,7 @@ module ActionRescuing rescue_from OauthAccessToken::ExpiredError, with: :unauthorized rescue_from OauthAccessToken::RevokedError, with: :unauthorized end - + module ClassMethods def disallow(*methods) methods.each do |method| @@ -40,39 +40,39 @@ def disallow(*methods) end end end - + def unauthorized(exception) render_exception :unauthorized, exception end - + def forbidden(exception) render_exception :forbidden, exception end - + def not_found(exception) render_exception :not_found, exception end - + def not_allowed(exception) render_exception :method_not_allowed, exception end - + def bad_request(exception) render_exception :bad_request, exception end - + def unprocessable(exception) render_exception :unprocessable_entity, exception end - + def not_implemented(exception) render_exception :not_implemented, exception end - + def conflict(exception) render_exception :conflict, exception end - + def render_exception(status, exception) self.response_body = nil render status: status, json: { error: exception.message } diff --git a/app/controllers/concerns/talk_resource.rb b/app/controllers/concerns/talk_resource.rb index 3ac80790..6cd08912 100644 --- a/app/controllers/concerns/talk_resource.rb +++ b/app/controllers/concerns/talk_resource.rb @@ -1,6 +1,6 @@ module TalkResource extend ActiveSupport::Concern - + included do class_attribute :model_class, :serializer_class, :schema_class, :serializer_class, :service_class self.model_class = name.sub(/Controller$/, '').singularize.constantize rescue nil @@ -9,7 +9,7 @@ module TalkResource self.service_class = "#{ model_class.name }Service".constantize rescue ApplicationService attr_accessor :resource end - + def service @service ||= service_class.new({ params: params, @@ -20,7 +20,7 @@ def service user_ip: current_user_ip }) end - + def resource_ids if params[:id].is_a? String params[:id].split(',').compact.collect &:to_i diff --git a/app/controllers/conversations_controller.rb b/app/controllers/conversations_controller.rb index 3298ac04..5306b251 100644 --- a/app/controllers/conversations_controller.rb +++ b/app/controllers/conversations_controller.rb @@ -1,7 +1,7 @@ class ConversationsController < ApplicationController include TalkResource disallow :update - + def index authorize model_class scoped = policy_scope model_class @@ -9,12 +9,12 @@ def index params[:sort] ||= serializer_class.default_sort if serializer_class.default_sort render json: serializer_class.page(params, scoped, current_user: current_user) end - + def show super Conversation.mark_as_read_by resource_ids, current_user.id end - + def destroy conversation = Conversation.find params[:id] authorize conversation diff --git a/app/controllers/moderations_controller.rb b/app/controllers/moderations_controller.rb index 3b284545..c55d0fae 100644 --- a/app/controllers/moderations_controller.rb +++ b/app/controllers/moderations_controller.rb @@ -1,15 +1,15 @@ class ModerationsController < ApplicationController include TalkResource - + before_action :invert_enums, only: [:index], if: ->{ params.has_key? :state } - + def create service.create render json: serializer_class.resource(service.resource, nil, current_user: current_user, **sanitize_resource) end - + protected - + def sanitize_resource { }.tap do |list| %w(actioned_at actions created_at reports destroyed_target user_ip).each do |attr| @@ -17,7 +17,7 @@ def sanitize_resource end end end - + def invert_enums inverted_state = Moderation.states[params[:state]] raise Talk::InvalidParameterError.new(:state, "in #{ Moderation.states.keys }", params[:state]) unless inverted_state diff --git a/app/controllers/notifications_controller.rb b/app/controllers/notifications_controller.rb index 8ebe4aac..dd823353 100644 --- a/app/controllers/notifications_controller.rb +++ b/app/controllers/notifications_controller.rb @@ -1,7 +1,7 @@ class NotificationsController < ApplicationController include TalkResource disallow :create, :destroy - + def read raise Pundit::NotAuthorizedError.new('not logged in') unless current_user scoped = policy_scope model_class diff --git a/app/controllers/searches_controller.rb b/app/controllers/searches_controller.rb index 0af9db54..c0904aff 100644 --- a/app/controllers/searches_controller.rb +++ b/app/controllers/searches_controller.rb @@ -2,7 +2,7 @@ class SearchesController < ApplicationController before_action :permit_params rescue_from InvalidSearchTypeError, with: :unprocessable after_action :log_search, only: [:index] - + def index render json: { searches: paginated_search.serialize_search, @@ -11,17 +11,17 @@ def index } } end - + protected - + def paginated_search @paginated_search ||= search.page(params[:page]).per params[:page_size] end - + def search @search ||= Search.of_type(models).in_section(section).with_content query end - + def search_meta { page: paginated_search.current_page, @@ -37,23 +37,23 @@ def search_meta last_href: search_href(page: paginated_search.total_pages) } end - + def next_href return unless paginated_search.next_page search_href page: paginated_search.next_page end - + def previous_href return unless paginated_search.prev_page search_href page: paginated_search.prev_page end - + def search_href(opts = { }) permitted = params.slice :page, :page_size, :section, :query, :types query_opts = permitted.merge(opts).collect{ |k, v| "#{ k }=#{ v }" }.join '&' "/searches?#{ query_opts }" end - + def models types = params.fetch(:types, %w(boards collections comments discussions)) types = types.split(',') if types.is_a?(String) @@ -61,24 +61,24 @@ def models rescue NameError => e raise InvalidSearchTypeError.new e.message end - + def section required_param :section end - + def query required_param :query end - + def required_param(name) raise ActionController::ParameterMissing.new(name) unless params[name] params[name] end - + def permit_params params.permit :page, :page_size, :section, :query, types: [] end - + def log_search log_event! 'search', params.except(:controller, :action, :format) end diff --git a/app/controllers/subscription_preferences_controller.rb b/app/controllers/subscription_preferences_controller.rb index e3d9044b..ff185801 100644 --- a/app/controllers/subscription_preferences_controller.rb +++ b/app/controllers/subscription_preferences_controller.rb @@ -2,7 +2,7 @@ class SubscriptionPreferencesController < ApplicationController include TalkResource disallow :create, :destroy before_action :ensure_defaults - + def ensure_defaults SubscriptionPreference.for_user(current_user) if current_user end diff --git a/app/controllers/tags_controller.rb b/app/controllers/tags_controller.rb index 22267549..397f8811 100644 --- a/app/controllers/tags_controller.rb +++ b/app/controllers/tags_controller.rb @@ -1,11 +1,11 @@ class TagsController < ApplicationController include TalkResource disallow :create, :update, :destroy - + def popular render json: popular_serializer.resource(params, nil, current_user: current_user) end - + def popular_serializer if params[:taggable_id] || params[:taggable_type] PopularFocusTagSerializer diff --git a/app/controllers/unsubscribe_controller.rb b/app/controllers/unsubscribe_controller.rb index ea1c65de..204167e9 100644 --- a/app/controllers/unsubscribe_controller.rb +++ b/app/controllers/unsubscribe_controller.rb @@ -1,29 +1,29 @@ class UnsubscribeController < ApplicationController before_action :permit_params skip_before_action :set_format, :enforce_ban, :enforce_ip_ban - + def index if current_user SubscriptionPreference.for_user(current_user).values.each do |preference| preference.update email_digest: never end end - + log_event! 'unsubscribe', ip: current_user_ip end - + def current_user @current_user ||= UnsubscribeToken.find_by_token(params[:token]).try(:user) end - + def never SubscriptionPreference.email_digests[:never] end - + def permit_params params.permit :token end - + def log_event!(label, payload) EventLog.create label: label, user_id: current_user.try(:id), payload: payload end diff --git a/app/helpers/mailer_helper.rb b/app/helpers/mailer_helper.rb index 52a91ec6..98313972 100644 --- a/app/helpers/mailer_helper.rb +++ b/app/helpers/mailer_helper.rb @@ -3,22 +3,22 @@ def h1(content = nil, &block) content ||= capture &block "

#{ content }

".html_safe end - + def h2(content = nil, &block) content ||= capture &block "

#{ content }

".html_safe end - + def h3(content = nil, &block) content ||= capture &block "

#{ content }

".html_safe end - + def container(content = nil, &block) content ||= capture &block "
#{ content }
".html_safe end - + def link_style 'color: #0072FF; text-decoration: none;' end diff --git a/app/mailers/notification_mailer.rb b/app/mailers/notification_mailer.rb index 07747a89..cc9d3bd7 100644 --- a/app/mailers/notification_mailer.rb +++ b/app/mailers/notification_mailer.rb @@ -5,23 +5,23 @@ class NotificationMailer < ApplicationMailer include ActionView::Helpers::DateHelper add_template_helper MailerHelper - + after_action :set_delivered - + def notify(user, digest_frequency) @user = user frequency_enum = normalize_frequency digest_frequency categories = find_categories_for frequency_enum find_notifications_for categories organize - + mail to: @user.email, subject: subject(digest_frequency) end - + def find_categories_for(digest_frequency) @user.subscription_preferences.enabled.where(email_digest: digest_frequency).pluck :category end - + def find_notifications_for(categories) @notifications = @user.notifications .undelivered @@ -30,7 +30,7 @@ def find_notifications_for(categories) .includes(:source, subscription: :source) .order(created_at: :asc) end - + def organize @messages = select_category('messages').group_by{ |n| n.source.conversation } @mentions = select_category('mentions', 'group_mentions').group_by &:section @@ -49,19 +49,19 @@ def organize @started_discussions[n.section][n.subscription.source] << n end end - + def select_category(*types) @notifications.select{ |notification| types.include? notification.subscription.category } end - + def set_delivered Notification.where(id: @notifications.map(&:id)).update_all delivered: true end - + def normalize_frequency(frequency) frequency.is_a?(Fixnum) ? frequency : SubscriptionPreference.email_digests[frequency] end - + def digest_label_for(frequency) { immediate: '', @@ -69,7 +69,7 @@ def digest_label_for(frequency) weekly: 'Weekly Digest of ' }[frequency] end - + def subject(digest_frequency) "#{ digest_label_for digest_frequency }Zooniverse Talk Notifications" end diff --git a/app/models/announcement.rb b/app/models/announcement.rb index dddaea29..65973888 100644 --- a/app/models/announcement.rb +++ b/app/models/announcement.rb @@ -1,16 +1,16 @@ class Announcement < ActiveRecord::Base include Expirable include Sectioned - + before_create :assign_default_expiration after_commit :publish, on: :create - + protected - + def assign_default_expiration self.expires_at ||= 1.month.from_now.utc end - + def publish AnnouncementWorker.perform_async id end diff --git a/app/models/board.rb b/app/models/board.rb index 6001bf81..f37f6834 100644 --- a/app/models/board.rb +++ b/app/models/board.rb @@ -3,7 +3,7 @@ class Board < ActiveRecord::Base include Sectioned include Subscribable include BooleanCoercion - + has_many :discussions, dependent: :destroy has_one :latest_discussion, ->{ includes(DiscussionSerializer.includes) @@ -14,17 +14,17 @@ class Board < ActiveRecord::Base has_many :mentions has_many :sub_boards, class_name: 'Board', foreign_key: 'parent_id' belongs_to :parent, class_name: 'Board' - + validates :title, presence: true validates :description, presence: true validates :section, presence: true - + after_update :cascade_searchable_later, if: :permissions_changed? - + def searchable? permissions['read'] == 'all' end - + def searchable_update <<-SQL update searchable_boards @@ -36,23 +36,23 @@ def searchable_update boards.id = #{ id } SQL end - + def count_users_and_comments! self.comments_count = Comment.where(board_id: id).count self.users_count = Comment.where(board_id: id).select(:user_id).distinct.count save if changed? end - + def cascade_searchable_later action = if permissions_was['read'] == 'all' && permissions['read'] != 'all' :destroy_searchable # was public, but isn't now elsif permissions_was['read'] != 'all' && permissions['read'] == 'all' :update_searchable # wasn't public, but is now end - + BoardVisibilityWorker.perform_in(1.second, id, action) if action end - + def each_discussion_and_comment discussions.find_each do |discussion| yield discussion diff --git a/app/models/collection.rb b/app/models/collection.rb index a1484c3a..00a5739d 100644 --- a/app/models/collection.rb +++ b/app/models/collection.rb @@ -1,19 +1,19 @@ class Collection < ActiveRecord::Base include Focusable include Searchable::Querying - + class_attribute :searchable_klass self.searchable_klass = SearchableCollection - + moderatable_with :destroy, by: [:moderator, :admin] moderatable_with :ignore, by: [:moderator, :admin] moderatable_with :report, by: [:all] moderatable_with :watch, by: [:moderator, :admin] - + def self.refresh! connection.execute 'refresh materialized view concurrently searchable_collections;' end - + def projects Project.where id: project_ids end diff --git a/app/models/comment.rb b/app/models/comment.rb index 2c8db51f..40640e72 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -6,25 +6,25 @@ class Comment < ActiveRecord::Base include Sectioned include Notifiable include BooleanCoercion - + include Comment::Tagging include Comment::Mentioning include Comment::Subscribing include Comment::Publishing - + has_many :mentions, dependent: :destroy has_many :group_mentions, dependent: :destroy has_many :tags, dependent: :destroy has_many :taggables, through: :tags - + belongs_to :reply, class_name: 'Comment' has_many :replies, class_name: 'Comment', foreign_key: :reply_id - + belongs_to :user, required: true belongs_to :discussion, counter_cache: true, touch: true, required: true belongs_to :focus, polymorphic: true belongs_to :board - + validates :body, presence: true validates :section, presence: true validates :focus_type, inclusion: { @@ -32,26 +32,26 @@ class Comment < ActiveRecord::Base if: ->{ focus_id.present? }, message: 'must be "Subject" or "Collection"' } - + before_validation :set_section before_create :denormalize_attributes after_create :update_discussion before_update :update_board_id, if: ->{ discussion_id_changed? } after_update :update_moved_discussion after_destroy :update_discussion, :clear_replies - + moderatable_with :destroy, by: [:moderator, :admin] moderatable_with :ignore, by: [:moderator, :admin] moderatable_with :report, by: [:all] - + def upvote!(voter) hstore_concat 'upvotes', voter.login => Time.now.to_i end - + def remove_upvote!(voter) hstore_delete_key 'upvotes', voter.login end - + def soft_destroy update_attributes is_deleted: true, body: 'This comment has been deleted' mentions.destroy_all @@ -62,11 +62,11 @@ def soft_destroy discussion.destroy unless discussion.comments.where(is_deleted: false).any? self end - + def searchable? discussion.searchable? && !is_deleted? end - + def searchable_update <<-SQL update searchable_comments @@ -86,7 +86,7 @@ def searchable_update comments.id = #{ id } SQL end - + def searchable_focus return '' unless focus <<-SQL @@ -95,19 +95,19 @@ def searchable_focus || setweight(to_tsvector(substring(focus_type, 1, 1) || focus_id::text), 'A') SQL end - + protected - + def set_section self.section = discussion.section end - + def denormalize_attributes self.focus ||= discussion.focus if discussion.focus self.user_login = user.login self.board_id = discussion.board_id end - + def update_discussion discussion.last_comment_created_at = discussion.comments.order(created_at: :desc).first.try :created_at discussion.last_comment_created_at ||= discussion.created_at @@ -115,17 +115,17 @@ def update_discussion board.save if board.changed? discussion.update_counters! end - + def update_board_id denormalize_attributes if board_id != discussion.board_id end - + def update_moved_discussion changes.fetch(:discussion_id, []).compact.each do |id| Discussion.find_by_id(id).try :update_counters! end end - + def clear_replies replies.update_all reply_id: nil end diff --git a/app/models/concerns/boolean_coercion.rb b/app/models/concerns/boolean_coercion.rb index e4a22d75..aa123de9 100644 --- a/app/models/concerns/boolean_coercion.rb +++ b/app/models/concerns/boolean_coercion.rb @@ -1,6 +1,6 @@ module BooleanCoercion extend ActiveSupport::Concern - + included do columns_hash.each_pair do |attribute, adapter| next unless adapter.type == :boolean diff --git a/app/models/concerns/comment/mentioning.rb b/app/models/concerns/comment/mentioning.rb index a9455e28..ad0a1d38 100644 --- a/app/models/concerns/comment/mentioning.rb +++ b/app/models/concerns/comment/mentioning.rb @@ -1,7 +1,7 @@ class Comment module Mentioning extend ActiveSupport::Concern - + MATCH_MENTIONS = / (?:^|\s) # match the beginning of the word ( \^([SC])(\d+) ) | # match mentioned focuses @@ -15,12 +15,12 @@ module Mentioning ) | ( @([-\w\d]+) ) # match mentioned users /imx - + included do before_save :parse_mentions before_update :parse_mentions, :update_mentions, :update_group_mentions end - + def parse_mentions self.mentioning = { } self.group_mentioning = { } @@ -37,33 +37,33 @@ def parse_mentions end end end - + def update_mentions removed_from(:mentioning).each_pair do |mention, hash| Mention.where(comment_id: id, mentionable_id: hash['id']).destroy_all end end - + def update_group_mentions removed_from(:group_mentioning).each_pair do |group_name, hash| GroupMention.where(comment_id: id, name: group_name).destroy_all end end - + def accessible_by?(user) return false unless user CommentPolicy.new(user, self).show? end - + protected - + def mentioned(mention, mentionable) return unless mentionable return if mentioning[mention] self.mentioning[mention] = { 'id' => mentionable.id, 'type' => mentionable.class.name } mentions.build(user: user, mentionable: mentionable) if added_to(:mentioning)[mention] end - + def group_mentioned(name) return if group_mentioning[name] self.group_mentioning[name] = "@#{ name }" diff --git a/app/models/concerns/comment/publishing.rb b/app/models/concerns/comment/publishing.rb index be0490ad..d1000ae5 100644 --- a/app/models/concerns/comment/publishing.rb +++ b/app/models/concerns/comment/publishing.rb @@ -3,19 +3,19 @@ class Comment module Publishing extend ActiveSupport::Concern - + included do after_commit :publish_to_event_stream_later, on: :create, if: :searchable? end - + def publish_to_event_stream_later CommentPublishWorker.perform_async id end - + def publish_to_event_stream ZooStream.publish event: 'comment', shard_by: discussion_id, data: to_event_stream end - + def to_event_stream { id: id, diff --git a/app/models/concerns/comment/subscribing.rb b/app/models/concerns/comment/subscribing.rb index 1f45a5a8..9b578572 100644 --- a/app/models/concerns/comment/subscribing.rb +++ b/app/models/concerns/comment/subscribing.rb @@ -1,16 +1,16 @@ class Comment module Subscribing extend ActiveSupport::Concern - + included do after_create :subscribe_user after_commit :notify_subscribers_later, on: :create end - + def notify_subscribers_later CommentSubscriptionWorker.perform_async id end - + def notify_subscribers subscriptions_to_notify.each do |subscription| Notification.create({ @@ -23,13 +23,13 @@ def notify_subscribers }) if subscription.try(:enabled?) end end - + def subscriptions_to_notify list = discussion.subscriptions.participating_discussions.enabled.where 'user_id <> ?', user.id list += discussion.subscriptions.followed_discussions.enabled.where 'user_id <> ?', user.id list.uniq{ |subscription| subscription.user_id } end - + def subscribe_user user.subscribe_to discussion, :participating_discussions end diff --git a/app/models/concerns/comment/tagging.rb b/app/models/concerns/comment/tagging.rb index 1e15c9ca..9e75852f 100644 --- a/app/models/concerns/comment/tagging.rb +++ b/app/models/concerns/comment/tagging.rb @@ -1,32 +1,32 @@ class Comment module Tagging extend ActiveSupport::Concern - + MATCH_TAGS = / (?:^|[^\w]) # match the beginning of the word (\#([-\w\d]{3,40})) # match tags /imx - + included do before_save :parse_tags before_update :parse_tags, :update_tags end - + def parse_tags self.tagging = { } body.scan(MATCH_TAGS).each do |tag, name| tagged tag, name.downcase end end - + def update_tags removed_from(:tagging).each_pair do |tag, name| Tag.where(comment_id: id, name: name).destroy_all end end - + protected - + def tagged(tag, name) self.tagging[tag] = name.downcase tags.build(name: name) if added_to(:tagging)[tag] diff --git a/app/models/concerns/discussion/subscribing.rb b/app/models/concerns/discussion/subscribing.rb index 626c1653..396c10a7 100644 --- a/app/models/concerns/discussion/subscribing.rb +++ b/app/models/concerns/discussion/subscribing.rb @@ -1,15 +1,15 @@ class Discussion module Subscribing extend ActiveSupport::Concern - + included do after_commit :notify_subscribers_later, on: :create end - + def notify_subscribers_later DiscussionSubscriptionWorker.perform_async id end - + def notify_subscribers subscriptions_to_notify.each do |subscription| Notification.create({ @@ -22,7 +22,7 @@ def notify_subscribers }) if subscription.try(:enabled?) end end - + def subscriptions_to_notify board.subscriptions.started_discussions.enabled.where 'user_id <> ?', user.id end diff --git a/app/models/concerns/expirable.rb b/app/models/concerns/expirable.rb index f3ba437b..bb1a188a 100644 --- a/app/models/concerns/expirable.rb +++ b/app/models/concerns/expirable.rb @@ -1,10 +1,10 @@ module Expirable extend ActiveSupport::Concern - + included do scope :expired, ->{ where 'expires_at < ?', Time.now.utc } end - + module ClassMethods def expires_in(amount) scope :expired, ->{ where 'created_at < ?', amount.ago.utc } diff --git a/app/models/concerns/focusable.rb b/app/models/concerns/focusable.rb index 842cceae..7eece411 100644 --- a/app/models/concerns/focusable.rb +++ b/app/models/concerns/focusable.rb @@ -1,18 +1,18 @@ module Focusable extend ActiveSupport::Concern - + included do include Moderatable has_many :mentions, as: :mentionable, dependent: :destroy has_many :comments, as: :focus belongs_to :user - + validates :section, presence: true - + moderatable_with :ignore, by: [:moderator, :admin] moderatable_with :report, by: [:all] end - + def section if self[:project_ids] project_ids.collect{ |project_id| "project-#{ project_id }" } @@ -20,7 +20,7 @@ def section "project-#{ project.id }" end end - + def mentioned_by(comment) # TO-DO: notification for collection mentions? end diff --git a/app/models/concerns/hash_changes.rb b/app/models/concerns/hash_changes.rb index feb6d62f..696840b0 100644 --- a/app/models/concerns/hash_changes.rb +++ b/app/models/concerns/hash_changes.rb @@ -1,20 +1,20 @@ module HashChanges extend ActiveSupport::Concern - + def added_to(key) changes_for(key) do |was, is| is.dup.reject{ |key, val| was.has_key? key } end end - + def removed_from(key) changes_for(key) do |was, is| was.dup.reject{ |key, val| is.has_key? key } end end - + protected - + def changes_for(key) return { } unless changes[key] yield *changes[key] diff --git a/app/models/concerns/hstore_update.rb b/app/models/concerns/hstore_update.rb index e6914c90..02345378 100644 --- a/app/models/concerns/hstore_update.rb +++ b/app/models/concerns/hstore_update.rb @@ -1,26 +1,26 @@ module HstoreUpdate extend ActiveSupport::Concern - + protected - + def hstore_concat(column, key_pairs) hstore_update column, "|| hstore(#{ hstore_escape key_pairs })" end - + def hstore_delete_key(column, key) hstore_update column, "- #{ hstore_sanitize key }::text" end - + def hstore_update(column, updater) updater = "#{ column } = #{ column } #{ updater }" self.class.where(id: id).update_all updater reload end - + def hstore_sanitize(string) self.class.sanitize string.to_s.gsub /["'=>\(\)]/, '' end - + def hstore_escape(key_pairs) key_pairs.collect do |pair| pair.collect{ |item| hstore_sanitize item }.join ', ' diff --git a/app/models/concerns/moderatable.rb b/app/models/concerns/moderatable.rb index b9e3cef3..52dd6b53 100644 --- a/app/models/concerns/moderatable.rb +++ b/app/models/concerns/moderatable.rb @@ -1,20 +1,20 @@ module Moderatable extend ActiveSupport::Concern - + included do has_one :moderation, as: :target class_attribute :moderatable self.moderatable = { } after_destroy :close_moderation end - + module ClassMethods def moderatable_with(action, by: []) self.moderatable = self.moderatable.merge action => { } by.each{ |role| self.moderatable[action][role] = true } end end - + def close_moderation return unless moderation && moderation.opened? moderation.state = 'closed' diff --git a/app/models/concerns/notifiable.rb b/app/models/concerns/notifiable.rb index 8a906f68..cff1ba1f 100644 --- a/app/models/concerns/notifiable.rb +++ b/app/models/concerns/notifiable.rb @@ -1,6 +1,6 @@ module Notifiable extend ActiveSupport::Concern - + included do has_many :notifications, as: :source, dependent: :destroy end diff --git a/app/models/concerns/searchable.rb b/app/models/concerns/searchable.rb index 2f90c50d..399aa415 100644 --- a/app/models/concerns/searchable.rb +++ b/app/models/concerns/searchable.rb @@ -1,6 +1,6 @@ module Searchable extend ActiveSupport::Concern - + included do include Searchable::Querying class_attribute :searchable_klass @@ -10,14 +10,14 @@ module Searchable else Object.const_set klass_name, _searchable_model end - + after_create :create_searchable, if: :searchable? after_update :update_searchable, if: :searchable? after_update :destroy_searchable, unless: :searchable? after_destroy :destroy_searchable has_one :searchable, class_name: searchable_klass.name, foreign_key: :searchable_id end - + module ClassMethods def _searchable_model klass = self @@ -27,19 +27,19 @@ def _searchable_model end end end - + def searchable? true # by default end - + def create_searchable searchable_klass.create searchable: self end - + def update_searchable searchable ? searchable.set_content : create_searchable end - + def destroy_searchable searchable.try :destroy end diff --git a/app/models/concerns/searchable/model.rb b/app/models/concerns/searchable/model.rb index f2583081..1e710ef4 100644 --- a/app/models/concerns/searchable/model.rb +++ b/app/models/concerns/searchable/model.rb @@ -1,30 +1,30 @@ module Searchable module Model extend ActiveSupport::Concern - + included do class_attribute :searchable_klass before_create :_denormalize after_create :set_content - + scope :with_content, ->(query_terms) { where 'content @@ to_tsquery(?)', query_terms } end - + module ClassMethods def searchable_model(klass) self.searchable_klass = klass belongs_to :searchable, class_name: klass.name end end - + def set_content self.class.connection.execute searchable.searchable_update end - + protected - + def _denormalize self.searchable_type = searchable_klass.name self.sections = searchable.respond_to?(:sections) ? searchable.sections : [searchable.section] diff --git a/app/models/concerns/searchable/querying.rb b/app/models/concerns/searchable/querying.rb index cd776ddd..5939eb7a 100644 --- a/app/models/concerns/searchable/querying.rb +++ b/app/models/concerns/searchable/querying.rb @@ -1,12 +1,12 @@ module Searchable module Querying extend ActiveSupport::Concern - + module ClassMethods def search(terms) searchable_klass.with_content _parse_query(terms) end - + def _parse_query(terms) terms[0..1000] .gsub(/\s+/m, ' ') # replace all forms of whitespace with spaces @@ -24,7 +24,7 @@ def _parse_query(terms) .gsub(/(?(user){ joins(:user_conversations).where user_conversations: { user_id: user.id } } scope :unread, ->{ joins(:user_conversations).where user_conversations: { is_unread: true } } - + default_scope do includes(:user_conversations) .order 'user_conversations.is_unread desc, conversations.updated_at desc' end - + validates :title, presence: true, length: 3..140 - + moderatable_with :destroy, by: [:moderator, :admin] moderatable_with :ignore, by: [:moderator, :admin] # All users able to view a conversation are able to report it # moderatable_with :report, by: [:participant] - + def self.mark_as_read_by(conversation_ids, user_id) UserConversation.where({ conversation_id: Array.wrap(conversation_ids), diff --git a/app/models/data_request.rb b/app/models/data_request.rb index a47ca2ac..0dc82fc3 100644 --- a/app/models/data_request.rb +++ b/app/models/data_request.rb @@ -3,33 +3,33 @@ class DataRequest < ActiveRecord::Base include Subscribable include Notifiable include Expirable - + belongs_to :user, required: true validates :section, presence: true validates :kind, presence: true, uniqueness: { scope: [:section, :user_id], message: 'has already been created' }, inclusion: { in: %w(tags comments), message: 'must be "tags" or "comments"' } - + before_create :set_expiration after_commit :spawn_worker, on: :create - + enum state: { pending: 0, started: 1, finished: 2, failed: 3 } - + def self.kinds { tags: TagExportWorker, comments: CommentExportWorker } end - + def notify_user(notification) subscription = user.subscribe_to self, :system - + Notification.create(notification.merge({ source: self, user_id: user.id, @@ -37,15 +37,15 @@ def notify_user(notification) subscription: subscription })) if subscription.try(:enabled?) end - + def worker self.class.kinds[self.kind.to_sym] end - + def set_expiration self.expires_at = 1.day.from_now.utc end - + def spawn_worker worker.perform_async id end diff --git a/app/models/discussion.rb b/app/models/discussion.rb index 3fd13898..e3ee5ffb 100644 --- a/app/models/discussion.rb +++ b/app/models/discussion.rb @@ -5,9 +5,9 @@ class Discussion < ActiveRecord::Base include Sectioned include Notifiable include BooleanCoercion - + include Discussion::Subscribing - + belongs_to :user, required: true belongs_to :board, required: true, counter_cache: true belongs_to :focus, polymorphic: true @@ -17,26 +17,26 @@ class Discussion < ActiveRecord::Base .select('distinct on(comments.discussion_id) comments.*') .reorder discussion_id: :asc, created_at: :desc }, class_name: 'Comment' - + validates :title, presence: true, length: { in: 3..140, unless: ->{ subject_default? } } validates :section, presence: true - + before_validation :set_section before_create :denormalize_attributes before_save :clear_sticky, unless: ->{ sticky? } before_save :set_sticky_position, if: ->{ sticky? && sticky_position.nil? } after_update :update_board_counters, if: ->{ board_id_changed? } - + moderatable_with :destroy, by: [:moderator, :admin] moderatable_with :ignore, by: [:moderator, :admin] moderatable_with :report, by: [:all] moderatable_with :watch, by: [:moderator, :admin] - + def searchable? return @searchable if @searchable @searchable = board.searchable? end - + def searchable_update <<-SQL update searchable_discussions @@ -47,38 +47,38 @@ def searchable_update discussions.id = #{ id } SQL end - + def count_users! self.users_count = Comment.where(discussion_id: id).select(:user_id).distinct.count save if changed? end - + def update_counters! count_users! board.count_users_and_comments! end - + protected - + def set_section self.section = board.section end - + def denormalize_attributes self.user_login = user.login end - + def update_board_counters comments.update_all board_id: board_id changes.fetch(:board_id, []).compact.each do |id| Board.find_by_id(id).try :count_users_and_comments! end end - + def clear_sticky self.sticky_position = nil end - + def set_sticky_position last_sticky = board.discussions.where(sticky: true).order(sticky_position: :desc).first self.sticky_position = (last_sticky.try(:sticky_position) || 0) + 1 diff --git a/app/models/group_mention.rb b/app/models/group_mention.rb index e8cb9104..167744d0 100644 --- a/app/models/group_mention.rb +++ b/app/models/group_mention.rb @@ -6,28 +6,28 @@ class GroupMention < ActiveRecord::Base in: %w(admins moderators researchers scientists team), message: 'must be "admins", "moderators", "researchers", "scientists", or "team"' } - + before_validation :denormalize_attributes, :downcase_name, on: :create after_commit :notify_later, on: :create - + def mentioned_users User.send name, on: section end - + def notify_later GroupMentionWorker.perform_async id end - + def notify_mentioned mentioned_users.each do |mentioned_user| mentioned_user.group_mentioned_by self end end - + def downcase_name self.name.try :downcase! end - + def denormalize_attributes self.user ||= comment.try(:user) self.section ||= comment.try(:section) diff --git a/app/models/mention.rb b/app/models/mention.rb index aecef812..aee2fa4a 100644 --- a/app/models/mention.rb +++ b/app/models/mention.rb @@ -1,26 +1,26 @@ class Mention < ActiveRecord::Base include Sectioned - + belongs_to :comment, required: true belongs_to :board, required: true belongs_to :user, required: true belongs_to :mentionable, polymorphic: true, required: true - + before_validation :set_section, :set_board_id after_commit :notify_later, on: :create - + def set_section self.section = comment.section end - + def set_board_id self.board_id = comment.board_id end - + def notify_later MentionWorker.perform_async id end - + def notify_mentioned mentionable.mentioned_by comment end diff --git a/app/models/message.rb b/app/models/message.rb index 929b04b6..e1234e9a 100644 --- a/app/models/message.rb +++ b/app/models/message.rb @@ -1,11 +1,11 @@ class Message < ActiveRecord::Base include Notifiable - + belongs_to :user, required: true belongs_to :conversation, required: true has_many :user_conversations, through: :conversation has_many :users, through: :user_conversations - + has_many :recipient_conversations, ->(message){ where.not(user_id: message.user_id) @@ -13,27 +13,27 @@ class Message < ActiveRecord::Base class_name: 'UserConversation', through: :conversation, source: :user_conversations - + has_many :recipients, class_name: 'User', through: :recipient_conversations, source: :user - + validates :body, presence: true - + before_create :ensure_user_conversations after_create :set_conversations_unread! - + concerning :Subscribing do included do after_create :subscribe_user after_commit :notify_subscribers_later, on: :create end - + def notify_subscribers_later MessageNotificationWorker.perform_async id end - + def notify_subscribers Subscription.messages.enabled.where(source: recipient_conversations, user: recipients).each do |subscription| Notification.create({ @@ -46,20 +46,20 @@ def notify_subscribers }) if subscription.try(:enabled?) end end - + def subscribe_user user_conversations.each do |user_conversation| user_conversation.user.subscribe_to user_conversation, :messages end end end - + def ensure_user_conversations conversation.participant_ids.each do |participant_id| conversation.user_conversations.where(user_id: participant_id).first_or_create end end - + def set_conversations_unread! recipient_conversations.update_all is_unread: true end diff --git a/app/models/moderation.rb b/app/models/moderation.rb index 980b0ec8..8341e518 100644 --- a/app/models/moderation.rb +++ b/app/models/moderation.rb @@ -1,31 +1,31 @@ class Moderation < ActiveRecord::Base include Notifiable include Sectioned - + belongs_to :target, polymorphic: true validates :target, presence: true, on: :create validates :section, presence: true - + before_update :reopen, if: :reports_changed? before_update :apply_action, if: :actions_changed? - + enum state: { opened: 0, ignored: 1, closed: 2, watched: 3 } - + concerning :Subscribing do included do after_create :subscribe_users after_commit :notify_subscribers_later, on: :create end - + def notify_subscribers_later ModerationNotificationWorker.perform_async id end - + def notify_subscribers Subscription.moderation_reports.enabled.where(source: self, user: moderators).each do |subscription| Notification.create({ @@ -38,20 +38,20 @@ def notify_subscribers }) if subscription.try(:enabled?) end end - + def subscribe_users moderators.find_each do |user| user.subscribe_to self, :moderation_reports end end - + def moderators @moderators ||= User.joins(:roles).where(roles: { name: %w(moderator admin), section: section }).all end end - + protected - + def apply_action self.actioned_at = Time.now.utc case actions.last.with_indifferent_access[:action] @@ -63,22 +63,22 @@ def apply_action watch_target end end - + def reopen self.state = 'opened' end - + def destroy_target update_columns state: Moderation.states[:closed] self.destroyed_target = target.as_json target_type == 'Comment' ? target.soft_destroy : target.destroy! self.target = nil end - + def ignore_target self.state = 'ignored' end - + def watch_target self.state = 'watched' end diff --git a/app/models/notification.rb b/app/models/notification.rb index 20844986..7f221948 100644 --- a/app/models/notification.rb +++ b/app/models/notification.rb @@ -2,23 +2,23 @@ class Notification < ActiveRecord::Base include Expirable include Sectioned include BooleanCoercion - + belongs_to :source, polymorphic: true belongs_to :user, required: true belongs_to :subscription, required: true - + after_commit :publish, on: :create validates :url, presence: true validates :message, presence: true validates :section, presence: true - + scope :undelivered, ->{ where delivered: false } - + expires_in 3.months delegate :immediate?, to: :'subscription.preference' - + protected - + def publish NotificationWorker.perform_async id end diff --git a/app/models/oauth_access_token.rb b/app/models/oauth_access_token.rb index 8eb4c9cf..e1fde414 100644 --- a/app/models/oauth_access_token.rb +++ b/app/models/oauth_access_token.rb @@ -1,27 +1,27 @@ class OauthAccessToken < ActiveRecord::Base belongs_to :resource_owner, class_name: 'User' - + class ExpiredError < StandardError def message; 'Access Token has expired'; end alias_method :to_s, :message end - + class RevokedError < StandardError def message; 'Access Token has been revoked'; end alias_method :to_s, :message end - + def resource_owner super.tap do |user| raise ExpiredError if expired? raise RevokedError if revoked? end end - + def expired? Time.now.utc > created_at + expires_in.seconds end - + def revoked? !!revoked_at end diff --git a/app/models/role.rb b/app/models/role.rb index 9e3a98e1..52dd6879 100644 --- a/app/models/role.rb +++ b/app/models/role.rb @@ -1,12 +1,12 @@ class Role < ActiveRecord::Base include BooleanCoercion belongs_to :user, required: true - + validates :is_shown, inclusion: { in: [true, false], message: 'must be true or false' } - + validates :user_id, uniqueness: { scope: [:name, :section], message: 'Role already exists for this user and section' diff --git a/app/models/search.rb b/app/models/search.rb index 7168f6ef..ccbfeda7 100644 --- a/app/models/search.rb +++ b/app/models/search.rb @@ -1,13 +1,13 @@ class Search < ActiveRecord::Base include Searchable::Querying - + class_attribute :serializers self.serializers = Hash.new do |hash, klass| hash[klass] = "#{ klass }Serializer".constantize end - + belongs_to :searchable, polymorphic: true - + scope :of_type, ->(types){ where searchable_type: types } scope :in_section, ->(section){ where('sections @> array[?]::varchar[]', section) } scope :with_content, ->(terms) { @@ -15,14 +15,14 @@ class Search < ActiveRecord::Base where('content @@ to_tsquery(?)', query) .order("ts_rank(content, #{ connection.quote query }) desc") } - + def self.serialize_search list = all.to_a ids_by_type = result_ids_by_type list results = load_results ids_by_type reorder_results list, results end - + def self.result_ids_by_type(list) { }.tap do |types| list.each do |search| @@ -31,7 +31,7 @@ def self.result_ids_by_type(list) end end end - + def self.load_results(ids_by_type) { }.tap do |results| ids_by_type.each_pair do |type, searchable_ids| @@ -43,7 +43,7 @@ def self.load_results(ids_by_type) end end end - + def self.reorder_results(list, results) [].tap do |reordered| list.each do |search| diff --git a/app/models/searchable_collection.rb b/app/models/searchable_collection.rb index fb3f7fe2..77ca5a3b 100644 --- a/app/models/searchable_collection.rb +++ b/app/models/searchable_collection.rb @@ -2,7 +2,7 @@ class SearchableCollection < ActiveRecord::Base class_attribute :searchable_klass self.searchable_klass = Collection belongs_to :searchable, class_name: 'Collection' - + scope :with_content, ->(query_terms) { where 'content @@ to_tsquery(?)', query_terms } diff --git a/app/models/subscription.rb b/app/models/subscription.rb index 8482cda7..bdcd3fb7 100644 --- a/app/models/subscription.rb +++ b/app/models/subscription.rb @@ -1,29 +1,29 @@ class Subscription < ActiveRecord::Base include SubscriptionCategories include BooleanCoercion - + has_many :notifications, dependent: :destroy belongs_to :user, required: true belongs_to :source, polymorphic: true, required: true - + before_create :ensure_enabled after_update :clear_notifications, if: ->{ enabled_change == [true, false] } - + validates_with SubscriptionUniquenessValidator, on: :create scope :enabled, ->{ where enabled: true } - + def preference @preference ||= SubscriptionPreference.find_or_default_for(user, category) end - + def ensure_enabled preference.enabled? end - + def enabled? persisted? && super end - + def clear_notifications notifications.destroy_all end diff --git a/app/models/subscription_preference.rb b/app/models/subscription_preference.rb index 1d0caad8..48f10f00 100644 --- a/app/models/subscription_preference.rb +++ b/app/models/subscription_preference.rb @@ -2,21 +2,21 @@ class SubscriptionPreference < ActiveRecord::Base include SubscriptionCategories include BooleanCoercion belongs_to :user, required: true - + validates :enabled, inclusion: { in: [true, false] } scope :enabled, ->{ where enabled: true } after_update :unsubscribe_user, if: ->{ enabled_change == [true, false] } validates :email_digest, inclusion: { in: %w(immediate daily weekly never) } - + enum email_digest: { immediate: 0, daily: 1, weekly: 2, never: 3 } - + def self.defaults { participating_discussions: :daily, @@ -29,7 +29,7 @@ def self.defaults started_discussions: :never }.with_indifferent_access end - + def self.for_user(user) { }.tap do |preferences| defaults.keys.each do |category| @@ -37,13 +37,13 @@ def self.for_user(user) end end.with_indifferent_access end - + def self.find_or_default_for(user, category) where(user_id: user.id, category: categories[category]).first_or_create do |preference| preference.email_digest = email_digests[defaults[category]] end end - + def unsubscribe_user user.subscriptions.where(category: category).each do |subscription| subscription.update enabled: false diff --git a/app/models/tag.rb b/app/models/tag.rb index b019fa95..f3984e68 100644 --- a/app/models/tag.rb +++ b/app/models/tag.rb @@ -1,17 +1,17 @@ class Tag < ActiveRecord::Base include Sectioned - + belongs_to :user, required: true belongs_to :comment, required: true belongs_to :taggable, polymorphic: true validates :section, presence: true - + before_validation :propagate_values, on: :create - + scope :in_section, ->(section){ where section: section } scope :of_type, ->(type){ where taggable_type: type.classify } scope :popular, ->(limit: 10){ group(:name).order('count_all desc').limit(limit).count :all } - + def propagate_values return unless comment self.section = comment.section diff --git a/app/models/unsubscribe_token.rb b/app/models/unsubscribe_token.rb index c20b1343..5bebbedf 100644 --- a/app/models/unsubscribe_token.rb +++ b/app/models/unsubscribe_token.rb @@ -4,7 +4,7 @@ class UnsubscribeToken < ActiveRecord::Base include Expirable belongs_to :user before_create :set_expiration, :set_token! - + def self.for_user(user) tries = 0 begin @@ -16,7 +16,7 @@ def self.for_user(user) tries < 5 ? retry : raise(e) end end - + def set_token! tries = 0 begin @@ -27,9 +27,9 @@ def set_token! tries < 5 ? retry : raise(e) end end - + protected - + def set_expiration self.expires_at ||= 1.month.from_now.utc end diff --git a/app/models/user.rb b/app/models/user.rb index 84317ca4..6d5cf153 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,6 +1,6 @@ class User < ActiveRecord::Base include Moderatable - + has_many :mentions, as: :mentionable has_many :roles has_many :user_conversations @@ -10,28 +10,28 @@ class User < ActiveRecord::Base has_many :subscription_preferences has_many :blocked_users has_one :unsubscribe_token - + scope :with_roles, ->(*roles, on:){ joins(:roles).merge Role.where(section: on, name: roles) } scope :admins, ->(on:){ with_roles 'admin', on: on } scope :moderators, ->(on:){ with_roles 'moderator', on: on } scope :scientists, ->(on:){ with_roles 'scientist', on: on } singleton_class.send :alias_method, :researchers, :scientists scope :team, ->(on:){ with_roles 'moderator', 'admin', 'scientist', 'team', on: on } - + moderatable_with :ignore, by: [:moderator, :admin] moderatable_with :report, by: [:all] moderatable_with :watch, by: [:moderator, :admin] - + def self.from_panoptes(bearer_token) token = OauthAccessToken.find_by_token bearer_token token.resource_owner rescue nil end - + def mentioned_by(comment) subscription = subscribe_to comment.discussion, :mentions - + Notification.create({ source: comment, user_id: id, @@ -41,10 +41,10 @@ def mentioned_by(comment) subscription: subscription }) if subscription.try(:enabled?) end - + def group_mentioned_by(group_mention) subscription = subscribe_to group_mention.comment.discussion, :group_mentions - + Notification.create({ source: group_mention.comment, user_id: id, @@ -54,11 +54,11 @@ def group_mentioned_by(group_mention) subscription: subscription }) if subscription.try(:enabled?) end - + def preference_for(category) SubscriptionPreference.find_or_default_for self, category end - + def subscribe_to(source, category) preference = preference_for category return unless preference.enabled? @@ -67,7 +67,7 @@ def subscribe_to(source, category) rescue nil end - + def unsubscribe_from(source, category = nil) query = { user: self, source: source } query[:category] = Subscription.categories[category] if category diff --git a/app/models/user_conversation.rb b/app/models/user_conversation.rb index 32498587..3657bfc3 100644 --- a/app/models/user_conversation.rb +++ b/app/models/user_conversation.rb @@ -1,19 +1,19 @@ class UserConversation < ActiveRecord::Base include Subscribable include BooleanCoercion - + belongs_to :conversation belongs_to :user, required: true - + before_create :update_conversation_participants after_destroy :remove_conversation - + protected - + def remove_conversation conversation.reload.destroy rescue nil end - + def update_conversation_participants conversation.participant_ids << user_id conversation.participant_ids.uniq! diff --git a/app/models/user_ip_ban.rb b/app/models/user_ip_ban.rb index 30ae9599..4096a8c7 100644 --- a/app/models/user_ip_ban.rb +++ b/app/models/user_ip_ban.rb @@ -1,6 +1,6 @@ class UserIpBan < ActiveRecord::Base validates :ip, presence: true - + def self.banned?(request) where("ip >>= inet('#{ request.remote_ip }')").exists? end diff --git a/app/policies/announcement_policy.rb b/app/policies/announcement_policy.rb index 35df7c46..7d95805d 100644 --- a/app/policies/announcement_policy.rb +++ b/app/policies/announcement_policy.rb @@ -2,23 +2,23 @@ class AnnouncementPolicy < ApplicationPolicy def index? true end - + def show? true end - + def create? admin? end - + def update? admin? end - + def destroy? admin? end - + class Scope < Scope def resolve scope diff --git a/app/policies/application_policy.rb b/app/policies/application_policy.rb index 63c087e9..bd666bbe 100644 --- a/app/policies/application_policy.rb +++ b/app/policies/application_policy.rb @@ -3,41 +3,41 @@ class ApplicationPolicy attr_reader :user, :record - + def initialize(user, record) @user = user @record = record end - + def index? true end - + def show? true end - + def create? false end - + def update? false end - + def destroy? false end - + concerning :UserPredicates do def logged_in? !!user end - + def owner? logged_in? && Array.wrap(record).all?{ |r| user.id == r.user_id } end - + # TO-DO: refactor to use an ALL query def participant? return false unless logged_in? @@ -46,27 +46,27 @@ def participant? end true end - + def moderator? has_role? 'moderator' end - + def admin? has_role? 'admin' end - + def scientist? has_role? 'scientist' end - + def zooniverse_admin? logged_in? && user.roles.where(section: 'zooniverse', name: 'admin').exists? end - + def team? moderator? || admin? || scientist? end - + def has_role?(role) return false unless logged_in? return true if roles_in('zooniverse').include?(role) @@ -76,11 +76,11 @@ def has_role?(role) end true end - + def roles_in(section) user_roles.fetch section, [] end - + def user_roles return @_roles if @_roles return { } unless logged_in? @@ -92,20 +92,20 @@ def user_roles end @_roles end - + def record_sections sections = Array.wrap(record).collect do |r| r.section if r.respond_to?(:section) end.compact.uniq sections.empty? ? ['zooniverse'] : sections end - + def privileged_sections(*roles) return [] unless user @privileged_sections ||= { } @privileged_sections[roles] ||= user.roles.where(name: roles).distinct(:section).pluck(:section) end - + def accessible_section?(roles = ['admin']) return true if zooniverse_admin? Array.wrap(record).each do |r| @@ -114,24 +114,24 @@ def accessible_section?(roles = ['admin']) true end end - + def scope Pundit.policy_scope!(user, Array.wrap(record).first.class) end - + class Scope include UserPredicates attr_reader :user, :scope - + def initialize(user, scope) @user = user @scope = scope end - + def resolve scope end - + def user_roles return @_roles if @_roles return { } unless logged_in? diff --git a/app/policies/blocked_user_policy.rb b/app/policies/blocked_user_policy.rb index 4c02f6e8..3dc8036b 100644 --- a/app/policies/blocked_user_policy.rb +++ b/app/policies/blocked_user_policy.rb @@ -2,23 +2,23 @@ class BlockedUserPolicy < ApplicationPolicy def index? logged_in? end - + def show? moderator? || admin? || owner? end - + def create? logged_in? end - + def update? false end - + def destroy? owner? end - + class Scope < Scope def resolve scope.where user: user diff --git a/app/policies/board_policy.rb b/app/policies/board_policy.rb index 2dc7042d..0baed460 100644 --- a/app/policies/board_policy.rb +++ b/app/policies/board_policy.rb @@ -2,51 +2,51 @@ class BoardPolicy < ApplicationPolicy def index? true end - + def show? readable? end - + def create? moderator? || admin? end - + def update? (moderator? || admin?) && writable? end - + def destroy? (moderator? || admin?) && writable? end - + def readable? return false unless board_ids.compact.present? ReadableScope.new(user, Board.where(id: board_ids)).resolve.pluck(:id).sort == board_ids.sort end - + def writable? return false unless board_ids.compact.present? && logged_in? WritableScope.new(user, Board.where(id: board_ids)).resolve.pluck(:id).sort == board_ids.sort end - + def board_ids Array.wrap(record).compact.collect &:id end - + class PermissionsScope < Scope def resolve return scope.all if zooniverse_admin? scope.where permissions.join(' or ') end - + def permissions [for_all] + for_roles end - + def for_all "(boards.permissions ->> '#{ @permission }' = 'all')" end - + def for_roles user_roles.collect do |section, roles| roles << 'team' if team_for?(section) @@ -55,35 +55,35 @@ def for_roles "(boards.permissions ->> '#{ @permission }' in (#{ quoted_roles }) and boards.section = #{ quote section })" end end - + def team_for?(section) roles = user_roles[section] + user_roles.fetch('zooniverse', []) 'admin'.in?(roles) || 'moderator'.in?(roles) || 'scientist'.in?(roles) end - + def zooniverse_admin? 'admin'.in? user_roles.fetch('zooniverse', []) end - + def quote(string) ActiveRecord::Base.connection.quote string end end - + class ReadableScope < PermissionsScope def initialize(user, scope) @permission = :read super end end - + class WritableScope < PermissionsScope def initialize(user, scope) @permission = :write super end end - + class Scope < ReadableScope end end diff --git a/app/policies/comment_policy.rb b/app/policies/comment_policy.rb index 99e8628f..87ab50e6 100644 --- a/app/policies/comment_policy.rb +++ b/app/policies/comment_policy.rb @@ -1,67 +1,67 @@ class CommentPolicy < ApplicationPolicy delegate :writable?, to: :discussion_policy - + def index? true end - + def show? discussion_policy.show? end - + def create? logged_in? && !locked? && writable? end - + def update? owner? && !deleted? && !locked? && writable? end - + def destroy? owner? && !deleted? && !locked? && writable? end - + def move? !locked? && !deleted? && (owner? || moderator? || admin?) && writable? end - + def upvote? logged_in? && !deleted? && !owner? && writable? end - + def remove_upvote? logged_in? && !deleted? && !owner? && writable? end - + def locked? discussions.any? &:locked? end - + def discussion_policy DiscussionPolicy.new user, discussions end - + def deleted? Array.wrap(record).compact.any? &:is_deleted? end - + def discussions @_discussions ||= Array.wrap(record).compact.collect(&:discussion).compact end - + class Scope < Scope delegate :zooniverse_admin?, :permissions, to: :@discussion_scope - + def initialize(user, scope) @discussion_scope = DiscussionPolicy::Scope.new user, Discussion super end - + def resolve return scope.all if zooniverse_admin? scope.joins(discussion: :board).where permissions.join(' or ') end - + def discussion_scope DiscussionPolicy::Scope.new user, Discussion end diff --git a/app/policies/conversation_policy.rb b/app/policies/conversation_policy.rb index 9c9573ae..522f9c4a 100644 --- a/app/policies/conversation_policy.rb +++ b/app/policies/conversation_policy.rb @@ -2,23 +2,23 @@ class ConversationPolicy < ApplicationPolicy def index? logged_in? end - + def show? moderator? || admin? || participant? end - + def create? logged_in? end - + def update? false end - + def destroy? participant? end - + class Scope < Scope def resolve scope.for_user user diff --git a/app/policies/data_request_policy.rb b/app/policies/data_request_policy.rb index af9a76a0..d3480e8e 100644 --- a/app/policies/data_request_policy.rb +++ b/app/policies/data_request_policy.rb @@ -2,23 +2,23 @@ class DataRequestPolicy < ApplicationPolicy def index? logged_in? end - + def show? zooniverse_admin? || owner? end - + def create? accessible_section? end - + def update? false end - + def destroy? false end - + class Scope < Scope def resolve zooniverse_admin? ? scope.all : scope.where(user_id: user.id) diff --git a/app/policies/discussion_policy.rb b/app/policies/discussion_policy.rb index 3adae48c..91f16424 100644 --- a/app/policies/discussion_policy.rb +++ b/app/policies/discussion_policy.rb @@ -1,48 +1,48 @@ class DiscussionPolicy < ApplicationPolicy delegate :writable?, to: :board_policy - + def index? true end - + def show? board_policy.show? end - + def create? writable? end - + def update? (owner? || moderator? || admin?) && writable? end alias_method :owner_update?, :update? - + def destroy? (moderator? || admin?) && writable? end - + def board_policy BoardPolicy.new user, boards end - + def boards Array.wrap(record).compact.collect &:board end - + class Scope < Scope delegate :zooniverse_admin?, :permissions, to: :@board_scope - + def initialize(user, scope) @board_scope = BoardPolicy::Scope.new user, Board super end - + def resolve return scope.all if zooniverse_admin? scope.joins(:board).where permissions.join(' or ') end - + def board_scope BoardPolicy::Scope.new user, Board end diff --git a/app/policies/mention_policy.rb b/app/policies/mention_policy.rb index 71ee4057..03fecc4b 100644 --- a/app/policies/mention_policy.rb +++ b/app/policies/mention_policy.rb @@ -2,35 +2,35 @@ class MentionPolicy < ApplicationPolicy def index? true end - + def show? board_policy.show? end - + def create? false end - + def update? false end - + def destroy? false end - + def board_policy BoardPolicy.new user, Array.wrap(record).compact.collect(&:board).compact end - + class Scope < Scope delegate :zooniverse_admin?, :permissions, to: :@board_scope - + def initialize(user, scope) @board_scope = BoardPolicy::Scope.new user, Board super end - + def resolve return scope.all if zooniverse_admin? scope.joins(:board).where permissions.join(' or ') diff --git a/app/policies/message_policy.rb b/app/policies/message_policy.rb index 122d5140..e97a871f 100644 --- a/app/policies/message_policy.rb +++ b/app/policies/message_policy.rb @@ -2,23 +2,23 @@ class MessagePolicy < ApplicationPolicy def index? logged_in? end - + def show? moderator? || admin? || participant? end - + def create? participant? end - + def update? false end - + def destroy? false end - + class Scope < Scope def resolve scope.joins(:conversation, :user_conversations).where({ diff --git a/app/policies/moderation_policy.rb b/app/policies/moderation_policy.rb index 5482b458..1bbfd77b 100644 --- a/app/policies/moderation_policy.rb +++ b/app/policies/moderation_policy.rb @@ -2,27 +2,27 @@ class ModerationPolicy < ApplicationPolicy def index? logged_in? end - + def show? moderator? || admin? end - + def create? logged_in? end - + def update? moderator? || admin? end - + def destroy? moderator? || admin? end - + def can_action?(action) available_actions.include? action.to_sym end - + def available_actions return [] unless logged_in? @available_actions ||= target.class.moderatable.select do |action, roles| @@ -32,11 +32,11 @@ def available_actions (roles[:team] && team?) end.keys end - + def target Array.wrap(record).first.target || (raise ActiveRecord::RecordNotFound.new("Couldn't find target")) end - + class Scope < Scope def resolve if user.roles.where(section: 'zooniverse', name: ['moderator', 'admin']).any? diff --git a/app/policies/notification_policy.rb b/app/policies/notification_policy.rb index acf9b3d5..69921a1d 100644 --- a/app/policies/notification_policy.rb +++ b/app/policies/notification_policy.rb @@ -2,23 +2,23 @@ class NotificationPolicy < ApplicationPolicy def index? logged_in? end - + def show? owner? end - + def create? false end - + def update? owner? end - + def destroy? false end - + class Scope < Scope def resolve scope.where user_id: user.id diff --git a/app/policies/role_policy.rb b/app/policies/role_policy.rb index cef62064..a4cad3ff 100644 --- a/app/policies/role_policy.rb +++ b/app/policies/role_policy.rb @@ -2,23 +2,23 @@ class RolePolicy < ApplicationPolicy def index? true end - + def show? true end - + def create? accessible_section? end - + def update? accessible_section? end - + def destroy? accessible_section? end - + class Scope < Scope def resolve scope diff --git a/app/policies/subscription_policy.rb b/app/policies/subscription_policy.rb index 6fb2906a..12005977 100644 --- a/app/policies/subscription_policy.rb +++ b/app/policies/subscription_policy.rb @@ -2,23 +2,23 @@ class SubscriptionPolicy < ApplicationPolicy def index? logged_in? end - + def show? owner? end - + def create? logged_in? end - + def update? owner? end - + def destroy? false end - + class Scope < Scope def resolve scope.where user: user diff --git a/app/policies/subscription_preference_policy.rb b/app/policies/subscription_preference_policy.rb index b90266ba..cfbea6f0 100644 --- a/app/policies/subscription_preference_policy.rb +++ b/app/policies/subscription_preference_policy.rb @@ -2,23 +2,23 @@ class SubscriptionPreferencePolicy < ApplicationPolicy def index? logged_in? end - + def show? owner? end - + def create? false end - + def update? owner? end - + def destroy? false end - + class Scope < Scope def resolve scope.where user: user diff --git a/app/policies/tag_policy.rb b/app/policies/tag_policy.rb index 3dc2b1f3..ce980a1c 100644 --- a/app/policies/tag_policy.rb +++ b/app/policies/tag_policy.rb @@ -2,43 +2,43 @@ class TagPolicy < ApplicationPolicy def index? true end - + def popular? true end - + def show? comment_policy.show? end - + def create? false end - + def update? false end - + def destroy? false end - + def comment_policy CommentPolicy.new user, comments end - + def comments Array.wrap(record).compact.collect &:comment end - + class Scope < Scope delegate :zooniverse_admin?, :permissions, to: :@comment_scope - + def initialize(user, scope) @comment_scope = CommentPolicy::Scope.new user, Comment super end - + def resolve return scope.all if zooniverse_admin? scope.joins(comment: { discussion: :board }).where permissions.join(' or ') diff --git a/app/policies/user_conversation_policy.rb b/app/policies/user_conversation_policy.rb index 037cfa2f..ba730fbd 100644 --- a/app/policies/user_conversation_policy.rb +++ b/app/policies/user_conversation_policy.rb @@ -2,23 +2,23 @@ class UserConversationPolicy < ApplicationPolicy def index? logged_in? end - + def show? moderator? || admin? || owner? end - + def create? logged_in? end - + def update? owner? end - + def destroy? owner? end - + class Scope < Scope def resolve scope.where user_id: user.id diff --git a/app/policies/user_ip_ban_policy.rb b/app/policies/user_ip_ban_policy.rb index 1f578e83..304ad671 100644 --- a/app/policies/user_ip_ban_policy.rb +++ b/app/policies/user_ip_ban_policy.rb @@ -2,23 +2,23 @@ class UserIpBanPolicy < ApplicationPolicy def index? zooniverse_admin? end - + def show? zooniverse_admin? end - + def create? zooniverse_admin? end - + def update? false end - + def destroy? zooniverse_admin? end - + class Scope < Scope def resolve zooniverse_admin? ? scope.all : scope.none diff --git a/app/policies/user_policy.rb b/app/policies/user_policy.rb index 529f6025..7f1a1ff3 100644 --- a/app/policies/user_policy.rb +++ b/app/policies/user_policy.rb @@ -2,23 +2,23 @@ class UserPolicy < ApplicationPolicy def index? logged_in? end - + def show? true end - + def create? false end - + def update? false end - + def destroy? false end - + class Scope < Scope def resolve scope diff --git a/app/schemas/announcement_schema.rb b/app/schemas/announcement_schema.rb index 512eb4df..a911deec 100644 --- a/app/schemas/announcement_schema.rb +++ b/app/schemas/announcement_schema.rb @@ -1,16 +1,16 @@ class AnnouncementSchema include JSON::SchemaBuilder - + root :announcements - + def create changes required: true end - + def update changes end - + def changes(required = { }) root do additional_properties false diff --git a/app/schemas/blocked_user_schema.rb b/app/schemas/blocked_user_schema.rb index 59f522bd..99e70f12 100644 --- a/app/schemas/blocked_user_schema.rb +++ b/app/schemas/blocked_user_schema.rb @@ -1,8 +1,8 @@ class BlockedUserSchema include JSON::SchemaBuilder - + root :blocked_users - + def create root do |root_object| additional_properties false @@ -10,7 +10,7 @@ def create id :blocked_user_id, required: true end end - + def update root do |root_object| additional_properties false diff --git a/app/schemas/board_schema.rb b/app/schemas/board_schema.rb index 3871720a..f903ff66 100644 --- a/app/schemas/board_schema.rb +++ b/app/schemas/board_schema.rb @@ -1,22 +1,22 @@ class BoardSchema include JSON::SchemaBuilder - + root :boards - + def create root do |root_object| changes root_object, required: true string :section, required: true end end - + def update root do |root_object| changes root_object integer :position end end - + def changes(obj, required = { }) obj.additional_properties false obj.string :title, **required @@ -25,14 +25,14 @@ def changes(obj, required = { }) obj.id :parent_id, null: true obj.permissions obj, required end - + def permissions(obj, required = { }) obj.object :permissions, **required do |permission_object| permission permission_object, :read permission permission_object, :write end end - + def permission(obj, name) obj.entity name, required: true do enum [:all, :team, :moderator, :admin] diff --git a/app/schemas/comment_schema.rb b/app/schemas/comment_schema.rb index 6c16eb73..a96ebb41 100644 --- a/app/schemas/comment_schema.rb +++ b/app/schemas/comment_schema.rb @@ -2,9 +2,9 @@ class CommentSchema include JSON::SchemaBuilder include FocusSchema attr_accessor :policy - + root :comments - + def create root do |root_object| changes root_object, required: true @@ -12,14 +12,14 @@ def create id :discussion_id, required: true end end - + def update root do |root_object| changes root_object id :discussion_id if policy.move? end end - + def changes(obj, required = { }) obj.additional_properties false obj.string :category diff --git a/app/schemas/concerns/focus_schema.rb b/app/schemas/concerns/focus_schema.rb index 5a16ac78..b13ef627 100644 --- a/app/schemas/concerns/focus_schema.rb +++ b/app/schemas/concerns/focus_schema.rb @@ -1,6 +1,6 @@ module FocusSchema extend ActiveSupport::Concern - + def focus(obj) obj.id :focus_id, null: true obj.entity :focus_type do diff --git a/app/schemas/conversation_schema.rb b/app/schemas/conversation_schema.rb index f4281f7f..2e7ef36c 100644 --- a/app/schemas/conversation_schema.rb +++ b/app/schemas/conversation_schema.rb @@ -1,21 +1,21 @@ class ConversationSchema include JSON::SchemaBuilder - + root :conversations - + def create root do additional_properties false string :title, required: true id :user_id, required: true string :body, required: true - + array :recipient_ids, required: true, unique_items: true, min_items: 1 do items type: :id end end end - + def update root do |root_object| additional_properties false diff --git a/app/schemas/data_request_schema.rb b/app/schemas/data_request_schema.rb index 62b2006b..88dff013 100644 --- a/app/schemas/data_request_schema.rb +++ b/app/schemas/data_request_schema.rb @@ -1,8 +1,8 @@ class DataRequestSchema include JSON::SchemaBuilder - + root :data_requests - + def create root do additional_properties false @@ -13,7 +13,7 @@ def create end end end - + def update root do |root_object| additional_properties false diff --git a/app/schemas/discussion_schema.rb b/app/schemas/discussion_schema.rb index f7aad413..1d0b20de 100644 --- a/app/schemas/discussion_schema.rb +++ b/app/schemas/discussion_schema.rb @@ -2,7 +2,7 @@ class DiscussionSchema include JSON::SchemaBuilder include FocusSchema root :discussions - + def create root do |root_object| additional_properties false @@ -14,14 +14,14 @@ def create comments root_object end end - + def owner_update root do |root_object| additional_properties false string :title, min_length: 3, max_length: 140 end end - + def update root do |root_object| additional_properties false @@ -31,14 +31,14 @@ def update sticky root_object end end - + def sticky(obj) obj.boolean :sticky, default: false obj.entity :sticky_position do one_of number, null end end - + def comments(obj) obj.array :comments, required: true, min_items: 1 do items type: :object do |comment_object| diff --git a/app/schemas/message_schema.rb b/app/schemas/message_schema.rb index dd646850..f0c55f53 100644 --- a/app/schemas/message_schema.rb +++ b/app/schemas/message_schema.rb @@ -1,8 +1,8 @@ class MessageSchema include JSON::SchemaBuilder - + root :messages - + def create root do additional_properties false @@ -11,7 +11,7 @@ def create string :body, required: true end end - + def update root do |root_object| additional_properties false diff --git a/app/schemas/moderation_schema.rb b/app/schemas/moderation_schema.rb index be14ad7e..ccb5f7a0 100644 --- a/app/schemas/moderation_schema.rb +++ b/app/schemas/moderation_schema.rb @@ -1,8 +1,8 @@ class ModerationSchema include JSON::SchemaBuilder - + root :moderations - + def create root do |root_object| additional_properties false @@ -12,7 +12,7 @@ def create reports root_object end end - + def update root do |root_object| additional_properties false @@ -20,15 +20,15 @@ def update actions root_object end end - + protected - + def action(obj) obj.string :action do enum [:destroy, :ignore, :watch] end end - + def reports(obj) obj.array :reports, min_items: 1 do items type: :object do @@ -37,7 +37,7 @@ def reports(obj) end end end - + def actions(obj) obj.array :actions, min_items: 1 do items type: :object do |item| diff --git a/app/schemas/notification_schema.rb b/app/schemas/notification_schema.rb index b4cbcf7f..f71ceb46 100644 --- a/app/schemas/notification_schema.rb +++ b/app/schemas/notification_schema.rb @@ -1,14 +1,14 @@ class NotificationSchema include JSON::SchemaBuilder - + root :notifications - + def create root do |root_object| additional_properties false end end - + def update root do additional_properties false diff --git a/app/schemas/role_schema.rb b/app/schemas/role_schema.rb index dfa2472a..84cf9d1e 100644 --- a/app/schemas/role_schema.rb +++ b/app/schemas/role_schema.rb @@ -1,8 +1,8 @@ class RoleSchema include JSON::SchemaBuilder - + root :roles - + def create root do |root_object| additional_properties false @@ -12,7 +12,7 @@ def create role_names root_object end end - + def update root do |root_object| additional_properties false @@ -20,7 +20,7 @@ def update boolean :is_shown, default: true end end - + def role_names(obj) obj.string :name, required: true do enum [:admin, :moderator, :scientist, :team] diff --git a/app/schemas/subscription_preference_schema.rb b/app/schemas/subscription_preference_schema.rb index 0371c937..20237cb0 100644 --- a/app/schemas/subscription_preference_schema.rb +++ b/app/schemas/subscription_preference_schema.rb @@ -1,14 +1,14 @@ class SubscriptionPreferenceSchema include JSON::SchemaBuilder - + root :subscription_preferences - + def create root do |root_object| additional_properties false end end - + def update root do additional_properties false diff --git a/app/schemas/subscription_schema.rb b/app/schemas/subscription_schema.rb index 5db556a8..07bf378c 100644 --- a/app/schemas/subscription_schema.rb +++ b/app/schemas/subscription_schema.rb @@ -1,8 +1,8 @@ class SubscriptionSchema include JSON::SchemaBuilder - + root :subscriptions - + # Currently creation is only allowed for # Discussion -> followed_discussions def create @@ -19,7 +19,7 @@ def create end end end - + def update root do additional_properties false diff --git a/app/schemas/user_ip_ban_schema.rb b/app/schemas/user_ip_ban_schema.rb index 9ae80c2f..7ff51d77 100644 --- a/app/schemas/user_ip_ban_schema.rb +++ b/app/schemas/user_ip_ban_schema.rb @@ -1,15 +1,15 @@ class UserIpBanSchema include JSON::SchemaBuilder - + root :user_ip_bans - + def create root do additional_properties false string :ip, required: true end end - + def update root do |root_object| additional_properties false diff --git a/app/serializers/announcement_serializer.rb b/app/serializers/announcement_serializer.rb index 57e29cae..4d0160c7 100644 --- a/app/serializers/announcement_serializer.rb +++ b/app/serializers/announcement_serializer.rb @@ -1,7 +1,7 @@ class AnnouncementSerializer include TalkSerializer include EmbeddedAttributes - + all_attributes can_sort_by :created_at can_filter_by :section diff --git a/app/serializers/blocked_user_serializer.rb b/app/serializers/blocked_user_serializer.rb index e7fbede6..856d5544 100644 --- a/app/serializers/blocked_user_serializer.rb +++ b/app/serializers/blocked_user_serializer.rb @@ -1,6 +1,6 @@ class BlockedUserSerializer include TalkSerializer - + all_attributes can_sort_by :created_at self.default_sort = 'created_at' diff --git a/app/serializers/board_serializer.rb b/app/serializers/board_serializer.rb index 077c504c..ff6279c9 100644 --- a/app/serializers/board_serializer.rb +++ b/app/serializers/board_serializer.rb @@ -1,7 +1,7 @@ class BoardSerializer include TalkSerializer include EmbeddedAttributes - + all_attributes attribute :latest_discussion can_filter_by :subject_default @@ -10,7 +10,7 @@ class BoardSerializer self.default_sort = 'position,-last_comment_created_at' self.preloads = [:latest_discussion] self.eager_loads = [:project, :parent] - + def latest_discussion DiscussionSerializer.as_json model.latest_discussion end diff --git a/app/serializers/comment_serializer.rb b/app/serializers/comment_serializer.rb index df6cda71..2d18de6f 100644 --- a/app/serializers/comment_serializer.rb +++ b/app/serializers/comment_serializer.rb @@ -2,7 +2,7 @@ class CommentSerializer include TalkSerializer include EmbeddedAttributes include ModerationActions - + all_attributes except: [:user_ip] attributes :reply_user_id, :reply_user_login, :reply_user_display_name can_sort_by :created_at @@ -10,25 +10,25 @@ class CommentSerializer embed_attributes_from :discussion, :board, :project self.default_sort = 'created_at' self.includes = [:user, :discussion, :board, :project] - + def custom_attributes super.merge user_display_name: model.user.display_name end - + def reply_user_id reply_user.id end - + def reply_user_login reply_user.login end - + def reply_user_display_name reply_user.display_name end - + protected - + def reply_user if model.reply_id _load_reply_user @@ -36,7 +36,7 @@ def reply_user OpenStruct.new end end - + def _load_reply_user return @reply_user if @reply_user login, display_name, id = Comment.where(id: model.reply_id).joins(:user).pluck('users.login', 'users.display_name', 'users.id').first diff --git a/app/serializers/concerns/embedded_attributes.rb b/app/serializers/concerns/embedded_attributes.rb index 1aa9d232..32f2cd93 100644 --- a/app/serializers/concerns/embedded_attributes.rb +++ b/app/serializers/concerns/embedded_attributes.rb @@ -1,30 +1,30 @@ module EmbeddedAttributes extend ActiveSupport::Concern - + included do class_attribute :_embedded_attributes self._embedded_attributes = [] end - + module ClassMethods def embed_attributes_from(*list) self._embedded_attributes += list.map(&:to_sym) self._embedded_attributes.uniq! end end - + def discussion_attributes %w(comments_count subject_default title updated_at users_count focus_id focus_type locked) end - + def board_attributes %w(comments_count description discussions_count id parent_id subject_default title users_count permissions) end - + def project_attributes %w(slug) end - + def custom_attributes super.tap do |custom_attrs| self._embedded_attributes.each do |relation| @@ -32,7 +32,7 @@ def custom_attributes end end end - + def attributes_from(relation) { }.tap do |attrs| record_attributes = model.send(relation).attributes rescue { } diff --git a/app/serializers/concerns/moderation_actions.rb b/app/serializers/concerns/moderation_actions.rb index a60da396..ced41c1c 100644 --- a/app/serializers/concerns/moderation_actions.rb +++ b/app/serializers/concerns/moderation_actions.rb @@ -1,6 +1,6 @@ module ModerationActions extend ActiveSupport::Concern - + def custom_attributes moderation = Moderation.new target: model moderation.section = model.section if model.respond_to?(:section) diff --git a/app/serializers/concerns/talk_serializer.rb b/app/serializers/concerns/talk_serializer.rb index 87f2f185..89f19f36 100644 --- a/app/serializers/concerns/talk_serializer.rb +++ b/app/serializers/concerns/talk_serializer.rb @@ -1,17 +1,17 @@ module TalkSerializer extend ActiveSupport::Concern - + included do |klass| include RestPack::Serializer extend ClassMethodOverrides attr_reader :model attributes :href, :links - + class << self attr_accessor :eager_loads, :preloads, :includes attr_accessor :default_sort end - + self.eager_loads ||= [] self.preloads ||= [] self.includes ||= [] @@ -20,21 +20,21 @@ class << self stringify_primary_key stringify_foreign_keys end - + module ClassMethods def all_attributes(except: []) attrs = model_class.attribute_names.sort rescue [] except = Array.wrap(except).sort.collect &:to_s attributes *(attrs.sort - except) end - + def stringify_primary_key primary_key = model_class.primary_key define_method primary_key do model[primary_key].to_s end end - + def stringify_foreign_keys belong_tos = model_class.reflect_on_all_associations.select{ |a| a.macro == :belongs_to } belong_tos.each do |association| @@ -44,14 +44,14 @@ def stringify_foreign_keys end end end - + def current_sort_from(options) options.sorting.map do |attribute, direction| direction == :asc ? attribute : "-#{ attribute }" end.join ',' end end - + module ClassMethodOverrides def serialize_meta(page, options) super.merge({ @@ -60,15 +60,15 @@ def serialize_meta(page, options) sortable_attributes: serializable_sorting_attributes || [] }) end - + def resource(params = { }, scope = nil, context = { }) super params, scope_preloading_for(scope), context.merge(params: params) end - + def page(params = { }, scope = nil, context = { }) super params, scope_preloading_for(scope), context.merge(params: params) end - + def scope_preloading_for(scope) scope ||= model_class.all scope = scope.includes(*includes) if includes.any? @@ -77,15 +77,15 @@ def scope_preloading_for(scope) scope end end - + def links { } end - + def current_user @context[:current_user] end - + def params @context.fetch :params, { } end diff --git a/app/serializers/conversation_serializer.rb b/app/serializers/conversation_serializer.rb index b75aa676..d89ada97 100644 --- a/app/serializers/conversation_serializer.rb +++ b/app/serializers/conversation_serializer.rb @@ -1,18 +1,18 @@ class ConversationSerializer include TalkSerializer include ModerationActions - + all_attributes attribute :is_unread can_include :messages, :users - + def custom_attributes super.tap do |attrs| # All users able to view a conversation are able to report it attrs[:moderatable_actions] << :report end end - + def is_unread return unless model && current_user model.user_conversations.where(user: current_user).first.try :is_unread diff --git a/app/serializers/discussion_serializer.rb b/app/serializers/discussion_serializer.rb index c30292a8..0dcf80ae 100644 --- a/app/serializers/discussion_serializer.rb +++ b/app/serializers/discussion_serializer.rb @@ -2,7 +2,7 @@ class DiscussionSerializer include TalkSerializer include EmbeddedAttributes include ModerationActions - + all_attributes attribute :latest_comment can_filter_by :title, :subject_default, :sticky @@ -11,15 +11,15 @@ class DiscussionSerializer self.default_sort = '-sticky,sticky_position,-last_comment_created_at' self.preloads = [:latest_comment, :user] self.eager_loads = [:board, :project] - + def custom_attributes super.merge user_display_name: model.user.display_name end - + def latest_comment CommentSerializer.as_json model.latest_comment end - + def links return { } unless comment_sorting = params[:sort_linked_comments] direction = comment_sorting =~ /^\-/ ? :desc : :asc diff --git a/app/serializers/mention_serializer.rb b/app/serializers/mention_serializer.rb index 5d080a2a..9bc668c8 100644 --- a/app/serializers/mention_serializer.rb +++ b/app/serializers/mention_serializer.rb @@ -1,7 +1,7 @@ class MentionSerializer include TalkSerializer all_attributes - + can_include :comment can_filter_by :section, :mentionable_id, :mentionable_type can_sort_by :created_at diff --git a/app/serializers/moderation_serializer.rb b/app/serializers/moderation_serializer.rb index 4406b1e1..e49f571c 100644 --- a/app/serializers/moderation_serializer.rb +++ b/app/serializers/moderation_serializer.rb @@ -4,17 +4,17 @@ class ModerationSerializer can_filter_by :state can_sort_by :updated_at self.default_sort = 'state,-updated_at' - + def custom_attributes super.merge target: target end - + def target target_serializer.as_json(model.target, current_user: current_user) if model.target rescue nil end - + def target_serializer RestPack::Serializer.class_map[model.target_type.underscore] end diff --git a/app/serializers/notification_serializer.rb b/app/serializers/notification_serializer.rb index 8e6ca7bd..7e4d993a 100644 --- a/app/serializers/notification_serializer.rb +++ b/app/serializers/notification_serializer.rb @@ -1,7 +1,7 @@ class NotificationSerializer include TalkSerializer include EmbeddedAttributes - + all_attributes can_sort_by :created_at, :delivered can_filter_by :section, :delivered @@ -9,17 +9,17 @@ class NotificationSerializer embed_attributes_from :project self.default_sort = '-created_at' self.includes = [:project, :source] - + def custom_attributes super.merge source: source end - + def source source_serializer.as_json(model.source, current_user: current_user) if model.source rescue nil end - + def source_serializer RestPack::Serializer.class_map[model.source_type.underscore] end diff --git a/app/serializers/popular_focus_tag_serializer.rb b/app/serializers/popular_focus_tag_serializer.rb index a33219cc..ef40046f 100644 --- a/app/serializers/popular_focus_tag_serializer.rb +++ b/app/serializers/popular_focus_tag_serializer.rb @@ -2,11 +2,11 @@ class PopularFocusTagSerializer include TalkSerializer all_attributes can_filter_by :name, :taggable_id, :taggable_type - + def self.key 'popular' end - + def self.href_prefix '/tags' end diff --git a/app/serializers/popular_tag_serializer.rb b/app/serializers/popular_tag_serializer.rb index 6d9eab3a..ebfcf2c8 100644 --- a/app/serializers/popular_tag_serializer.rb +++ b/app/serializers/popular_tag_serializer.rb @@ -2,11 +2,11 @@ class PopularTagSerializer include TalkSerializer all_attributes can_filter_by :name - + def self.key 'popular' end - + def self.href_prefix '/tags' end diff --git a/app/serializers/user_serializer.rb b/app/serializers/user_serializer.rb index 3ee29700..509df3ed 100644 --- a/app/serializers/user_serializer.rb +++ b/app/serializers/user_serializer.rb @@ -1,7 +1,7 @@ class UserSerializer include TalkSerializer include ModerationActions - + attributes :id, :created_at, :updated_at, :login, :display_name, :zooniverse_id, :credited_name, :admin, :banned end diff --git a/app/services/application_service.rb b/app/services/application_service.rb index 494e4041..14dfea7e 100644 --- a/app/services/application_service.rb +++ b/app/services/application_service.rb @@ -1,6 +1,6 @@ class ApplicationService include TalkService - + def self.inherited(base) base.model_class = base.name.sub(/Service$/, '').constantize base.schema_class = "#{ base.model_class.name }Schema".constantize diff --git a/app/services/comment_service.rb b/app/services/comment_service.rb index 10c063c2..3d6f87a8 100644 --- a/app/services/comment_service.rb +++ b/app/services/comment_service.rb @@ -5,19 +5,19 @@ def build built.user_ip = user_ip end end - + def upvote find_and_authorize resource.upvote! current_user end - + def remove_upvote find_and_authorize resource.remove_upvote! current_user end - + protected - + def find_and_authorize find_resource unless resource authorize unless authorized? diff --git a/app/services/concerns/talk_service.rb b/app/services/concerns/talk_service.rb index c0c3b6f9..be15ff26 100644 --- a/app/services/concerns/talk_service.rb +++ b/app/services/concerns/talk_service.rb @@ -1,16 +1,16 @@ module TalkService extend ActiveSupport::Concern - + class ParameterError < StandardError; end - + included do class_attribute :model_class class_attribute :schema_class - + attr_accessor :params, :action, :current_user, :model_class, :schema_class, :user_ip attr_accessor :resource end - + def initialize(params:, action:, current_user:, model_class: nil, schema_class: nil, user_ip: nil) @params = params @action = action.to_sym @@ -19,7 +19,7 @@ def initialize(params:, action:, current_user:, model_class: nil, schema_class: @model_class = model_class || self.class.model_class @schema_class = schema_class || self.class.schema_class end - + def build @resource = model_class.new unrooted_params rescue ArgumentError => e @@ -27,11 +27,11 @@ def build rescue NameError => e reraise_constantize_errors e end - + def find_resource @resource = model_class.find params[:id] end - + def update_resource resource.assign_attributes unrooted_params rescue ArgumentError => e @@ -39,7 +39,7 @@ def update_resource rescue NameError => e reraise_constantize_errors e end - + def create build unless resource authorize unless authorized? @@ -48,7 +48,7 @@ def create rescue NameError => e reraise_constantize_errors e end - + def update find_resource unless resource authorize unless authorized? @@ -58,31 +58,31 @@ def update rescue NameError => e reraise_constantize_errors e end - + def authorize build unless resource @authorized = policy.send "#{ action }?" unauthorized! unless authorized? end - + def validate schema = schema_class.new(policy: policy).send action @validated = schema.validate! rooted_params end - + def policy build unless resource Pundit.policy! current_user, resource end - + def validated? !!@validated end - + def authorized? !!@authorized end - + def set_user(&block) unauthorized! unless current_user begin @@ -95,21 +95,21 @@ def set_user(&block) raise TalkService::ParameterError.new end end - + def permitted_params params.permitted? ? params : params.permit! end - + def rooted_params permitted_params.slice model_class.table_name end - + def unrooted_params permitted_params[model_class.table_name] || ActionController::Parameters.new end - + protected - + # Rails raises an ArgumentError when assigning an invalid value to an enum # This is just stupid, so generate a rescuable exception with a helpful message instead def reraise_enum_errors(e) @@ -118,7 +118,7 @@ def reraise_enum_errors(e) enum = model_class.send attribute.pluralize raise Talk::InvalidParameterError.new(attribute, "in #{ enum.keys }", unrooted_params[attribute]) end - + def reraise_constantize_errors(e) if e.message =~ /wrong constant name/ raise TalkService::ParameterError.new('Invalid type') @@ -126,7 +126,7 @@ def reraise_constantize_errors(e) raise e end end - + def unauthorized! raise Pundit::NotAuthorizedError.new "not allowed to #{ action } this #{ model_class }" end diff --git a/app/services/conversation_service.rb b/app/services/conversation_service.rb index fddc1daf..3bb7854e 100644 --- a/app/services/conversation_service.rb +++ b/app/services/conversation_service.rb @@ -7,17 +7,17 @@ def build conversation.messages << build_message end end - + def build_user_conversations_for(conversation) conversation.user_conversations.build user_id: current_user.id, is_unread: false - + raise Talk::BlockedUserError.new if blocked? raise Talk::UserBlockedError.new if blocking? recipient_ids.each do |recipient_id| conversation.user_conversations.build user_id: recipient_id, is_unread: true end end - + def build_message MessageService.new({ params: message_params, @@ -26,25 +26,25 @@ def build_message user_ip: user_ip }).build end - + def conversation_params unrooted_params.except :body, :user_id, :recipient_ids end - + def recipient_ids unrooted_params.fetch :recipient_ids, [] end - + def message_params ActionController::Parameters.new({ messages: unrooted_params.slice(:body) }) end - + def blocked? BlockedUser.blocked_by(recipient_ids).blocking(current_user.id).exists? end - + def blocking? BlockedUser.blocked_by(current_user.id).blocking(recipient_ids).exists? end diff --git a/app/services/discussion_service.rb b/app/services/discussion_service.rb index 01b32ab4..993f0cf4 100644 --- a/app/services/discussion_service.rb +++ b/app/services/discussion_service.rb @@ -12,17 +12,17 @@ def build discussion.comments << new_comment end end - + def update find_resource unless resource self.action = :owner_update if !policy.admin? && !policy.moderator? && policy.owner? super end - + def discussion_params unrooted_params.except :comments end - + def comment_params ActionController::Parameters.new({ comments: unrooted_params[:comments].first diff --git a/app/services/message_service.rb b/app/services/message_service.rb index a5862d56..bee69353 100644 --- a/app/services/message_service.rb +++ b/app/services/message_service.rb @@ -1,24 +1,24 @@ class MessageService < ApplicationService def build set_user - + super.tap do |built| built.user_ip = user_ip raise Talk::BlockedUserError.new if blocked? raise Talk::UserBlockedError.new if blocking? end end - + def blocked? return false unless resource.conversation BlockedUser.blocked_by(recipient_ids).blocking(current_user.id).exists? end - + def blocking? return false unless resource.conversation BlockedUser.blocked_by(current_user.id).blocking(recipient_ids).exists? end - + def recipient_ids resource.conversation.participant_ids - [current_user.id] end diff --git a/app/services/moderation_service.rb b/app/services/moderation_service.rb index ff863a90..88ebc3ef 100644 --- a/app/services/moderation_service.rb +++ b/app/services/moderation_service.rb @@ -3,44 +3,44 @@ def build set_user_on :reports existing_resource ? add_report : super end - + def add_report new_report = unrooted_params.fetch :reports, [] unrooted_params[:reports] = resource.reports + new_report update_resource end - + def update_resource set_user_on(:actions) if actioning? super end - + def authorize build unless resource @authorized = policy.send "#{ action }?" @authorized &= actioning? && can_action? if updating? unauthorized! unless authorized? end - + def updating? action == :update end - + def actioning? updating? && unrooted_params[:actions].is_a?(Array) end - + def can_action? moderation_action = unrooted_params[:actions].first || { } policy.can_action? moderation_action.fetch('action', '') end - + protected - + def existing_resource @resource = Moderation.find_by_target_id unrooted_params[:target_id] end - + def set_user_on(key) set_user do unrooted_params[key].each do |embedded| diff --git a/app/services/user_ip_ban_service.rb b/app/services/user_ip_ban_service.rb index 546188a4..44e90598 100644 --- a/app/services/user_ip_ban_service.rb +++ b/app/services/user_ip_ban_service.rb @@ -1,3 +1,3 @@ class UserIpBanService < ApplicationService - + end diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 711baac1..7afd463c 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -10,32 +10,32 @@ color: #404040; background-color: #F1F1F1; } - + h1 { font-size: 30px; font-weight: 700; line-height: 45px; } - + h2 { font-size: 22px; font-weight: 700; color: #4F4F4F; line-height: 33px; } - + h3 { font-size: 18px; font-weight: 700; color: #4F4F4F; line-height: 26px; } - + a, a:active, a:visited { color: #0072FF; text-decoration: none; } - + header { text-align: center; margin-top: 20px; @@ -43,13 +43,13 @@ margin-bottom: 40px; margin-bottom: 20vh; } - + footer { text-align: center; margin-top: 30px; margin-top: 20vh; } - + .centering { text-align: center; } diff --git a/app/views/notification_mailer/notify.html.erb b/app/views/notification_mailer/notify.html.erb index abae6cb9..2cb6d287 100644 --- a/app/views/notification_mailer/notify.html.erb +++ b/app/views/notification_mailer/notify.html.erb @@ -2,7 +2,7 @@ <%= h1 'Zooniverse Talk Notifications' %> <% if @system.any? %> <%= h2 'System' %> - + <% @system.each_pair do |section, notifications| %> <%= container do %> <%= h2 do %> @@ -12,7 +12,7 @@ <%= link_to 'Zooniverse', FrontEnd.zooniverse_talk, style: link_style %> <% end %> <% end %> - +
<% notifications.each do |notification| %> <%= container do %> @@ -25,10 +25,10 @@ <% end %> <% end %> <% end %> - + <% if @moderations.any? %> <%= h2 'Moderation Reports' %> - + <% @moderations.each_pair do |section, notifications| %> <%= container do %> <% project = notifications.first.project %> @@ -39,7 +39,7 @@ <%= link_to 'Zooniverse', FrontEnd.zooniverse_talk, style: link_style %> <% end %> <% end %> - +
<% notifications.each do |notification| %> <%= container do %> @@ -61,23 +61,23 @@ <% end %> <% end %> <% end %> - + <% if @messages.any? %> <%= h2 'Messages' %> - + <% @messages.each_pair do |conversation, notifications| %> <%= container do %> <%= h3 do %> <%= link_to conversation.title, FrontEnd.link_to(conversation), style: link_style %> <% end %> - +

You have <%= pluralize notifications.length, 'New message' %> in conversation <%= link_to conversation.title, FrontEnd.link_to(conversation), style: link_style %>

- +
<% notifications.each do |notification| %> <%= container do %> @@ -93,10 +93,10 @@ <% end %> <% end %> <% end %> - + <% if @mentions.any? %> <%= h2 'Mentions' %> - + <% @mentions.each_pair do |section, notifications| %>
<% project = notifications.first.project %> @@ -107,7 +107,7 @@ <%= link_to 'Zooniverse', FrontEnd.zooniverse_talk, style: link_style %> <% end %> <% end %> - +
<% notifications.each do |notification| %> <%= container do %> @@ -123,10 +123,10 @@
<% end %> <% end %> - + <% if @started_discussions.any? %> <%= h2 "Boards you're following" %> - + <% @started_discussions.each_pair do |section, boards| %> <% project = boards.first.last.first.project %>
@@ -137,7 +137,7 @@ <%= link_to 'Zooniverse', FrontEnd.zooniverse_talk, style: link_style %> <% end %> <% end %> - +
<% boards.each_pair do |board, notifications| %> <%= container do %> @@ -146,7 +146,7 @@ on <%= link_to board.title, FrontEnd.link_to(board, project), style: link_style %> - +
<% notifications.each do |notification| %> <%= container do %> @@ -162,10 +162,10 @@
<% end %> <% end %> - + <% if @discussions.any? %> <%= h2 "Discussions you're following" %> - + <% @discussions.each_pair do |section, discussions| %> <% project = discussions.first.last.first.project %>
@@ -176,7 +176,7 @@ <%= link_to 'Zooniverse', FrontEnd.zooniverse_talk, style: link_style %> <% end %> <% end %> - +
<% discussions.each_pair do |discussion, notifications| %> <%= container do %> @@ -185,7 +185,7 @@ on <%= link_to discussion.title, FrontEnd.link_to(discussion, project), style: link_style %> - +
<% notifications.each do |notification| %> <%= container do %> @@ -204,7 +204,7 @@
<% end %> <% end %> - +