diff --git a/back/app/controllers/web_api/v1/action_descriptors_controller.rb b/back/app/controllers/web_api/v1/action_descriptors_controller.rb deleted file mode 100644 index 18b724bcb092..000000000000 --- a/back/app/controllers/web_api/v1/action_descriptors_controller.rb +++ /dev/null @@ -1,11 +0,0 @@ -# frozen_string_literal: true - -class WebApi::V1::ActionDescriptorsController < ApplicationController - skip_before_action :authenticate_user - skip_after_action :verify_authorized, only: [:initiatives] - - def initiatives - descriptors = Permissions::InitiativePermissionsService.new(current_user).action_descriptors - render(json: raw_json(descriptors)) - end -end diff --git a/back/app/controllers/web_api/v1/files_controller.rb b/back/app/controllers/web_api/v1/files_controller.rb index 0d96eea89f0f..8b374627ee0c 100644 --- a/back/app/controllers/web_api/v1/files_controller.rb +++ b/back/app/controllers/web_api/v1/files_controller.rb @@ -13,13 +13,6 @@ class WebApi::V1::FilesController < ApplicationController file_relationship: :idea_files, container_id: :idea_id }, - 'Initiative' => { - container_class: Initiative, - file_class: InitiativeFile, - policy_scope_class: InitiativeFilePolicy::Scope, - file_relationship: :initiative_files, - container_id: :initiative_id - }, 'Project' => { container_class: Project, file_class: ProjectFile, diff --git a/back/app/controllers/web_api/v1/images_controller.rb b/back/app/controllers/web_api/v1/images_controller.rb index 925f64124fb1..339f4598a8e6 100644 --- a/back/app/controllers/web_api/v1/images_controller.rb +++ b/back/app/controllers/web_api/v1/images_controller.rb @@ -18,13 +18,6 @@ class WebApi::V1::ImagesController < ApplicationController image_relationship: :event_images, container_id: :event_id }, - 'Initiative' => { - container_class: Initiative, - image_class: InitiativeImage, - policy_scope_class: InitiativeImagePolicy::Scope, - image_relationship: :initiative_images, - container_id: :initiative_id - }, 'Project' => { container_class: Project, image_class: ProjectImage, diff --git a/back/app/controllers/web_api/v1/initiative_status_changes_controller.rb b/back/app/controllers/web_api/v1/initiative_status_changes_controller.rb deleted file mode 100644 index f67d986c2141..000000000000 --- a/back/app/controllers/web_api/v1/initiative_status_changes_controller.rb +++ /dev/null @@ -1,83 +0,0 @@ -# frozen_string_literal: true - -class WebApi::V1::InitiativeStatusChangesController < ApplicationController - before_action :set_initiative, only: %i[index create] - before_action :set_change, only: %i[show] - skip_before_action :authenticate_user - - def index - @changes = policy_scope(InitiativeStatusChange) - .where(initiative: @initiative) - .order(created_at: :desc) - @changes = paginate @changes - - render json: linked_json(@changes, WebApi::V1::InitiativeStatusChangeSerializer, params: jsonapi_serializer_params) - end - - def show - render json: WebApi::V1::InitiativeStatusChangeSerializer.new( - @change, - params: jsonapi_serializer_params - ).serializable_hash - end - - def create - attributes = status_change_params.to_h - if attributes[:initiative_status_id] == @initiative.initiative_status_id - skip_authorization - render json: { errors: { base: [{ error: 'initiative_status_transition_without_change' }] } }, status: :unprocessable_entity - return - end - if attributes[:official_feedback_attributes].present? # If not nil nor empty hash - attributes[:official_feedback_attributes].merge! post_id: @initiative.id, post_type: 'Initiative' - else - attributes.except! :official_feedback_attributes - end - @change = InitiativeStatusChange.new attributes - @change.initiative = @initiative - @change.user ||= current_user - authorize @change - SideFxInitiativeStatusChangeService.new.before_create @change, current_user - if InitiativeStatusService.new.transition_allowed?( - @initiative, - @initiative.initiative_status, - @change.initiative_status, - with_feedback: attributes[:official_feedback_attributes].present? || attributes[:official_feedback_id] - ) - if @change.save - SideFxInitiativeStatusChangeService.new.after_create @change, current_user - render json: WebApi::V1::InitiativeStatusChangeSerializer.new( - @change, - params: jsonapi_serializer_params - ).serializable_hash, status: :created - else - render json: { errors: @change.errors.details }, status: :unprocessable_entity - end - else - render json: { errors: { base: [{ error: 'initiative_status_transition_not_allowed' }] } }, status: :unprocessable_entity - end - end - - private - - def set_change - @change = InitiativeStatusChange.find params[:id] - authorize @change - end - - def set_initiative - @initiative = Initiative.find params[:initiative_id] - end - - def status_change_params - params.require(:initiative_status_change).permit( - :initiative_status_id, - :user_id, - :official_feedback_id, - official_feedback_attributes: [ - body_multiloc: CL2_SUPPORTED_LOCALES, - author_multiloc: CL2_SUPPORTED_LOCALES - ] - ) - end -end diff --git a/back/app/controllers/web_api/v1/initiative_statuses_controller.rb b/back/app/controllers/web_api/v1/initiative_statuses_controller.rb deleted file mode 100644 index 76398d7154c6..000000000000 --- a/back/app/controllers/web_api/v1/initiative_statuses_controller.rb +++ /dev/null @@ -1,22 +0,0 @@ -# frozen_string_literal: true - -class WebApi::V1::InitiativeStatusesController < ApplicationController - before_action :set_initiative_status, only: :show - skip_before_action :authenticate_user - - def index - @initiative_statuses = policy_scope(InitiativeStatus).order(:ordering) - render json: WebApi::V1::InitiativeStatusSerializer.new(@initiative_statuses, params: jsonapi_serializer_params).serializable_hash - end - - def show - render json: WebApi::V1::InitiativeStatusSerializer.new(@initiative_status, params: jsonapi_serializer_params).serializable_hash - end - - private - - def set_initiative_status - @initiative_status = InitiativeStatus.find(params[:id]) - authorize @initiative_status - end -end diff --git a/back/app/controllers/web_api/v1/initiatives_controller.rb b/back/app/controllers/web_api/v1/initiatives_controller.rb deleted file mode 100644 index 9049aea14cb8..000000000000 --- a/back/app/controllers/web_api/v1/initiatives_controller.rb +++ /dev/null @@ -1,244 +0,0 @@ -# frozen_string_literal: true - -class WebApi::V1::InitiativesController < ApplicationController - include BlockingProfanity - - before_action :set_initiative, only: %i[show update destroy allowed_transitions accept_cosponsorship_invite] - skip_before_action :authenticate_user - skip_after_action :verify_authorized, only: %i[index_xlsx index_initiative_markers filter_counts] - - rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized - - def index - initiatives = InitiativesFinder.new( - params, - current_user: current_user, - scope: policy_scope(Initiative), - includes: %i[author assignee topics areas] - ).find_records - initiatives = paginate SortByParamsService.new.sort_initiatives(initiatives, params, current_user) - render json: linked_json(initiatives, WebApi::V1::InitiativeSerializer, serialization_options_for(initiatives)) - end - - def index_initiative_markers - initiatives = InitiativesFinder.new( - params, - current_user: current_user, - scope: policy_scope(Initiative) - ).find_records - initiatives = paginate SortByParamsService.new.sort_initiatives(initiatives, params, current_user) - render json: linked_json(initiatives, WebApi::V1::PostMarkerSerializer, params: jsonapi_serializer_params) - end - - def index_xlsx - authorize :initiative, :index_xlsx? - initiatives = InitiativesFinder.new( - params, - current_user: current_user, - scope: policy_scope(Initiative).where(publication_status: 'published'), - includes: %i[author cosponsors initiative_status topics areas] - ).find_records - initiatives = SortByParamsService.new.sort_initiatives(initiatives, params, current_user) - - I18n.with_locale(current_user&.locale) do - xlsx = XlsxService.new.generate_initiatives_xlsx( - initiatives, - view_private_attributes: policy(User).view_private_attributes? - ) - - xlsx_content_type = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' - send_data(xlsx, type: xlsx_content_type, filename: 'initiatives.xlsx') - end - end - - def filter_counts - initiatives = policy_scope(Initiative) - search_last_names = !UserDisplayNameService.new(AppConfiguration.instance, current_user).restricted? - initiatives = PostsFilteringService.new.apply_common_initiative_index_filters( - initiatives, params, - search_last_names - ) - counts = { - 'initiative_status_id' => {}, - 'area_id' => {}, - 'topic_id' => {} - } - attributes = %w[initiative_status_id area_id topic_id] - initiatives - .joins('FULL OUTER JOIN initiatives_topics ON initiatives_topics.initiative_id = initiatives.id') - .joins('FULL OUTER JOIN areas_initiatives ON areas_initiatives.initiative_id = initiatives.id') - .joins('FULL OUTER JOIN initiative_initiative_statuses ON initiative_initiative_statuses.initiative_id = initiatives.id') - .select('initiative_initiative_statuses.initiative_status_id, areas_initiatives.area_id, initiatives_topics.topic_id, COUNT(DISTINCT(initiatives.id)) as count') - .reorder(nil) # Avoids SQL error on GROUP BY when a search string is used - .group('GROUPING SETS (initiative_initiative_statuses.initiative_status_id, areas_initiatives.area_id, initiatives_topics.topic_id)') - .each do |record| - attributes.each do |attribute| - id = record.send attribute - counts[attribute][id] = record.count if id - end - end - counts['total'] = initiatives.reorder(nil).distinct.count # reorder(nil) avoids SQL error on SELECT DISTINCT when a search string is used - render json: raw_json(counts) - end - - def show - render json: WebApi::V1::InitiativeSerializer.new( - @initiative, - params: jsonapi_serializer_params, - include: %i[author cosponsors topics areas user_reaction initiative_images] - ).serializable_hash - end - - def by_slug - @initiative = Initiative.find_by!(slug: params[:slug]) - authorize @initiative - show - end - - def create - service = SideFxInitiativeService.new - - @initiative = Initiative.new(permitted_attributes(Initiative)) - @initiative.author ||= current_user - - service.before_create(@initiative, current_user) - - authorize @initiative - if anonymous_not_allowed? - render json: { errors: { base: [{ error: :anonymous_participation_not_allowed }] } }, status: :unprocessable_entity - return - end - verify_profanity @initiative - - save_options = {} - save_options[:context] = :publication if params.dig(:initiative, :publication_status) == 'published' - ActiveRecord::Base.transaction do - if @initiative.save(**save_options) - service.after_create(@initiative, current_user) - render json: WebApi::V1::InitiativeSerializer.new( - @initiative.reload, - params: jsonapi_serializer_params, - include: %i[author cosponsors topics areas user_reaction initiative_images] - ).serializable_hash, status: :created - else - render json: { errors: @initiative.errors.details }, status: :unprocessable_entity - end - end - end - - def update - service = SideFxInitiativeService.new - - cosponsor_ids = @initiative.cosponsors.map(&:id) - initiative_params = permitted_attributes(@initiative) - @initiative.assign_attributes(initiative_params) - remove_image_if_requested!(@initiative, initiative_params, :header_bg) - - authorize @initiative - if anonymous_not_allowed? - render json: { errors: { base: [{ error: :anonymous_participation_not_allowed }] } }, status: :unprocessable_entity - return - end - verify_profanity @initiative - - service.before_update(@initiative, current_user) - - save_options = {} - save_options[:context] = :publication if params.dig(:initiative, :publication_status) == 'published' - saved = nil - ActiveRecord::Base.transaction do - saved = @initiative.save(**save_options) - if saved - service.after_update(@initiative, current_user, cosponsor_ids) - end - end - - # Keeping `render` outside of the transaction is better anyway. - # Additionally, if we wouldn't do it here, we're running into an issue - # where carrierwave is not storing the actual header_bg file on the - # filesystem. The root cause it not exactly clear. - if saved - render json: WebApi::V1::InitiativeSerializer.new( - @initiative.reload, - params: jsonapi_serializer_params, - include: %i[author cosponsors topics areas user_reaction initiative_images] - ).serializable_hash, status: :ok - else - render json: { errors: @initiative.errors.details }, status: :unprocessable_entity - end - end - - def destroy - service = SideFxInitiativeService.new - - initiative = @initiative.destroy - if initiative.destroyed? - service.after_destroy(initiative, current_user) - head :ok - else - head :internal_server_error - end - end - - def accept_cosponsorship_invite - @cosponsors_initiative = @initiative.cosponsors_initiatives.find_by(user_id: current_user.id) - - if @cosponsors_initiative.update(status: 'accepted') - SideFxInitiativeService.new.after_accept_cosponsorship_invite(@cosponsors_initiative, current_user) - - render json: WebApi::V1::InitiativeSerializer.new( - @initiative.reload, - params: jsonapi_serializer_params, - include: %i[author cosponsors topics areas user_reaction initiative_images] - ).serializable_hash, status: :ok - else - render json: { errors: @initiative.errors.details }, status: :unprocessable_entity - end - end - - def allowed_transitions - authorize @initiative - render json: raw_json(InitiativeStatusService.new.allowed_transitions(@initiative)) - end - - private - - # renders errors in the new HookForm format - def render_profanity_blocked(exception) - errors = exception.violating_attributes.index_with { [{ error: :includes_banned_words }] } - render json: { errors: errors }, status: :unprocessable_entity - end - - def set_initiative - @initiative = Initiative.find params[:id] - authorize @initiative - end - - def serialization_options_for(initiatives) - default_params = jsonapi_serializer_params - - if current_user - reactions = current_user.reactions.where( - reactable_id: initiatives.pluck(:id), - reactable_type: 'Initiative' - ).index_by(&:reactable_id) - user_followers = current_user.follows - .where(followable_type: 'Initiative') - .group_by do |follower| - [follower.followable_id, follower.followable_type] - end - user_followers ||= {} - { params: default_params.merge(vbii: reactions, user_followers: user_followers), include: %i[author cosponsors user_reaction initiative_images assignee] } - else - { params: default_params, include: %i[author cosponsors initiative_images] } - end - end - - def display_names_restricted? - UserDisplayNameService.new(Tenant.current, current_user).restricted? - end - - def anonymous_not_allowed? - params.dig('initiative', 'anonymous') && !AppConfiguration.instance.settings.dig('initiatives', 'allow_anonymous_participation') - end -end diff --git a/back/app/controllers/web_api/v1/stats_initiatives_controller.rb b/back/app/controllers/web_api/v1/stats_initiatives_controller.rb deleted file mode 100644 index 95fbff5ae713..000000000000 --- a/back/app/controllers/web_api/v1/stats_initiatives_controller.rb +++ /dev/null @@ -1,16 +0,0 @@ -# frozen_string_literal: true - -class WebApi::V1::StatsInitiativesController < WebApi::V1::StatsController - def initiatives_count - initiatives = policy_scope(Initiative.published).where(published_at: @start_at..@end_at) - initiatives = PostsFilteringService.new.apply_common_initiative_index_filters initiatives, params - - render json: raw_json({ count: initiatives.count }) - end - - private - - def do_authorize - authorize :stat_initiative - end -end diff --git a/back/app/controllers/web_api/v1/user_comments_controller.rb b/back/app/controllers/web_api/v1/user_comments_controller.rb index b3e0800a4ec8..d7413325348b 100644 --- a/back/app/controllers/web_api/v1/user_comments_controller.rb +++ b/back/app/controllers/web_api/v1/user_comments_controller.rb @@ -9,22 +9,17 @@ def index comment_allowed_ideas = policy_scope(Comment, policy_scope_class: IdeaCommentPolicy::Scope) .published .where(author_id: params[:user_id]) - comment_allowed_initiatives = policy_scope(Comment, policy_scope_class: InitiativeCommentPolicy::Scope) - .published - .where(author_id: params[:user_id]) # Apply pagination to the posts, using the union_posts # view and ordering by publication date. joined_posts = UnionPost.joins('INNER JOIN comments ON comments.post_id = union_posts.id') paged_posts = joined_posts.where(comments: { id: comment_allowed_ideas }) - .or(joined_posts.where(comments: { id: comment_allowed_initiatives })) .order(published_at: :desc) .group('union_posts.id, union_posts.published_at') # Remove union_post duplicates .select('union_posts.id') paged_posts = paginate paged_posts - # Get the comments, grouped by the corresponding posts - # page. + # Get the comments, grouped by the corresponding posts page. comments = Comment.where(post_id: paged_posts) .where(author_id: params[:user_id]) .includes(:post) diff --git a/back/app/controllers/web_api/v1/users_controller.rb b/back/app/controllers/web_api/v1/users_controller.rb index 8f2e579fd60b..d49677b0d44b 100644 --- a/back/app/controllers/web_api/v1/users_controller.rb +++ b/back/app/controllers/web_api/v1/users_controller.rb @@ -203,18 +203,14 @@ def initiatives_count def comments_count count = 0 published_comments = @user.comments.published + # TODO: cleanup-after-proposals-migration + # Simplify when Comment.post_type is removed if !params[:post_type] || params[:post_type] == 'Idea' count += policy_scope( published_comments.where(post_type: 'Idea'), policy_scope_class: IdeaCommentPolicy::Scope ).count end - if !params[:post_type] || params[:post_type] == 'Initiative' - count += policy_scope( - published_comments.where(post_type: 'Initiative'), - policy_scope_class: InitiativeCommentPolicy::Scope - ).count - end render json: raw_json({ count: count }), status: :ok end diff --git a/back/app/finders/initiatives_finder.rb b/back/app/finders/initiatives_finder.rb deleted file mode 100644 index 25b9f02d7eb9..000000000000 --- a/back/app/finders/initiatives_finder.rb +++ /dev/null @@ -1,62 +0,0 @@ -# frozen_string_literal: true - -class InitiativesFinder < ApplicationFinder - def find_records - initiatives = super - # We use Initiative.where to avoid duplicates caused by `left_outer_joins(:cosponsors_initiatives)`. - # #distinct fails with `ERROR: for SELECT DISTINCT, ORDER BY expressions must appear in select list ... md5(` - Initiative.where(id: initiatives).includes(@includes) - end - - private - - def topics_condition(topics) - scope(:with_some_topics, topics) - end - - def areas_condition(areas) - scope(:with_some_areas, areas) - end - - def initiative_status_condition(status_id) - records.left_outer_joins(:initiative_initiative_status) - .where(initiative_initiative_statuses: { initiative_status_id: status_id }) - end - - def assignee_condition(assignee_id) - assignee_id = nil if assignee_id == 'unassigned' - where(assignee_id: assignee_id) - end - - def feedback_needed_condition(feedback_needed) - feedback_needed ? scope(:feedback_needed) : scope(:no_feedback_needed) - end - - def author_condition(author_id) - records.includes(:author).where(author_id: author_id) - end - - def search_condition(search_term) - if _search_restricted? - scope(:restricted_search, search_term) - else - scope(:search_by_all, search_term) - end - end - - def publication_status_condition(status) - where(publication_status: status) - end - - def bounding_box_condition(bounding_box) - scope(:with_bounding_box, bounding_box) - end - - def initiatives_condition(initiative_ids) - where(id: initiative_ids) - end - - def _search_restricted? - UserDisplayNameService.new(AppConfiguration.instance, current_user).restricted? - end -end diff --git a/back/app/models/follower.rb b/back/app/models/follower.rb index 71c7efc4361b..cd316243db8f 100644 --- a/back/app/models/follower.rb +++ b/back/app/models/follower.rb @@ -23,7 +23,7 @@ # fk_rails_... (user_id => users.id) # class Follower < ApplicationRecord - FOLLOWABLE_TYPES = %w[Project ProjectFolders::Folder Idea Initiative Topic Area] + FOLLOWABLE_TYPES = %w[Project ProjectFolders::Folder Idea Topic Area] belongs_to :user belongs_to :followable, polymorphic: true diff --git a/back/app/models/notifications/comment_on_initiative_you_follow.rb b/back/app/models/notifications/comment_on_initiative_you_follow.rb deleted file mode 100644 index 31e98d3135db..000000000000 --- a/back/app/models/notifications/comment_on_initiative_you_follow.rb +++ /dev/null @@ -1,99 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: notifications -# -# id :uuid not null, primary key -# type :string -# read_at :datetime -# recipient_id :uuid -# post_id :uuid -# comment_id :uuid -# project_id :uuid -# created_at :datetime not null -# updated_at :datetime not null -# initiating_user_id :uuid -# spam_report_id :uuid -# invite_id :uuid -# reason_code :string -# other_reason :string -# post_status_id :uuid -# official_feedback_id :uuid -# phase_id :uuid -# post_type :string -# post_status_type :string -# project_folder_id :uuid -# inappropriate_content_flag_id :uuid -# internal_comment_id :uuid -# basket_id :uuid -# cosponsors_initiative_id :uuid -# cosponsorship_id :uuid -# project_review_id :uuid -# -# Indexes -# -# index_notifications_on_basket_id (basket_id) -# index_notifications_on_cosponsors_initiative_id (cosponsors_initiative_id) -# index_notifications_on_cosponsorship_id (cosponsorship_id) -# index_notifications_on_created_at (created_at) -# index_notifications_on_inappropriate_content_flag_id (inappropriate_content_flag_id) -# index_notifications_on_initiating_user_id (initiating_user_id) -# index_notifications_on_internal_comment_id (internal_comment_id) -# index_notifications_on_invite_id (invite_id) -# index_notifications_on_official_feedback_id (official_feedback_id) -# index_notifications_on_phase_id (phase_id) -# index_notifications_on_post_id_and_post_type (post_id,post_type) -# index_notifications_on_post_status_id (post_status_id) -# index_notifications_on_post_status_id_and_post_status_type (post_status_id,post_status_type) -# index_notifications_on_project_review_id (project_review_id) -# index_notifications_on_recipient_id (recipient_id) -# index_notifications_on_recipient_id_and_read_at (recipient_id,read_at) -# index_notifications_on_spam_report_id (spam_report_id) -# -# Foreign Keys -# -# fk_rails_... (basket_id => baskets.id) -# fk_rails_... (comment_id => comments.id) -# fk_rails_... (cosponsors_initiative_id => cosponsors_initiatives.id) -# fk_rails_... (cosponsorship_id => cosponsorships.id) -# fk_rails_... (inappropriate_content_flag_id => flag_inappropriate_content_inappropriate_content_flags.id) -# fk_rails_... (initiating_user_id => users.id) -# fk_rails_... (internal_comment_id => internal_comments.id) -# fk_rails_... (invite_id => invites.id) -# fk_rails_... (official_feedback_id => official_feedbacks.id) -# fk_rails_... (phase_id => phases.id) -# fk_rails_... (project_id => projects.id) -# fk_rails_... (project_review_id => project_reviews.id) -# fk_rails_... (recipient_id => users.id) -# fk_rails_... (spam_report_id => spam_reports.id) -# -module Notifications - class CommentOnInitiativeYouFollow < Notification - validates :comment, :initiating_user, :post, presence: true - validates :post_type, inclusion: { in: ['Initiative'] } - - ACTIVITY_TRIGGERS = { 'Comment' => { 'created' => true } } - EVENT_NAME = 'Comment on initiative you follow' - - def self.make_notifications_on(activity) - return [] unless AppConfiguration.instance.feature_activated? 'follow' - - comment = activity.item - initiator_id = comment&.author_id - - if comment.post_type == 'Initiative' && initiator_id - User.from_follows(comment.post.followers).where.not(id: initiator_id).map do |recipient| - new( - recipient_id: recipient.id, - initiating_user_id: initiator_id, - post: comment.post, - comment: comment - ) - end - else - [] - end - end - end -end diff --git a/back/app/models/notifications/cosponsor_of_your_initiative.rb b/back/app/models/notifications/cosponsor_of_your_initiative.rb deleted file mode 100644 index 731528cf6028..000000000000 --- a/back/app/models/notifications/cosponsor_of_your_initiative.rb +++ /dev/null @@ -1,96 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: notifications -# -# id :uuid not null, primary key -# type :string -# read_at :datetime -# recipient_id :uuid -# post_id :uuid -# comment_id :uuid -# project_id :uuid -# created_at :datetime not null -# updated_at :datetime not null -# initiating_user_id :uuid -# spam_report_id :uuid -# invite_id :uuid -# reason_code :string -# other_reason :string -# post_status_id :uuid -# official_feedback_id :uuid -# phase_id :uuid -# post_type :string -# post_status_type :string -# project_folder_id :uuid -# inappropriate_content_flag_id :uuid -# internal_comment_id :uuid -# basket_id :uuid -# cosponsors_initiative_id :uuid -# cosponsorship_id :uuid -# project_review_id :uuid -# -# Indexes -# -# index_notifications_on_basket_id (basket_id) -# index_notifications_on_cosponsors_initiative_id (cosponsors_initiative_id) -# index_notifications_on_cosponsorship_id (cosponsorship_id) -# index_notifications_on_created_at (created_at) -# index_notifications_on_inappropriate_content_flag_id (inappropriate_content_flag_id) -# index_notifications_on_initiating_user_id (initiating_user_id) -# index_notifications_on_internal_comment_id (internal_comment_id) -# index_notifications_on_invite_id (invite_id) -# index_notifications_on_official_feedback_id (official_feedback_id) -# index_notifications_on_phase_id (phase_id) -# index_notifications_on_post_id_and_post_type (post_id,post_type) -# index_notifications_on_post_status_id (post_status_id) -# index_notifications_on_post_status_id_and_post_status_type (post_status_id,post_status_type) -# index_notifications_on_project_review_id (project_review_id) -# index_notifications_on_recipient_id (recipient_id) -# index_notifications_on_recipient_id_and_read_at (recipient_id,read_at) -# index_notifications_on_spam_report_id (spam_report_id) -# -# Foreign Keys -# -# fk_rails_... (basket_id => baskets.id) -# fk_rails_... (comment_id => comments.id) -# fk_rails_... (cosponsors_initiative_id => cosponsors_initiatives.id) -# fk_rails_... (cosponsorship_id => cosponsorships.id) -# fk_rails_... (inappropriate_content_flag_id => flag_inappropriate_content_inappropriate_content_flags.id) -# fk_rails_... (initiating_user_id => users.id) -# fk_rails_... (internal_comment_id => internal_comments.id) -# fk_rails_... (invite_id => invites.id) -# fk_rails_... (official_feedback_id => official_feedbacks.id) -# fk_rails_... (phase_id => phases.id) -# fk_rails_... (project_id => projects.id) -# fk_rails_... (project_review_id => project_reviews.id) -# fk_rails_... (recipient_id => users.id) -# fk_rails_... (spam_report_id => spam_reports.id) -# -module Notifications - class CosponsorOfYourInitiative < Notification - validates :initiating_user, :cosponsors_initiative, presence: true - - ACTIVITY_TRIGGERS = { 'CosponsorsInitiative' => { 'cosponsorship_accepted' => true } } - EVENT_NAME = 'Cosponsor has accepted your invitation to cosponsor your initiative' - - def self.make_notifications_on(activity) - cosponsors_initiative = activity&.item - initiating_user = activity&.user - initiative = cosponsors_initiative&.initiative - recipient_id = initiative&.author_id - - if recipient_id && initiating_user && initiative - [new( - recipient_id: recipient_id, - initiating_user: initiating_user, - cosponsors_initiative: cosponsors_initiative, - post: initiative - )] - else - [] - end - end - end -end diff --git a/back/app/models/notifications/initiative_assigned_to_you.rb b/back/app/models/notifications/initiative_assigned_to_you.rb deleted file mode 100644 index 23c2303b4bd1..000000000000 --- a/back/app/models/notifications/initiative_assigned_to_you.rb +++ /dev/null @@ -1,99 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: notifications -# -# id :uuid not null, primary key -# type :string -# read_at :datetime -# recipient_id :uuid -# post_id :uuid -# comment_id :uuid -# project_id :uuid -# created_at :datetime not null -# updated_at :datetime not null -# initiating_user_id :uuid -# spam_report_id :uuid -# invite_id :uuid -# reason_code :string -# other_reason :string -# post_status_id :uuid -# official_feedback_id :uuid -# phase_id :uuid -# post_type :string -# post_status_type :string -# project_folder_id :uuid -# inappropriate_content_flag_id :uuid -# internal_comment_id :uuid -# basket_id :uuid -# cosponsors_initiative_id :uuid -# cosponsorship_id :uuid -# project_review_id :uuid -# -# Indexes -# -# index_notifications_on_basket_id (basket_id) -# index_notifications_on_cosponsors_initiative_id (cosponsors_initiative_id) -# index_notifications_on_cosponsorship_id (cosponsorship_id) -# index_notifications_on_created_at (created_at) -# index_notifications_on_inappropriate_content_flag_id (inappropriate_content_flag_id) -# index_notifications_on_initiating_user_id (initiating_user_id) -# index_notifications_on_internal_comment_id (internal_comment_id) -# index_notifications_on_invite_id (invite_id) -# index_notifications_on_official_feedback_id (official_feedback_id) -# index_notifications_on_phase_id (phase_id) -# index_notifications_on_post_id_and_post_type (post_id,post_type) -# index_notifications_on_post_status_id (post_status_id) -# index_notifications_on_post_status_id_and_post_status_type (post_status_id,post_status_type) -# index_notifications_on_project_review_id (project_review_id) -# index_notifications_on_recipient_id (recipient_id) -# index_notifications_on_recipient_id_and_read_at (recipient_id,read_at) -# index_notifications_on_spam_report_id (spam_report_id) -# -# Foreign Keys -# -# fk_rails_... (basket_id => baskets.id) -# fk_rails_... (comment_id => comments.id) -# fk_rails_... (cosponsors_initiative_id => cosponsors_initiatives.id) -# fk_rails_... (cosponsorship_id => cosponsorships.id) -# fk_rails_... (inappropriate_content_flag_id => flag_inappropriate_content_inappropriate_content_flags.id) -# fk_rails_... (initiating_user_id => users.id) -# fk_rails_... (internal_comment_id => internal_comments.id) -# fk_rails_... (invite_id => invites.id) -# fk_rails_... (official_feedback_id => official_feedbacks.id) -# fk_rails_... (phase_id => phases.id) -# fk_rails_... (project_id => projects.id) -# fk_rails_... (project_review_id => project_reviews.id) -# fk_rails_... (recipient_id => users.id) -# fk_rails_... (spam_report_id => spam_reports.id) -# -module Notifications - class InitiativeAssignedToYou < Notification - validates :initiating_user, :post, presence: true - validates :post_type, inclusion: { in: ['Initiative'] } - - ACTIVITY_TRIGGERS = { 'Initiative' => { 'changed_assignee' => true } } - EVENT_NAME = 'Initiative assigned to you' - - def self.make_notifications_on(activity) - initiative = activity.item - recipient_id = initiative.assignee_id - initiator_id = activity.user_id - - # We only notify manual assignments, meaning there needs to be an - # initiator - if recipient_id && initiator_id && recipient_id != initiator_id - [ - new( - recipient_id: recipient_id, - post: initiative, - initiating_user_id: initiator_id - ) - ] - else - [] - end - end - end -end diff --git a/back/app/models/notifications/initiative_marked_as_spam.rb b/back/app/models/notifications/initiative_marked_as_spam.rb deleted file mode 100644 index 811ca53d2944..000000000000 --- a/back/app/models/notifications/initiative_marked_as_spam.rb +++ /dev/null @@ -1,96 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: notifications -# -# id :uuid not null, primary key -# type :string -# read_at :datetime -# recipient_id :uuid -# post_id :uuid -# comment_id :uuid -# project_id :uuid -# created_at :datetime not null -# updated_at :datetime not null -# initiating_user_id :uuid -# spam_report_id :uuid -# invite_id :uuid -# reason_code :string -# other_reason :string -# post_status_id :uuid -# official_feedback_id :uuid -# phase_id :uuid -# post_type :string -# post_status_type :string -# project_folder_id :uuid -# inappropriate_content_flag_id :uuid -# internal_comment_id :uuid -# basket_id :uuid -# cosponsors_initiative_id :uuid -# cosponsorship_id :uuid -# project_review_id :uuid -# -# Indexes -# -# index_notifications_on_basket_id (basket_id) -# index_notifications_on_cosponsors_initiative_id (cosponsors_initiative_id) -# index_notifications_on_cosponsorship_id (cosponsorship_id) -# index_notifications_on_created_at (created_at) -# index_notifications_on_inappropriate_content_flag_id (inappropriate_content_flag_id) -# index_notifications_on_initiating_user_id (initiating_user_id) -# index_notifications_on_internal_comment_id (internal_comment_id) -# index_notifications_on_invite_id (invite_id) -# index_notifications_on_official_feedback_id (official_feedback_id) -# index_notifications_on_phase_id (phase_id) -# index_notifications_on_post_id_and_post_type (post_id,post_type) -# index_notifications_on_post_status_id (post_status_id) -# index_notifications_on_post_status_id_and_post_status_type (post_status_id,post_status_type) -# index_notifications_on_project_review_id (project_review_id) -# index_notifications_on_recipient_id (recipient_id) -# index_notifications_on_recipient_id_and_read_at (recipient_id,read_at) -# index_notifications_on_spam_report_id (spam_report_id) -# -# Foreign Keys -# -# fk_rails_... (basket_id => baskets.id) -# fk_rails_... (comment_id => comments.id) -# fk_rails_... (cosponsors_initiative_id => cosponsors_initiatives.id) -# fk_rails_... (cosponsorship_id => cosponsorships.id) -# fk_rails_... (inappropriate_content_flag_id => flag_inappropriate_content_inappropriate_content_flags.id) -# fk_rails_... (initiating_user_id => users.id) -# fk_rails_... (internal_comment_id => internal_comments.id) -# fk_rails_... (invite_id => invites.id) -# fk_rails_... (official_feedback_id => official_feedbacks.id) -# fk_rails_... (phase_id => phases.id) -# fk_rails_... (project_id => projects.id) -# fk_rails_... (project_review_id => project_reviews.id) -# fk_rails_... (recipient_id => users.id) -# fk_rails_... (spam_report_id => spam_reports.id) -# -module Notifications - class InitiativeMarkedAsSpam < MarkedAsSpam - validates :post, presence: true - validates :post_type, inclusion: { in: ['Initiative'] } - - ACTIVITY_TRIGGERS = { 'SpamReport' => { 'created' => true } } - EVENT_NAME = 'Initiative marked as spam' - - def self.make_notifications_on(activity) - spam_report = activity.item - if spam_report.spam_reportable_type == 'Initiative' - initiator_id = spam_report.user_id - recipient_ids(initiator_id).map do |recipient_id| - new( - recipient_id: recipient_id, - initiating_user_id: initiator_id, - spam_report: spam_report, - post: spam_report.spam_reportable - ) - end - else - [] - end - end - end -end diff --git a/back/app/models/notifications/initiative_resubmitted_for_review.rb b/back/app/models/notifications/initiative_resubmitted_for_review.rb deleted file mode 100644 index c0eaf89d7afe..000000000000 --- a/back/app/models/notifications/initiative_resubmitted_for_review.rb +++ /dev/null @@ -1,104 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: notifications -# -# id :uuid not null, primary key -# type :string -# read_at :datetime -# recipient_id :uuid -# post_id :uuid -# comment_id :uuid -# project_id :uuid -# created_at :datetime not null -# updated_at :datetime not null -# initiating_user_id :uuid -# spam_report_id :uuid -# invite_id :uuid -# reason_code :string -# other_reason :string -# post_status_id :uuid -# official_feedback_id :uuid -# phase_id :uuid -# post_type :string -# post_status_type :string -# project_folder_id :uuid -# inappropriate_content_flag_id :uuid -# internal_comment_id :uuid -# basket_id :uuid -# cosponsors_initiative_id :uuid -# cosponsorship_id :uuid -# project_review_id :uuid -# -# Indexes -# -# index_notifications_on_basket_id (basket_id) -# index_notifications_on_cosponsors_initiative_id (cosponsors_initiative_id) -# index_notifications_on_cosponsorship_id (cosponsorship_id) -# index_notifications_on_created_at (created_at) -# index_notifications_on_inappropriate_content_flag_id (inappropriate_content_flag_id) -# index_notifications_on_initiating_user_id (initiating_user_id) -# index_notifications_on_internal_comment_id (internal_comment_id) -# index_notifications_on_invite_id (invite_id) -# index_notifications_on_official_feedback_id (official_feedback_id) -# index_notifications_on_phase_id (phase_id) -# index_notifications_on_post_id_and_post_type (post_id,post_type) -# index_notifications_on_post_status_id (post_status_id) -# index_notifications_on_post_status_id_and_post_status_type (post_status_id,post_status_type) -# index_notifications_on_project_review_id (project_review_id) -# index_notifications_on_recipient_id (recipient_id) -# index_notifications_on_recipient_id_and_read_at (recipient_id,read_at) -# index_notifications_on_spam_report_id (spam_report_id) -# -# Foreign Keys -# -# fk_rails_... (basket_id => baskets.id) -# fk_rails_... (comment_id => comments.id) -# fk_rails_... (cosponsors_initiative_id => cosponsors_initiatives.id) -# fk_rails_... (cosponsorship_id => cosponsorships.id) -# fk_rails_... (inappropriate_content_flag_id => flag_inappropriate_content_inappropriate_content_flags.id) -# fk_rails_... (initiating_user_id => users.id) -# fk_rails_... (internal_comment_id => internal_comments.id) -# fk_rails_... (invite_id => invites.id) -# fk_rails_... (official_feedback_id => official_feedbacks.id) -# fk_rails_... (phase_id => phases.id) -# fk_rails_... (project_id => projects.id) -# fk_rails_... (project_review_id => project_reviews.id) -# fk_rails_... (recipient_id => users.id) -# fk_rails_... (spam_report_id => spam_reports.id) -# -module Notifications - class InitiativeResubmittedForReview < Notification - validates :post_status, :post, presence: true - validates :post_type, inclusion: { in: ['Initiative'] } - - ACTIVITY_TRIGGERS = { 'Initiative' => { 'changed_status' => true } } - EVENT_NAME = 'Initiative resubmitted for review' - - class << self - def make_notifications_on(activity) - initiative = activity.item - recipient_id = initiative&.assignee_id - - if initiative && recipient_id && transitioned_from_rejected_to_pending?(initiative) - [new( - recipient_id: recipient_id, - initiating_user_id: activity.user_id, - post: initiative, - post_status: initiative.initiative_status - )] - else - [] - end - end - - private - - def transitioned_from_rejected_to_pending?(initiative) - previous_change = initiative.initiative_status_changes.order(created_at: :asc)[-2] - previous_change && previous_change.initiative_status.code == 'changes_requested' && initiative.initiative_status.code == 'review_pending' - end - end - end -end diff --git a/back/app/models/notifications/internal_comments/internal_comment_on_initiative_assigned_to_you.rb b/back/app/models/notifications/internal_comments/internal_comment_on_initiative_assigned_to_you.rb deleted file mode 100644 index 9f2c328a172b..000000000000 --- a/back/app/models/notifications/internal_comments/internal_comment_on_initiative_assigned_to_you.rb +++ /dev/null @@ -1,82 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: notifications -# -# id :uuid not null, primary key -# type :string -# read_at :datetime -# recipient_id :uuid -# post_id :uuid -# comment_id :uuid -# project_id :uuid -# created_at :datetime not null -# updated_at :datetime not null -# initiating_user_id :uuid -# spam_report_id :uuid -# invite_id :uuid -# reason_code :string -# other_reason :string -# post_status_id :uuid -# official_feedback_id :uuid -# phase_id :uuid -# post_type :string -# post_status_type :string -# project_folder_id :uuid -# inappropriate_content_flag_id :uuid -# internal_comment_id :uuid -# basket_id :uuid -# cosponsors_initiative_id :uuid -# cosponsorship_id :uuid -# project_review_id :uuid -# -# Indexes -# -# index_notifications_on_basket_id (basket_id) -# index_notifications_on_cosponsors_initiative_id (cosponsors_initiative_id) -# index_notifications_on_cosponsorship_id (cosponsorship_id) -# index_notifications_on_created_at (created_at) -# index_notifications_on_inappropriate_content_flag_id (inappropriate_content_flag_id) -# index_notifications_on_initiating_user_id (initiating_user_id) -# index_notifications_on_internal_comment_id (internal_comment_id) -# index_notifications_on_invite_id (invite_id) -# index_notifications_on_official_feedback_id (official_feedback_id) -# index_notifications_on_phase_id (phase_id) -# index_notifications_on_post_id_and_post_type (post_id,post_type) -# index_notifications_on_post_status_id (post_status_id) -# index_notifications_on_post_status_id_and_post_status_type (post_status_id,post_status_type) -# index_notifications_on_project_review_id (project_review_id) -# index_notifications_on_recipient_id (recipient_id) -# index_notifications_on_recipient_id_and_read_at (recipient_id,read_at) -# index_notifications_on_spam_report_id (spam_report_id) -# -# Foreign Keys -# -# fk_rails_... (basket_id => baskets.id) -# fk_rails_... (comment_id => comments.id) -# fk_rails_... (cosponsors_initiative_id => cosponsors_initiatives.id) -# fk_rails_... (cosponsorship_id => cosponsorships.id) -# fk_rails_... (inappropriate_content_flag_id => flag_inappropriate_content_inappropriate_content_flags.id) -# fk_rails_... (initiating_user_id => users.id) -# fk_rails_... (internal_comment_id => internal_comments.id) -# fk_rails_... (invite_id => invites.id) -# fk_rails_... (official_feedback_id => official_feedbacks.id) -# fk_rails_... (phase_id => phases.id) -# fk_rails_... (project_id => projects.id) -# fk_rails_... (project_review_id => project_reviews.id) -# fk_rails_... (recipient_id => users.id) -# fk_rails_... (spam_report_id => spam_reports.id) -# -module Notifications - class InternalComments::InternalCommentOnInitiativeAssignedToYou < Notification - validates :initiating_user, :internal_comment, :post, presence: true - - ACTIVITY_TRIGGERS = { 'InternalComment' => { 'created' => true } } - EVENT_NAME = 'Internal comment on initiative assigned to you' - - def self.make_notifications_on(activity) - InternalComments::InternalCommentOnInitiativeAssignedToYouBuilder.new(activity).build_notifications - end - end -end diff --git a/back/app/models/notifications/internal_comments/internal_comment_on_initiative_you_commented_internally_on.rb b/back/app/models/notifications/internal_comments/internal_comment_on_initiative_you_commented_internally_on.rb deleted file mode 100644 index 21647cfe14b8..000000000000 --- a/back/app/models/notifications/internal_comments/internal_comment_on_initiative_you_commented_internally_on.rb +++ /dev/null @@ -1,82 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: notifications -# -# id :uuid not null, primary key -# type :string -# read_at :datetime -# recipient_id :uuid -# post_id :uuid -# comment_id :uuid -# project_id :uuid -# created_at :datetime not null -# updated_at :datetime not null -# initiating_user_id :uuid -# spam_report_id :uuid -# invite_id :uuid -# reason_code :string -# other_reason :string -# post_status_id :uuid -# official_feedback_id :uuid -# phase_id :uuid -# post_type :string -# post_status_type :string -# project_folder_id :uuid -# inappropriate_content_flag_id :uuid -# internal_comment_id :uuid -# basket_id :uuid -# cosponsors_initiative_id :uuid -# cosponsorship_id :uuid -# project_review_id :uuid -# -# Indexes -# -# index_notifications_on_basket_id (basket_id) -# index_notifications_on_cosponsors_initiative_id (cosponsors_initiative_id) -# index_notifications_on_cosponsorship_id (cosponsorship_id) -# index_notifications_on_created_at (created_at) -# index_notifications_on_inappropriate_content_flag_id (inappropriate_content_flag_id) -# index_notifications_on_initiating_user_id (initiating_user_id) -# index_notifications_on_internal_comment_id (internal_comment_id) -# index_notifications_on_invite_id (invite_id) -# index_notifications_on_official_feedback_id (official_feedback_id) -# index_notifications_on_phase_id (phase_id) -# index_notifications_on_post_id_and_post_type (post_id,post_type) -# index_notifications_on_post_status_id (post_status_id) -# index_notifications_on_post_status_id_and_post_status_type (post_status_id,post_status_type) -# index_notifications_on_project_review_id (project_review_id) -# index_notifications_on_recipient_id (recipient_id) -# index_notifications_on_recipient_id_and_read_at (recipient_id,read_at) -# index_notifications_on_spam_report_id (spam_report_id) -# -# Foreign Keys -# -# fk_rails_... (basket_id => baskets.id) -# fk_rails_... (comment_id => comments.id) -# fk_rails_... (cosponsors_initiative_id => cosponsors_initiatives.id) -# fk_rails_... (cosponsorship_id => cosponsorships.id) -# fk_rails_... (inappropriate_content_flag_id => flag_inappropriate_content_inappropriate_content_flags.id) -# fk_rails_... (initiating_user_id => users.id) -# fk_rails_... (internal_comment_id => internal_comments.id) -# fk_rails_... (invite_id => invites.id) -# fk_rails_... (official_feedback_id => official_feedbacks.id) -# fk_rails_... (phase_id => phases.id) -# fk_rails_... (project_id => projects.id) -# fk_rails_... (project_review_id => project_reviews.id) -# fk_rails_... (recipient_id => users.id) -# fk_rails_... (spam_report_id => spam_reports.id) -# -module Notifications - class InternalComments::InternalCommentOnInitiativeYouCommentedInternallyOn < Notification - validates :initiating_user, :internal_comment, :post, presence: true - - ACTIVITY_TRIGGERS = { 'InternalComment' => { 'created' => true } } - EVENT_NAME = 'Internal comment on initiative you commented internally on' - - def self.make_notifications_on(activity) - InternalComments::InternalCommentOnInitiativeYouCommentedInternallyOnBuilder.new(activity).build_notifications - end - end -end diff --git a/back/app/models/notifications/internal_comments/internal_comment_on_unassigned_initiative.rb b/back/app/models/notifications/internal_comments/internal_comment_on_unassigned_initiative.rb deleted file mode 100644 index dec2b97b3c8b..000000000000 --- a/back/app/models/notifications/internal_comments/internal_comment_on_unassigned_initiative.rb +++ /dev/null @@ -1,82 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: notifications -# -# id :uuid not null, primary key -# type :string -# read_at :datetime -# recipient_id :uuid -# post_id :uuid -# comment_id :uuid -# project_id :uuid -# created_at :datetime not null -# updated_at :datetime not null -# initiating_user_id :uuid -# spam_report_id :uuid -# invite_id :uuid -# reason_code :string -# other_reason :string -# post_status_id :uuid -# official_feedback_id :uuid -# phase_id :uuid -# post_type :string -# post_status_type :string -# project_folder_id :uuid -# inappropriate_content_flag_id :uuid -# internal_comment_id :uuid -# basket_id :uuid -# cosponsors_initiative_id :uuid -# cosponsorship_id :uuid -# project_review_id :uuid -# -# Indexes -# -# index_notifications_on_basket_id (basket_id) -# index_notifications_on_cosponsors_initiative_id (cosponsors_initiative_id) -# index_notifications_on_cosponsorship_id (cosponsorship_id) -# index_notifications_on_created_at (created_at) -# index_notifications_on_inappropriate_content_flag_id (inappropriate_content_flag_id) -# index_notifications_on_initiating_user_id (initiating_user_id) -# index_notifications_on_internal_comment_id (internal_comment_id) -# index_notifications_on_invite_id (invite_id) -# index_notifications_on_official_feedback_id (official_feedback_id) -# index_notifications_on_phase_id (phase_id) -# index_notifications_on_post_id_and_post_type (post_id,post_type) -# index_notifications_on_post_status_id (post_status_id) -# index_notifications_on_post_status_id_and_post_status_type (post_status_id,post_status_type) -# index_notifications_on_project_review_id (project_review_id) -# index_notifications_on_recipient_id (recipient_id) -# index_notifications_on_recipient_id_and_read_at (recipient_id,read_at) -# index_notifications_on_spam_report_id (spam_report_id) -# -# Foreign Keys -# -# fk_rails_... (basket_id => baskets.id) -# fk_rails_... (comment_id => comments.id) -# fk_rails_... (cosponsors_initiative_id => cosponsors_initiatives.id) -# fk_rails_... (cosponsorship_id => cosponsorships.id) -# fk_rails_... (inappropriate_content_flag_id => flag_inappropriate_content_inappropriate_content_flags.id) -# fk_rails_... (initiating_user_id => users.id) -# fk_rails_... (internal_comment_id => internal_comments.id) -# fk_rails_... (invite_id => invites.id) -# fk_rails_... (official_feedback_id => official_feedbacks.id) -# fk_rails_... (phase_id => phases.id) -# fk_rails_... (project_id => projects.id) -# fk_rails_... (project_review_id => project_reviews.id) -# fk_rails_... (recipient_id => users.id) -# fk_rails_... (spam_report_id => spam_reports.id) -# -module Notifications - class InternalComments::InternalCommentOnUnassignedInitiative < Notification - validates :initiating_user, :internal_comment, :post, presence: true - - ACTIVITY_TRIGGERS = { 'InternalComment' => { 'created' => true } } - EVENT_NAME = 'Internal comment on unassigned initiative' - - def self.make_notifications_on(activity) - InternalComments::InternalCommentOnUnassignedInitiativeBuilder.new(activity).build_notifications - end - end -end diff --git a/back/app/models/notifications/invitation_to_cosponsor_initiative.rb b/back/app/models/notifications/invitation_to_cosponsor_initiative.rb deleted file mode 100644 index e3e1be3e092f..000000000000 --- a/back/app/models/notifications/invitation_to_cosponsor_initiative.rb +++ /dev/null @@ -1,96 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: notifications -# -# id :uuid not null, primary key -# type :string -# read_at :datetime -# recipient_id :uuid -# post_id :uuid -# comment_id :uuid -# project_id :uuid -# created_at :datetime not null -# updated_at :datetime not null -# initiating_user_id :uuid -# spam_report_id :uuid -# invite_id :uuid -# reason_code :string -# other_reason :string -# post_status_id :uuid -# official_feedback_id :uuid -# phase_id :uuid -# post_type :string -# post_status_type :string -# project_folder_id :uuid -# inappropriate_content_flag_id :uuid -# internal_comment_id :uuid -# basket_id :uuid -# cosponsors_initiative_id :uuid -# cosponsorship_id :uuid -# project_review_id :uuid -# -# Indexes -# -# index_notifications_on_basket_id (basket_id) -# index_notifications_on_cosponsors_initiative_id (cosponsors_initiative_id) -# index_notifications_on_cosponsorship_id (cosponsorship_id) -# index_notifications_on_created_at (created_at) -# index_notifications_on_inappropriate_content_flag_id (inappropriate_content_flag_id) -# index_notifications_on_initiating_user_id (initiating_user_id) -# index_notifications_on_internal_comment_id (internal_comment_id) -# index_notifications_on_invite_id (invite_id) -# index_notifications_on_official_feedback_id (official_feedback_id) -# index_notifications_on_phase_id (phase_id) -# index_notifications_on_post_id_and_post_type (post_id,post_type) -# index_notifications_on_post_status_id (post_status_id) -# index_notifications_on_post_status_id_and_post_status_type (post_status_id,post_status_type) -# index_notifications_on_project_review_id (project_review_id) -# index_notifications_on_recipient_id (recipient_id) -# index_notifications_on_recipient_id_and_read_at (recipient_id,read_at) -# index_notifications_on_spam_report_id (spam_report_id) -# -# Foreign Keys -# -# fk_rails_... (basket_id => baskets.id) -# fk_rails_... (comment_id => comments.id) -# fk_rails_... (cosponsors_initiative_id => cosponsors_initiatives.id) -# fk_rails_... (cosponsorship_id => cosponsorships.id) -# fk_rails_... (inappropriate_content_flag_id => flag_inappropriate_content_inappropriate_content_flags.id) -# fk_rails_... (initiating_user_id => users.id) -# fk_rails_... (internal_comment_id => internal_comments.id) -# fk_rails_... (invite_id => invites.id) -# fk_rails_... (official_feedback_id => official_feedbacks.id) -# fk_rails_... (phase_id => phases.id) -# fk_rails_... (project_id => projects.id) -# fk_rails_... (project_review_id => project_reviews.id) -# fk_rails_... (recipient_id => users.id) -# fk_rails_... (spam_report_id => spam_reports.id) -# -module Notifications - class InvitationToCosponsorInitiative < Notification - validates :initiating_user, :cosponsors_initiative, presence: true - - ACTIVITY_TRIGGERS = { 'CosponsorsInitiative' => { 'created' => true } } - EVENT_NAME = 'Invitation to cosponsor an initiative' - - def self.make_notifications_on(activity) - cosponsors_initiative = activity&.item - initiating_user = activity&.user - recipient_id = cosponsors_initiative&.user_id - initiative = cosponsors_initiative&.initiative - - if recipient_id && initiating_user && initiative - [new( - recipient_id: recipient_id, - initiating_user: initiating_user, - cosponsors_initiative: cosponsors_initiative, - post: initiative - )] - else - [] - end - end - end -end diff --git a/back/app/models/notifications/official_feedback_on_initiative_you_follow.rb b/back/app/models/notifications/official_feedback_on_initiative_you_follow.rb deleted file mode 100644 index 5b8fd8e96f45..000000000000 --- a/back/app/models/notifications/official_feedback_on_initiative_you_follow.rb +++ /dev/null @@ -1,101 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: notifications -# -# id :uuid not null, primary key -# type :string -# read_at :datetime -# recipient_id :uuid -# post_id :uuid -# comment_id :uuid -# project_id :uuid -# created_at :datetime not null -# updated_at :datetime not null -# initiating_user_id :uuid -# spam_report_id :uuid -# invite_id :uuid -# reason_code :string -# other_reason :string -# post_status_id :uuid -# official_feedback_id :uuid -# phase_id :uuid -# post_type :string -# post_status_type :string -# project_folder_id :uuid -# inappropriate_content_flag_id :uuid -# internal_comment_id :uuid -# basket_id :uuid -# cosponsors_initiative_id :uuid -# cosponsorship_id :uuid -# project_review_id :uuid -# -# Indexes -# -# index_notifications_on_basket_id (basket_id) -# index_notifications_on_cosponsors_initiative_id (cosponsors_initiative_id) -# index_notifications_on_cosponsorship_id (cosponsorship_id) -# index_notifications_on_created_at (created_at) -# index_notifications_on_inappropriate_content_flag_id (inappropriate_content_flag_id) -# index_notifications_on_initiating_user_id (initiating_user_id) -# index_notifications_on_internal_comment_id (internal_comment_id) -# index_notifications_on_invite_id (invite_id) -# index_notifications_on_official_feedback_id (official_feedback_id) -# index_notifications_on_phase_id (phase_id) -# index_notifications_on_post_id_and_post_type (post_id,post_type) -# index_notifications_on_post_status_id (post_status_id) -# index_notifications_on_post_status_id_and_post_status_type (post_status_id,post_status_type) -# index_notifications_on_project_review_id (project_review_id) -# index_notifications_on_recipient_id (recipient_id) -# index_notifications_on_recipient_id_and_read_at (recipient_id,read_at) -# index_notifications_on_spam_report_id (spam_report_id) -# -# Foreign Keys -# -# fk_rails_... (basket_id => baskets.id) -# fk_rails_... (comment_id => comments.id) -# fk_rails_... (cosponsors_initiative_id => cosponsors_initiatives.id) -# fk_rails_... (cosponsorship_id => cosponsorships.id) -# fk_rails_... (inappropriate_content_flag_id => flag_inappropriate_content_inappropriate_content_flags.id) -# fk_rails_... (initiating_user_id => users.id) -# fk_rails_... (internal_comment_id => internal_comments.id) -# fk_rails_... (invite_id => invites.id) -# fk_rails_... (official_feedback_id => official_feedbacks.id) -# fk_rails_... (phase_id => phases.id) -# fk_rails_... (project_id => projects.id) -# fk_rails_... (project_review_id => project_reviews.id) -# fk_rails_... (recipient_id => users.id) -# fk_rails_... (spam_report_id => spam_reports.id) -# -module Notifications - class OfficialFeedbackOnInitiativeYouFollow < Notification - validates :initiating_user, :post, :official_feedback, presence: true - validates :post_type, inclusion: { in: ['Initiative'] } - - ACTIVITY_TRIGGERS = { 'OfficialFeedback' => { 'created' => true } } - EVENT_NAME = 'Official feedback on initiative you follow' - - def self.make_notifications_on(activity) - return [] unless AppConfiguration.instance.feature_activated? 'follow' - - official_feedback = activity.item - initiator_id = official_feedback.user_id - - if official_feedback.post_type == 'Initiative' && initiator_id - User.from_follows(official_feedback.post.followers).where.not(id: initiator_id).filter_map do |recipient| - next if InitiativeStatusChange.exists?(official_feedback: official_feedback) - - new( - recipient_id: recipient.id, - initiating_user_id: initiator_id, - post: official_feedback.post, - official_feedback: official_feedback - ) - end - else - [] - end - end - end -end diff --git a/back/app/models/notifications/status_change_on_initiative_you_follow.rb b/back/app/models/notifications/status_change_on_initiative_you_follow.rb deleted file mode 100644 index 7e397946c37a..000000000000 --- a/back/app/models/notifications/status_change_on_initiative_you_follow.rb +++ /dev/null @@ -1,96 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: notifications -# -# id :uuid not null, primary key -# type :string -# read_at :datetime -# recipient_id :uuid -# post_id :uuid -# comment_id :uuid -# project_id :uuid -# created_at :datetime not null -# updated_at :datetime not null -# initiating_user_id :uuid -# spam_report_id :uuid -# invite_id :uuid -# reason_code :string -# other_reason :string -# post_status_id :uuid -# official_feedback_id :uuid -# phase_id :uuid -# post_type :string -# post_status_type :string -# project_folder_id :uuid -# inappropriate_content_flag_id :uuid -# internal_comment_id :uuid -# basket_id :uuid -# cosponsors_initiative_id :uuid -# cosponsorship_id :uuid -# project_review_id :uuid -# -# Indexes -# -# index_notifications_on_basket_id (basket_id) -# index_notifications_on_cosponsors_initiative_id (cosponsors_initiative_id) -# index_notifications_on_cosponsorship_id (cosponsorship_id) -# index_notifications_on_created_at (created_at) -# index_notifications_on_inappropriate_content_flag_id (inappropriate_content_flag_id) -# index_notifications_on_initiating_user_id (initiating_user_id) -# index_notifications_on_internal_comment_id (internal_comment_id) -# index_notifications_on_invite_id (invite_id) -# index_notifications_on_official_feedback_id (official_feedback_id) -# index_notifications_on_phase_id (phase_id) -# index_notifications_on_post_id_and_post_type (post_id,post_type) -# index_notifications_on_post_status_id (post_status_id) -# index_notifications_on_post_status_id_and_post_status_type (post_status_id,post_status_type) -# index_notifications_on_project_review_id (project_review_id) -# index_notifications_on_recipient_id (recipient_id) -# index_notifications_on_recipient_id_and_read_at (recipient_id,read_at) -# index_notifications_on_spam_report_id (spam_report_id) -# -# Foreign Keys -# -# fk_rails_... (basket_id => baskets.id) -# fk_rails_... (comment_id => comments.id) -# fk_rails_... (cosponsors_initiative_id => cosponsors_initiatives.id) -# fk_rails_... (cosponsorship_id => cosponsorships.id) -# fk_rails_... (inappropriate_content_flag_id => flag_inappropriate_content_inappropriate_content_flags.id) -# fk_rails_... (initiating_user_id => users.id) -# fk_rails_... (internal_comment_id => internal_comments.id) -# fk_rails_... (invite_id => invites.id) -# fk_rails_... (official_feedback_id => official_feedbacks.id) -# fk_rails_... (phase_id => phases.id) -# fk_rails_... (project_id => projects.id) -# fk_rails_... (project_review_id => project_reviews.id) -# fk_rails_... (recipient_id => users.id) -# fk_rails_... (spam_report_id => spam_reports.id) -# -module Notifications - class StatusChangeOnInitiativeYouFollow < Notification - validates :post_status, :post, presence: true - validates :post_type, inclusion: { in: ['Initiative'] } - - ACTIVITY_TRIGGERS = { 'Initiative' => { 'changed_status' => true } } - EVENT_NAME = 'Status change on initiative you follow' - - def self.make_notifications_on(activity) - return [] unless AppConfiguration.instance.feature_activated? 'follow' - - initiator_id = activity.user_id - initiative = activity.item - return [] if !initiative - - User.from_follows(initiative.followers).where.not(id: initiator_id).map do |recipient| - new( - recipient_id: recipient.id, - initiating_user_id: initiator_id, - post: initiative, - post_status: initiative.initiative_status - ) - end - end - end -end diff --git a/back/app/models/permission.rb b/back/app/models/permission.rb index ef63779294bf..56b96a29a69e 100644 --- a/back/app/models/permission.rb +++ b/back/app/models/permission.rb @@ -24,7 +24,7 @@ class Permission < ApplicationRecord PERMITTED_BIES = %w[everyone everyone_confirmed_email users admins_moderators verified].freeze ACTIONS = { # NOTE: Order of actions in each array is used when using :order_by_action - nil => %w[visiting following posting_initiative commenting_initiative reacting_initiative attending_event], + nil => %w[visiting following attending_event], 'information' => %w[attending_event], 'ideation' => %w[posting_idea commenting_idea reacting_idea attending_event], 'proposals' => %w[posting_idea commenting_idea reacting_idea attending_event], diff --git a/back/app/policies/initiative_comment_policy.rb b/back/app/policies/initiative_comment_policy.rb deleted file mode 100644 index 5b2a4bec46f2..000000000000 --- a/back/app/policies/initiative_comment_policy.rb +++ /dev/null @@ -1,58 +0,0 @@ -# frozen_string_literal: true - -class InitiativeCommentPolicy < ApplicationPolicy - class Scope < ApplicationPolicy::Scope - def resolve - scope.where(post: scope_for(Initiative)) - end - end - - def index_xlsx? - user&.admin? - end - - def create? - return unless active? - return true if admin? - - owner? && commenting_allowed?(user) - end - - def children? - show? - end - - def show? - policy_for(record.post).show? - end - - def update? - create? - end - - def mark_as_deleted? - update? - end - - def destroy? - false - end - - def permitted_attributes_for_update - attrs = %i[parent_id author_id] - if record.author_id == user&.id - attrs += [body_multiloc: CL2_SUPPORTED_LOCALES] - end - attrs - end - - private - - def commenting_allowed?(user) - !Permissions::InitiativePermissionsService.new(user).denied_reason_for_action 'commenting_initiative' - end - - def owner? - user && (record.author_id == user.id) - end -end diff --git a/back/app/policies/initiative_file_policy.rb b/back/app/policies/initiative_file_policy.rb deleted file mode 100644 index 910b88e275fc..000000000000 --- a/back/app/policies/initiative_file_policy.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -class InitiativeFilePolicy < ApplicationPolicy - class Scope < ApplicationPolicy::Scope - def resolve - scope.where(initiative: scope_for(Initiative)) - end - end - - def create? - policy_for(record.initiative).update? - end - - def show? - policy_for(record.initiative).show? - end - - def update? - policy_for(record.initiative).update? - end - - def destroy? - policy_for(record.initiative).update? - end -end diff --git a/back/app/policies/initiative_image_policy.rb b/back/app/policies/initiative_image_policy.rb deleted file mode 100644 index 8cd7e7a670b0..000000000000 --- a/back/app/policies/initiative_image_policy.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -class InitiativeImagePolicy < ApplicationPolicy - class Scope < ApplicationPolicy::Scope - def resolve - scope.where(initiative: scope_for(Initiative)) - end - end - - def create? - policy_for(record.initiative).update? - end - - def show? - policy_for(record.initiative).show? - end - - def update? - policy_for(record.initiative).update? - end - - def destroy? - policy_for(record.initiative).update? - end -end diff --git a/back/app/policies/initiative_official_feedback_policy.rb b/back/app/policies/initiative_official_feedback_policy.rb deleted file mode 100644 index 48e888bd605d..000000000000 --- a/back/app/policies/initiative_official_feedback_policy.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -class InitiativeOfficialFeedbackPolicy < ApplicationPolicy - class Scope < ApplicationPolicy::Scope - def resolve - scope.where(post: scope_for(Initiative)) - end - end - - def create? - user&.admin? - end - - def show? - policy_for(record.post).show? - end - - def update? - create? - end - - def destroy? - create? - end -end diff --git a/back/app/policies/initiative_policy.rb b/back/app/policies/initiative_policy.rb deleted file mode 100644 index faf4e8add487..000000000000 --- a/back/app/policies/initiative_policy.rb +++ /dev/null @@ -1,92 +0,0 @@ -# frozen_string_literal: true - -class InitiativePolicy < ApplicationPolicy - class Scope < ApplicationPolicy::Scope - def resolve - published = scope.where(publication_status: 'published') - - if UserRoleService.new.can_moderate_initiatives?(user) - published - elsif user&.active? - published.left_outer_joins(:cosponsors_initiatives).with_status_code(InitiativeStatus::NOT_REVIEW_CODES) - .or(published.where(author: user)) - .or(published.where(cosponsors_initiatives: { user: user })) - else - published.with_status_code(InitiativeStatus::NOT_REVIEW_CODES) - end - end - end - - def index_xlsx? - active? && UserRoleService.new.can_moderate_initiatives?(user) - end - - def create? - return true if active? && can_moderate? - - reason = posting_denied_reason user - raise_not_authorized reason if reason - - active? && owner? - end - - def show? - return true if active? && (owner? || cosponsor? || can_moderate?) - return false if record.review_status? - - true - end - - def by_slug? - show? - end - - def update? - return true if active? && can_moderate? - - create? && !record.editing_locked - end - - def destroy? - create? - end - - def accept_cosponsorship_invite? - cosponsor? - end - - def allowed_transitions? - can_moderate? - end - - def permitted_attributes - shared = [ - :publication_status, - :location_description, - :header_bg, - :anonymous, - { cosponsor_ids: [] }, - { location_point_geojson: [:type, { coordinates: [] }], - title_multiloc: CL2_SUPPORTED_LOCALES, - body_multiloc: CL2_SUPPORTED_LOCALES, - topic_ids: [], - area_ids: [] } - ] - - can_moderate? ? [:author_id, :assignee_id, *shared] : shared - end - - private - - def posting_denied_reason(user) - Permissions::InitiativePermissionsService.new(user).denied_reason_for_action 'posting_initiative' - end - - def owner? - user && record.author_id == user.id - end - - def cosponsor? - user && record&.cosponsors&.include?(user) - end -end diff --git a/back/app/policies/initiative_reaction_policy.rb b/back/app/policies/initiative_reaction_policy.rb deleted file mode 100644 index cc61ba356128..000000000000 --- a/back/app/policies/initiative_reaction_policy.rb +++ /dev/null @@ -1,45 +0,0 @@ -# frozen_string_literal: true - -class InitiativeReactionPolicy < ApplicationPolicy - class Scope < ApplicationPolicy::Scope - def resolve - if user&.admin? - scope.all - elsif user - scope.where(user: user) - else - scope.none - end - end - end - - def create? - return if !user&.active? || !owner? - - reason = reacting_denied_reason user - reason ? raise_not_authorized(reason) : true - end - - def destroy? - create? - end - - def up? - create? - end - - def down? - # TODO: JS - should this reason be in a permissions service - raise_not_authorized('dislikes_not_supported') - end - - def show? - active? && (owner? || admin?) - end - - private - - def reacting_denied_reason(user) - Permissions::InitiativePermissionsService.new(user).denied_reason_for_action 'reacting_initiative' - end -end diff --git a/back/app/policies/initiative_status_change_policy.rb b/back/app/policies/initiative_status_change_policy.rb deleted file mode 100644 index 137857fefdcc..000000000000 --- a/back/app/policies/initiative_status_change_policy.rb +++ /dev/null @@ -1,22 +0,0 @@ -# frozen_string_literal: true - -class InitiativeStatusChangePolicy < ApplicationPolicy - class Scope < ApplicationPolicy::Scope - def resolve - # Disabled - if user&.active? && user&.admin? - scope.all - else - scope.none - end - end - end - - def create? - user&.active? && user&.admin? - end - - def show? - user&.active? && user&.admin? - end -end diff --git a/back/app/policies/initiative_status_policy.rb b/back/app/policies/initiative_status_policy.rb deleted file mode 100644 index 0a34855457fc..000000000000 --- a/back/app/policies/initiative_status_policy.rb +++ /dev/null @@ -1,43 +0,0 @@ -# frozen_string_literal: true - -class InitiativeStatusPolicy < ApplicationPolicy - class Scope < ApplicationPolicy::Scope - def resolve - filter_out_review_statuses - end - - private - - # This method handles the case when review was turned on, - # some initiatives were created, and then review was turned off. - # We still want to show statuses of all used initiatives after that. - def filter_out_review_statuses - if UserRoleService.new.can_moderate_initiatives?(user) - if Initiative.review_required? - scope.all - else - review_initiatives = initiatives.with_status_code(InitiativeStatus::REVIEW_CODES) - - if review_initiatives.any? - scope.all # make it possible to change the status of initiatives that were created when review was on - else - scope.where(code: InitiativeStatus::NOT_REVIEW_CODES) - end - end - else - public_codes = InitiativeStatus::NOT_REVIEW_CODES - # We want to show only statuses of initiatives available to the current user. - user_codes = initiatives.joins(:initiative_status).distinct.pluck('initiative_status.code') - scope.where(code: (public_codes + user_codes).uniq) - end - end - - def initiatives - scope_for(Initiative) - end - end - - def show? - true - end -end diff --git a/back/app/serializers/web_api/v1/initiative_serializer.rb b/back/app/serializers/web_api/v1/initiative_serializer.rb deleted file mode 100644 index e3acfcd4cffb..000000000000 --- a/back/app/serializers/web_api/v1/initiative_serializer.rb +++ /dev/null @@ -1,98 +0,0 @@ -# frozen_string_literal: true - -class WebApi::V1::InitiativeSerializer < WebApi::V1::BaseSerializer - attributes :title_multiloc, - :slug, - :publication_status, - :likes_count, - :comments_count, - :internal_comments_count, - :official_feedbacks_count, - :followers_count, - :location_point_geojson, - :location_description, - :created_at, - :updated_at, - :published_at, - :expires_at, - :reactions_needed, - :anonymous, - :author_hash, - :editing_locked, - :public, - :proposed_at - - attribute :author_name do |object, params| - name_service = UserDisplayNameService.new(AppConfiguration.instance, current_user(params)) - name_service.display_name!(object.author) - end - - attribute :body_multiloc do |object| - TextImageService.new.render_data_images_multiloc object.body_multiloc, field: :body_multiloc, imageable: object - end - - attribute :header_bg do |object| - object.header_bg && object.header_bg.versions.to_h { |k, v| [k.to_s, v.url] } - end - - attribute :internal_comments_count, if: proc { |object, params| - can_moderate?(object, params) - } - - attribute :cosponsorships do |object, params| - name_service = UserDisplayNameService.new(AppConfiguration.instance, current_user(params)) - - object.cosponsors_initiatives.includes(:user).map do |ci| - { user_id: ci.user_id, name: name_service.display_name!(ci.user), status: ci.status } - end - end - - attribute :public do |object| - object.initiative_status ? object.initiative_status.public? : false - end - - has_many :initiative_images, serializer: WebApi::V1::ImageSerializer - has_many :topics - has_many :areas - has_many :cosponsors, record_type: :user, serializer: WebApi::V1::UserSerializer - - belongs_to :author, record_type: :user, serializer: WebApi::V1::UserSerializer - belongs_to :initiative_status - belongs_to :assignee, if: proc { |object, params| - can_moderate? object, params - }, record_type: :user, serializer: WebApi::V1::UserSerializer - - has_one :user_reaction, if: proc { |object, params| - signed_in? object, params - }, record_type: :reaction, serializer: WebApi::V1::ReactionSerializer do |object, params| - cached_user_reaction object, params - end - - has_one :user_follower, record_type: :follower, if: proc { |object, params| - signed_in? object, params - } do |object, params| - user_follower object, params - end - - def self.can_moderate?(_object, params) - current_user(params) && UserRoleService.new.can_moderate_initiatives?(current_user(params)) - end - - def self.cached_user_reaction(object, params) - if params[:vbii] - params.dig(:vbii, object.id) - else - object.reactions.where(user_id: current_user(params)&.id).first - end - end - - def self.user_follower(object, params) - if params[:user_followers] - params.dig(:user_followers, [object.id, 'Initiative'])&.first - else - current_user(params)&.follows&.find do |follow| - follow.followable_id == object.id && follow.followable_type == 'Initiative' - end - end - end -end diff --git a/back/app/serializers/web_api/v1/initiative_status_change_serializer.rb b/back/app/serializers/web_api/v1/initiative_status_change_serializer.rb deleted file mode 100644 index 950140ce95ab..000000000000 --- a/back/app/serializers/web_api/v1/initiative_status_change_serializer.rb +++ /dev/null @@ -1,10 +0,0 @@ -# frozen_string_literal: true - -class WebApi::V1::InitiativeStatusChangeSerializer < WebApi::V1::BaseSerializer - attribute :created_at, :updated_at - - belongs_to :initiative_status - belongs_to :initiative - belongs_to :user - belongs_to :official_feedback -end diff --git a/back/app/serializers/web_api/v1/initiative_status_serializer.rb b/back/app/serializers/web_api/v1/initiative_status_serializer.rb deleted file mode 100644 index 7f32a528a01d..000000000000 --- a/back/app/serializers/web_api/v1/initiative_status_serializer.rb +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true - -class WebApi::V1::InitiativeStatusSerializer < WebApi::V1::BaseSerializer - attributes :title_multiloc, :description_multiloc, :ordering, :code, :color - - attribute :transition_type do |object| - InitiativeStatusService.new.transition_type object - end -end diff --git a/back/app/serializers/web_api/v1/notifications/comment_on_initiative_you_follow_serializer.rb b/back/app/serializers/web_api/v1/notifications/comment_on_initiative_you_follow_serializer.rb deleted file mode 100644 index 7222480ac0f7..000000000000 --- a/back/app/serializers/web_api/v1/notifications/comment_on_initiative_you_follow_serializer.rb +++ /dev/null @@ -1,24 +0,0 @@ -# frozen_string_literal: true - -class WebApi::V1::Notifications::CommentOnInitiativeYouFollowSerializer < WebApi::V1::Notifications::NotificationSerializer - attribute :initiating_user_first_name do |object| - object.initiating_user&.first_name - end - - attribute :initiating_user_last_name do |object, params| - name_service = UserDisplayNameService.new(AppConfiguration.instance, current_user(params)) - name_service.last_name!(object.initiating_user) - end - - attribute :initiating_user_slug do |object| - object.initiating_user&.slug - end - - attribute :post_title_multiloc do |object| - object.post&.title_multiloc - end - - attribute :post_slug do |object| - object.post&.slug - end -end diff --git a/back/app/serializers/web_api/v1/notifications/cosponsor_of_your_initiative_serializer.rb b/back/app/serializers/web_api/v1/notifications/cosponsor_of_your_initiative_serializer.rb deleted file mode 100644 index 3704d93821ce..000000000000 --- a/back/app/serializers/web_api/v1/notifications/cosponsor_of_your_initiative_serializer.rb +++ /dev/null @@ -1,24 +0,0 @@ -# frozen_string_literal: true - -class WebApi::V1::Notifications::CosponsorOfYourInitiativeSerializer < WebApi::V1::Notifications::NotificationSerializer - attribute :initiating_user_first_name do |object| - object.initiating_user&.first_name - end - - attribute :initiating_user_last_name do |object, params| - name_service = UserDisplayNameService.new(AppConfiguration.instance, current_user(params)) - name_service.last_name!(object.initiating_user) - end - - attribute :initiating_user_slug do |object| - object.initiating_user&.slug - end - - attribute :post_title_multiloc do |object| - object.post&.title_multiloc - end - - attribute :post_slug do |object| - object.post&.slug - end -end diff --git a/back/app/serializers/web_api/v1/notifications/initiative_assigned_to_you_serializer.rb b/back/app/serializers/web_api/v1/notifications/initiative_assigned_to_you_serializer.rb deleted file mode 100644 index f1e9b5b509d6..000000000000 --- a/back/app/serializers/web_api/v1/notifications/initiative_assigned_to_you_serializer.rb +++ /dev/null @@ -1,24 +0,0 @@ -# frozen_string_literal: true - -class WebApi::V1::Notifications::InitiativeAssignedToYouSerializer < WebApi::V1::Notifications::NotificationSerializer - attribute :initiating_user_first_name do |object| - object.initiating_user&.first_name - end - - attribute :initiating_user_last_name do |object, params| - name_service = UserDisplayNameService.new(AppConfiguration.instance, current_user(params)) - name_service.last_name!(object.initiating_user) - end - - attribute :initiating_user_slug do |object| - object.initiating_user&.slug - end - - attribute :post_title_multiloc do |object| - object.post&.title_multiloc - end - - attribute :post_slug do |object| - object.post&.slug - end -end diff --git a/back/app/serializers/web_api/v1/notifications/initiative_marked_as_spam_serializer.rb b/back/app/serializers/web_api/v1/notifications/initiative_marked_as_spam_serializer.rb deleted file mode 100644 index 0758c665535a..000000000000 --- a/back/app/serializers/web_api/v1/notifications/initiative_marked_as_spam_serializer.rb +++ /dev/null @@ -1,24 +0,0 @@ -# frozen_string_literal: true - -class WebApi::V1::Notifications::InitiativeMarkedAsSpamSerializer < WebApi::V1::Notifications::NotificationSerializer - attribute :initiating_user_first_name do |object| - object.initiating_user&.first_name - end - - attribute :initiating_user_last_name do |object, params| - name_service = UserDisplayNameService.new(AppConfiguration.instance, current_user(params)) - name_service.last_name!(object.initiating_user) - end - - attribute :initiating_user_slug do |object| - object.initiating_user&.slug - end - - attribute :post_title_multiloc do |object| - object.post&.title_multiloc - end - - attribute :post_slug do |object| - object.post&.slug - end -end diff --git a/back/app/serializers/web_api/v1/notifications/initiative_resubmitted_for_review_serializer.rb b/back/app/serializers/web_api/v1/notifications/initiative_resubmitted_for_review_serializer.rb deleted file mode 100644 index e9f4bd27f94e..000000000000 --- a/back/app/serializers/web_api/v1/notifications/initiative_resubmitted_for_review_serializer.rb +++ /dev/null @@ -1,11 +0,0 @@ -# frozen_string_literal: true - -class WebApi::V1::Notifications::InitiativeResubmittedForReviewSerializer < WebApi::V1::Notifications::NotificationSerializer - attribute :post_title_multiloc do |object| - object.post&.title_multiloc - end - - attribute :post_slug do |object| - object.post&.slug - end -end diff --git a/back/app/serializers/web_api/v1/notifications/internal_comment_on_initiative_assigned_to_you_serializer.rb b/back/app/serializers/web_api/v1/notifications/internal_comment_on_initiative_assigned_to_you_serializer.rb deleted file mode 100644 index 14d31cd81403..000000000000 --- a/back/app/serializers/web_api/v1/notifications/internal_comment_on_initiative_assigned_to_you_serializer.rb +++ /dev/null @@ -1,4 +0,0 @@ -# frozen_string_literal: true - -class WebApi::V1::Notifications::InternalCommentOnInitiativeAssignedToYouSerializer < WebApi::V1::Notifications::InternalCommentNotificationSerializer -end diff --git a/back/app/serializers/web_api/v1/notifications/internal_comment_on_initiative_you_commented_internally_on_serializer.rb b/back/app/serializers/web_api/v1/notifications/internal_comment_on_initiative_you_commented_internally_on_serializer.rb deleted file mode 100644 index 870840dc0666..000000000000 --- a/back/app/serializers/web_api/v1/notifications/internal_comment_on_initiative_you_commented_internally_on_serializer.rb +++ /dev/null @@ -1,4 +0,0 @@ -# frozen_string_literal: true - -class WebApi::V1::Notifications::InternalCommentOnInitiativeYouCommentedInternallyOnSerializer < WebApi::V1::Notifications::InternalCommentNotificationSerializer -end diff --git a/back/app/serializers/web_api/v1/notifications/internal_comment_on_unassigned_initiative_serializer.rb b/back/app/serializers/web_api/v1/notifications/internal_comment_on_unassigned_initiative_serializer.rb deleted file mode 100644 index 76470100b128..000000000000 --- a/back/app/serializers/web_api/v1/notifications/internal_comment_on_unassigned_initiative_serializer.rb +++ /dev/null @@ -1,4 +0,0 @@ -# frozen_string_literal: true - -class WebApi::V1::Notifications::InternalCommentOnUnassignedInitiativeSerializer < WebApi::V1::Notifications::InternalCommentNotificationSerializer -end diff --git a/back/app/serializers/web_api/v1/notifications/invitation_to_cosponsor_initiative_serializer.rb b/back/app/serializers/web_api/v1/notifications/invitation_to_cosponsor_initiative_serializer.rb deleted file mode 100644 index 7eb8c49e16a7..000000000000 --- a/back/app/serializers/web_api/v1/notifications/invitation_to_cosponsor_initiative_serializer.rb +++ /dev/null @@ -1,24 +0,0 @@ -# frozen_string_literal: true - -class WebApi::V1::Notifications::InvitationToCosponsorInitiativeSerializer < WebApi::V1::Notifications::NotificationSerializer - attribute :initiating_user_first_name do |object| - object.initiating_user&.first_name - end - - attribute :initiating_user_last_name do |object, params| - name_service = UserDisplayNameService.new(AppConfiguration.instance, current_user(params)) - name_service.last_name!(object.initiating_user) - end - - attribute :initiating_user_slug do |object| - object.initiating_user&.slug - end - - attribute :post_title_multiloc do |object| - object.post&.title_multiloc - end - - attribute :post_slug do |object| - object.post&.slug - end -end diff --git a/back/app/serializers/web_api/v1/notifications/official_feedback_on_initiative_you_follow_serializer.rb b/back/app/serializers/web_api/v1/notifications/official_feedback_on_initiative_you_follow_serializer.rb deleted file mode 100644 index 869016f45276..000000000000 --- a/back/app/serializers/web_api/v1/notifications/official_feedback_on_initiative_you_follow_serializer.rb +++ /dev/null @@ -1,15 +0,0 @@ -# frozen_string_literal: true - -class WebApi::V1::Notifications::OfficialFeedbackOnInitiativeYouFollowSerializer < WebApi::V1::Notifications::NotificationSerializer - attribute :official_feedback_author do |object| - object.official_feedback&.author_multiloc - end - - attribute :post_title_multiloc do |object| - object.post&.title_multiloc - end - - attribute :post_slug do |object| - object.post&.slug - end -end diff --git a/back/app/serializers/web_api/v1/notifications/status_change_on_initiative_you_follow_serializer.rb b/back/app/serializers/web_api/v1/notifications/status_change_on_initiative_you_follow_serializer.rb deleted file mode 100644 index a4aa6571309b..000000000000 --- a/back/app/serializers/web_api/v1/notifications/status_change_on_initiative_you_follow_serializer.rb +++ /dev/null @@ -1,15 +0,0 @@ -# frozen_string_literal: true - -class WebApi::V1::Notifications::StatusChangeOnInitiativeYouFollowSerializer < WebApi::V1::Notifications::NotificationSerializer - attribute :post_title_multiloc do |object| - object.post&.title_multiloc - end - - attribute :post_slug do |object| - object.post&.slug - end - - attribute :initiative_status_title_multiloc do |object| - object.post_status&.title_multiloc - end -end diff --git a/back/app/services/notification_service.rb b/back/app/services/notification_service.rb index b836c6ce1334..3300a002d4d8 100644 --- a/back/app/services/notification_service.rb +++ b/back/app/services/notification_service.rb @@ -6,31 +6,21 @@ class NotificationService Notifications::CommentDeletedByAdmin, Notifications::CommentMarkedAsSpam, Notifications::CommentOnIdeaYouFollow, - Notifications::CommentOnInitiativeYouFollow, Notifications::CommentOnYourComment, Notifications::CosponsorOfYourIdea, - Notifications::CosponsorOfYourInitiative, Notifications::IdeaMarkedAsSpam, - Notifications::InitiativeAssignedToYou, - Notifications::InitiativeMarkedAsSpam, - Notifications::InitiativeResubmittedForReview, Notifications::InternalComments::InternalCommentOnIdeaAssignedToYou, Notifications::InternalComments::InternalCommentOnIdeaYouCommentedInternallyOn, Notifications::InternalComments::InternalCommentOnIdeaYouModerate, - Notifications::InternalComments::InternalCommentOnInitiativeAssignedToYou, - Notifications::InternalComments::InternalCommentOnInitiativeYouCommentedInternallyOn, - Notifications::InternalComments::InternalCommentOnUnassignedInitiative, Notifications::InternalComments::InternalCommentOnUnassignedUnmoderatedIdea, Notifications::InternalComments::InternalCommentOnYourInternalComment, Notifications::InternalComments::MentionInInternalComment, Notifications::InvitationToCosponsorIdea, - Notifications::InvitationToCosponsorInitiative, Notifications::InviteAccepted, Notifications::MentionInComment, Notifications::MentionInOfficialFeedback, Notifications::NativeSurveyNotSubmitted, Notifications::OfficialFeedbackOnIdeaYouFollow, - Notifications::OfficialFeedbackOnInitiativeYouFollow, Notifications::ProjectFolderModerationRightsReceived, Notifications::ProjectModerationRightsReceived, Notifications::ProjectPhaseStarted, @@ -39,7 +29,6 @@ class NotificationService Notifications::ProjectReviewRequest, Notifications::ProjectReviewStateChange, Notifications::StatusChangeOnIdeaYouFollow, - Notifications::StatusChangeOnInitiativeYouFollow, Notifications::ThresholdReachedForAdmin, Notifications::VotingBasketNotSubmitted, Notifications::VotingBasketSubmitted, diff --git a/back/app/services/notifications/internal_comments/internal_comment_on_initiative_assigned_to_you_builder.rb b/back/app/services/notifications/internal_comments/internal_comment_on_initiative_assigned_to_you_builder.rb deleted file mode 100644 index c02106ad6009..000000000000 --- a/back/app/services/notifications/internal_comments/internal_comment_on_initiative_assigned_to_you_builder.rb +++ /dev/null @@ -1,26 +0,0 @@ -# frozen_string_literal: true - -module Notifications - class InternalComments::InternalCommentOnInitiativeAssignedToYouBuilder < InternalComments::BaseNotificationBuilder - protected - - def skip_recipient?(recipient) - initiator_id.nil? || - initiator_id == recipient.id || - recipient.id == parent_author_id || - MentionService.new.user_mentioned?(internal_comment.body, recipient) - end - - def recipients - [User.find_by(id: internal_comment&.post&.assignee_id)].compact - end - - def preconditions_met? - post_type == 'Initiative' - end - - def notification_class - Notifications::InternalComments::InternalCommentOnInitiativeAssignedToYou - end - end -end diff --git a/back/app/services/notifications/internal_comments/internal_comment_on_initiative_you_commented_internally_on_builder.rb b/back/app/services/notifications/internal_comments/internal_comment_on_initiative_you_commented_internally_on_builder.rb deleted file mode 100644 index 3c71e349d370..000000000000 --- a/back/app/services/notifications/internal_comments/internal_comment_on_initiative_you_commented_internally_on_builder.rb +++ /dev/null @@ -1,27 +0,0 @@ -# frozen_string_literal: true - -module Notifications - class InternalComments::InternalCommentOnInitiativeYouCommentedInternallyOnBuilder < InternalComments::BaseNotificationBuilder - protected - - def skip_recipient?(recipient) - initiator_id.nil? || - initiator_id == recipient.id || - recipient.id == parent_author_id || - recipient.id == assignee_id || - MentionService.new.user_mentioned?(internal_comment.body, recipient) - end - - def recipients - InternalComment.where(post_id: internal_comment.post_id).map(&:author).uniq - end - - def preconditions_met? - post_type == 'Initiative' - end - - def notification_class - Notifications::InternalComments::InternalCommentOnInitiativeYouCommentedInternallyOn - end - end -end diff --git a/back/app/services/notifications/internal_comments/internal_comment_on_unassigned_initiative_builder.rb b/back/app/services/notifications/internal_comments/internal_comment_on_unassigned_initiative_builder.rb deleted file mode 100644 index 3a4f84233efb..000000000000 --- a/back/app/services/notifications/internal_comments/internal_comment_on_unassigned_initiative_builder.rb +++ /dev/null @@ -1,29 +0,0 @@ -# frozen_string_literal: true - -module Notifications - class InternalComments::InternalCommentOnUnassignedInitiativeBuilder < InternalComments::BaseNotificationBuilder - protected - - def skip_recipient?(recipient) - initiator_id.nil? || - initiator_id == recipient.id || - recipient.id == parent_author_id || - recipient.id == assignee_id || - MentionService.new.user_mentioned?(internal_comment.body, recipient) - end - - def recipients - commenters = InternalComment.where(post_id: internal_comment.post_id).map(&:author).uniq - - User.admin - commenters - end - - def preconditions_met? - post_type == 'Initiative' && assignee_id.nil? - end - - def notification_class - Notifications::InternalComments::InternalCommentOnUnassignedInitiative - end - end -end diff --git a/back/app/services/permissions/base_permissions_service.rb b/back/app/services/permissions/base_permissions_service.rb index d1e44d3a8a0e..cee6d2b7b782 100644 --- a/back/app/services/permissions/base_permissions_service.rb +++ b/back/app/services/permissions/base_permissions_service.rb @@ -3,9 +3,6 @@ class BasePermissionsService SUPPORTED_ACTIONS = %w[ following visiting - posting_initiative - commenting_initiative - reacting_initiative posting_idea commenting_idea reacting_idea diff --git a/back/app/services/permissions/initiative_permissions_service.rb b/back/app/services/permissions/initiative_permissions_service.rb deleted file mode 100644 index 13ac58a152c9..000000000000 --- a/back/app/services/permissions/initiative_permissions_service.rb +++ /dev/null @@ -1,22 +0,0 @@ -# frozen_string_literal: true - -module Permissions - class InitiativePermissionsService < BasePermissionsService - def action_descriptors - posting_disabled_reason = denied_reason_for_action 'posting_initiative' - commenting_disabled_reason = denied_reason_for_action 'commenting_initiative' - reacting_disabled_reason = denied_reason_for_action 'reacting_initiative' - - descriptors = { - posting_initiative: { disabled_reason: posting_disabled_reason }, - commenting_initiative: { disabled_reason: commenting_disabled_reason }, - reacting_initiative: { disabled_reason: reacting_disabled_reason } - } - - descriptors.each_value { |desc| desc[:enabled] = !desc[:disabled_reason] } - descriptors[:comment_reacting_initiative] = descriptors[:commenting_initiative] - descriptors[:cancelling_initiative_reactions] = descriptors[:reacting_initiative] - descriptors - end - end -end diff --git a/back/app/services/side_fx_initiative_service.rb b/back/app/services/side_fx_initiative_service.rb deleted file mode 100644 index ce9199d5c45f..000000000000 --- a/back/app/services/side_fx_initiative_service.rb +++ /dev/null @@ -1,158 +0,0 @@ -# frozen_string_literal: true - -class SideFxInitiativeService - include SideFxHelper - - def initialize - @automatic_assignment = false - end - - def before_create(initiative, user) - before_publish initiative, user if initiative.published? - end - - def after_create(initiative, user) - initiative.update!(body_multiloc: TextImageService.new.swap_data_images_multiloc(initiative.body_multiloc, field: :body_multiloc, imageable: initiative)) - return unless initiative.published? - - after_publish initiative, user - - log_activities_if_cosponsors_added(initiative, user, _old_cosponsor_ids = []) - end - - def before_update(initiative, user) - initiative.body_multiloc = TextImageService.new.swap_data_images_multiloc(initiative.body_multiloc, field: :body_multiloc, imageable: initiative) - return unless initiative.publication_status_change == %w[draft published] - - before_publish initiative, user - end - - def after_update(initiative, user, old_cosponsor_ids) - transition_to_review_pending_if_required(initiative, user) - remove_user_from_past_activities_with_item(initiative, user) if initiative.anonymous_previously_changed?(to: true) - - LogActivityJob.perform_later(initiative, 'changed', user_for_activity_on_anonymizable_item(initiative, user), initiative.updated_at.to_i) - - if initiative.assignee_id_previously_changed? - initiating_user = @automatic_assignment ? nil : user - LogActivityJob.perform_later(initiative, 'changed_assignee', user_for_activity_on_anonymizable_item(initiative, initiating_user), initiative.updated_at.to_i, payload: { change: initiative.assignee_id_previous_change }) - end - - if initiative.title_multiloc_previously_changed? - LogActivityJob.perform_later(initiative, 'changed_title', user_for_activity_on_anonymizable_item(initiative, user), initiative.updated_at.to_i, payload: { change: initiative.title_multiloc_previous_change }) - end - - if initiative.body_multiloc_previously_changed? - LogActivityJob.perform_later(initiative, 'changed_body', user_for_activity_on_anonymizable_item(initiative, user), initiative.updated_at.to_i, payload: { change: initiative.body_multiloc_previous_change }) - end - - log_activities_if_cosponsors_added(initiative, user, old_cosponsor_ids) - end - - def after_accept_cosponsorship_invite(cosponsors_initiative, user) - LogActivityJob.perform_later( - cosponsors_initiative, - 'cosponsorship_accepted', - user, # We don't want anonymized users being cosponsors - cosponsors_initiative.updated_at.to_i, - payload: { change: cosponsors_initiative.status_previous_change } - ) - create_followers cosponsors_initiative.initiative, cosponsors_initiative.user - end - - def before_destroy(initiative, user); end - - def after_destroy(frozen_initiative, user) - serialized_initiative = clean_time_attributes(frozen_initiative.attributes) - serialized_initiative['location_point'] = serialized_initiative['location_point'].to_s - LogActivityJob.perform_later(encode_frozen_resource(frozen_initiative), 'deleted', user, Time.now.to_i, payload: { initiative: serialized_initiative }) - end - - def log_initiative_proposed_activity(initiative, user) - LogActivityJob.perform_later( - initiative, - 'proposed', - user_for_activity_on_anonymizable_item(initiative, user), - initiative.updated_at.to_i - ) - end - - private - - def log_activities_if_cosponsors_added(initiative, user, old_cosponsor_ids) - added_ids = initiative.reload.cosponsors.map(&:id) - old_cosponsor_ids - - if added_ids.present? - new_cosponsors_initiatives = initiative.cosponsors_initiatives.where(user_id: added_ids) - - new_cosponsors_initiatives.each do |cosponsors_initiative| - LogActivityJob.perform_later( - cosponsors_initiative, - 'created', - user, # We don't want anonymized authors when cosponsors feature in use - cosponsors_initiative.created_at.to_i - ) - end - end - end - - def transition_to_review_pending_if_required(initiative, user) - if initiative.initiative_status&.code == 'changes_requested' && user == initiative.author - status_id_to = InitiativeStatus.find_by(code: 'review_pending')&.id - InitiativeStatusService.new.transition!([initiative.id], status_id_to) - end - end - - def before_publish(initiative, _user) - set_assignee initiative - end - - def after_publish(initiative, user) - add_autoreaction(initiative, user) - log_activity_jobs_after_published(initiative, user) - create_followers(initiative, user) unless initiative.anonymous? - end - - def set_assignee(initiative) - default_assignee = User.active.admin.order(:created_at).reject(&:super_admin?).first - return unless !initiative.assignee && default_assignee - - initiative.assignee = default_assignee - @automatic_assignment = true - end - - def add_autoreaction(initiative, user) - reaction = Reaction.new(reactable: initiative, user: user, mode: 'up') - - begin - Pundit.authorize( - user, - reaction, - :create?, - policy_class: InitiativeReactionPolicy - ) - rescue Pundit::NotAuthorizedErrorWithReason - # Do not create the auto-reaction. - else - initiative.reactions.create!(mode: 'up', user: initiative.author) - initiative.reload - end - end - - def log_activity_jobs_after_published(initiative, user) - LogActivityJob.set(wait: 20.seconds).perform_later( - initiative, - 'published', - user_for_activity_on_anonymizable_item(initiative, user), - initiative.published_at.to_i - ) - - log_initiative_proposed_activity(initiative, user) if initiative.initiative_status.code == 'proposed' - end - - def create_followers(initiative, user) - Follower.find_or_create_by(followable: initiative, user: user) - end -end - -SideFxInitiativeService.prepend(FlagInappropriateContent::Patches::SideFxInitiativeService) diff --git a/back/app/services/side_fx_initiative_status_change_service.rb b/back/app/services/side_fx_initiative_status_change_service.rb deleted file mode 100644 index 8a53c4ae56cc..000000000000 --- a/back/app/services/side_fx_initiative_status_change_service.rb +++ /dev/null @@ -1,21 +0,0 @@ -# frozen_string_literal: true - -class SideFxInitiativeStatusChangeService - include SideFxHelper - - def before_create(change, user); end - - def after_create(change, user) - InitiativeStatusService.new.log_status_change change, user: user - - # Behaviour we want when proposal review feature is on and initiative_status_change is to 'proposed': - # * Create an Initiative 'proposed' activity type, to trigger the InitiativePublished campaign. - # * Lock (prevent) editing of the initiative. - # This works because, if the review feature is off, the associated initiative_status_change with code 'proposed' - # is created at the model level when the initiative is published, and does not invoke this method. - if change.initiative_status.code == 'proposed' - SideFxInitiativeService.new.log_initiative_proposed_activity(change.initiative, user) - change.initiative.update!(editing_locked: true) - end - end -end diff --git a/back/config/routes.rb b/back/config/routes.rb index 6674a5f89cd9..9c2cb889a981 100644 --- a/back/config/routes.rb +++ b/back/config/routes.rb @@ -73,22 +73,6 @@ get :similarities, on: :member end - resources :initiatives, - concerns: %i[reactable spam_reportable post followable], - defaults: { reactable: 'Initiative', spam_reportable: 'Initiative', post: 'Initiative', followable: 'Initiative' } do - resources :images, defaults: { container_type: 'Initiative' } - resources :files, defaults: { container_type: 'Initiative' } - - resources :initiative_status_changes, shallow: true, except: %i[update destroy] - - get :as_xlsx, on: :collection, action: 'index_xlsx' - get 'by_slug/:slug', on: :collection, to: 'initiatives#by_slug' - get :as_markers, on: :collection, action: 'index_initiative_markers' - get :filter_counts, on: :collection - get :allowed_transitions, on: :member - patch :accept_cosponsorship_invite, on: :member - end - resources :background_jobs, only: %i[index] resources :idea_statuses do @@ -116,7 +100,6 @@ get 'by_slug/:slug', on: :collection, to: 'users#by_slug' get 'by_invite/:token', on: :collection, to: 'users#by_invite' get 'ideas_count', on: :member - get 'initiatives_count', on: :member get 'comments_count', on: :member get 'blocked_count', on: :collection get 'check/:email', on: :collection, to: 'users#check', constraints: { email: /.*/ } @@ -279,8 +262,6 @@ get 'ideas_by_project_as_xlsx' end - get 'initiatives_count', controller: 'stats_initiatives' - with_options controller: 'stats_comments' do get 'comments_count' get 'comments_by_topic' diff --git a/back/engines/commercial/admin_api/app/graphql/admin_api/types/initiative_type.rb b/back/engines/commercial/admin_api/app/graphql/admin_api/types/initiative_type.rb deleted file mode 100644 index 65bf2ec62b8c..000000000000 --- a/back/engines/commercial/admin_api/app/graphql/admin_api/types/initiative_type.rb +++ /dev/null @@ -1,54 +0,0 @@ -# frozen_string_literal: true - -module AdminApi - class Types::InitiativeType < GraphQL::Schema::Object - description 'Single unit of citizen input' - - class InitiativePublicationStatus < GraphQL::Schema::Enum - Initiative::PUBLICATION_STATUSES.each do |ps| - value ps - end - end - - class InitiativeImage < GraphQL::Schema::Object - description 'An image associates with an initiative' - - field :id, ID, null: false - field :ordering, Integer, null: true - field :small_url, String, null: false - def small_url - object.image.versions[:small].url - end - field :medium_url, String, null: false - def medium_url - object.image.versions[:medium].url - end - field :large_url, String, null: false - def large_url - object.image.versions[:large].url - end - field :updated_at, String, null: false - field :created_at, String, null: false - end - - field :id, ID, null: false - field :title_multiloc, Types::MultilocType, null: false - field :slug, String, null: false - field :publication_status, InitiativePublicationStatus, null: false - field :updated_at, String, null: false - field :created_at, String, null: false - field :published_at, String, null: false - field :href, String, null: true - field :likes_count, Integer, null: false - field :comments_count, Integer, null: false - field :internal_comments_count, Integer, null: false - field :images, InitiativeImage.connection_type, null: true - def images - object.initiative_images - end - - def href - Frontend::UrlService.new.model_to_url(object) - end - end -end diff --git a/back/engines/commercial/admin_api/app/graphql/admin_api/types/query_type.rb b/back/engines/commercial/admin_api/app/graphql/admin_api/types/query_type.rb index cc8c3de750b9..623fdc355136 100644 --- a/back/engines/commercial/admin_api/app/graphql/admin_api/types/query_type.rb +++ b/back/engines/commercial/admin_api/app/graphql/admin_api/types/query_type.rb @@ -72,21 +72,6 @@ def public_ideas(args = {}) ideas end - field :initiatives, Types::InitiativeType.connection_type, null: false - - def initiatives - Initiative.all - end - - field :public_initiatives, Types::InitiativeType.connection_type, null: false - - def public_initiatives(_args = {}) - ::InitiativePolicy::Scope.new(nil, Initiative) - .resolve - .includes(:initiative_images) - .published - end - field :user, Types::UserType, null: false do argument :id, ID, required: true end @@ -132,15 +117,6 @@ def idea(args) Idea.find(args[:id]) end - field :initiative, Types::InitiativeType, null: false do - argument :id, ID, required: true - description 'Find an initiative by ID' - end - - def initiative(args) - Initiative.find(args[:id]) - end - field :public_pages, Types::PageType.connection_type, null: false def public_pages ::StaticPagePolicy::Scope.new(nil, StaticPage).resolve diff --git a/back/engines/commercial/admin_api/spec/graphql/initiatives_spec.rb b/back/engines/commercial/admin_api/spec/graphql/initiatives_spec.rb deleted file mode 100644 index 1451fb27ae6f..000000000000 --- a/back/engines/commercial/admin_api/spec/graphql/initiatives_spec.rb +++ /dev/null @@ -1,67 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -# TODO: cleanup-after-proposals-migration -RSpec.describe AdminApi::Schema do - let(:context) { {} } - let(:variables) { {} } - let(:result) do - res = described_class.execute( - query_string, - context: context, - variables: variables - ) - if res['errors'] - pp res - end - res - end - - describe 'publicInitiatives' do - let(:query_string) do - %| - query { - publicInitiatives(first: 5) { - edges { - node { - id - href - titleMultiloc { - en - nlBe - frBe - frFr - } - images(first: 1) { - edges { - node { - smallUrl - } - } - } - likesCount - commentsCount - internalCommentsCount - } - } - } - } - | - end - - it 'returns all public initiatives with fields' do - create_list(:initiative, 5) - create(:initiative, publication_status: 'draft') - response = result - edges = response.dig('data', 'publicInitiatives', 'edges') - expect(edges&.size).to eq 5 - expect(edges&.first&.dig('node', 'id')).to be_present - expect(edges&.first&.dig('node', 'href')).to be_present - expect(edges&.first&.dig('node', 'titleMultiloc')&.values&.compact&.size).to be >= 1 - expect(edges&.first&.dig('node', 'likesCount')).to be_present - expect(edges&.first&.dig('node', 'commentsCount')).to be_present - expect(edges&.first&.dig('node', 'internalCommentsCount')).to be_present - end - end -end diff --git a/back/engines/commercial/analytics/app/services/analytics/populate_dimensions_service.rb b/back/engines/commercial/analytics/app/services/analytics/populate_dimensions_service.rb index 236c1d82c391..7fd955a9571c 100644 --- a/back/engines/commercial/analytics/app/services/analytics/populate_dimensions_service.rb +++ b/back/engines/commercial/analytics/app/services/analytics/populate_dimensions_service.rb @@ -13,12 +13,9 @@ def run def populate_types types = [ { name: 'idea', parent: 'post' }, - { name: 'initiative', parent: 'post' }, { name: 'proposal', parent: 'post' }, - { name: 'comment', parent: 'initiative' }, { name: 'comment', parent: 'idea' }, { name: 'comment', parent: 'proposal' }, - { name: 'reaction', parent: 'initiative' }, { name: 'reaction', parent: 'idea' }, { name: 'reaction', parent: 'comment' }, { name: 'poll', parent: nil }, @@ -43,8 +40,7 @@ def populate_dates tenant_creation = AppConfiguration.instance.created_at first_idea = Idea.order(:created_at).limit(1).pluck(:created_at)[0] - first_initiative = Initiative.order(:created_at).limit(1).pluck(:created_at)[0] - first_activity_date = [tenant_creation, first_idea, first_initiative].compact.min.to_date + first_activity_date = [tenant_creation, first_idea].compact.min.to_date if Analytics::DimensionDate.none? from = first_activity_date diff --git a/back/engines/commercial/analytics/spec/models/analytics/fact_participation_spec.rb b/back/engines/commercial/analytics/spec/models/analytics/fact_participation_spec.rb index 5dafc9046703..493ab1fea6bd 100644 --- a/back/engines/commercial/analytics/spec/models/analytics/fact_participation_spec.rb +++ b/back/engines/commercial/analytics/spec/models/analytics/fact_participation_spec.rb @@ -34,16 +34,6 @@ end end - # TODO: cleanup-after-proposals-migration - context 'when an initiative is created' do - let!(:initiative) { create(:initiative) } - - it 'is also available as a participation fact' do - participation = described_class.find(initiative.id) - expect(participation.dimension_type.name).to eq('initiative') - end - end - context 'when a comment is added' do let!(:comment) { create(:comment) } diff --git a/back/engines/commercial/analytics/spec/services/analytics/populate_dimensions_service_spec.rb b/back/engines/commercial/analytics/spec/services/analytics/populate_dimensions_service_spec.rb index 0a6544482012..044a7ba01b65 100644 --- a/back/engines/commercial/analytics/spec/services/analytics/populate_dimensions_service_spec.rb +++ b/back/engines/commercial/analytics/spec/services/analytics/populate_dimensions_service_spec.rb @@ -3,7 +3,7 @@ require 'rails_helper' describe Analytics::PopulateDimensionsService do - describe 'run with no ideas or initiatives' do + describe 'run with no ideas' do before_all do described_class.run end @@ -21,7 +21,7 @@ end it 'has 20 dimension types' do - expect(Analytics::DimensionType.count).to eq(20) + expect(Analytics::DimensionType.count).to eq(16) end it 'has 5 referrer types' do diff --git a/back/engines/commercial/flag_inappropriate_content/app/services/flag_inappropriate_content/patches/side_fx_initiative_service.rb b/back/engines/commercial/flag_inappropriate_content/app/services/flag_inappropriate_content/patches/side_fx_initiative_service.rb deleted file mode 100644 index 97c0aa60c78e..000000000000 --- a/back/engines/commercial/flag_inappropriate_content/app/services/flag_inappropriate_content/patches/side_fx_initiative_service.rb +++ /dev/null @@ -1,34 +0,0 @@ -# frozen_string_literal: true - -module FlagInappropriateContent - module Patches - module SideFxInitiativeService - SUPPORTED_ATTRS = %i[title_multiloc body_multiloc location_description].freeze - - def after_create(initiative, user) - super - ToxicityDetectionJob.perform_later initiative, attributes: SUPPORTED_ATTRS - end - - def after_update(initiative, user, _old_cosponsor_ids) - # before super to reliably detect attribute changes - atrs = updated_supported_attrs initiative - if atrs.present? - # retry all attributes to consider removing flag - atrs = SUPPORTED_ATTRS if initiative.inappropriate_content_flag - ToxicityDetectionJob.perform_later initiative, attributes: atrs - end - - super - end - - private - - def updated_supported_attrs(initiative) - SUPPORTED_ATTRS.select do |atr| - initiative.saved_change_to_attribute? atr - end - end - end - end -end diff --git a/back/engines/commercial/flag_inappropriate_content/spec/acceptance/initiatives_spec.rb b/back/engines/commercial/flag_inappropriate_content/spec/acceptance/initiatives_spec.rb deleted file mode 100644 index 428e736a6999..000000000000 --- a/back/engines/commercial/flag_inappropriate_content/spec/acceptance/initiatives_spec.rb +++ /dev/null @@ -1,74 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' -require 'rspec_api_documentation/dsl' - -# TODO: cleanup-after-proposals-migration -resource 'Initiatives' do - before do - header 'Content-Type', 'application/json' - @user = create(:user) - header_token_for @user - - create(:initiative_status, code: 'proposed') - end - - post 'web_api/v1/initiatives' do - with_options scope: :initiative do - parameter :publication_status - parameter :title_multiloc - parameter :body_multiloc - parameter :location_description - end - - let(:initiative) { build(:initiative) } - let(:publication_status) { 'published' } - let(:title_multiloc) { initiative.title_multiloc } - let(:body_multiloc) { initiative.body_multiloc } - - example 'Toxicity detection job is enqueued when creating an initiative', document: false do - SettingsService.new.activate_feature! 'moderation' - SettingsService.new.activate_feature! 'flag_inappropriate_content' - expect do - do_request - end.to have_enqueued_job(ToxicityDetectionJob) - end - end - - patch 'web_api/v1/initiatives/:id' do - before do - SettingsService.new.activate_feature! 'moderation' - SettingsService.new.activate_feature! 'flag_inappropriate_content' - @initiative = create(:initiative, author: @user) - end - - with_options scope: :initiative do - parameter :title_multiloc - parameter :body_multiloc - parameter :location_description - parameter :topic_ids - end - - let(:id) { @initiative.id } - - describe do - let(:location_description) { 'Watkins Road 8' } - - example 'Toxicity detection job is enqueued when updating an initiative\'s title', document: false do - expect do - do_request - end.to have_enqueued_job(ToxicityDetectionJob).with(@initiative, attributes: [:location_description]) - end - end - - describe do - let(:topic_ids) { [create(:topic).id] } - - example 'No toxicity detection job is enqueued when updating initiative attributes without text', document: false do - expect do - do_request - end.not_to have_enqueued_job(ToxicityDetectionJob) - end - end - end -end diff --git a/back/engines/commercial/machine_translations/app/controllers/machine_translations/web_api/v1/machine_translations_controller.rb b/back/engines/commercial/machine_translations/app/controllers/machine_translations/web_api/v1/machine_translations_controller.rb index c39099e30117..b724b51e4035 100644 --- a/back/engines/commercial/machine_translations/app/controllers/machine_translations/web_api/v1/machine_translations_controller.rb +++ b/back/engines/commercial/machine_translations/app/controllers/machine_translations/web_api/v1/machine_translations_controller.rb @@ -11,10 +11,6 @@ class MachineTranslationsController < ApplicationController translatable_class: Idea, translatable_id: :idea_id }, - 'Initiative' => { - translatable_class: Initiative, - translatable_id: :initiative_id - }, 'Comment' => { translatable_class: Comment, translatable_id: :comment_id diff --git a/back/engines/commercial/machine_translations/config/routes.rb b/back/engines/commercial/machine_translations/config/routes.rb index e0f7df62a830..e5aa47c5db0f 100644 --- a/back/engines/commercial/machine_translations/config/routes.rb +++ b/back/engines/commercial/machine_translations/config/routes.rb @@ -6,9 +6,6 @@ defaults translatable_type: 'Idea' do get 'ideas/:idea_id/machine_translation', action: :show, controller: 'machine_translations' end - defaults translatable_type: 'Initiative' do - get 'initiatives/:initiative_id/machine_translation', action: :show, controller: 'machine_translations' - end defaults translatable_type: 'Comment' do get 'comments/:comment_id/machine_translation', action: :show, controller: 'machine_translations' end diff --git a/back/engines/commercial/machine_translations/spec/acceptance/machine_translations_spec.rb b/back/engines/commercial/machine_translations/spec/acceptance/machine_translations_spec.rb index 409347b9a1ae..406eb1bf6e19 100644 --- a/back/engines/commercial/machine_translations/spec/acceptance/machine_translations_spec.rb +++ b/back/engines/commercial/machine_translations/spec/acceptance/machine_translations_spec.rb @@ -80,55 +80,6 @@ end end - # TODO: cleanup-after-proposals-migration - get '/web_api/v1/initiatives/:initiative_id/machine_translation' do - with_options scope: :machine_translation do - parameter :attribute_name, 'The name of the attribute to translate (e.g. title_multiloc)' - parameter :locale_to, 'The locale to translate to' - end - - describe 'Get one machine translation:' do - let(:translation) { create(:machine_translation, translatable: create(:initiative, title_multiloc: { 'nl-BE' => 'Fietssnelweg doorheen het stadcentrum' })) } - let(:initiative_id) { translation.translatable_id } - let(:attribute_name) { translation.attribute_name } - let(:locale_to) { translation.locale_to } - - example_request 'Return an up to date translation if it has already been created' do - expect(status).to eq 200 - json_response = json_parse response_body - expect(json_response.dig(:data, :attributes, :translation)).to eq translation.translation - end - - example 'Update the translation if it has already been created but the original text might have changed' do - prev_updated_at = translation.updated_at - translation.translatable.update! title_multiloc: { 'nl-BE' => 'Fietssnelweg doorheen elke deelgemeente' } - do_request - expect(status).to eq 200 - expect(translation.reload.updated_at).to be > prev_updated_at - end - - example '[error] Create a translation for an unknown resource' do - translation.translatable.destroy! - do_request - expect(status).to eq 404 - end - end - - describe do - let(:translation) { nil } - let(:initiative_id) { create(:initiative).id } - let(:attribute_name) { 'title_multiloc' } - let(:locale_to) { 'en' } - - example_request 'A new machine translation is created when the translation was never done before' do - expect(status).to eq 200 - json_response = json_parse response_body - expect(json_response.dig(:data, :attributes, :attribute_name)).to eq attribute_name - expect(json_response.dig(:data, :relationships, :translatable, :data, :id)).to eq initiative_id - end - end - end - get '/web_api/v1/comments/:comment_id/machine_translation' do with_options scope: :machine_translation do parameter :attribute_name, 'The name of the attribute to translate (e.g. title_multiloc)' diff --git a/back/engines/commercial/multi_tenancy/db/seeds/initiatives.rb b/back/engines/commercial/multi_tenancy/db/seeds/initiatives.rb deleted file mode 100644 index 5cbd84219395..000000000000 --- a/back/engines/commercial/multi_tenancy/db/seeds/initiatives.rb +++ /dev/null @@ -1,56 +0,0 @@ -# frozen_string_literal: true - -require_relative 'base' - -module MultiTenancy - module Seeds - class Initiatives < Base - def run - runner.num_initiatives.times do - created_at = Faker::Date.between(from: Tenant.current.created_at, to: Time.zone.now) - initiative = Initiative.create!( - title_multiloc: runner.create_for_some_locales { Faker::Lorem.sentence[0...80] }, - body_multiloc: runner.rand_description_multiloc, - author: User.offset(rand(User.count)).first, - publication_status: 'published', - published_at: Faker::Date.between(from: created_at, to: Time.zone.now), - created_at: created_at, - location_point: rand(3) == 0 ? nil : "POINT(#{runner.map_center[1] + (((rand * 2) - 1) * runner.map_offset)} #{runner.map_center[0] + (((rand * 2) - 1) * runner.map_offset)})", - location_description: rand(2) == 0 ? nil : Faker::Address.street_address, - header_bg: rand(25) == 0 ? nil : Rails.root.join("spec/fixtures/image#{rand(20)}.png").open, - topics: Array.new(rand(3)) { rand(Topic.count) }.uniq.map { |offset| Topic.offset(offset).first }, - areas: Array.new(rand(3)) { rand(Area.count) }.uniq.map { |offset| Area.offset(offset).first }, - assignee: rand(5) == 0 ? User.admin.sample : nil - ) - - [1, 1, 2, 2, 3][rand(5)].times do |_i| - initiative.initiative_images.create!(image: Rails.root.join("spec/fixtures/image#{rand(20)}.png").open) - end - if rand(5) == 0 - rand(1..3).times do - initiative.initiative_files.create!(runner.generate_file_attributes) - end - end - - User.all.each do |u| - r = rand(5) - if r < 2 - Reaction.create!(reactable: initiative, user: u, mode: 'up', - created_at: Faker::Date.between(from: initiative.published_at, to: Time.zone.now)) - end - end - - rand(5).times do - initiative.official_feedbacks.create!( - body_multiloc: runner.rand_description_multiloc, - author_multiloc: runner.create_for_some_locales { Faker::FunnyName.name }, - user: User.admin.sample - ) - end - - runner.create_comment_tree(initiative, nil) - end - end - end - end -end diff --git a/back/engines/commercial/multi_tenancy/db/seeds/runner.rb b/back/engines/commercial/multi_tenancy/db/seeds/runner.rb index 4ff5ab51c17d..17cc1b61d41a 100644 --- a/back/engines/commercial/multi_tenancy/db/seeds/runner.rb +++ b/back/engines/commercial/multi_tenancy/db/seeds/runner.rb @@ -16,7 +16,6 @@ require_relative 'followers' require_relative 'groups' require_relative 'ideas' -require_relative 'initiatives' require_relative 'internal_comments' require_relative 'invites' require_relative 'permissions' @@ -44,20 +43,17 @@ class Runner small: { num_users: 5, num_projects: 1, - num_ideas: 4, - num_initiatives: 3 + num_ideas: 4 }, medium: { num_users: 10, num_projects: 5, - num_ideas: 15, - num_initiatives: 10 + num_ideas: 15 }, large: { num_users: 50, num_projects: 20, - num_ideas: 100, - num_initiatives: 60 + num_ideas: 100 } }.freeze @@ -111,7 +107,6 @@ def seed_localhost_tenant MultiTenancy::Seeds::ProjectFolders.new(runner: self).run MultiTenancy::Seeds::Projects.new(runner: self).run MultiTenancy::Seeds::Ideas.new(runner: self).run - MultiTenancy::Seeds::Initiatives.new(runner: self).run MultiTenancy::Seeds::InternalComments.new(runner: self).run InitiativeStatusService.new.automated_transitions! @@ -150,11 +145,6 @@ def num_ideas SEED_SIZES.dig(seed_size, :num_ideas) end - # @return [Integer] Number of iniatives to be seeded - def num_initiatives - SEED_SIZES.dig(seed_size, :num_initiatives) - end - # @return [Float] default map center def map_center MAP_CENTER diff --git a/back/engines/commercial/public_api/app/controllers/public_api/v2/initiatives_controller.rb b/back/engines/commercial/public_api/app/controllers/public_api/v2/initiatives_controller.rb deleted file mode 100644 index 79be12f1a4cf..000000000000 --- a/back/engines/commercial/public_api/app/controllers/public_api/v2/initiatives_controller.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -module PublicApi - class V2::InitiativesController < PublicApiController - include DeletedItemsAction - - def index - initiatives = Initiative - .order(created_at: :desc) - .page(params[:page_number]) - .per(num_per_page) - - initiatives = common_date_filters(initiatives) - - render json: initiatives, - each_serializer: V2::InitiativeSerializer, - adapter: :json, - meta: meta_properties(initiatives) - end - - def show - show_item Initiative.find(params[:id]), V2::InitiativeSerializer - end - end -end diff --git a/back/engines/commercial/public_api/config/routes.rb b/back/engines/commercial/public_api/config/routes.rb index 6c6593eedf26..c7e359e4b6af 100644 --- a/back/engines/commercial/public_api/config/routes.rb +++ b/back/engines/commercial/public_api/config/routes.rb @@ -26,7 +26,6 @@ route_mapper.resources :events route_mapper.resources :event_attendances route_mapper.resources :ideas - route_mapper.resources :initiatives route_mapper.resources :phases route_mapper.resources :project_folders route_mapper.resources :reactions, only: %i[index] diff --git a/back/engines/commercial/public_api/spec/acceptance/v2/initiatives_spec.rb b/back/engines/commercial/public_api/spec/acceptance/v2/initiatives_spec.rb deleted file mode 100644 index e97199abb191..000000000000 --- a/back/engines/commercial/public_api/spec/acceptance/v2/initiatives_spec.rb +++ /dev/null @@ -1,89 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' -require 'rspec_api_documentation/dsl' -require './engines/commercial/public_api/spec/acceptance/v2/support/shared' - -# TODO: cleanup-after-proposals-migration -resource 'Posts' do - # NOTE: Same name as in ideas_spec to combine the documentation into the same section - explanation <<~DESC.squish - Posts are written inputs created by citizens. These can be ideas, initiatives - (proposals), or survey responses. - DESC - - include_context 'common_auth' - - let!(:initiatives) { create_list(:initiative, 5, created_at: '2020-01-01') } - - get '/api/v2/initiatives/' do - route_summary 'List initiatives - known as proposals in the platform' - route_description <<~DESC.squish - Retrieve a paginated list of all the initiatives in the platform, with the most - recent ones appearing first. - DESC - - include_context 'common_list_params' - - context 'when the page size is smaller than the total number of initiatives' do - let(:page_size) { 2 } - - example_request 'List only the first initiatives' do - assert_status 200 - expect(json_response_body[:initiatives].size).to eq(page_size) - - total_pages = (initiatives.size.to_f / page_size).ceil - expect(json_response_body[:meta]).to eq({ total_pages: total_pages, current_page: 1 }) - end - end - - include_examples 'filtering_by_date', :initiative, :created_at - include_examples 'filtering_by_date', :initiative, :updated_at - end - - get '/api/v2/initiatives/:id' do - route_summary 'Get an initiative (proposal)' - route_description 'Retrieve a single initiative (proposal) by its ID.' - - include_context 'common_item_params' - - let(:initiative) { initiatives.first } - let(:id) { initiative.id } - - before do - # NOTE: Temp fix until locales of factories and tenants are consistent - # Currently, the tenant locales are ["en", "fr-FR", "nl-NL"], while the factory - # locales are ["en", "nl-BE"]. The following code aligns the two by replacing - # the "nl-BE" locale with "nl-NL" in the initiative. - title = initiative[:title_multiloc] - title['nl-NL'] = title.delete 'nl-BE' - initiative.update!(title_multiloc: title) - end - - example_request 'Return the initiative in the default locale' do - assert_status 200 - expect(json_response_body[:initiative]).to include({ id: id }) - expect(json_response_body.dig(:initiative, :threshold_reached_at)).to be_nil - end - - context 'when requesting the initiative in a specific locale' do - let(:locale) { 'nl-NL' } - - example_request 'Return the initiative in the requested locale', document: false do - assert_status 200 - expect(json_response_body.dig(:initiative, :title)).to eq initiative.title_multiloc['nl-NL'] - end - end - - context 'when initiative is successful' do - let(:initiative) { create(:initiative, initiative_status: create(:initiative_status_threshold_reached)) } - - example_request 'Return the initiative with successful date', document: false do - assert_status 200 - expect(json_response_body.dig(:initiative, :threshold_reached_at)).not_to be_nil - end - end - end - - include_examples '/api/v2/.../deleted', :initiatives -end diff --git a/back/engines/free/email_campaigns/app/mailers/email_campaigns/comment_on_initiative_you_follow_mailer.rb b/back/engines/free/email_campaigns/app/mailers/email_campaigns/comment_on_initiative_you_follow_mailer.rb deleted file mode 100644 index 3888e0ef4fa4..000000000000 --- a/back/engines/free/email_campaigns/app/mailers/email_campaigns/comment_on_initiative_you_follow_mailer.rb +++ /dev/null @@ -1,30 +0,0 @@ -# frozen_string_literal: true - -module EmailCampaigns - class CommentOnInitiativeYouFollowMailer < ApplicationMailer - protected - - def subject - format_message('subject', values: { organizationName: organization_name, authorName: event.comment_author_name }) - end - - def header_title - format_message('main_header', values: { commentAuthor: event.comment_author_name }) - end - - def header_message - format_message( - 'event_description', - values: { - authorNameFull: event.comment_author_name, - authorName: event.initiating_user_first_name, - initiativeTitle: localize_for_recipient(event.post_title_multiloc) - } - ) - end - - def preheader - format_message('preheader', values: { organizationName: organization_name, authorName: event.comment_author_name }) - end - end -end diff --git a/back/engines/free/email_campaigns/app/mailers/email_campaigns/cosponsor_of_your_initiative_mailer.rb b/back/engines/free/email_campaigns/app/mailers/email_campaigns/cosponsor_of_your_initiative_mailer.rb deleted file mode 100644 index fc73e3166a6c..000000000000 --- a/back/engines/free/email_campaigns/app/mailers/email_campaigns/cosponsor_of_your_initiative_mailer.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -module EmailCampaigns - class CosponsorOfYourInitiativeMailer < ApplicationMailer - protected - - def subject - format_message('subject', values: { cosponsorName: event.post_cosponsor_name }) - end - - def header_title - format_message('main_header', values: { cosponsorName: event.post_cosponsor_name }) - end - - private - - def header_message - format_message('event_description_initiative', values: { cosponsorName: event.post_cosponsor_name }) - end - - def preheader - format_message('preheader_initiative', values: { cosponsorName: event.post_cosponsor_name }) - end - end -end diff --git a/back/engines/free/email_campaigns/app/mailers/email_campaigns/initiative_assigned_to_you_mailer.rb b/back/engines/free/email_campaigns/app/mailers/email_campaigns/initiative_assigned_to_you_mailer.rb deleted file mode 100644 index 85b45b1d7e1f..000000000000 --- a/back/engines/free/email_campaigns/app/mailers/email_campaigns/initiative_assigned_to_you_mailer.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -module EmailCampaigns - class InitiativeAssignedToYouMailer < ApplicationMailer - protected - - def subject - format_message('subject', values: { organizationName: organization_name }) - end - - private - - def header_title - format_message('main_header', values: { firstName: recipient_first_name }) - end - - def header_message - format_message('event_description_initiative') - end - - def preheader - format_message('preheader_initiative', values: { organizationName: organization_name, authorName: event.post_author_name }) - end - end -end diff --git a/back/engines/free/email_campaigns/app/mailers/email_campaigns/initiative_marked_as_spam_mailer.rb b/back/engines/free/email_campaigns/app/mailers/email_campaigns/initiative_marked_as_spam_mailer.rb deleted file mode 100644 index f5184b8460eb..000000000000 --- a/back/engines/free/email_campaigns/app/mailers/email_campaigns/initiative_marked_as_spam_mailer.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -module EmailCampaigns - class InitiativeMarkedAsSpamMailer < ApplicationMailer - protected - - def subject - format_message('subject', values: { organizationName: organization_name }) - end - - private - - def header_title - format_message('title_spam_report', values: { firstName: event.initiating_user_first_name, lastName: event.initiating_user_last_name }) - end - - def header_message - false - end - - def preheader - format_message('preheader') - end - end -end diff --git a/back/engines/free/email_campaigns/app/mailers/email_campaigns/initiative_published_mailer.rb b/back/engines/free/email_campaigns/app/mailers/email_campaigns/initiative_published_mailer.rb deleted file mode 100644 index 88a29be4f472..000000000000 --- a/back/engines/free/email_campaigns/app/mailers/email_campaigns/initiative_published_mailer.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -module EmailCampaigns - class InitiativePublishedMailer < ApplicationMailer - protected - - def subject - format_message('subject', values: { organizationName: organization_name }) - end - - private - - def header_title - format_message('main_header') - end - - def header_message - format_message('message_next_steps', values: { userFirstName: recipient_first_name, organizationName: organization_name }) - end - - def preheader - format_message('preheader', values: { initiativeTitle: localize_for_recipient(event.post_title_multiloc), organizationName: organization_name }) - end - end -end diff --git a/back/engines/free/email_campaigns/app/mailers/email_campaigns/initiative_resubmitted_for_review_mailer.rb b/back/engines/free/email_campaigns/app/mailers/email_campaigns/initiative_resubmitted_for_review_mailer.rb deleted file mode 100644 index b82ac46ce727..000000000000 --- a/back/engines/free/email_campaigns/app/mailers/email_campaigns/initiative_resubmitted_for_review_mailer.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -module EmailCampaigns - class InitiativeResubmittedForReviewMailer < ApplicationMailer - protected - - def subject - format_message('subject', values: { organizationName: organization_name }) - end - - private - - def header_title - format_message('main_header', values: { firstName: recipient_first_name }) - end - - def header_message - format_message('event_description_initiative') - end - - def preheader - format_message('preheader_initiative', values: { organizationName: organization_name, authorName: event.post_author_name }) - end - end -end diff --git a/back/engines/free/email_campaigns/app/mailers/email_campaigns/internal_comment_on_initiative_assigned_to_you_mailer.rb b/back/engines/free/email_campaigns/app/mailers/email_campaigns/internal_comment_on_initiative_assigned_to_you_mailer.rb deleted file mode 100644 index 7ee697d6d952..000000000000 --- a/back/engines/free/email_campaigns/app/mailers/email_campaigns/internal_comment_on_initiative_assigned_to_you_mailer.rb +++ /dev/null @@ -1,6 +0,0 @@ -# frozen_string_literal: true - -module EmailCampaigns - class InternalCommentOnInitiativeAssignedToYouMailer < BaseInternalCommentMailer - end -end diff --git a/back/engines/free/email_campaigns/app/mailers/email_campaigns/internal_comment_on_initiative_you_commented_internally_on_mailer.rb b/back/engines/free/email_campaigns/app/mailers/email_campaigns/internal_comment_on_initiative_you_commented_internally_on_mailer.rb deleted file mode 100644 index 433d8d883d52..000000000000 --- a/back/engines/free/email_campaigns/app/mailers/email_campaigns/internal_comment_on_initiative_you_commented_internally_on_mailer.rb +++ /dev/null @@ -1,6 +0,0 @@ -# frozen_string_literal: true - -module EmailCampaigns - class InternalCommentOnInitiativeYouCommentedInternallyOnMailer < BaseInternalCommentMailer - end -end diff --git a/back/engines/free/email_campaigns/app/mailers/email_campaigns/internal_comment_on_unassigned_initiative_mailer.rb b/back/engines/free/email_campaigns/app/mailers/email_campaigns/internal_comment_on_unassigned_initiative_mailer.rb deleted file mode 100644 index 6e193e272dac..000000000000 --- a/back/engines/free/email_campaigns/app/mailers/email_campaigns/internal_comment_on_unassigned_initiative_mailer.rb +++ /dev/null @@ -1,6 +0,0 @@ -# frozen_string_literal: true - -module EmailCampaigns - class InternalCommentOnUnassignedInitiativeMailer < BaseInternalCommentMailer - end -end diff --git a/back/engines/free/email_campaigns/app/mailers/email_campaigns/invitation_to_cosponsor_initiative_mailer.rb b/back/engines/free/email_campaigns/app/mailers/email_campaigns/invitation_to_cosponsor_initiative_mailer.rb deleted file mode 100644 index ec1ccb891097..000000000000 --- a/back/engines/free/email_campaigns/app/mailers/email_campaigns/invitation_to_cosponsor_initiative_mailer.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -module EmailCampaigns - class InvitationToCosponsorInitiativeMailer < ApplicationMailer - protected - - def subject - format_message('subject') - end - - def header_title - format_message('main_header') - end - - private - - def header_message - format_message('event_description_initiative', values: { authorName: event.post_author_name }) - end - - def preheader - format_message('preheader_initiative', values: { authorName: event.post_author_name }) - end - end -end diff --git a/back/engines/free/email_campaigns/app/mailers/email_campaigns/new_initiative_for_admin_mailer.rb b/back/engines/free/email_campaigns/app/mailers/email_campaigns/new_initiative_for_admin_mailer.rb deleted file mode 100644 index f84102d7eea8..000000000000 --- a/back/engines/free/email_campaigns/app/mailers/email_campaigns/new_initiative_for_admin_mailer.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -module EmailCampaigns - class NewInitiativeForAdminMailer < ApplicationMailer - protected - - def subject - format_message('subject', values: { organizationName: organization_name }) - end - - private - - def header_title - format_message('main_header', values: { firstName: recipient_first_name }) - end - - def header_message - format_message('event_description', values: { authorName: event.post_author_name, organizationName: organization_name }) - end - - def preheader - format_message('preheader', values: { authorName: event.post_author_name, organizationName: organization_name }) - end - end -end diff --git a/back/engines/free/email_campaigns/app/mailers/email_campaigns/official_feedback_on_initiative_you_follow_mailer.rb b/back/engines/free/email_campaigns/app/mailers/email_campaigns/official_feedback_on_initiative_you_follow_mailer.rb deleted file mode 100644 index 058398f9070b..000000000000 --- a/back/engines/free/email_campaigns/app/mailers/email_campaigns/official_feedback_on_initiative_you_follow_mailer.rb +++ /dev/null @@ -1,36 +0,0 @@ -# frozen_string_literal: true - -module EmailCampaigns - class OfficialFeedbackOnInitiativeYouFollowMailer < ApplicationMailer - private - - helper_method :author_name - - def author_name - localize_for_recipient(event.official_feedback_author_multiloc) - end - - def preheader - format_message('preheader') - end - - def subject - format_message('subject', values: { organizationName: organization_name }) - end - - def header_title - format_message('header_title', values: { officialName: organization_name }) - end - - def header_message - format_message( - 'header_message', - values: { - initiativeTitle: localize_for_recipient(event.post_title_multiloc), - officialName: localize_for_recipient(event.official_feedback_author_multiloc), - organizationName: organization_name - } - ) - end - end -end diff --git a/back/engines/free/email_campaigns/app/mailers/email_campaigns/status_change_on_initiative_you_follow_mailer.rb b/back/engines/free/email_campaigns/app/mailers/email_campaigns/status_change_on_initiative_you_follow_mailer.rb deleted file mode 100644 index ff92361291e7..000000000000 --- a/back/engines/free/email_campaigns/app/mailers/email_campaigns/status_change_on_initiative_you_follow_mailer.rb +++ /dev/null @@ -1,29 +0,0 @@ -# frozen_string_literal: true - -module EmailCampaigns - class StatusChangeOnInitiativeYouFollowMailer < ApplicationMailer - private - - def preheader - format_message('preheader') - end - - def subject - format_message('subject') - end - - def header_title - format_message('header_title', values: { organizationName: organization_name }) - end - - def header_message - format_message( - 'header_message', - values: { - initiativeTitle: localize_for_recipient(event.post_title_multiloc), - organizationName: organization_name - } - ) - end - end -end diff --git a/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/comment_on_initiative_you_follow.rb b/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/comment_on_initiative_you_follow.rb deleted file mode 100644 index c10b7a3d45c3..000000000000 --- a/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/comment_on_initiative_you_follow.rb +++ /dev/null @@ -1,89 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: email_campaigns_campaigns -# -# id :uuid not null, primary key -# type :string not null -# author_id :uuid -# enabled :boolean -# sender :string -# reply_to :string -# schedule :jsonb -# subject_multiloc :jsonb -# body_multiloc :jsonb -# created_at :datetime not null -# updated_at :datetime not null -# deliveries_count :integer default(0), not null -# context_id :uuid -# -# Indexes -# -# index_email_campaigns_campaigns_on_author_id (author_id) -# index_email_campaigns_campaigns_on_context_id (context_id) -# index_email_campaigns_campaigns_on_type (type) -# -# Foreign Keys -# -# fk_rails_... (author_id => users.id) -# -module EmailCampaigns - class Campaigns::CommentOnInitiativeYouFollow < Campaign - include Consentable - include Disableable - include ActivityTriggerable - include RecipientConfigurable - include LifecycleStageRestrictable - include Trackable - allow_lifecycle_stages only: %w[trial active] - - recipient_filter :filter_notification_recipient - - def mailer_class - CommentOnInitiativeYouFollowMailer - end - - def activity_triggers - { 'Notifications::CommentOnInitiativeYouFollow' => { 'created' => true } } - end - - def filter_notification_recipient(users_scope, activity:, time: nil) - users_scope.where(id: activity.item.recipient.id) - end - - def self.recipient_role_multiloc_key - 'email_campaigns.admin_labels.recipient_role.registered_users' - end - - def self.recipient_segment_multiloc_key - 'email_campaigns.admin_labels.recipient_segment.users_who_follow_the_proposal' - end - - def self.content_type_multiloc_key - 'email_campaigns.admin_labels.content_type.proposals' - end - - def self.trigger_multiloc_key - 'email_campaigns.admin_labels.trigger.user_comments_on_proposal' - end - - def generate_commands(recipient:, activity:, time: nil) - notification = activity.item - name_service = UserDisplayNameService.new(AppConfiguration.instance, recipient) - [{ - event_payload: { - initiating_user_first_name: notification.initiating_user&.first_name, - initiating_user_last_name: name_service.last_name!(notification.initiating_user), - comment_author_name: name_service.display_name!(notification.comment.author), - comment_body_multiloc: notification.comment.body_multiloc, - comment_url: Frontend::UrlService.new.model_to_url(notification.comment, locale: Locale.new(recipient.locale)), - post_published_at: notification.post.published_at.iso8601, - post_title_multiloc: notification.post.title_multiloc, - post_author_name: name_service.display_name!(notification.post.author), - unfollow_url: Frontend::UrlService.new.unfollow_url(Follower.new(followable: notification.post, user: recipient)) - } - }] - end - end -end diff --git a/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/cosponsor_of_your_initiative.rb b/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/cosponsor_of_your_initiative.rb deleted file mode 100644 index e70a52021bc0..000000000000 --- a/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/cosponsor_of_your_initiative.rb +++ /dev/null @@ -1,95 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: email_campaigns_campaigns -# -# id :uuid not null, primary key -# type :string not null -# author_id :uuid -# enabled :boolean -# sender :string -# reply_to :string -# schedule :jsonb -# subject_multiloc :jsonb -# body_multiloc :jsonb -# created_at :datetime not null -# updated_at :datetime not null -# deliveries_count :integer default(0), not null -# context_id :uuid -# -# Indexes -# -# index_email_campaigns_campaigns_on_author_id (author_id) -# index_email_campaigns_campaigns_on_context_id (context_id) -# index_email_campaigns_campaigns_on_type (type) -# -# Foreign Keys -# -# fk_rails_... (author_id => users.id) -# -module EmailCampaigns - class Campaigns::CosponsorOfYourInitiative < Campaign - include Consentable - include ActivityTriggerable - include RecipientConfigurable - include Disableable - include Trackable - include LifecycleStageRestrictable - allow_lifecycle_stages only: %w[trial active] - - recipient_filter :filter_notification_recipient - - def mailer_class - CosponsorOfYourInitiativeMailer - end - - def activity_triggers - { 'Notifications::CosponsorOfYourInitiative' => { 'created' => true } } - end - - def filter_notification_recipient(users_scope, activity:, time: nil) - users_scope.where(id: activity.item.recipient.id) - end - - def self.recipient_role_multiloc_key - 'email_campaigns.admin_labels.recipient_role.registered_users' - end - - def self.recipient_segment_multiloc_key - 'email_campaigns.admin_labels.recipient_segment.user_who_published_the_proposal' - end - - def self.content_type_multiloc_key - 'email_campaigns.admin_labels.content_type.proposals' - end - - def self.trigger_multiloc_key - 'email_campaigns.admin_labels.trigger.user_accepts_invitation_to_cosponsor_a_proposal' - end - - def generate_commands(recipient:, activity:) - initiative = activity.item.post - cosponsor = activity.item.initiating_user - name_service = UserDisplayNameService.new(AppConfiguration.instance, recipient) - - [{ - event_payload: { - post_title_multiloc: initiative.title_multiloc, - post_body_multiloc: initiative.body_multiloc, - post_author_name: name_service.display_name!(initiative.author), - post_cosponsor_name: name_service.display_name!(cosponsor), - post_url: Frontend::UrlService.new.model_to_url(initiative, locale: Locale.new(recipient.locale)), - post_image_medium_url: post_image_medium_url(initiative) - } - }] - end - - private - - def post_image_medium_url(initiative) - image = initiative&.initiative_images&.first - image.image.versions[:medium].url if image&.image&.versions - end - end -end diff --git a/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/initiative_assigned_to_you.rb b/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/initiative_assigned_to_you.rb deleted file mode 100644 index 507bb010407e..000000000000 --- a/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/initiative_assigned_to_you.rb +++ /dev/null @@ -1,91 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: email_campaigns_campaigns -# -# id :uuid not null, primary key -# type :string not null -# author_id :uuid -# enabled :boolean -# sender :string -# reply_to :string -# schedule :jsonb -# subject_multiloc :jsonb -# body_multiloc :jsonb -# created_at :datetime not null -# updated_at :datetime not null -# deliveries_count :integer default(0), not null -# context_id :uuid -# -# Indexes -# -# index_email_campaigns_campaigns_on_author_id (author_id) -# index_email_campaigns_campaigns_on_context_id (context_id) -# index_email_campaigns_campaigns_on_type (type) -# -# Foreign Keys -# -# fk_rails_... (author_id => users.id) -# -module EmailCampaigns - class Campaigns::InitiativeAssignedToYou < Campaign - include Consentable - include ActivityTriggerable - include Disableable - include Trackable - include LifecycleStageRestrictable - allow_lifecycle_stages only: %w[trial active] - - recipient_filter :filter_notification_recipient - - def self.consentable_roles - ['admin'] - end - - def mailer_class - InitiativeAssignedToYouMailer - end - - def activity_triggers - { 'Notifications::InitiativeAssignedToYou' => { 'created' => true } } - end - - def filter_notification_recipient(users_scope, activity:, time: nil) - users_scope.where(id: activity.item.recipient.id) - end - - def self.recipient_role_multiloc_key - 'email_campaigns.admin_labels.recipient_role.admins_and_managers' - end - - def self.recipient_segment_multiloc_key - 'email_campaigns.admin_labels.recipient_segment.admins_assigned_to_a_proposal' - end - - def self.content_type_multiloc_key - 'email_campaigns.admin_labels.content_type.proposals' - end - - def self.trigger_multiloc_key - 'email_campaigns.admin_labels.trigger.proposal_is_assigned_to_admin' - end - - def generate_commands(recipient:, activity:, time: nil) - notification = activity.item - name_service = UserDisplayNameService.new(AppConfiguration.instance, recipient) - [{ - event_payload: { - post_title_multiloc: notification.post.title_multiloc, - post_body_multiloc: notification.post.body_multiloc, - post_author_name: name_service.display_name!(notification.post.author), - post_published_at: notification.post.published_at.iso8601, - post_url: Frontend::UrlService.new.model_to_url(notification.post, locale: Locale.new(recipient.locale)), - post_assigned_at: notification.post.assigned_at&.iso8601, - initiative_reactions_needed: notification.post.reactions_needed, - initiative_expires_at: notification.post.expires_at.iso8601 - } - }] - end - end -end diff --git a/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/initiative_marked_as_spam.rb b/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/initiative_marked_as_spam.rb deleted file mode 100644 index 9029de537cfd..000000000000 --- a/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/initiative_marked_as_spam.rb +++ /dev/null @@ -1,93 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: email_campaigns_campaigns -# -# id :uuid not null, primary key -# type :string not null -# author_id :uuid -# enabled :boolean -# sender :string -# reply_to :string -# schedule :jsonb -# subject_multiloc :jsonb -# body_multiloc :jsonb -# created_at :datetime not null -# updated_at :datetime not null -# deliveries_count :integer default(0), not null -# context_id :uuid -# -# Indexes -# -# index_email_campaigns_campaigns_on_author_id (author_id) -# index_email_campaigns_campaigns_on_context_id (context_id) -# index_email_campaigns_campaigns_on_type (type) -# -# Foreign Keys -# -# fk_rails_... (author_id => users.id) -# -module EmailCampaigns - class Campaigns::InitiativeMarkedAsSpam < Campaign - include Consentable - include ActivityTriggerable - include RecipientConfigurable - include Disableable - include LifecycleStageRestrictable - include Trackable - allow_lifecycle_stages only: %w[trial active] - - recipient_filter :filter_notification_recipient - - def self.consentable_roles - ['admin'] - end - - def mailer_class - InitiativeMarkedAsSpamMailer - end - - def activity_triggers - { 'Notifications::InitiativeMarkedAsSpam' => { 'created' => true } } - end - - def filter_notification_recipient(users_scope, activity:, time: nil) - users_scope.where(id: activity.item.recipient.id) - end - - def self.recipient_role_multiloc_key - 'email_campaigns.admin_labels.recipient_role.admins_and_managers' - end - - def self.recipient_segment_multiloc_key - 'email_campaigns.admin_labels.recipient_segment.admins' - end - - def self.content_type_multiloc_key - 'email_campaigns.admin_labels.content_type.proposals' - end - - def self.trigger_multiloc_key - 'email_campaigns.admin_labels.trigger.proposal_gets_reported_as_spam' - end - - def generate_commands(recipient:, activity:, time: nil) - notification = activity.item - [{ - event_payload: { - initiating_user_first_name: notification.initiating_user&.first_name, - initiating_user_last_name: notification.initiating_user&.last_name, - post_created_at: notification.post.created_at.iso8601, - post_title_multiloc: notification.post.title_multiloc, - post_author_name: notification.post.author_name, - post_url: Frontend::UrlService.new.model_to_url(notification.post, locale: Locale.new(recipient.locale)), - initiative_reactions_needed: notification.post.reactions_needed, - initiative_expires_at: notification.post.expires_at.iso8601, - spam_report_reason_code: notification.spam_report.reason_code, - spam_report_other_reason: notification.spam_report.other_reason - } - }] - end - end -end diff --git a/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/initiative_published.rb b/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/initiative_published.rb deleted file mode 100644 index b2640b2897e5..000000000000 --- a/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/initiative_published.rb +++ /dev/null @@ -1,94 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: email_campaigns_campaigns -# -# id :uuid not null, primary key -# type :string not null -# author_id :uuid -# enabled :boolean -# sender :string -# reply_to :string -# schedule :jsonb -# subject_multiloc :jsonb -# body_multiloc :jsonb -# created_at :datetime not null -# updated_at :datetime not null -# deliveries_count :integer default(0), not null -# context_id :uuid -# -# Indexes -# -# index_email_campaigns_campaigns_on_author_id (author_id) -# index_email_campaigns_campaigns_on_context_id (context_id) -# index_email_campaigns_campaigns_on_type (type) -# -# Foreign Keys -# -# fk_rails_... (author_id => users.id) -# -module EmailCampaigns - class Campaigns::InitiativePublished < Campaign - include Consentable - include ActivityTriggerable - include RecipientConfigurable - include Disableable - include Trackable - include LifecycleStageRestrictable - allow_lifecycle_stages only: %w[trial active] - - recipient_filter :filter_recipient - - def mailer_class - InitiativePublishedMailer - end - - def activity_triggers - { 'Initiative' => { 'proposed' => true } } - end - - def filter_recipient(users_scope, activity:, time: nil) - users_scope.where(id: activity.item.author_id) - end - - def self.recipient_role_multiloc_key - 'email_campaigns.admin_labels.recipient_role.registered_users' - end - - def self.recipient_segment_multiloc_key - 'email_campaigns.admin_labels.recipient_segment.user_who_published_the_proposal' - end - - def self.content_type_multiloc_key - 'email_campaigns.admin_labels.content_type.proposals' - end - - def self.trigger_multiloc_key - 'email_campaigns.admin_labels.trigger.proposal_is_published' - end - - def generate_commands(recipient:, activity:) - initiative = activity.item - [{ - event_payload: { - post_id: initiative.id, - post_title_multiloc: initiative.title_multiloc, - post_body_multiloc: initiative.body_multiloc, - post_url: Frontend::UrlService.new.model_to_url(initiative, locale: Locale.new(recipient.locale)), - post_images: initiative.initiative_images.map do |image| - { - ordering: image.ordering, - versions: image.image.versions.to_h { |k, v| [k.to_s, v.url] } - } - end, - initiative_header_bg: { - versions: initiative.header_bg.versions.to_h { |k, v| [k.to_s, v.url] } - }, - initiative_reactions_needed: initiative.reactions_needed, - initiative_expires_at: initiative.expires_at.iso8601 - } - }] - end - end -end diff --git a/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/initiative_resubmitted_for_review.rb b/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/initiative_resubmitted_for_review.rb deleted file mode 100644 index eb3831d95d36..000000000000 --- a/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/initiative_resubmitted_for_review.rb +++ /dev/null @@ -1,84 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: email_campaigns_campaigns -# -# id :uuid not null, primary key -# type :string not null -# author_id :uuid -# enabled :boolean -# sender :string -# reply_to :string -# schedule :jsonb -# subject_multiloc :jsonb -# body_multiloc :jsonb -# created_at :datetime not null -# updated_at :datetime not null -# deliveries_count :integer default(0), not null -# context_id :uuid -# -# Indexes -# -# index_email_campaigns_campaigns_on_author_id (author_id) -# index_email_campaigns_campaigns_on_context_id (context_id) -# index_email_campaigns_campaigns_on_type (type) -# -# Foreign Keys -# -# fk_rails_... (author_id => users.id) -# -module EmailCampaigns - class Campaigns::InitiativeResubmittedForReview < Campaign - include Consentable - include ActivityTriggerable - include RecipientConfigurable - include Disableable - include Trackable - include LifecycleStageRestrictable - allow_lifecycle_stages only: %w[trial active] - - recipient_filter :filter_notification_recipient - - def mailer_class - InitiativeResubmittedForReviewMailer - end - - def activity_triggers - { 'Notifications::InitiativeResubmittedForReview' => { 'created' => true } } - end - - def filter_notification_recipient(users_scope, activity:, time: nil) - users_scope.where(id: activity.item.recipient.id) - end - - def self.recipient_role_multiloc_key - 'email_campaigns.admin_labels.recipient_role.admins_and_managers' - end - - def self.recipient_segment_multiloc_key - 'email_campaigns.admin_labels.recipient_segment.admins_assigned_to_a_proposal' - end - - def self.content_type_multiloc_key - 'email_campaigns.admin_labels.content_type.proposals' - end - - def self.trigger_multiloc_key - 'email_campaigns.admin_labels.trigger.initiative_resubmitted_for_review' - end - - def generate_commands(recipient:, activity:) - initiative = activity.item.post - name_service = UserDisplayNameService.new(AppConfiguration.instance, recipient) - - [{ - event_payload: { - post_title_multiloc: initiative.title_multiloc, - post_body_multiloc: initiative.body_multiloc, - post_author_name: name_service.display_name!(initiative.author) - } - }] - end - end -end diff --git a/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/internal_comment_on_initiative_assigned_to_you.rb b/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/internal_comment_on_initiative_assigned_to_you.rb deleted file mode 100644 index ba84d3b3fc62..000000000000 --- a/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/internal_comment_on_initiative_assigned_to_you.rb +++ /dev/null @@ -1,79 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: email_campaigns_campaigns -# -# id :uuid not null, primary key -# type :string not null -# author_id :uuid -# enabled :boolean -# sender :string -# reply_to :string -# schedule :jsonb -# subject_multiloc :jsonb -# body_multiloc :jsonb -# created_at :datetime not null -# updated_at :datetime not null -# deliveries_count :integer default(0), not null -# context_id :uuid -# -# Indexes -# -# index_email_campaigns_campaigns_on_author_id (author_id) -# index_email_campaigns_campaigns_on_context_id (context_id) -# index_email_campaigns_campaigns_on_type (type) -# -# Foreign Keys -# -# fk_rails_... (author_id => users.id) -# -module EmailCampaigns - class Campaigns::InternalCommentOnInitiativeAssignedToYou < Campaign - include Consentable - include ActivityTriggerable - include RecipientConfigurable - include Disableable - include LifecycleStageRestrictable - include Trackable - allow_lifecycle_stages only: %w[trial active] - - recipient_filter :filter_notification_recipient - - def activity_triggers - { 'Notifications::InternalComments::InternalCommentOnInitiativeAssignedToYou' => { 'created' => true } } - end - - def filter_notification_recipient(users_scope, activity:, time: nil) - users_scope.where(id: activity.item.recipient.id) - end - - def self.consentable_roles - %w[admin] - end - - def self.recipient_role_multiloc_key - 'email_campaigns.admin_labels.recipient_role.admins' - end - - def self.recipient_segment_multiloc_key - 'email_campaigns.admin_labels.recipient_segment.admins' - end - - def self.content_type_multiloc_key - 'email_campaigns.admin_labels.content_type.internal_comments' - end - - def self.trigger_multiloc_key - 'email_campaigns.admin_labels.trigger.internal_comment_is_posted_on_initiative_assigned_to_user' - end - - def mailer_class - InternalCommentOnInitiativeAssignedToYouMailer - end - - def generate_commands(recipient:, activity:, time: nil) - InternalCommentCampaignCommandsBuilder.new.build_commands(recipient, activity) - end - end -end diff --git a/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/internal_comment_on_initiative_you_commented_internally_on.rb b/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/internal_comment_on_initiative_you_commented_internally_on.rb deleted file mode 100644 index 311c18a46a17..000000000000 --- a/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/internal_comment_on_initiative_you_commented_internally_on.rb +++ /dev/null @@ -1,79 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: email_campaigns_campaigns -# -# id :uuid not null, primary key -# type :string not null -# author_id :uuid -# enabled :boolean -# sender :string -# reply_to :string -# schedule :jsonb -# subject_multiloc :jsonb -# body_multiloc :jsonb -# created_at :datetime not null -# updated_at :datetime not null -# deliveries_count :integer default(0), not null -# context_id :uuid -# -# Indexes -# -# index_email_campaigns_campaigns_on_author_id (author_id) -# index_email_campaigns_campaigns_on_context_id (context_id) -# index_email_campaigns_campaigns_on_type (type) -# -# Foreign Keys -# -# fk_rails_... (author_id => users.id) -# -module EmailCampaigns - class Campaigns::InternalCommentOnInitiativeYouCommentedInternallyOn < Campaign - include Consentable - include ActivityTriggerable - include RecipientConfigurable - include Disableable - include LifecycleStageRestrictable - include Trackable - allow_lifecycle_stages only: %w[trial active] - - recipient_filter :filter_notification_recipient - - def activity_triggers - { 'Notifications::InternalComments::InternalCommentOnInitiativeYouCommentedInternallyOn' => { 'created' => true } } - end - - def filter_notification_recipient(users_scope, activity:, time: nil) - users_scope.where(id: activity.item.recipient.id) - end - - def self.consentable_roles - %w[admin] - end - - def self.recipient_role_multiloc_key - 'email_campaigns.admin_labels.recipient_role.admins' - end - - def self.recipient_segment_multiloc_key - 'email_campaigns.admin_labels.recipient_segment.admins' - end - - def self.content_type_multiloc_key - 'email_campaigns.admin_labels.content_type.internal_comments' - end - - def self.trigger_multiloc_key - 'email_campaigns.admin_labels.trigger.internal_comment_is_posted_on_initiative_user_has_commented_internally_on' - end - - def mailer_class - InternalCommentOnInitiativeYouCommentedInternallyOnMailer - end - - def generate_commands(recipient:, activity:, time: nil) - InternalCommentCampaignCommandsBuilder.new.build_commands(recipient, activity) - end - end -end diff --git a/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/internal_comment_on_unassigned_initiative.rb b/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/internal_comment_on_unassigned_initiative.rb deleted file mode 100644 index c52df10f5ffb..000000000000 --- a/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/internal_comment_on_unassigned_initiative.rb +++ /dev/null @@ -1,79 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: email_campaigns_campaigns -# -# id :uuid not null, primary key -# type :string not null -# author_id :uuid -# enabled :boolean -# sender :string -# reply_to :string -# schedule :jsonb -# subject_multiloc :jsonb -# body_multiloc :jsonb -# created_at :datetime not null -# updated_at :datetime not null -# deliveries_count :integer default(0), not null -# context_id :uuid -# -# Indexes -# -# index_email_campaigns_campaigns_on_author_id (author_id) -# index_email_campaigns_campaigns_on_context_id (context_id) -# index_email_campaigns_campaigns_on_type (type) -# -# Foreign Keys -# -# fk_rails_... (author_id => users.id) -# -module EmailCampaigns - class Campaigns::InternalCommentOnUnassignedInitiative < Campaign - include Consentable - include ActivityTriggerable - include RecipientConfigurable - include Disableable - include LifecycleStageRestrictable - include Trackable - allow_lifecycle_stages only: %w[trial active] - - recipient_filter :filter_notification_recipient - - def activity_triggers - { 'Notifications::InternalComments::InternalCommentOnUnassignedInitiative' => { 'created' => true } } - end - - def filter_notification_recipient(users_scope, activity:, time: nil) - users_scope.where(id: activity.item.recipient.id) - end - - def self.consentable_roles - %w[admin] - end - - def self.recipient_role_multiloc_key - 'email_campaigns.admin_labels.recipient_role.admins' - end - - def self.recipient_segment_multiloc_key - 'email_campaigns.admin_labels.recipient_segment.admins' - end - - def self.content_type_multiloc_key - 'email_campaigns.admin_labels.content_type.internal_comments' - end - - def self.trigger_multiloc_key - 'email_campaigns.admin_labels.trigger.internal_comment_is_posted_on_unassigned_initiative' - end - - def mailer_class - InternalCommentOnUnassignedInitiativeMailer - end - - def generate_commands(recipient:, activity:, time: nil) - InternalCommentCampaignCommandsBuilder.new.build_commands(recipient, activity) - end - end -end diff --git a/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/invitation_to_cosponsor_initiative.rb b/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/invitation_to_cosponsor_initiative.rb deleted file mode 100644 index c3a94bbb91d7..000000000000 --- a/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/invitation_to_cosponsor_initiative.rb +++ /dev/null @@ -1,93 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: email_campaigns_campaigns -# -# id :uuid not null, primary key -# type :string not null -# author_id :uuid -# enabled :boolean -# sender :string -# reply_to :string -# schedule :jsonb -# subject_multiloc :jsonb -# body_multiloc :jsonb -# created_at :datetime not null -# updated_at :datetime not null -# deliveries_count :integer default(0), not null -# context_id :uuid -# -# Indexes -# -# index_email_campaigns_campaigns_on_author_id (author_id) -# index_email_campaigns_campaigns_on_context_id (context_id) -# index_email_campaigns_campaigns_on_type (type) -# -# Foreign Keys -# -# fk_rails_... (author_id => users.id) -# -module EmailCampaigns - class Campaigns::InvitationToCosponsorInitiative < Campaign - include Consentable - include ActivityTriggerable - include RecipientConfigurable - include Disableable - include Trackable - include LifecycleStageRestrictable - allow_lifecycle_stages only: %w[trial active] - - recipient_filter :filter_notification_recipient - - def mailer_class - InvitationToCosponsorInitiativeMailer - end - - def activity_triggers - { 'Notifications::InvitationToCosponsorInitiative' => { 'created' => true } } - end - - def filter_notification_recipient(users_scope, activity:, time: nil) - users_scope.where(id: activity.item.recipient.id) - end - - def self.recipient_role_multiloc_key - 'email_campaigns.admin_labels.recipient_role.registered_users' - end - - def self.recipient_segment_multiloc_key - 'email_campaigns.admin_labels.recipient_segment.user_who_is_invited_to_cosponsor_a_proposal' - end - - def self.content_type_multiloc_key - 'email_campaigns.admin_labels.content_type.proposals' - end - - def self.trigger_multiloc_key - 'email_campaigns.admin_labels.trigger.user_is_invited_to_cosponsor_a_proposal' - end - - def generate_commands(recipient:, activity:) - initiative = activity.item.post - name_service = UserDisplayNameService.new(AppConfiguration.instance, recipient) - - [{ - event_payload: { - post_title_multiloc: initiative.title_multiloc, - post_body_multiloc: initiative.body_multiloc, - post_author_name: name_service.display_name!(initiative.author), - post_url: Frontend::UrlService.new.model_to_url(initiative, locale: Locale.new(recipient.locale)), - post_image_medium_url: post_image_medium_url(initiative) - } - }] - end - - private - - def post_image_medium_url(initiative) - image = initiative&.initiative_images&.first - image.image.versions[:medium].url if image&.image&.versions - end - end -end diff --git a/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/new_initiative_for_admin.rb b/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/new_initiative_for_admin.rb deleted file mode 100644 index 9b539b06d32e..000000000000 --- a/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/new_initiative_for_admin.rb +++ /dev/null @@ -1,106 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: email_campaigns_campaigns -# -# id :uuid not null, primary key -# type :string not null -# author_id :uuid -# enabled :boolean -# sender :string -# reply_to :string -# schedule :jsonb -# subject_multiloc :jsonb -# body_multiloc :jsonb -# created_at :datetime not null -# updated_at :datetime not null -# deliveries_count :integer default(0), not null -# context_id :uuid -# -# Indexes -# -# index_email_campaigns_campaigns_on_author_id (author_id) -# index_email_campaigns_campaigns_on_context_id (context_id) -# index_email_campaigns_campaigns_on_type (type) -# -# Foreign Keys -# -# fk_rails_... (author_id => users.id) -# -module EmailCampaigns - class Campaigns::NewInitiativeForAdmin < Campaign - include Consentable - include ActivityTriggerable - include RecipientConfigurable - include Disableable - include LifecycleStageRestrictable - include Trackable - allow_lifecycle_stages only: %w[trial active] - - recipient_filter :filter_recipient - - def self.consentable_roles - ['admin'] - end - - def mailer_class - NewInitiativeForAdminMailer - end - - def activity_triggers - { 'Initiative' => { 'published' => true } } - end - - def filter_recipient(users_scope, activity:, time: nil) - initiative = activity.item - initiator = initiative.author - - recipient_ids = if initiator && !initiator.admin? - User.admin.ids.reject do |recipient_id| - recipient_id == initiative&.assignee_id - end - else - [] - end - - users_scope.where(id: recipient_ids) - end - - def self.recipient_role_multiloc_key - 'email_campaigns.admin_labels.recipient_role.admins_and_managers' - end - - def self.recipient_segment_multiloc_key - 'email_campaigns.admin_labels.recipient_segment.admins' - end - - def self.content_type_multiloc_key - 'email_campaigns.admin_labels.content_type.proposals' - end - - def self.trigger_multiloc_key - 'email_campaigns.admin_labels.trigger.new_proposal_is_posted' - end - - def generate_commands(recipient:, activity:, time: nil) - initiative = activity.item - [{ - event_payload: { - post_published_at: initiative.published_at.iso8601, - post_title_multiloc: initiative.title_multiloc, - post_author_name: initiative.author_name, - post_url: Frontend::UrlService.new.model_to_url(initiative, locale: Locale.new(recipient.locale)), - initiative_reactions_needed: initiative.reactions_needed, - initiative_expires_at: initiative.expires_at.iso8601 - } - }] - end - - protected - - def set_enabled - self.enabled = false if enabled.nil? - end - end -end diff --git a/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/official_feedback_on_initiative_you_follow.rb b/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/official_feedback_on_initiative_you_follow.rb deleted file mode 100644 index eeb2878f80ea..000000000000 --- a/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/official_feedback_on_initiative_you_follow.rb +++ /dev/null @@ -1,87 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: email_campaigns_campaigns -# -# id :uuid not null, primary key -# type :string not null -# author_id :uuid -# enabled :boolean -# sender :string -# reply_to :string -# schedule :jsonb -# subject_multiloc :jsonb -# body_multiloc :jsonb -# created_at :datetime not null -# updated_at :datetime not null -# deliveries_count :integer default(0), not null -# context_id :uuid -# -# Indexes -# -# index_email_campaigns_campaigns_on_author_id (author_id) -# index_email_campaigns_campaigns_on_context_id (context_id) -# index_email_campaigns_campaigns_on_type (type) -# -# Foreign Keys -# -# fk_rails_... (author_id => users.id) -# -module EmailCampaigns - class Campaigns::OfficialFeedbackOnInitiativeYouFollow < Campaign - include Consentable - include Disableable - include ActivityTriggerable - include RecipientConfigurable - include Trackable - include LifecycleStageRestrictable - allow_lifecycle_stages only: %w[trial active] - - recipient_filter :filter_notification_recipient - - def mailer_class - OfficialFeedbackOnInitiativeYouFollowMailer - end - - def activity_triggers - { 'Notifications::OfficialFeedbackOnInitiativeYouFollow' => { 'created' => true } } - end - - def filter_notification_recipient(users_scope, activity:, time: nil) - users_scope.where(id: activity.item.recipient.id) - end - - def self.recipient_role_multiloc_key - 'email_campaigns.admin_labels.recipient_role.registered_users' - end - - def self.recipient_segment_multiloc_key - 'email_campaigns.admin_labels.recipient_segment.users_who_follow_the_proposal' - end - - def self.content_type_multiloc_key - 'email_campaigns.admin_labels.content_type.proposals' - end - - def self.trigger_multiloc_key - 'email_campaigns.admin_labels.trigger.proposal_is_updated' - end - - def generate_commands(recipient:, activity:, time: nil) - notification = activity.item - name_service = UserDisplayNameService.new(AppConfiguration.instance, recipient) - [{ - event_payload: { - official_feedback_author_multiloc: notification.official_feedback.author_multiloc, - official_feedback_body_multiloc: notification.official_feedback.body_multiloc, - official_feedback_url: Frontend::UrlService.new.model_to_url(notification.official_feedback, locale: Locale.new(recipient.locale)), - post_published_at: notification.post.published_at.iso8601, - post_title_multiloc: notification.post.title_multiloc, - post_author_name: name_service.display_name!(notification.post.author), - unfollow_url: Frontend::UrlService.new.unfollow_url(Follower.new(followable: notification.post, user: recipient)) - } - }] - end - end -end diff --git a/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/status_change_on_initiative_you_follow.rb b/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/status_change_on_initiative_you_follow.rb deleted file mode 100644 index 514573af0cad..000000000000 --- a/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/status_change_on_initiative_you_follow.rb +++ /dev/null @@ -1,95 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: email_campaigns_campaigns -# -# id :uuid not null, primary key -# type :string not null -# author_id :uuid -# enabled :boolean -# sender :string -# reply_to :string -# schedule :jsonb -# subject_multiloc :jsonb -# body_multiloc :jsonb -# created_at :datetime not null -# updated_at :datetime not null -# deliveries_count :integer default(0), not null -# context_id :uuid -# -# Indexes -# -# index_email_campaigns_campaigns_on_author_id (author_id) -# index_email_campaigns_campaigns_on_context_id (context_id) -# index_email_campaigns_campaigns_on_type (type) -# -# Foreign Keys -# -# fk_rails_... (author_id => users.id) -# -module EmailCampaigns - class Campaigns::StatusChangeOnInitiativeYouFollow < Campaign - include Consentable - include Disableable - include ActivityTriggerable - include RecipientConfigurable - include Trackable - include LifecycleStageRestrictable - allow_lifecycle_stages only: %w[trial active] - - recipient_filter :filter_notification_recipient - - def mailer_class - StatusChangeOnInitiativeYouFollowMailer - end - - def activity_triggers - { 'Notifications::StatusChangeOnInitiativeYouFollow' => { 'created' => true } } - end - - def filter_notification_recipient(users_scope, activity:, time: nil) - users_scope.where(id: activity.item.recipient_id) - end - - def self.recipient_role_multiloc_key - 'email_campaigns.admin_labels.recipient_role.registered_users' - end - - def self.recipient_segment_multiloc_key - 'email_campaigns.admin_labels.recipient_segment.users_who_follow_the_proposal' - end - - def self.content_type_multiloc_key - 'email_campaigns.admin_labels.content_type.proposals' - end - - def self.trigger_multiloc_key - 'email_campaigns.admin_labels.trigger.proposal_status_changes' - end - - def generate_commands(recipient:, activity:) - initiative = activity.item.post - status = initiative.initiative_status - [{ - event_payload: { - post_id: initiative.id, - post_title_multiloc: initiative.title_multiloc, - post_body_multiloc: initiative.body_multiloc, - post_url: Frontend::UrlService.new.model_to_url(initiative, locale: Locale.new(recipient.locale)), - post_images: initiative.initiative_images.map do |image| - { - ordering: image.ordering, - versions: image.image.versions.to_h { |k, v| [k.to_s, v.url] } - } - end, - initiative_status_id: status.id, - initiative_status_title_multiloc: status.title_multiloc, - initiative_status_code: status.code, - initiative_status_color: status.color, - unfollow_url: Frontend::UrlService.new.unfollow_url(Follower.new(followable: initiative, user: recipient)) - } - }] - end - end -end diff --git a/back/engines/free/email_campaigns/app/services/email_campaigns/delivery_service.rb b/back/engines/free/email_campaigns/app/services/email_campaigns/delivery_service.rb index f1c056601489..17c44abd2342 100644 --- a/back/engines/free/email_campaigns/app/services/email_campaigns/delivery_service.rb +++ b/back/engines/free/email_campaigns/app/services/email_campaigns/delivery_service.rb @@ -9,27 +9,17 @@ class DeliveryService Campaigns::CommentDeletedByAdmin, Campaigns::CommentMarkedAsSpam, Campaigns::CommentOnIdeaYouFollow, - Campaigns::CommentOnInitiativeYouFollow, Campaigns::CommentOnYourComment, - Campaigns::CosponsorOfYourInitiative, Campaigns::CosponsorOfYourIdea, Campaigns::EventRegistrationConfirmation, Campaigns::IdeaMarkedAsSpam, Campaigns::IdeaPublished, - Campaigns::InitiativeAssignedToYou, - Campaigns::InitiativeMarkedAsSpam, - Campaigns::InitiativePublished, - Campaigns::InitiativeResubmittedForReview, Campaigns::InternalCommentOnIdeaAssignedToYou, Campaigns::InternalCommentOnIdeaYouCommentedInternallyOn, Campaigns::InternalCommentOnIdeaYouModerate, - Campaigns::InternalCommentOnInitiativeAssignedToYou, - Campaigns::InternalCommentOnInitiativeYouCommentedInternallyOn, - Campaigns::InternalCommentOnUnassignedInitiative, Campaigns::InternalCommentOnUnassignedUnmoderatedIdea, Campaigns::InternalCommentOnYourInternalComment, Campaigns::InvitationToCosponsorIdea, - Campaigns::InvitationToCosponsorInitiative, Campaigns::InviteReceived, Campaigns::InviteReminder, Campaigns::Manual, @@ -40,9 +30,7 @@ class DeliveryService Campaigns::NativeSurveyNotSubmitted, Campaigns::NewCommentForAdmin, Campaigns::NewIdeaForAdmin, - Campaigns::NewInitiativeForAdmin, Campaigns::OfficialFeedbackOnIdeaYouFollow, - Campaigns::OfficialFeedbackOnInitiativeYouFollow, Campaigns::ProjectFolderModerationRightsReceived, Campaigns::ProjectModerationRightsReceived, Campaigns::ProjectPhaseStarted, @@ -51,7 +39,6 @@ class DeliveryService Campaigns::ProjectReviewRequest, Campaigns::ProjectReviewStateChange, Campaigns::StatusChangeOnIdeaYouFollow, - Campaigns::StatusChangeOnInitiativeYouFollow, Campaigns::ThresholdReachedForAdmin, Campaigns::UserDigest, Campaigns::VotingBasketNotSubmitted, diff --git a/back/engines/free/email_campaigns/app/views/email_campaigns/comment_on_initiative_you_follow_mailer/campaign_mail.mjml b/back/engines/free/email_campaigns/app/views/email_campaigns/comment_on_initiative_you_follow_mailer/campaign_mail.mjml deleted file mode 100644 index ee1b0af013e5..000000000000 --- a/back/engines/free/email_campaigns/app/views/email_campaigns/comment_on_initiative_you_follow_mailer/campaign_mail.mjml +++ /dev/null @@ -1,23 +0,0 @@ - - - - <%= format_message('author_wrote', component: 'general', values: { - authorName: " - #{event.initiating_user_first_name&.upcase} - " - }).html_safe %> -

- <%= localize_for_recipient_and_truncate(event.comment_body_multiloc, 140) %> -

-
-
-
- - -<%= render partial: 'application/cta_button', locals: { href: event.comment_url, message: format_message('cta_reply_to', values: {commentAuthor: event.initiating_user_first_name&.capitalize}) } %> - -<% if AppConfiguration.instance.feature_activated?('follow') %> - <%= render 'email_campaigns/follow/card' %> -<% end %> diff --git a/back/engines/free/email_campaigns/app/views/email_campaigns/cosponsor_of_your_initiative_mailer/campaign_mail.mjml b/back/engines/free/email_campaigns/app/views/email_campaigns/cosponsor_of_your_initiative_mailer/campaign_mail.mjml deleted file mode 100644 index 297e57cace57..000000000000 --- a/back/engines/free/email_campaigns/app/views/email_campaigns/cosponsor_of_your_initiative_mailer/campaign_mail.mjml +++ /dev/null @@ -1,2 +0,0 @@ -<%= render 'email_campaigns/posts/post_with_image', post_image_url: event&.post_image_medium_url, post_title_multiloc: event.post_title_multiloc, post_body_multiloc: event.post_body_multiloc %> -<%= render partial: 'application/cta_button', locals: { href: event.post_url, message: format_message('cta_reply_to', values: { authorName: event.post_author_name }) } %> diff --git a/back/engines/free/email_campaigns/app/views/email_campaigns/initiative_assigned_to_you_mailer/campaign_mail.mjml b/back/engines/free/email_campaigns/app/views/email_campaigns/initiative_assigned_to_you_mailer/campaign_mail.mjml deleted file mode 100644 index b7eadf7f4740..000000000000 --- a/back/engines/free/email_campaigns/app/views/email_campaigns/initiative_assigned_to_you_mailer/campaign_mail.mjml +++ /dev/null @@ -1,20 +0,0 @@ - - - - -

- <%= localize_for_recipient(event.post_title_multiloc) %> -

- <%= format_message('by_author', component: 'general', values: { authorName: event.post_author_name }) %> -

-

- <%= localize_for_recipient(event.post_body_multiloc).html_safe %> -

-
-
-
- -<%= render partial: 'application/cta_button', locals: { href: url_service.admin_initiatives_url, message: format_message('cta_reply_to', values: { authorName: event.post_author_name }) } %> diff --git a/back/engines/free/email_campaigns/app/views/email_campaigns/initiative_marked_as_spam_mailer/campaign_mail.mjml b/back/engines/free/email_campaigns/app/views/email_campaigns/initiative_marked_as_spam_mailer/campaign_mail.mjml deleted file mode 100644 index c0b7d5b69873..000000000000 --- a/back/engines/free/email_campaigns/app/views/email_campaigns/initiative_marked_as_spam_mailer/campaign_mail.mjml +++ /dev/null @@ -1,42 +0,0 @@ - - - - -

- <%= localize_for_recipient(event.post_title_multiloc) %> -

-

- <%= format_message('by_author', component: 'general', values: { authorName: event.post_author_name }) %> -

-
-
-
- - - - - - <%= format_message('reported_this_because', values: { - reporterFirstName: "#{event.initiating_user_first_name}" - }, escape_html: false) %> - - -

- <% case event.spam_report_reason_code %> - <% when 'other' %> - <%= event.spam_report_other_reason %> - <% when 'inappropriate' %> - <%= format_message('report_inappropriate_offensive_content') %> - <% when 'wrong_content' %> - <%= format_message('report_not_an_initiative') %> - <% end %> -

-
-
-
- - -<%= render partial: 'application/cta_button', locals: { href: event.post_url, message: format_message('cta_review_initiative') } %> diff --git a/back/engines/free/email_campaigns/app/views/email_campaigns/initiative_published_mailer/campaign_mail.mjml b/back/engines/free/email_campaigns/app/views/email_campaigns/initiative_published_mailer/campaign_mail.mjml deleted file mode 100644 index b64cecf1ffb9..000000000000 --- a/back/engines/free/email_campaigns/app/views/email_campaigns/initiative_published_mailer/campaign_mail.mjml +++ /dev/null @@ -1,111 +0,0 @@ - - - - -

- <%= format_message("message_get_votes") %> -

-
- - - - - Checked checkmark - - - - <%= format_message('action_published_initiative') %> - - - - - - - - Empty checkmark - - - <%= format_message('action_share_fb', values: { - fbLink: "Facebook" - }, escape_html: false) - %> - - - Facebook icon - - - - - - Empty checkmark - - - <%= format_message('action_share_twitter', values: { - twitterLink: "Twitter" - }, escape_html: false) - %> - - - Twitter icon - - - - - - - Empty checkmark - - - <%= format_message('action_send_email', values: { - sendEmailLink: "#{format_message('send_email')}" - }, escape_html: false) - %> - - - Paper plane - - - - - - - Empty checkmark - - - <%= format_message('action_share_link', values: { - link: "#{format_message('link')}" - }, escape_html: false) - %> - - - Copy - - - - -
-
- - - <%= render partial: 'application/cta_button', locals: { href: event.post_url, message: format_message('cta_goto_proposal', component: 'general') } %> diff --git a/back/engines/free/email_campaigns/app/views/email_campaigns/initiative_resubmitted_for_review_mailer/campaign_mail.mjml b/back/engines/free/email_campaigns/app/views/email_campaigns/initiative_resubmitted_for_review_mailer/campaign_mail.mjml deleted file mode 100644 index 5eb306cae8c8..000000000000 --- a/back/engines/free/email_campaigns/app/views/email_campaigns/initiative_resubmitted_for_review_mailer/campaign_mail.mjml +++ /dev/null @@ -1,20 +0,0 @@ - - - - -

- <%= localize_for_recipient(event.post_title_multiloc) %> -

- <%= format_message('by_author', values: { authorName: event.post_author_name }) %> -

-

- <%= localize_for_recipient(event.post_body_multiloc).html_safe %> -

-
-
-
- -<%= render partial: 'application/cta_button', locals: { href: url_service.admin_initiatives_url, message: format_message('cta_reply_to', values: { authorName: event.post_author_name }) } %> diff --git a/back/engines/free/email_campaigns/app/views/email_campaigns/internal_comment_on_initiative_assigned_to_you_mailer/campaign_mail.mjml b/back/engines/free/email_campaigns/app/views/email_campaigns/internal_comment_on_initiative_assigned_to_you_mailer/campaign_mail.mjml deleted file mode 100644 index 328c41c16b0a..000000000000 --- a/back/engines/free/email_campaigns/app/views/email_campaigns/internal_comment_on_initiative_assigned_to_you_mailer/campaign_mail.mjml +++ /dev/null @@ -1 +0,0 @@ -<%= render 'email_campaigns/internal_comments/internal_comment_mail', event: event %> diff --git a/back/engines/free/email_campaigns/app/views/email_campaigns/internal_comment_on_initiative_you_commented_internally_on_mailer/campaign_mail.mjml b/back/engines/free/email_campaigns/app/views/email_campaigns/internal_comment_on_initiative_you_commented_internally_on_mailer/campaign_mail.mjml deleted file mode 100644 index 328c41c16b0a..000000000000 --- a/back/engines/free/email_campaigns/app/views/email_campaigns/internal_comment_on_initiative_you_commented_internally_on_mailer/campaign_mail.mjml +++ /dev/null @@ -1 +0,0 @@ -<%= render 'email_campaigns/internal_comments/internal_comment_mail', event: event %> diff --git a/back/engines/free/email_campaigns/app/views/email_campaigns/internal_comment_on_unassigned_initiative_mailer/campaign_mail.mjml b/back/engines/free/email_campaigns/app/views/email_campaigns/internal_comment_on_unassigned_initiative_mailer/campaign_mail.mjml deleted file mode 100644 index 328c41c16b0a..000000000000 --- a/back/engines/free/email_campaigns/app/views/email_campaigns/internal_comment_on_unassigned_initiative_mailer/campaign_mail.mjml +++ /dev/null @@ -1 +0,0 @@ -<%= render 'email_campaigns/internal_comments/internal_comment_mail', event: event %> diff --git a/back/engines/free/email_campaigns/app/views/email_campaigns/invitation_to_cosponsor_initiative_mailer/campaign_mail.mjml b/back/engines/free/email_campaigns/app/views/email_campaigns/invitation_to_cosponsor_initiative_mailer/campaign_mail.mjml deleted file mode 100644 index f53f543d4270..000000000000 --- a/back/engines/free/email_campaigns/app/views/email_campaigns/invitation_to_cosponsor_initiative_mailer/campaign_mail.mjml +++ /dev/null @@ -1,26 +0,0 @@ - - - -

<%= format_message('event_description_cosponsoring', escape_html: false) %>

-
-
-
- - - - -

<%= format_message('event_description_before_action', escape_html: false) %>

-
-
-
- - - - -

<%= format_message('event_description_action') %>

-
-
-
- -<%= render 'email_campaigns/posts/post_with_image', post_image_url: event&.post_image_medium_url, post_title_multiloc: event.post_title_multiloc, post_body_multiloc: event.post_body_multiloc %> -<%= render partial: 'application/cta_button', locals: { href: event.post_url, message: format_message('cta_reply_to', values: { authorName: event.post_author_name }) } %> diff --git a/back/engines/free/email_campaigns/app/views/email_campaigns/new_initiative_for_admin_mailer/campaign_mail.mjml b/back/engines/free/email_campaigns/app/views/email_campaigns/new_initiative_for_admin_mailer/campaign_mail.mjml deleted file mode 100644 index f228934cc515..000000000000 --- a/back/engines/free/email_campaigns/app/views/email_campaigns/new_initiative_for_admin_mailer/campaign_mail.mjml +++ /dev/null @@ -1,19 +0,0 @@ - - - - -

- <%= localize_for_recipient(event.post_title_multiloc) %> -

-

- <%= format_message('by_author', component: 'general', values: { authorName: event.post_author_name }) %> -

-
-
-
- - -<%= render partial: 'application/cta_button', locals: { href: event.post_url, message: format_message('cta_reply_to', values: { authorName: event.post_author_name }) } %> diff --git a/back/engines/free/email_campaigns/app/views/email_campaigns/official_feedback_on_initiative_you_follow_mailer/campaign_mail.mjml b/back/engines/free/email_campaigns/app/views/email_campaigns/official_feedback_on_initiative_you_follow_mailer/campaign_mail.mjml deleted file mode 100644 index 27b52c41206d..000000000000 --- a/back/engines/free/email_campaigns/app/views/email_campaigns/official_feedback_on_initiative_you_follow_mailer/campaign_mail.mjml +++ /dev/null @@ -1,7 +0,0 @@ -<%= render 'email_campaigns/official_feedbacks/card' %> - -<%= render partial: 'application/cta_button', locals: { href: event.official_feedback_url , message: format_message('cta_goto_proposal', component: 'general') } %> - -<% if AppConfiguration.instance.feature_activated?('follow') %> - <%= render 'email_campaigns/follow/card' %> -<% end %> diff --git a/back/engines/free/email_campaigns/app/views/email_campaigns/status_change_on_initiative_you_follow_mailer/campaign_mail.mjml b/back/engines/free/email_campaigns/app/views/email_campaigns/status_change_on_initiative_you_follow_mailer/campaign_mail.mjml deleted file mode 100644 index c7ef267bd366..000000000000 --- a/back/engines/free/email_campaigns/app/views/email_campaigns/status_change_on_initiative_you_follow_mailer/campaign_mail.mjml +++ /dev/null @@ -1,17 +0,0 @@ - - - - <%= format_message('status_change', values: { - initiativeStatus: localize_for_recipient(event.initiative_status_title_multiloc) - }) %> - - - - -<%= render partial: 'application/cta_button', locals: { href: event.post_url , message: format_message('cta_goto_proposal', component: 'general') } %> - -<% if AppConfiguration.instance.feature_activated?('follow') %> - <%= render 'email_campaigns/follow/card' %> -<% end %> diff --git a/back/engines/free/email_campaigns/config/locales/en.yml b/back/engines/free/email_campaigns/config/locales/en.yml index 34594a924179..a39d0da8b62b 100644 --- a/back/engines/free/email_campaigns/config/locales/en.yml +++ b/back/engines/free/email_campaigns/config/locales/en.yml @@ -8,35 +8,23 @@ en: "comment_marked_as_spam": Comment spam report "comment_on_your_comment": A reply on my comment "comment_on_idea_you_follow": A comment on an idea that you follow - "comment_on_initiative_you_follow": A comment on a proposal that you follow - "cosponsor_of_your_initiative": A user accepts my invitation to co-sponsor my proposal "cosponsor_of_your_idea": A user accepts my invitation to co-sponsor my proposal "event_registration_confirmation": Event registration confirmation "idea_marked_as_spam": Idea spam report "idea_published": Publication of my input - "initiative_assigned_to_you": Assignment of a proposal to me - "initiative_marked_as_spam": Proposal spam report - "initiative_published": Publication of my proposal - "invitation_to_cosponsor_initiative": Invitation to co-sponsor a proposal "invitation_to_cosponsor_idea": Invitation to co-sponsor a proposal - "initiative_resubmitted_for_review": Proposal resubmitted for review "invite_received": Invitation "invite_reminder": Invitation reminder "internal_comment_on_idea_assigned_to_you": Internal comment on input assigned to me "internal_comment_on_idea_you_commented_internally_on": Internal comment on input I commented internally on "internal_comment_on_idea_you_moderate": Internal comment on input in project or folder I manage - "internal_comment_on_initiative_assigned_to_you": Internal comment on proposal assigned to me - "internal_comment_on_initiative_you_commented_internally_on": Internal comment on proposal I commented internally on - "internal_comment_on_unassigned_initiative": Internal comment on unassigned proposal "internal_comment_on_unassigned_unmoderated_idea": Internal comment on unassigned input in unmanaged project "internal_comment_on_your_internal_comment": Internal comment on my internal comment "mention_in_official_feedback": Mention in an update "mention_in_internal_comment": Mention in an internal comment "new_comment_for_admin": New comment in a project I moderate "new_idea_for_admin": New input in a project I moderate - "new_initiative_for_admin": New proposal "official_feedback_on_idea_you_follow": Update on an input that you follow - "official_feedback_on_initiative_you_follow": Update on a proposal that you follow "password_reset": Password reset "project_moderation_rights_received": Project moderation rights received "project_folder_moderation_rights_received": Folder manager rights received @@ -46,7 +34,6 @@ en: "project_review_request": Project review request "project_review_state_change": Project approved "status_change_on_idea_you_follow": Status change of an input that you follow - "status_change_on_initiative_you_follow": Status change of a proposal that you follow "threshold_reached_for_admin": Proposal reached the voting threshold "welcome": After registration "admin_digest": Weekly overview for admins @@ -73,7 +60,6 @@ en: issue: 'Go to this issue' option: 'Go to this option' proposal: 'Go to this proposal' - initiative: 'Go to this initiative' petition: 'Go to this petition' cta_goto_your: idea: 'Go to your idea' @@ -83,7 +69,6 @@ en: issue: 'Go to your issue' option: 'Go to your option' proposal: 'Go to your proposal' - initiative: 'Go to your initiative' petition: 'Go to your petition' cta_goto_proposal: 'Go to this proposal' cta_goto_project: 'Go to this project' @@ -130,7 +115,6 @@ en: new_comments: 'New comments' title_activity_past_week: 'Activity of the past week' title_no_activity_past_week: 'There was no activity in the past week' - title_initiatives_past_week: 'New proposals of the past week' reached_threshold: 'Reached the threshold' yesterday_by_author: 'Yesterday by %{author}' today_by_author: 'Today by %{author}' @@ -168,12 +152,6 @@ en: main_header: '%{cosponsorName} has accepted your invitation to co-sponsor your proposal' subject: '%{cosponsorName} has accepted your invitation to co-sponsor your proposal' preheader: '%{cosponsorName} has accepted your invitation to co-sponsor your proposal' - cosponsor_of_your_initiative: - cta_reply_to: 'View your proposal' - event_description_initiative: 'Congratulations! %{cosponsorName} has accepted your invitation to co-sponsor your proposal.' - main_header: '%{cosponsorName} has accepted your invitation to co-sponsor your proposal' - subject: '%{cosponsorName} has accepted your invitation to co-sponsor your proposal' - preheader_initiative: '%{cosponsorName} has accepted your invitation to co-sponsor your proposal' assignee_digest: subject: 'Inputs requiring your feedback: %{numberIdeas}' preheader: 'Assignee digest of %{organizationName}' @@ -194,20 +172,6 @@ en: preheader: 'Act upon this spam report' reported_this_because: '%{reporterFirstName} reported this because:' title_spam_report: '%{firstName} %{lastName} reported spam' - initiative_marked_as_spam: - cta_review_initiative: 'Review proposal' - report_inappropriate_offensive_content: 'I find this content inappropriate or offensive.' - report_not_an_initiative: 'This content is not a proposal and does not belong here.' - subject: 'You have a spam report on the platform of %{organizationName}' - preheader: 'Act upon this spam report' - reported_this_because: '%{reporterFirstName} reported this because:' - title_spam_report: '%{firstName} %{lastName} reported spam' - initiative_assigned_to_you: - cta_reply_to: 'Give feedback to %{authorName}' - event_description_initiative: 'A proposal has been assigned to you. Give feedback by writing an official update or by changing its status.' - main_header: '%{firstName}, you have a new assignment' - subject: 'You have an assignment on the platform of %{organizationName}' - preheader_initiative: 'The proposal of %{authorName} has been assigned to you' invitation_to_cosponsor_idea: cta_reply_to: 'Co-sponsor this proposal' event_description: '%{authorName} has created a new proposal and would like you to co-sponsor it.' @@ -217,22 +181,6 @@ en: main_header: 'You have been invited to co-sponsor a proposal' subject: 'You have been invited to co-sponsor a proposal' preheader: 'You have been invited to co-sponsor the proposal of %{authorName}' - invitation_to_cosponsor_initiative: - cta_reply_to: 'Co-sponsor this proposal' - event_description_initiative: '%{authorName} has created a new proposal and would like you to co-sponsor it.' - event_description_cosponsoring: 'Co-sponsoring a proposal means that your name will be displayed with the names of other co-sponsors of the proposal.' - event_description_before_action: 'To see the proposal and accept the invitation, you must be logged in to your account.' - event_description_action: 'Click below to read the proposal.' - main_header: 'You have been invited to co-sponsor a proposal' - subject: 'You have been invited to co-sponsor a proposal' - preheader_initiative: 'You have been invited to co-sponsor the proposal of %{authorName}' - initiative_resubmitted_for_review: - by_author: 'by %{authorName}' - cta_reply_to: 'Give feedback to %{authorName}' - event_description_initiative: 'A proposal has been resubmitted for review. Give feedback by writing an official update or by changing its status.' - main_header: '%{firstName}, you have a proposal to review' - subject: 'You have a proposal to review on the platform of %{organizationName}' - preheader_initiative: 'The proposal of %{authorName} has been resubmitted for review' invite_reminder: cta_accept_invitation: 'Accept your invitation' invitation_header: 'Your invitation is pending' @@ -247,20 +195,6 @@ en: invitation_expiry_message: 'This invitation expires in %{expiryDays} days.' preheader: '%{organizationName} sent you an invite to join their participation platform.' subject: 'You are invited to join the platform of %{organizationName}' - initiative_published: - action_add_image: '%{addImageLink} to increase visibility' - action_published_initiative: 'Published proposal' - action_send_email: 'Send your contacts an %{sendEmailLink}' - action_share_fb: 'Let your friends know on %{fbLink}' - action_share_link: 'Share it via any channel by copying the %{link}' - action_share_twitter: 'Inform your followers on %{twitterLink}' - link: link - main_header: 'You posted a proposal! Let''s gather support for it.' - message_next_steps: '%{userFirstName}, your proposal has successfully been published on the participation platform of %{organizationName}. You will be notified when people interact with your proposal. Find out what next steps you can take below.' - message_get_votes: 'Reach the voting threshold:' - preheader: 'Thank you for sharing your proposal %{initiativeTitle} for %{organizationName}. The next step is to get support for your proposal by sharing it across your network.' - send_email: email - subject: 'Your proposal was published on the platform of %{organizationName}' mention_in_comment: cta_reply_to: 'Reply to %{commentAuthor}' event_description: '%{commentAuthorFull} has mentioned you in his comment on the idea ''%{post}‘. Click the link below to enter the conversation with %{commentAuthor}' @@ -309,22 +243,9 @@ en: issue: '%{authorName} commented on an issue you follow' option: '%{authorName} commented on an option you follow' proposal: '%{authorName} commented on a proposal you follow' - initiative: '%{authorName} commented on an initiative you follow' petition: '%{authorName} commented on a petition you follow' subject: 'There''s a new comment on "%{input_title}"' preheader: '%{authorName} left a comment on an idea for %{organizationName}' - comment_on_initiative_you_follow: - cta_reply_to: 'Reply to %{commentAuthor}' - event_description: '%{authorNameFull} placed a reaction on the proposal ''%{initiativeTitle}''. Click the button below to continue the conversation with %{authorName}.' - main_header: '%{commentAuthor} commented on an proposal you follow' - subject: 'There''s a new comment on the proposal you follow' - preheader: '%{authorName} left a comment on an proposal for %{organizationName}' - new_initiative_for_admin: - cta_reply_to: 'Give feedback to %{authorName}' - event_description: '%{authorName} has published a new proposal on your platform. Discover it now, give some feedback or change its status!' - main_header: '%{firstName}, a new proposal has been published on your platform' - subject: 'Someone published a new proposal on the platform of %{organizationName}' - preheader: '%{authorName} published a new proposal on your platform' new_idea_for_admin: main_header_publication: 'A new input has been published on your platform' event_description_publication: '%{authorName} has submitted a new input on your platform. Discover it now, give some feedback or change its status!' @@ -368,24 +289,6 @@ en: subject: 'New internal comment on ''%{post}''' main_header: 'New internal comment on ''%{post}''' preheader: 'New internal comment on ''%{post}''' - internal_comment_on_initiative_assigned_to_you: - cta_reply_to: 'View comment by %{firstName}' - event_description: '%{authorNameFull} commented internally on a proposal assigned to you.' - subject: 'New internal comment on ''%{post}''' - main_header: 'New internal comment on ''%{post}''' - preheader: 'New internal comment on ''%{post}''' - internal_comment_on_initiative_you_commented_internally_on: - cta_reply_to: 'View comment by %{firstName}' - event_description: '%{authorNameFull} commented internally on a proposal you commented internally on.' - subject: 'New internal comment on ''%{post}''' - main_header: 'New internal comment on ''%{post}''' - preheader: 'New internal comment on ''%{post}''' - internal_comment_on_unassigned_initiative: - cta_reply_to: 'View comment by %{firstName}' - event_description: '%{authorNameFull} commented internally on an unassigned proposal.' - subject: 'New internal comment on ''%{post}''' - main_header: 'New internal comment on ''%{post}''' - preheader: 'New internal comment on ''%{post}''' internal_comment_on_unassigned_unmoderated_idea: cta_reply_to: 'View comment by %{firstName}' event_description: '%{authorNameFull} commented internally on an unassigned input in an unmanaged project.' @@ -397,11 +300,6 @@ en: header_title: 'There''s an update on "%{input_title}"' subject: 'Official feedback was posted on "%{input_title}"' preheader: 'There''s an update on an input you follow' - official_feedback_on_initiative_you_follow: - header_message: '%{officialName} gave an update on the proposal ''%{initiativeTitle}''. Click the button below to enter the conversation with %{organizationName}.' - header_title: 'There''s an update on a proposal you follow' - subject: 'A proposal you follow has received an official update on the platform of %{organizationName}' - preheader: 'There''s an update on a proposal you follow' mention_in_official_feedback: cta_reply_to: 'Reply to %{organizationName}' event_description: '%{organizationName} mentioned you in their feedback on the idea ''%{post}‘. Click the link below to enter the conversation with %{organizationName}' @@ -475,12 +373,6 @@ en: header_title: 'An input you follow has a new status' subject: 'The status of "%{input_title}" has changed' preheader: 'An input you follow has a new status' - status_change_on_initiative_you_follow: - status_change: 'The new status of this proposal is ''%{initiativeStatus}''' - header_message: 'The status of the proposal ''%{initiativeTitle}'' has been updated on the digital participation platform of %{organizationName}.' - header_title: 'A proposal you follow has a new status' - subject: 'The status of a proposal you follow has changed' - preheader: 'A proposal you follow has a new status' user_digest: subject: "This week on the participation platform of %{organizationName}" commented: "%{authorFirstName} commented:" @@ -521,7 +413,6 @@ en: issue: 'Your issue has been published' option: 'Your option has been published' proposal: 'Your proposal has been published' - initiative: 'Your initiative has been published' petition: 'Your petition has been published' main_header: 'You posted "%{input_title}"' header_message: 'Let''s make sure it gets read.' @@ -545,7 +436,6 @@ en: issue: 'Your issue is in "%{prescreening_status_title}"' option: 'Your option is in "%{prescreening_status_title}"' proposal: 'Your proposal is in "%{prescreening_status_title}"' - initiative: 'Your initiative is in "%{prescreening_status_title}"' petition: 'Your petition is in "%{prescreening_status_title}"' message: '"%{input_title}" will become visible to others once it has been reviewed and approved.' subject: '"%{input_title}" is almost published' @@ -674,9 +564,6 @@ en: internal_comment_is_posted_on_idea_in_project_or_folder_user_manages: 'Internal comment is posted on input in project or folder user manages' internal_comment_is_posted_on_idea_user_has_commented_internally_on: 'Internal comment is posted on input user commented internally on' internal_comment_is_posted_on_idea_user_moderates: 'Internal comment is posted on input user moderates' - internal_comment_is_posted_on_initiative_assigned_to_user: 'Internal comment is posted on proposal assigned to user' - internal_comment_is_posted_on_initiative_user_has_commented_internally_on: 'Internal comment is posted on proposal user commented internally on' - internal_comment_is_posted_on_unassigned_initiative: 'Internal comment is posted on unassigned proposal' internal_comment_is_posted_on_unassigned_unmoderated_idea: 'Internal comment is posted on unassigned input in unmanaged project' project_review_request: 'Moderator requested a project review' project_review_state_change: 'Reviewer approved the project' diff --git a/back/engines/free/email_campaigns/spec/acceptance/campaigns_spec.rb b/back/engines/free/email_campaigns/spec/acceptance/campaigns_spec.rb index 47de9df0ca43..77b3fd4f90e8 100644 --- a/back/engines/free/email_campaigns/spec/acceptance/campaigns_spec.rb +++ b/back/engines/free/email_campaigns/spec/acceptance/campaigns_spec.rb @@ -9,7 +9,7 @@ before do @manual_campaigns = create_list(:manual_campaign, 4) @manual_project_participants_campaign = create(:manual_project_participants_campaign) - @automated_campaigns = create_list(:official_feedback_on_initiative_you_follow_campaign, 2) + @automated_campaigns = create_list(:official_feedback_on_idea_you_follow_campaign, 2) end context 'as an admin' do diff --git a/back/engines/free/email_campaigns/spec/factories/campaigns.rb b/back/engines/free/email_campaigns/spec/factories/campaigns.rb index 2f45ee7afadf..9758bfd3e8e7 100644 --- a/back/engines/free/email_campaigns/spec/factories/campaigns.rb +++ b/back/engines/free/email_campaigns/spec/factories/campaigns.rb @@ -42,14 +42,6 @@ enabled { true } end - factory :comment_on_initiative_you_follow_campaign, class: EmailCampaigns::Campaigns::CommentOnInitiativeYouFollow do - enabled { true } - end - - factory :cosponsor_of_your_initiative_campaign, class: EmailCampaigns::Campaigns::CosponsorOfYourInitiative do - enabled { true } - end - factory :cosponsor_of_your_idea_campaign, class: EmailCampaigns::Campaigns::CosponsorOfYourIdea do enabled { true } end @@ -74,22 +66,6 @@ enabled { true } end - factory :initiative_assigned_to_you_campaign, class: EmailCampaigns::Campaigns::InitiativeAssignedToYou do - enabled { true } - end - - factory :initiative_marked_as_spam_campaign, class: EmailCampaigns::Campaigns::InitiativeMarkedAsSpam do - enabled { true } - end - - factory :initiative_published_campaign, class: EmailCampaigns::Campaigns::InitiativePublished do - enabled { true } - end - - factory :initiative_resubmitted_for_review_campaign, class: EmailCampaigns::Campaigns::InitiativeResubmittedForReview do - enabled { true } - end - factory :internal_comment_on_idea_assigned_to_you_campaign, class: EmailCampaigns::Campaigns::InternalCommentOnIdeaAssignedToYou do enabled { true } end @@ -102,18 +78,6 @@ enabled { true } end - factory :internal_comment_on_initiative_assigned_to_you_campaign, class: EmailCampaigns::Campaigns::InternalCommentOnInitiativeAssignedToYou do - enabled { true } - end - - factory :internal_comment_on_initiative_you_commented_internally_on_campaign, class: EmailCampaigns::Campaigns::InternalCommentOnInitiativeYouCommentedInternallyOn do - enabled { true } - end - - factory :internal_comment_on_unassigned_initiative_campaign, class: EmailCampaigns::Campaigns::InternalCommentOnUnassignedInitiative do - enabled { true } - end - factory :internal_comment_on_unassigned_unmoderated_idea_campaign, class: EmailCampaigns::Campaigns::InternalCommentOnUnassignedUnmoderatedIdea do enabled { true } end @@ -126,10 +90,6 @@ enabled { true } end - factory :invitation_to_cosponsor_initiative_campaign, class: EmailCampaigns::Campaigns::InvitationToCosponsorInitiative do - enabled { true } - end - factory :mention_in_internal_comment_campaign, class: EmailCampaigns::Campaigns::MentionInInternalComment do enabled { true } end @@ -146,18 +106,10 @@ enabled { true } end - factory :new_initiative_for_admin_campaign, class: EmailCampaigns::Campaigns::NewInitiativeForAdmin do - enabled { true } - end - factory :official_feedback_on_idea_you_follow_campaign, class: EmailCampaigns::Campaigns::OfficialFeedbackOnIdeaYouFollow do enabled { true } end - factory :official_feedback_on_initiative_you_follow_campaign, class: EmailCampaigns::Campaigns::OfficialFeedbackOnInitiativeYouFollow do - enabled { true } - end - factory :project_folder_moderation_rights_received_campaign, class: EmailCampaigns::Campaigns::ProjectFolderModerationRightsReceived do enabled { true } end @@ -190,10 +142,6 @@ enabled { true } end - factory :status_change_on_initiative_you_follow_campaign, class: EmailCampaigns::Campaigns::StatusChangeOnInitiativeYouFollow do - enabled { true } - end - factory :threshold_reached_for_admin_campaign, class: EmailCampaigns::Campaigns::ThresholdReachedForAdmin do enabled { true } end diff --git a/back/engines/free/email_campaigns/spec/fixtures/locales/mailers.en.yml b/back/engines/free/email_campaigns/spec/fixtures/locales/mailers.en.yml index 43f6ac448615..1bdc6797a535 100644 --- a/back/engines/free/email_campaigns/spec/fixtures/locales/mailers.en.yml +++ b/back/engines/free/email_campaigns/spec/fixtures/locales/mailers.en.yml @@ -8,31 +8,20 @@ en: "comment_marked_as_spam": Comment spam report "comment_on_your_comment": A reply on my comment "comment_on_idea_you_follow": A comment on an idea that you follow - "comment_on_initiative_you_follow": A comment on a proposal that you follow - "cosponsor_of_your_initiative": A user accepts my invitation to co-sponor my proposal "idea_marked_as_spam": Idea spam report "idea_published": Publication of my input - "initiative_assigned_to_you": Assignment of a proposal to me - "initiative_marked_as_spam": Proposal spam report - "initiative_published": Publication of my proposal "internal_comment_on_idea_assigned_to_you": Internal comment on input assigned to me "internal_comment_on_idea_you_commented_internally_on": Internal comment on input I commented internally on "internal_comment_on_idea_you_moderate": Internal comment on input in project or folder I manage - "internal_comment_on_initiative_assigned_to_you": Internal comment on proposal assigned to me - "internal_comment_on_initiative_you_commented_internally_on": Internal comment on proposal I commented internally on - "internal_comment_on_unassigned_initiative": Internal comment on unassigned proposal "internal_comment_on_unassigned_unmoderated_idea": Internal comment on unassigned input in unmanaged project "internal_comment_on_your_internal_comment": Internal comment on my internal comment "invite_received": Invitation "invite_reminder": Invitation reminder - "invitation_to_cosponsor_initiative": Invitation to co-sponsor a proposal "mention_in_internal_comment": Mention in an internal comment "mention_in_official_feedback": Mention in an update "new_comment_for_admin": New comment in a project I moderate "new_idea_for_admin": New input in a project I moderate - "new_initiative_for_admin": New proposal "official_feedback_on_idea_you_follow": Update on an input that you follow - "official_feedback_on_initiative_you_follow": Update on a proposal that you follow "password_reset": Password reset "project_moderation_rights_received": Project moderation rights received "project_folder_moderation_rights_received": Project folder moderation rights received @@ -40,7 +29,6 @@ en: "project_phase_upcoming": Upcoming new project phase "project_published": Project published "status_change_on_idea_you_follow": Status change of an input that you follow - "status_change_on_initiative_you_follow": Status change of a proposal that you follow "threshold_reached_for_admin": Proposal reached the voting threshold "welcome": After registration "admin_digest": Weekly overview for admins @@ -99,8 +87,6 @@ en: new_comments: 'New comments' title_activity_past_week: 'Activity of the past week' title_no_activity_past_week: 'There was no activity in the past week' - title_initiatives_past_week: 'New proposals of the past week' - title_successful_initiatives_past_week: 'Proposals that reached the threshold in the past week' yesterday_by_author: 'Yesterday by %{author}' today_by_author: 'Today by %{author}' x_days_ago_by_author: '%{x} days ago by %{author}' @@ -131,12 +117,6 @@ en: today: Today wrong_content: 'The comment is not relevant.' yesterday: Yesterday - cosponsor_of_your_initiative: - cta_reply_to: 'View your proposal' - event_description_initiative: 'Congratulations! %{cosponsorName} has accepted your invitation to co-sponsor your proposal.' - main_header: '%{cosponsorName} has accepted your invitation to co-sponsor your proposal' - subject: '%{cosponsorName} has accepted your invitation to co-sponsor your proposal' - preheader_initiative: '%{cosponsorName} has accepted your invitation to co-sponsor your proposal' assignee_digest: subject: 'Ideas requiring your feedback: %{numberIdeas}' preheader: 'Assignee digest of %{organizationName}' @@ -149,9 +129,6 @@ en: today_by_author: 'Today by %{author}' x_days_ago_by_author: '%{x} days ago by %{author}' idea_section: Ideas - initiative_section: Proposals - title_initiatives_past_week: 'New proposals assigned to you' - title_successful_initiatives_past_week: 'Proposals assigned to you that reached the threshold' idea_marked_as_spam: cta_review_idea: 'Review idea' report_inappropriate_offensive_content: 'I find this content inappropriate or offensive.' @@ -160,20 +137,6 @@ en: preheader: 'Act upon this spam report' reported_this_because: '%{reporterFirstName} reported this because:' title_spam_report: '%{firstName} %{lastName} reported spam' - initiative_marked_as_spam: - cta_review_initiative: 'Review proposal' - report_inappropriate_offensive_content: 'I find this content inappropriate or offensive.' - report_not_an_initiative: 'This content is not a proposal and does not belong here.' - subject: 'You have a spam report on the platform of %{organizationName}' - preheader: 'Act upon this spam report' - reported_this_because: '%{reporterFirstName} reported this because:' - title_spam_report: '%{firstName} %{lastName} reported spam' - initiative_assigned_to_you: - cta_reply_to: 'Give feedback to %{authorName}' - event_description_initiative: 'An proposal has been assigned to you. Give feedback by writing an official update or by changing its status.' - main_header: '%{firstName}, you have a new assignment' - subject: 'You have an assignment on the platform of %{organizationName}' - preheader_initiative: 'The proposal of %{authorName} has been assigned to you' invite_reminder: cta_accept_invitation: 'Accept your invitation' invitation_header: 'Your invitation is pending' @@ -197,29 +160,6 @@ en: main_header: 'You have been invited to co-sponsor a proposal' subject: 'You have been invited to co-sponsor a proposal' preheader: 'You have been invited to co-sponsor the proposal of %{authorName}' - invitation_to_cosponsor_initative: - cta_reply_to: 'Co-sponsor this proposal' - event_description_initiative: '%{authorName} has created a new proposal and would like you to co-sponsor it.' - event_description_cosponsoring: 'Co-sponsoring a proposal means that your name will be displayed with the names of other co-sponsors of the proposal' - event_description_before_action: 'To see the proposal and accept the invitation, you must be logged in to your account.' - event_description_action: 'Click below to read the proposal' - main_header: 'You have been invited to co-sponsor a proposal' - subject: 'You have been invited to co-sponsor a proposal' - preheader_initiative: 'You have been invited to co-sponsor the proposal of %{authorName}' - initiative_published: - action_add_image: '%{addImageLink} to increase visibility' - action_published_initiative: 'Published proposal' - action_send_email: 'Send your contacts an %{sendEmailLink}' - action_share_fb: 'Let your friends know on %{fbLink}' - action_share_link: 'Share it via any channel by copying the %{link}' - action_share_twitter: 'Inform your followers on %{twitterLink}' - link: link - main_header: 'You posted a proposal! Let''s gather support for it.' - message_next_steps: '%{userFirstName}, your proposal has successfully been published on the participation platform of %{organizationName}. You will be notified when people interact with your proposal. Find out what next steps you can take below.' - message_get_votes: 'Reach the voting threshold:' - preheader: 'Thank you for sharing your proposal %{initiativeTitle} for %{organizationName}. The next step is to get support for your proposal by sharing it across your network.' - send_email: email - subject: 'Your proposal was published on the platform of %{organizationName}' new_comment_on_commented_idea: cta_reply_to: 'Reply to %{commentAuthor}' event_description: '%{commentAuthorFull} also wrote a comment on the idea ''%{ideaTitle}'' on the participation platform of %{organizationName}. Click the button below to start the conversation with %{commentAuthor}.' @@ -252,13 +192,6 @@ en: yesterday_by_author: 'Yesterday by %{author}' today_by_author: 'Today by %{author}' x_days_ago_by_author: '%{x} days ago by %{author}' - new_comment_on_commented_initiative: - commented: '%{commentAuthor} wrote:' - cta_reply_to: 'Reply to %{commentAuthor}' - event_description: '%{commentAuthorFull} also wrote a comment on the proposal ''%{initiativeTitle}'' on the participation platform of %{organizationName}. Click the button below to start the conversation with %{commentAuthor}.' - main_header: '%{commentAuthor} wrote a comment on the same proposal' - subject: 'There''s another comment on the proposal you commented on' - preheader: '%{commentAuthor} also left a comment on an proposal for %{organizationName}' new_comment_for_admin: commented: '%{authorFirstName} commented:' cta_reply_to: 'View %{commentAuthor}''s comment' @@ -275,18 +208,6 @@ en: main_header: '%{authorName} commented on an idea you follow' subject: 'There''s a new comment on an idea you follow' preheader: '%{authorName} left a comment on an idea for %{organizationName}' - comment_on_initiative_you_follow: - cta_reply_to: 'Reply to %{commentAuthor}' - event_description: '%{authorNameFull} placed a reaction on the initiative ''%{initiativeTitle}''. Click the button below to continue the conversation with %{authorName}.' - main_header: '%{commentAuthor} commented on an initiative you follow' - subject: 'There''s a new comment on the proposal you follow' - preheader: '%{authorName} left a comment on an initiative for %{organizationName}' - new_initiative_for_admin: - cta_reply_to: 'Give feedback to %{authorName}' - event_description: '%{authorName} has published a new proposal on your platform. Discover it now, give some feedback or change its status!' - main_header: '%{firstName}, a new proposal has been published on your platform' - subject: 'Someone published a new proposal on the platform of %{organizationName}' - preheader: '%{authorName} published a new proposal on your platform' new_idea_for_admin: new_idea_for_admin: main_header_publication: '%{firstName}, a new input has been published on your platform' @@ -331,24 +252,6 @@ en: subject: '''%{post}'' has a new internal comment' main_header: '''%{post}'' has a new internal comment' preheader: '''%{post}'' has a new internal comment' - internal_comment_on_initiative_assigned_to_you: - cta_reply_to: 'View comment by %{firstName}' - event_description: '%{authorNameFull} commented internally on a proposal assigned to you.' - subject: '''%{post}'' has a new internal comment' - main_header: '''%{post}'' has a new internal comment' - preheader: '''%{post}'' has a new internal comment' - internal_comment_on_initiative_you_commented_internally_on: - cta_reply_to: 'View comment by %{firstName}' - event_description: '%{authorNameFull} commented internally on a proposal you commented internally on.' - subject: '''%{post}'' has a new internal comment' - main_header: '''%{post}'' has a new internal comment' - preheader: '''%{post}'' has a new internal comment' - internal_comment_on_unassigned_initiative: - cta_reply_to: 'View comment by %{firstName}' - event_description: '%{authorNameFull} commented internally on an unassigned proposal.' - subject: '''%{post}'' has a new internal comment' - main_header: '''%{post}'' has a new internal comment' - preheader: '''%{post}'' has a new internal comment' internal_comment_on_unassigned_unmoderated_idea: cta_reply_to: 'View comment by %{firstName}' event_description: '%{authorNameFull} commented internally on an unassigned input in an unmanaged project.' @@ -360,11 +263,6 @@ en: header_title: 'There''s an update on an input you follow' subject: 'An input you follow has received an official update on the platform of %{organizationName}' preheader: 'There''s an update on an input you follow' - official_feedback_on_initiative_you_follow: - header_message: '%{officialName} gave an update on the proposal ''%{initiativeTitle}''. Click the button below to enter the conversation with %{organizationName}.' - header_title: 'There''s an update on a proposal you follow' - subject: 'A proposal you follow has received an official update on the platform of %{organizationName}' - preheader: 'There''s an update on a proposal you follow' mention_in_official_feedback: cta_reply_to: 'Reply to %{organizationName}' event_description: '%{organizationName} mentioned you in their feedback on the idea ''%{post}‘. Click the link below to enter the conversation with %{organizationName}' @@ -428,12 +326,6 @@ en: header_title: 'An input you follow has a new status' subject: 'The status of "%{input_title}" has changed' preheader: 'An input you follow has a new status' - status_change_on_initiative_you_follow: - status_change: 'The new status of this proposal is ''%{initiativeStatus}''' - header_message: 'The status of the proposal ''%{initiativeTitle}'' has been updated on the digital participation platform of %{organizationName}.' - header_title: 'A proposal you follow has a new status' - subject: 'The status of a proposal you follow has changed' - preheader: 'A proposal you follow has a new status' user_digest: subject: "Your activity on the participation platform of %{organizationName}" commented: "%{authorFirstName} commented:" diff --git a/back/engines/free/email_campaigns/spec/mailers/comment_on_initiative_you_follow_mailer_spec.rb b/back/engines/free/email_campaigns/spec/mailers/comment_on_initiative_you_follow_mailer_spec.rb deleted file mode 100644 index 2042e403a333..000000000000 --- a/back/engines/free/email_campaigns/spec/mailers/comment_on_initiative_you_follow_mailer_spec.rb +++ /dev/null @@ -1,53 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe EmailCampaigns::CommentOnInitiativeYouFollowMailer do - describe 'CommentOnInitiativeYouFollow' do - let_it_be(:recipient) { create(:user, locale: 'en') } - let_it_be(:campaign) { EmailCampaigns::Campaigns::CommentOnInitiativeYouFollow.create! } - let_it_be(:initiative) { create(:initiative) } - let_it_be(:initiator) { create(:user, first_name: 'Dries') } - let_it_be(:comment) { create(:comment, post: initiative, body_multiloc: { 'en' => 'I agree' }, author: initiator) } - let_it_be(:notification) { create(:comment_on_initiative_you_follow, recipient: recipient, post: initiative, comment: comment) } - let_it_be(:command) do - activity = create(:activity, item: notification, action: 'created') - create(:comment_on_initiative_you_follow_campaign).generate_commands( - activity: activity, - recipient: recipient - ).first.merge({ recipient: recipient }) - end - - let_it_be(:mail) { described_class.with(command: command, campaign: campaign).campaign_mail.deliver_now } - - before { EmailCampaigns::UnsubscriptionToken.create!(user_id: recipient.id) } - - it 'renders the subject' do - expect(mail.subject).to be_present - end - - it 'renders the receiver email' do - expect(mail.to).to eq([recipient.email]) - end - - it 'renders the sender email' do - expect(mail.from).to all(end_with('@citizenlab.co')) - end - - it 'assigns organisation name' do - expect(mail.body.encoded).to match(AppConfiguration.instance.settings('core', 'organization_name')['en']) - end - - it 'includes the comment author name' do - expect(mail.body.encoded).to include('Dries') - end - - it 'includes the comment body' do - expect(mail.body.encoded).to include('I agree') - end - - it 'includes the unfollow url' do - expect(mail.body.encoded).to match(Frontend::UrlService.new.unfollow_url(Follower.new(followable: initiative, user: recipient))) - end - end -end diff --git a/back/engines/free/email_campaigns/spec/mailers/cosponsor_of_your_initiative_mailer_spec.rb b/back/engines/free/email_campaigns/spec/mailers/cosponsor_of_your_initiative_mailer_spec.rb deleted file mode 100644 index 1ec16b426a74..000000000000 --- a/back/engines/free/email_campaigns/spec/mailers/cosponsor_of_your_initiative_mailer_spec.rb +++ /dev/null @@ -1,46 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -# TODO: move-old-proposals-test -RSpec.describe EmailCampaigns::CosponsorOfYourInitiativeMailer do - describe 'campaign_mail' do - let_it_be(:recipient) { create(:user, locale: 'en') } - let_it_be(:author) { create(:user) } - let_it_be(:cosponsor) { create(:user) } - let_it_be(:initiative) { create(:initiative, author: author) } - let_it_be(:campaign) { EmailCampaigns::Campaigns::CosponsorOfYourInitiative.create! } - let_it_be(:cosponsor_name) { UserDisplayNameService.new(AppConfiguration.instance, cosponsor).display_name!(cosponsor) } - let_it_be(:command) do - item = Notifications::CosponsorOfYourInitiative.new(post: initiative, initiating_user: cosponsor) - activity = Activity.new(item: item, user: author) - commands = EmailCampaigns::Campaigns::CosponsorOfYourInitiative.new.generate_commands(recipient: recipient, activity: activity) - commands[0].merge({ recipient: recipient }) - end - - let_it_be(:mail) { described_class.with(command: command, campaign: campaign).campaign_mail.deliver_now } - - before_all { EmailCampaigns::UnsubscriptionToken.create!(user_id: recipient.id) } - - it 'renders the subject' do - expect(mail.subject).to match('has accepted your invitation to co-sponsor your proposal') - end - - it 'renders the receiver email' do - expect(mail.to).to eq([recipient.email]) - end - - it 'renders the sender email' do - expect(mail.from).to all(end_with('@citizenlab.co')) - end - - it 'assigns initiative cosponsor name' do - expect(mail.body.encoded).to match(cosponsor_name) - end - - it 'assigns cta url' do - post_url = Frontend::UrlService.new.model_to_url(initiative, locale: Locale.new(recipient.locale)) - expect(mail.body.encoded).to match(post_url) - end - end -end diff --git a/back/engines/free/email_campaigns/spec/mailers/footer_spec.rb b/back/engines/free/email_campaigns/spec/mailers/footer_spec.rb index febaf59c6904..888ca058e9b7 100644 --- a/back/engines/free/email_campaigns/spec/mailers/footer_spec.rb +++ b/back/engines/free/email_campaigns/spec/mailers/footer_spec.rb @@ -10,15 +10,15 @@ let(:locale) { 'en' } let(:recipient) { create(:user, locale: locale) } - let(:campaign) { EmailCampaigns::Campaigns::CommentOnInitiativeYouFollow.create! } + let(:campaign) { EmailCampaigns::Campaigns::CommentOnIdeaYouFollow.create! } let(:command) do - create(:comment_on_initiative_you_follow_campaign).generate_commands( - activity: create(:activity, item: create(:comment_on_initiative_you_follow), action: 'created'), + create(:comment_on_idea_you_follow_campaign).generate_commands( + activity: create(:activity, item: create(:comment_on_idea_you_follow), action: 'created'), recipient: recipient ).first.merge({ recipient: recipient }) end - let(:mail) { EmailCampaigns::CommentOnInitiativeYouFollowMailer.with(command: command, campaign: campaign).campaign_mail.deliver_now } + let(:mail) { EmailCampaigns::CommentOnIdeaYouFollowMailer.with(command: command, campaign: campaign).campaign_mail.deliver_now } it 'includes Go Vocal logo' do expect(mail.body.encoded).to have_tag('a', with: { href: 'https://govocal.com/' }) do diff --git a/back/engines/free/email_campaigns/spec/mailers/initiative_assigned_to_you_mailer_spec.rb b/back/engines/free/email_campaigns/spec/mailers/initiative_assigned_to_you_mailer_spec.rb deleted file mode 100644 index cc93cf523fed..000000000000 --- a/back/engines/free/email_campaigns/spec/mailers/initiative_assigned_to_you_mailer_spec.rb +++ /dev/null @@ -1,53 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe EmailCampaigns::InitiativeAssignedToYouMailer do - describe 'campaign_mail' do - let_it_be(:recipient) { create(:user, locale: 'en') } - let_it_be(:campaign) { EmailCampaigns::Campaigns::InitiativeAssignedToYou.create! } - let_it_be(:assigned_at) { Time.zone.now } - let_it_be(:initiative) { create(:initiative, author: recipient, assignee: create(:admin), assigned_at: assigned_at) } - let_it_be(:author_name) { UserDisplayNameService.new(AppConfiguration.instance, recipient).display_name!(initiative.author) } - let_it_be(:command) do - { - recipient: recipient, - event_payload: { - post_title_multiloc: initiative.title_multiloc, - post_body_multiloc: initiative.body_multiloc, - post_author_name: author_name, - post_published_at: initiative.published_at&.iso8601, - post_url: Frontend::UrlService.new.model_to_url(initiative, locale: Locale.new(recipient.locale)), - post_assigned_at: initiative.assigned_at&.iso8601 || Time.now.iso8601, - initiative_reactions_needed: initiative.reactions_needed, - initiative_expires_at: initiative.expires_at.iso8601 - } - } - end - - let_it_be(:mail) { described_class.with(command: command, campaign: campaign).campaign_mail.deliver_now } - - before_all { EmailCampaigns::UnsubscriptionToken.create!(user_id: recipient.id) } - - it 'renders the subject' do - expect(mail.subject).to start_with('You have an assignment on the platform of') - end - - it 'renders the receiver email' do - expect(mail.to).to eq([recipient.email]) - end - - it 'renders the sender email' do - expect(mail.from).to all(end_with('@citizenlab.co')) - end - - it 'assigns initiative author name' do - expect(mail.body.encoded).to match(author_name) - end - - it 'assigns go to initiative CTA' do - admin_initiatives_url = Frontend::UrlService.new.admin_initiatives_url - expect(mail.body.encoded).to match(admin_initiatives_url) - end - end -end diff --git a/back/engines/free/email_campaigns/spec/mailers/initiative_marked_as_spam_mailer_spec.rb b/back/engines/free/email_campaigns/spec/mailers/initiative_marked_as_spam_mailer_spec.rb deleted file mode 100644 index 7106b6e2d348..000000000000 --- a/back/engines/free/email_campaigns/spec/mailers/initiative_marked_as_spam_mailer_spec.rb +++ /dev/null @@ -1,58 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe EmailCampaigns::InitiativeMarkedAsSpamMailer do - describe 'campaign_mail' do - let_it_be(:recipient) { create(:user, locale: 'en') } - let_it_be(:campaign) { EmailCampaigns::Campaigns::InitiativeMarkedAsSpam.create! } - let_it_be(:initiating_user) { create(:user) } - let_it_be(:initiative) { create(:initiative, author: recipient) } - let_it_be(:command) do - { - recipient: recipient, - event_payload: { - initiating_user_first_name: initiating_user&.first_name, - initiating_user_last_name: initiating_user&.last_name, - post_created_at: initiative.created_at.iso8601, - post_title_multiloc: initiative.title_multiloc, - post_author_name: initiative.author_name, - post_url: Frontend::UrlService.new.model_to_url(initiative, locale: Locale.new(recipient.locale)), - initiative_reactions_needed: initiative.reactions_needed, - initiative_expires_at: initiative.expires_at.iso8601, - spam_report_reason_code: 'other', - spam_report_other_reason: 'This post is an intrusion to my privacy' - } - } - end - - let_it_be(:mail) { described_class.with(command: command, campaign: campaign).campaign_mail.deliver_now } - - before { EmailCampaigns::UnsubscriptionToken.create!(user_id: recipient.id) } - - it 'renders the subject' do - expect(mail.subject).to start_with('You have a spam report on the platform of') - end - - it 'renders the receiver email' do - expect(mail.to).to eq([recipient.email]) - end - - it 'renders the sender email' do - expect(mail.from).to all(end_with('@citizenlab.co')) - end - - it 'assigns reporter\'s name' do - expect(mail.body.encoded).to match(initiating_user.first_name) - end - - it 'assigns the reason' do - expect(mail.body.encoded).to match('This post is an intrusion to my privacy') - end - - it 'assigns go to initiative CTA' do - initiative_url = Frontend::UrlService.new.model_to_url(initiative, locale: Locale.new(recipient.locale)) - expect(mail.body.encoded).to match(initiative_url) - end - end -end diff --git a/back/engines/free/email_campaigns/spec/mailers/initiative_published_mailer_spec.rb b/back/engines/free/email_campaigns/spec/mailers/initiative_published_mailer_spec.rb deleted file mode 100644 index 13b017a19bbd..000000000000 --- a/back/engines/free/email_campaigns/spec/mailers/initiative_published_mailer_spec.rb +++ /dev/null @@ -1,58 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe EmailCampaigns::InitiativePublishedMailer do - describe 'campaign_mail' do - let_it_be(:recipient) { create(:user, locale: 'en') } - let_it_be(:campaign) { EmailCampaigns::Campaigns::InitiativePublished.create! } - let_it_be(:initiative) { create(:initiative, author: recipient) } - let_it_be(:command) do - { - recipient: recipient, - event_payload: { - post_id: initiative.id, - post_title_multiloc: initiative.title_multiloc, - post_body_multiloc: initiative.body_multiloc, - post_url: Frontend::UrlService.new.model_to_url(initiative, locale: Locale.new(recipient.locale)), - post_images: initiative.initiative_images.map do |image| - { - ordering: image.ordering, - versions: image.image.versions.to_h { |k, v| [k.to_s, v.url] } - } - end, - initiative_header_bg: { - versions: initiative.header_bg.versions.to_h { |k, v| [k.to_s, v.url] } - }, - initiative_reactions_needed: initiative.reactions_needed, - initiative_expires_at: initiative.expires_at.iso8601 - } - } - end - - let_it_be(:mail) { described_class.with(command: command, campaign: campaign).campaign_mail.deliver_now } - - before_all { EmailCampaigns::UnsubscriptionToken.create!(user_id: recipient.id) } - - it 'renders the subject' do - expect(mail.subject).to start_with('Your proposal was published on the platform of') - end - - it 'renders the receiver email' do - expect(mail.to).to eq([recipient.email]) - end - - it 'renders the sender email' do - expect(mail.from).to all(end_with('@citizenlab.co')) - end - - it 'assigns organisation name' do - expect(mail.body.encoded).to match(AppConfiguration.instance.settings('core', 'organization_name', 'en')) - end - - it 'assigns go to initiative CTA' do - initiative_url = Frontend::UrlService.new.model_to_url(initiative, locale: Locale.new(recipient.locale)) - expect(mail.body.encoded).to match(initiative_url) - end - end -end diff --git a/back/engines/free/email_campaigns/spec/mailers/initiative_resubmitted_for_review_mailer_spec.rb b/back/engines/free/email_campaigns/spec/mailers/initiative_resubmitted_for_review_mailer_spec.rb deleted file mode 100644 index c6f4f7f78a5e..000000000000 --- a/back/engines/free/email_campaigns/spec/mailers/initiative_resubmitted_for_review_mailer_spec.rb +++ /dev/null @@ -1,45 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -# TODO: move-old-proposals-test -RSpec.describe EmailCampaigns::InitiativeResubmittedForReviewMailer do - describe 'campaign_mail' do - let_it_be(:recipient) { create(:user, locale: 'en') } - let_it_be(:author) { create(:user) } - let_it_be(:initiative) { create(:initiative, author: author) } - let_it_be(:campaign) { EmailCampaigns::Campaigns::InitiativeResubmittedForReview.create! } - let_it_be(:author_name) { UserDisplayNameService.new(AppConfiguration.instance, author).display_name!(initiative.author) } - let_it_be(:command) do - item = Notifications::InitiativeResubmittedForReview.new(post: initiative) - activity = Activity.new(item: item) - commands = EmailCampaigns::Campaigns::InitiativeResubmittedForReview.new.generate_commands(recipient: recipient, activity: activity) - commands[0].merge({ recipient: recipient }) - end - - let_it_be(:mail) { described_class.with(command: command, campaign: campaign).campaign_mail.deliver_now } - - before_all { EmailCampaigns::UnsubscriptionToken.create!(user_id: recipient.id) } - - it 'renders the subject' do - expect(mail.subject).to start_with('You have a proposal to review on the platform of') - end - - it 'renders the receiver email' do - expect(mail.to).to eq([recipient.email]) - end - - it 'renders the sender email' do - expect(mail.from).to all(end_with('@citizenlab.co')) - end - - it 'assigns initiative author name' do - expect(mail.body.encoded).to match(author_name) - end - - it 'assigns cta url' do - admin_initiatives_url = Frontend::UrlService.new.admin_initiatives_url - expect(mail.body.encoded).to match(admin_initiatives_url) - end - end -end diff --git a/back/engines/free/email_campaigns/spec/mailers/internal_comment_on_initiative_assigned_to_you_mailer_spec.rb b/back/engines/free/email_campaigns/spec/mailers/internal_comment_on_initiative_assigned_to_you_mailer_spec.rb deleted file mode 100644 index c1e9e20328bc..000000000000 --- a/back/engines/free/email_campaigns/spec/mailers/internal_comment_on_initiative_assigned_to_you_mailer_spec.rb +++ /dev/null @@ -1,36 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe EmailCampaigns::InternalCommentOnInitiativeAssignedToYouMailer do - describe 'InternalCommentOnInitiativeAssignedToYouMailer' do - let_it_be(:recipient) { create(:user, locale: 'en') } - let_it_be(:post_image) { create(:idea_image) } - let_it_be(:campaign) { EmailCampaigns::Campaigns::InternalCommentOnInitiativeAssignedToYou.create! } - let_it_be(:token) { ResetPasswordService.new.generate_reset_password_token recipient } - let_it_be(:command) do - { - recipient: recipient, - event_payload: { - initiating_user_first_name: 'Matthias', - initiating_user_last_name: 'Geeke', - internal_comment_author_name: 'Matthias Geeke', - internal_comment_body: 'I agree. I really think this proposal is amazing! Wowzers!', - internal_comment_url: 'http://localhost:3000/en/internal_comments/fake-url-comment-does-not-exist', - post_title_multiloc: { en: 'Permit paving of front gardens' }, - post_body_multiloc: { - en: 'There are many advantages to paving your front garden. Less cars on the road and more space for pedestrians.' - }, - post_type: 'Initiative', - post_image_medium_url: post_image.image.versions[:medium].url - } - } - end - - let_it_be(:mail) { described_class.with(command: command, campaign: campaign).campaign_mail.deliver_now } - - before_all { EmailCampaigns::UnsubscriptionToken.create!(user_id: recipient.id) } - - include_examples 'internal_comment_campaign_mailer_examples' - end -end diff --git a/back/engines/free/email_campaigns/spec/mailers/internal_comment_on_initiative_you_commented_internally_on_mailer_spec.rb b/back/engines/free/email_campaigns/spec/mailers/internal_comment_on_initiative_you_commented_internally_on_mailer_spec.rb deleted file mode 100644 index 019256ecf14a..000000000000 --- a/back/engines/free/email_campaigns/spec/mailers/internal_comment_on_initiative_you_commented_internally_on_mailer_spec.rb +++ /dev/null @@ -1,36 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe EmailCampaigns::InternalCommentOnInitiativeYouCommentedInternallyOnMailer do - describe 'InternalCommentOnInitiativeYouCommentedInternallyOnMailer' do - let_it_be(:recipient) { create(:user, locale: 'en') } - let_it_be(:post_image) { create(:idea_image) } - let_it_be(:campaign) { EmailCampaigns::Campaigns::InternalCommentOnInitiativeYouCommentedInternallyOn.create! } - let_it_be(:token) { ResetPasswordService.new.generate_reset_password_token recipient } - let_it_be(:command) do - { - recipient: recipient, - event_payload: { - initiating_user_first_name: 'Matthias', - initiating_user_last_name: 'Geeke', - internal_comment_author_name: 'Matthias Geeke', - internal_comment_body: 'I also think this proposal is amazing! Wowzers!', - internal_comment_url: 'http://localhost:3000/en/internal_comments/fake-url-comment-does-not-exist', - post_title_multiloc: { en: 'Permit paving of front gardens' }, - post_body_multiloc: { - en: 'There are many advantages to paving your front garden. Less cars on the road and more space for pedestrians.' - }, - post_type: 'Initiative', - post_image_medium_url: post_image.image.versions[:medium].url - } - } - end - - let_it_be(:mail) { described_class.with(command: command, campaign: campaign).campaign_mail.deliver_now } - - before_all { EmailCampaigns::UnsubscriptionToken.create!(user_id: recipient.id) } - - include_examples 'internal_comment_campaign_mailer_examples' - end -end diff --git a/back/engines/free/email_campaigns/spec/mailers/internal_comment_on_unassigned_initiative_mailer_spec.rb b/back/engines/free/email_campaigns/spec/mailers/internal_comment_on_unassigned_initiative_mailer_spec.rb deleted file mode 100644 index 06f9c022491d..000000000000 --- a/back/engines/free/email_campaigns/spec/mailers/internal_comment_on_unassigned_initiative_mailer_spec.rb +++ /dev/null @@ -1,36 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe EmailCampaigns::InternalCommentOnUnassignedInitiativeMailer do - describe 'InternalCommentOnUnassignedInitiativeMailer' do - let_it_be(:recipient) { create(:user, locale: 'en') } - let_it_be(:post_image) { create(:idea_image) } - let_it_be(:campaign) { EmailCampaigns::Campaigns::InternalCommentOnUnassignedInitiative.create! } - let_it_be(:token) { ResetPasswordService.new.generate_reset_password_token recipient } - let_it_be(:command) do - { - recipient: recipient, - event_payload: { - initiating_user_first_name: 'Matthias', - initiating_user_last_name: 'Geeke', - internal_comment_author_name: 'Matthias Geeke', - internal_comment_body: 'I think this proposal is amazing! Wowzers!', - internal_comment_url: 'http://localhost:3000/en/internal_comments/fake-url-comment-does-not-exist', - post_title_multiloc: { en: 'Permit paving of front gardens' }, - post_body_multiloc: { - en: 'There are many advantages to paving your front garden. Less cars on the road and more space for pedestrians.' - }, - post_type: 'Initiative', - post_image_medium_url: post_image.image.versions[:medium].url - } - } - end - - let_it_be(:mail) { described_class.with(command: command, campaign: campaign).campaign_mail.deliver_now } - - before_all { EmailCampaigns::UnsubscriptionToken.create!(user_id: recipient.id) } - - include_examples 'internal_comment_campaign_mailer_examples' - end -end diff --git a/back/engines/free/email_campaigns/spec/mailers/invitation_to_cosponsor_initiative_mailer_spec.rb b/back/engines/free/email_campaigns/spec/mailers/invitation_to_cosponsor_initiative_mailer_spec.rb deleted file mode 100644 index c34322c99cf6..000000000000 --- a/back/engines/free/email_campaigns/spec/mailers/invitation_to_cosponsor_initiative_mailer_spec.rb +++ /dev/null @@ -1,45 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -# TODO: move-old-proposals-test -RSpec.describe EmailCampaigns::InvitationToCosponsorInitiativeMailer do - describe 'campaign_mail' do - let_it_be(:recipient) { create(:user, locale: 'en') } - let_it_be(:author) { create(:user) } - let_it_be(:initiative) { create(:initiative, author: author) } - let_it_be(:campaign) { EmailCampaigns::Campaigns::InvitationToCosponsorInitiative.create! } - let_it_be(:author_name) { UserDisplayNameService.new(AppConfiguration.instance, author).display_name!(initiative.author) } - let_it_be(:command) do - item = Notifications::InvitationToCosponsorInitiative.new(post: initiative) - activity = Activity.new(item: item) - commands = EmailCampaigns::Campaigns::InvitationToCosponsorInitiative.new.generate_commands(recipient: recipient, activity: activity) - commands[0].merge({ recipient: recipient }) - end - - let_it_be(:mail) { described_class.with(command: command, campaign: campaign).campaign_mail.deliver_now } - - before_all { EmailCampaigns::UnsubscriptionToken.create!(user_id: recipient.id) } - - it 'renders the subject' do - expect(mail.subject).to start_with('You have been invited to co-sponsor a proposal') - end - - it 'renders the receiver email' do - expect(mail.to).to eq([recipient.email]) - end - - it 'renders the sender email' do - expect(mail.from).to all(end_with('@citizenlab.co')) - end - - it 'assigns initiative author name' do - expect(mail.body.encoded).to match(author_name) - end - - it 'assigns cta url' do - post_url = Frontend::UrlService.new.model_to_url(initiative, locale: Locale.new(recipient.locale)) - expect(mail.body.encoded).to match(post_url) - end - end -end diff --git a/back/engines/free/email_campaigns/spec/mailers/new_initiative_for_admin_mailer_spec.rb b/back/engines/free/email_campaigns/spec/mailers/new_initiative_for_admin_mailer_spec.rb deleted file mode 100644 index 97c38455c4d6..000000000000 --- a/back/engines/free/email_campaigns/spec/mailers/new_initiative_for_admin_mailer_spec.rb +++ /dev/null @@ -1,49 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe EmailCampaigns::NewInitiativeForAdminMailer do - describe 'campaign_mail' do - let_it_be(:recipient) { create(:user, locale: 'en') } - let_it_be(:campaign) { EmailCampaigns::Campaigns::NewInitiativeForAdmin.create! } - let_it_be(:initiative) { create(:initiative, author: recipient) } - let_it_be(:command) do - { - recipient: recipient, - event_payload: { - post_published_at: initiative.published_at.iso8601, - post_title_multiloc: initiative.title_multiloc, - post_author_name: initiative.author_name, - post_url: Frontend::UrlService.new.model_to_url(initiative, locale: Locale.new(recipient.locale)), - initiative_reactions_needed: initiative.reactions_needed, - initiative_expires_at: initiative.expires_at.iso8601 - } - } - end - - let_it_be(:mail) { described_class.with(command: command, campaign: campaign).campaign_mail.deliver_now } - - before_all { EmailCampaigns::UnsubscriptionToken.create!(user_id: recipient.id) } - - it 'renders the subject' do - expect(mail.subject).to start_with('Someone published a new proposal on the platform of') - end - - it 'renders the receiver email' do - expect(mail.to).to eq([recipient.email]) - end - - it 'renders the sender email' do - expect(mail.from).to all(end_with('@citizenlab.co')) - end - - it 'assigns author name' do - expect(mail.body.encoded).to match(initiative.author_name) - end - - it 'assigns go to initiative CTA' do - initiative_url = Frontend::UrlService.new.model_to_url(initiative, locale: Locale.new(recipient.locale)) - expect(mail.body.encoded).to match(initiative_url) - end - end -end diff --git a/back/engines/free/email_campaigns/spec/mailers/official_feedback_on_initiative_you_follow_mailer_spec.rb b/back/engines/free/email_campaigns/spec/mailers/official_feedback_on_initiative_you_follow_mailer_spec.rb deleted file mode 100644 index bd1ace30947c..000000000000 --- a/back/engines/free/email_campaigns/spec/mailers/official_feedback_on_initiative_you_follow_mailer_spec.rb +++ /dev/null @@ -1,48 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe EmailCampaigns::OfficialFeedbackOnInitiativeYouFollowMailer do - describe 'campaign_mail' do - let_it_be(:recipient) { create(:user, locale: 'en') } - let_it_be(:initiative) { create(:initiative, title_multiloc: { 'en' => 'Initiative title' }) } - let_it_be(:feedback) { create(:official_feedback, body_multiloc: { 'en' => 'We appreciate your participation' }, post: initiative) } - let_it_be(:campaign) { EmailCampaigns::Campaigns::OfficialFeedbackOnInitiativeYouFollow.create! } - let_it_be(:notification) { create(:official_feedback_on_initiative_you_follow, recipient: recipient, post: initiative, official_feedback: feedback) } - let_it_be(:command) do - activity = create(:activity, item: notification, action: 'created') - create(:official_feedback_on_initiative_you_follow_campaign).generate_commands( - activity: activity, - recipient: recipient - ).first.merge({ recipient: recipient }) - end - - let_it_be(:mail) { described_class.with(command: command, campaign: campaign).campaign_mail.deliver_now } - - before { EmailCampaigns::UnsubscriptionToken.create!(user_id: recipient.id) } - - it 'renders the subject' do - expect(mail.subject).to start_with('A proposal you follow has received an official update') - end - - it 'renders the sender email' do - expect(mail.from).to all(end_with('@citizenlab.co')) - end - - it 'assigns organisation name' do - expect(mail.body.encoded).to match(AppConfiguration.instance.settings('core', 'organization_name', 'en')) - end - - it 'assigns cta url' do - expect(mail.body.encoded).to match(command.dig(:event_payload, :official_feedback_url)) - end - - it 'includes the initiative title' do - expect(mail.body.encoded).to match('Initiative title') - end - - it 'includes the unfollow url' do - expect(mail.body.encoded).to match(Frontend::UrlService.new.unfollow_url(Follower.new(followable: notification.post, user: recipient))) - end - end -end diff --git a/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/comment_on_initiative_you_follow_mailer_preview.rb b/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/comment_on_initiative_you_follow_mailer_preview.rb deleted file mode 100644 index 96cc5395ce50..000000000000 --- a/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/comment_on_initiative_you_follow_mailer_preview.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -module EmailCampaigns - class CommentOnInitiativeYouFollowMailerPreview < ActionMailer::Preview - include EmailCampaigns::MailerPreviewRecipient - - def campaign_mail - comment = Comment.where(post_type: 'Initiative').first || Comment.create(post: Initiative.first, author: User.first, body_multiloc: { 'en' => 'I agree' }) - notification = Notifications::CommentOnInitiativeYouFollow.create!( - recipient_id: recipient_user.id, - initiating_user: comment.author, - post: comment.post, - comment: comment - ) - activity = Activity.new(item: notification, action: 'created') - - campaign = EmailCampaigns::Campaigns::CommentOnInitiativeYouFollow.first - command = campaign.generate_commands( - activity: activity, - recipient: recipient_user - ).first.merge({ recipient: recipient_user }) - campaign.mailer_class.with(campaign: campaign, command: command).campaign_mail - end - end -end diff --git a/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/cosponsor_of_your_initiative_mailer_preview.rb b/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/cosponsor_of_your_initiative_mailer_preview.rb deleted file mode 100644 index 27dfe1df492f..000000000000 --- a/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/cosponsor_of_your_initiative_mailer_preview.rb +++ /dev/null @@ -1,19 +0,0 @@ -# frozen_string_literal: true - -module EmailCampaigns - class CosponsorOfYourInitiativeMailerPreview < ActionMailer::Preview - include EmailCampaigns::MailerPreviewRecipient - - def campaign_mail - campaign = EmailCampaigns::Campaigns::CosponsorOfYourInitiative.first - initiative = Initiative.order(created_at: :asc).first - user = User.order(created_at: :asc).first - item = Notifications::CosponsorOfYourInitiative.new(post: initiative) - activity = Activity.new(item: item, user: user) - commands = EmailCampaigns::Campaigns::CosponsorOfYourInitiative.new.generate_commands(recipient: user, activity: activity) - command = commands[0].merge({ recipient: user }) - - campaign.mailer_class.with(campaign: campaign, command: command).campaign_mail - end - end -end diff --git a/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/initiative_assigned_to_you_mailer_preview.rb b/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/initiative_assigned_to_you_mailer_preview.rb deleted file mode 100644 index 51e22cd563e7..000000000000 --- a/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/initiative_assigned_to_you_mailer_preview.rb +++ /dev/null @@ -1,28 +0,0 @@ -# frozen_string_literal: true - -module EmailCampaigns - class InitiativeAssignedToYouMailerPreview < ActionMailer::Preview - include EmailCampaigns::MailerPreviewRecipient - - def campaign_mail - initiative = Initiative.order(created_at: :asc).first - # TODO: generate commands with campaign#generate_commands method - command = { - recipient: recipient_user, - event_payload: { - post_title_multiloc: initiative.title_multiloc, - post_body_multiloc: initiative.body_multiloc, - post_author_name: UserDisplayNameService.new(AppConfiguration.instance, recipient_user).display_name!(initiative.author), - post_published_at: initiative.published_at&.iso8601, - post_url: Frontend::UrlService.new.model_to_url(initiative, locale: Locale.new(recipient_user.locale)), - post_assigned_at: initiative.assigned_at&.iso8601 || Time.now.iso8601, - initiative_reactions_needed: initiative.reactions_needed, - initiative_expires_at: initiative.expires_at.iso8601 - } - } - campaign = EmailCampaigns::Campaigns::InitiativeAssignedToYou.first - - campaign.mailer_class.with(campaign: campaign, command: command).campaign_mail - end - end -end diff --git a/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/initiative_marked_as_spam_mailer_preview.rb b/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/initiative_marked_as_spam_mailer_preview.rb deleted file mode 100644 index cdeeb07a0016..000000000000 --- a/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/initiative_marked_as_spam_mailer_preview.rb +++ /dev/null @@ -1,31 +0,0 @@ -# frozen_string_literal: true - -module EmailCampaigns - class InitiativeMarkedAsSpamMailerPreview < ActionMailer::Preview - include EmailCampaigns::MailerPreviewRecipient - - def campaign_mail - initiative = Initiative.first - initiating_user = User.last - # TODO: generate commands with campaign#generate_commands method - command = { - recipient: recipient_user, - event_payload: { - initiating_user_first_name: initiating_user&.first_name, - initiating_user_last_name: initiating_user&.last_name, - post_created_at: initiative.created_at.iso8601, - post_title_multiloc: initiative.title_multiloc, - post_author_name: initiative.author_name, - post_url: Frontend::UrlService.new.model_to_url(initiative, locale: Locale.new(recipient_user.locale)), - initiative_reactions_needed: initiative.reactions_needed, - initiative_expires_at: initiative.expires_at.iso8601, - spam_report_reason_code: 'other', - spam_report_other_reason: 'This post is an intrusion to my privacy' - } - } - campaign = EmailCampaigns::Campaigns::InitiativeMarkedAsSpam.first - - campaign.mailer_class.with(campaign: campaign, command: command).campaign_mail - end - end -end diff --git a/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/initiative_published_mailer_preview.rb b/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/initiative_published_mailer_preview.rb deleted file mode 100644 index 6b9109be956d..000000000000 --- a/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/initiative_published_mailer_preview.rb +++ /dev/null @@ -1,35 +0,0 @@ -# frozen_string_literal: true - -module EmailCampaigns - class InitiativePublishedMailerPreview < ActionMailer::Preview - include EmailCampaigns::MailerPreviewRecipient - - def campaign_mail - initiative = Initiative.first - # TODO: generate commands with campaign#generate_commands method - command = { - recipient: recipient_user, - event_payload: { - post_id: initiative.id, - post_title_multiloc: initiative.title_multiloc, - post_body_multiloc: initiative.body_multiloc, - post_url: Frontend::UrlService.new.model_to_url(initiative, locale: Locale.new(recipient_user.locale)), - post_images: initiative.initiative_images.map do |image| - { - ordering: image.ordering, - versions: image.image.versions.to_h { |k, v| [k.to_s, v.url] } - } - end, - initiative_header_bg: { - versions: initiative.header_bg.versions.to_h { |k, v| [k.to_s, v.url] } - }, - initiative_reactions_needed: initiative.reactions_needed, - initiative_expires_at: initiative.expires_at.iso8601 - } - } - campaign = EmailCampaigns::Campaigns::InitiativePublished.first - - campaign.mailer_class.with(campaign: campaign, command: command).campaign_mail - end - end -end diff --git a/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/initiative_resubmitted_for_review_mailer_preview.rb b/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/initiative_resubmitted_for_review_mailer_preview.rb deleted file mode 100644 index 31388f5b3df3..000000000000 --- a/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/initiative_resubmitted_for_review_mailer_preview.rb +++ /dev/null @@ -1,19 +0,0 @@ -# frozen_string_literal: true - -module EmailCampaigns - class InitiativeResubmittedForReviewMailerPreview < ActionMailer::Preview - include EmailCampaigns::MailerPreviewRecipient - - def campaign_mail - campaign = EmailCampaigns::Campaigns::InitiativeResubmittedForReview.first - initiative = Initiative.order(created_at: :asc).first - user = User.order(created_at: :asc).first - item = Notifications::InitiativeResubmittedForReview.new(post: initiative) - activity = Activity.new(item: item) - commands = EmailCampaigns::Campaigns::InitiativeResubmittedForReview.new.generate_commands(recipient: user, activity: activity) - command = commands[0].merge({ recipient: user }) - - campaign.mailer_class.with(campaign: campaign, command: command).campaign_mail - end - end -end diff --git a/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/internal_comment_on_initiative_assigned_to_you_mailer_preview.rb b/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/internal_comment_on_initiative_assigned_to_you_mailer_preview.rb deleted file mode 100644 index 3667b9257f29..000000000000 --- a/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/internal_comment_on_initiative_assigned_to_you_mailer_preview.rb +++ /dev/null @@ -1,31 +0,0 @@ -# frozen_string_literal: true - -module EmailCampaigns - class InternalCommentOnInitiativeAssignedToYouMailerPreview < ActionMailer::Preview - include EmailCampaigns::MailerPreviewRecipient - - def campaign_mail - # TODO: generate commands with campaign#generate_commands method - command = { - recipient: recipient_user, - event_payload: { - initiating_user_first_name: 'Matthias', - initiating_user_last_name: 'Geeke', - internal_comment_author_name: 'Matthias Geeke', - internal_comment_body: 'I agree. I really think this proposal is amazing!', - internal_comment_url: 'http://localhost:3000/en/internal_comments/fake-url-comment-does-not-exist', - post_title_multiloc: { en: 'Permit paving of front gardens' }, - post_body_multiloc: { - en: 'There are many advantages to paving your front garden. Less cars on the road and more space for pedestrians.' - }, - post_type: 'Initiative', - post_image_medium_url: InitiativeImage.first.image.versions[:medium].url - } - } - - campaign = EmailCampaigns::Campaigns::InternalCommentOnInitiativeAssignedToYou.first - - campaign.mailer_class.with(campaign: campaign, command: command).campaign_mail - end - end -end diff --git a/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/internal_comment_on_initiative_you_commented_internally_on_mailer_preview.rb b/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/internal_comment_on_initiative_you_commented_internally_on_mailer_preview.rb deleted file mode 100644 index e74f00c7d510..000000000000 --- a/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/internal_comment_on_initiative_you_commented_internally_on_mailer_preview.rb +++ /dev/null @@ -1,31 +0,0 @@ -# frozen_string_literal: true - -module EmailCampaigns - class InternalCommentOnInitiativeYouCommentedInternallyOnMailerPreview < ActionMailer::Preview - include EmailCampaigns::MailerPreviewRecipient - - def campaign_mail - # TODO: generate commands with campaign#generate_commands method - command = { - recipient: recipient_user, - event_payload: { - initiating_user_first_name: 'Matthias', - initiating_user_last_name: 'Geeke', - internal_comment_author_name: 'Matthias Geeke', - internal_comment_body: 'I also think this proposal is amazing!', - internal_comment_url: 'http://localhost:3000/en/internal_comments/fake-url-comment-does-not-exist', - post_title_multiloc: { en: 'Permit paving of front gardens' }, - post_body_multiloc: { - en: 'There are many advantages to paving your front garden. Less cars on the road and more space for pedestrians.' - }, - post_type: 'Initiative', - post_image_medium_url: InitiativeImage.first.image.versions[:medium].url - } - } - - campaign = EmailCampaigns::Campaigns::InternalCommentOnInitiativeYouCommentedInternallyOn.first - - campaign.mailer_class.with(campaign: campaign, command: command).campaign_mail - end - end -end diff --git a/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/internal_comment_on_unassigned_initiative_mailer_preview.rb b/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/internal_comment_on_unassigned_initiative_mailer_preview.rb deleted file mode 100644 index f0ec38d39870..000000000000 --- a/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/internal_comment_on_unassigned_initiative_mailer_preview.rb +++ /dev/null @@ -1,31 +0,0 @@ -# frozen_string_literal: true - -module EmailCampaigns - class InternalCommentOnUnassignedInitiativeMailerPreview < ActionMailer::Preview - include EmailCampaigns::MailerPreviewRecipient - - def campaign_mail - # TODO: generate commands with campaign#generate_commands method - command = { - recipient: recipient_user, - event_payload: { - initiating_user_first_name: 'Matthias', - initiating_user_last_name: 'Geeke', - internal_comment_author_name: 'Matthias Geeke', - internal_comment_body: 'I think this proposal is amazing!', - internal_comment_url: 'http://localhost:3000/en/internal_comments/fake-url-comment-does-not-exist', - post_title_multiloc: { en: 'Permit paving of front gardens' }, - post_body_multiloc: { - en: 'There are many advantages to paving your front garden. Less cars on the road and more space for pedestrians.' - }, - post_type: 'Initiative', - post_image_medium_url: InitiativeImage.first.image.versions[:medium].url - } - } - - campaign = EmailCampaigns::Campaigns::InternalCommentOnUnassignedInitiative.first - - campaign.mailer_class.with(campaign: campaign, command: command).campaign_mail - end - end -end diff --git a/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/invitation_to_cosponsor_initiative_mailer_preview.rb b/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/invitation_to_cosponsor_initiative_mailer_preview.rb deleted file mode 100644 index 835a4ea94466..000000000000 --- a/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/invitation_to_cosponsor_initiative_mailer_preview.rb +++ /dev/null @@ -1,19 +0,0 @@ -# frozen_string_literal: true - -module EmailCampaigns - class InvitationToCosponsorInitiativeMailerPreview < ActionMailer::Preview - include EmailCampaigns::MailerPreviewRecipient - - def campaign_mail - campaign = EmailCampaigns::Campaigns::InvitationToCosponsorInitiative.first - initiative = Initiative.order(created_at: :asc).first - user = User.order(created_at: :asc).first - item = Notifications::InvitationToCosponsorInitiative.new(post: initiative) - activity = Activity.new(item: item) - commands = EmailCampaigns::Campaigns::InvitationToCosponsorInitiative.new.generate_commands(recipient: user, activity: activity) - command = commands[0].merge({ recipient: user }) - - campaign.mailer_class.with(campaign: campaign, command: command).campaign_mail - end - end -end diff --git a/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/new_initiative_for_admin_mailer_preview.rb b/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/new_initiative_for_admin_mailer_preview.rb deleted file mode 100644 index 944fc3893a1a..000000000000 --- a/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/new_initiative_for_admin_mailer_preview.rb +++ /dev/null @@ -1,26 +0,0 @@ -# frozen_string_literal: true - -module EmailCampaigns - class NewInitiativeForAdminMailerPreview < ActionMailer::Preview - include EmailCampaigns::MailerPreviewRecipient - - def campaign_mail - initiative = Initiative.first - # TODO: generate commands with campaign#generate_commands method - command = { - recipient: recipient_user, - event_payload: { - post_published_at: initiative.published_at.iso8601, - post_title_multiloc: initiative.title_multiloc, - post_author_name: initiative.author_name, - post_url: Frontend::UrlService.new.model_to_url(initiative, locale: Locale.new(recipient_user.locale)), - initiative_reactions_needed: initiative.reactions_needed, - initiative_expires_at: initiative.expires_at.iso8601 - } - } - campaign = EmailCampaigns::Campaigns::NewInitiativeForAdmin.first - - campaign.mailer_class.with(campaign: campaign, command: command).campaign_mail - end - end -end diff --git a/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/official_feedback_on_initiative_you_follow_mailer_preview.rb b/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/official_feedback_on_initiative_you_follow_mailer_preview.rb deleted file mode 100644 index 3b92ef49b676..000000000000 --- a/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/official_feedback_on_initiative_you_follow_mailer_preview.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -module EmailCampaigns - class OfficialFeedbackOnInitiativeYouFollowMailerPreview < ActionMailer::Preview - include EmailCampaigns::MailerPreviewRecipient - - def campaign_mail - campaign = EmailCampaigns::Campaigns::OfficialFeedbackOnInitiativeYouFollow.first - - command = { - recipient: recipient_user, - event_payload: { - official_feedback_author_multiloc: { 'en' => 'City of Plattsburgh Official' }, - official_feedback_body_multiloc: { 'en' => 'Thank you for taking the time to comment on our parks. We value your feedback and will be looking at improving the dog park in the same way the one at the city beach has been improved.' }, - official_feedback_url: 'https://demo.stg.govocal.com', - post_published_at: Time.zone.today.prev_week.iso8601, - post_title_multiloc: { 'en' => 'Fence around the park' }, - post_author_name: 'Julia Langer' - } - } - - campaign.mailer_class.with(campaign: campaign, command: command).campaign_mail - end - end -end diff --git a/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/status_change_on_initiative_you_follow_mailer_preview.rb b/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/status_change_on_initiative_you_follow_mailer_preview.rb deleted file mode 100644 index d864c049650c..000000000000 --- a/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/status_change_on_initiative_you_follow_mailer_preview.rb +++ /dev/null @@ -1,18 +0,0 @@ -# frozen_string_literal: true - -module EmailCampaigns - class StatusChangeOnInitiativeYouFollowMailerPreview < ActionMailer::Preview - include EmailCampaigns::MailerPreviewRecipient - - def campaign_mail - campaign = EmailCampaigns::Campaigns::StatusChangeOnInitiativeYouFollow.first - - command = campaign.generate_commands( - recipient: recipient_user, - activity: Activity.new(item: Notification.new(post: Initiative.first)) - ).first.merge({ recipient: recipient_user }) - - campaign.mailer_class.with(campaign: campaign, command: command).campaign_mail - end - end -end diff --git a/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/threshold_reached_for_admin_mailer_preview.rb b/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/threshold_reached_for_admin_mailer_preview.rb index 5d5dd4ab80af..ab35caf3be16 100644 --- a/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/threshold_reached_for_admin_mailer_preview.rb +++ b/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/threshold_reached_for_admin_mailer_preview.rb @@ -6,7 +6,7 @@ class ThresholdReachedForAdminMailerPreview < ActionMailer::Preview def campaign_mail campaign = EmailCampaigns::Campaigns::ThresholdReachedForAdmin.first - post = Initiative.first + post = Idea.first # TODO: generate commands with campaign#generate_commands method command = { @@ -19,15 +19,12 @@ def campaign_mail post_url: 'demo.stg.govocal.com', post_likes_count: 3, post_comments_count: 4, - post_images: post.initiative_images.map do |image| + post_images: post.idea_images.map do |image| { ordering: image.ordering, versions: image.image.versions.to_h { |k, v| [k.to_s, v.url] } } end, - initiative_header_bg: { - versions: post.header_bg.versions.to_h { |k, v| [k.to_s, v.url] } - }, assignee_first_name: 'Lady', assignee_last_name: 'Gaga' } diff --git a/back/engines/free/email_campaigns/spec/mailers/status_change_on_initiative_you_follow_mailer_spec.rb b/back/engines/free/email_campaigns/spec/mailers/status_change_on_initiative_you_follow_mailer_spec.rb deleted file mode 100644 index 1624a76761c4..000000000000 --- a/back/engines/free/email_campaigns/spec/mailers/status_change_on_initiative_you_follow_mailer_spec.rb +++ /dev/null @@ -1,45 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe EmailCampaigns::StatusChangeOnInitiativeYouFollowMailer do - describe 'campaign_mail' do - let_it_be(:recipient) { create(:user, locale: 'en') } - let_it_be(:campaign) { EmailCampaigns::Campaigns::StatusChangeOnInitiativeYouFollow.create! } - let_it_be(:initiative) { create(:initiative) } - let_it_be(:command) do - campaign.generate_commands( - recipient: recipient, - activity: Activity.new(item: Notification.new(post: initiative)) - ).first.merge({ recipient: recipient }) - end - - let_it_be(:mail) { described_class.with(command: command, campaign: campaign).campaign_mail.deliver_now } - - before { EmailCampaigns::UnsubscriptionToken.create!(user_id: recipient.id) } - - it 'renders the subject' do - expect(mail.subject).to start_with('The status of a proposal you follow has changed') - end - - it 'renders the sender email' do - expect(mail.from).to all(end_with('@citizenlab.co')) - end - - it 'assigns organisation name' do - expect(mail.body.encoded).to match(AppConfiguration.instance.settings('core', 'organization_name', 'en')) - end - - it 'assigns cta url' do - expect(mail.body.encoded).to match(command.dig(:event_payload, :post_url)) - end - - it 'includes the initiative title' do - expect(mail.body.encoded).to match(initiative.title_multiloc['en']) - end - - it 'includes the unfollow url' do - expect(mail.body.encoded).to match(Frontend::UrlService.new.unfollow_url(Follower.new(followable: initiative, user: recipient))) - end - end -end diff --git a/back/engines/free/email_campaigns/spec/models/email_campaigns/campaigns/comment_on_initiative_you_follow_spec.rb b/back/engines/free/email_campaigns/spec/models/email_campaigns/campaigns/comment_on_initiative_you_follow_spec.rb deleted file mode 100644 index 0965d35bcbc8..000000000000 --- a/back/engines/free/email_campaigns/spec/models/email_campaigns/campaigns/comment_on_initiative_you_follow_spec.rb +++ /dev/null @@ -1,53 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe EmailCampaigns::Campaigns::CommentOnInitiativeYouFollow do - describe 'CommentOnYourInitiative Campaign default factory' do - it 'is valid' do - expect(build(:comment_on_initiative_you_follow_campaign)).to be_valid - end - end - - describe '#generate_commands' do - let(:campaign) { create(:comment_on_initiative_you_follow_campaign) } - let(:notification) { create(:comment_on_initiative_you_follow) } - let(:notification_activity) { create(:activity, item: notification, action: 'created') } - let(:command) do - campaign.generate_commands(recipient: notification_activity.item.recipient, activity: notification_activity).first - end - - it 'generates a command with the desired payload and tracked content' do - expect(command).to match({ - event_payload: hash_including( - initiating_user_first_name: notification.initiating_user.first_name, - initiating_user_last_name: notification.initiating_user.last_name, - comment_author_name: notification.comment.author.full_name, - comment_body_multiloc: notification.comment.body_multiloc, - comment_url: an_instance_of(String), - post_published_at: an_instance_of(String), - post_title_multiloc: notification.post.title_multiloc, - post_author_name: notification.post.author.full_name, - unfollow_url: an_instance_of(String) - ) - }) - - expect( - command.dig(:event_payload, :initiating_user_first_name) - ).to eq(notification.initiating_user.first_name) - expect( - command.dig(:event_payload, :comment_body_multiloc) - ).to eq(notification.comment.body_multiloc) - end - - it 'generates a command with an abbreviated name' do - SettingsService.new.activate_feature! 'abbreviated_user_names' - - expect(notification.recipient.admin?).to be false - expect(notification.initiating_user.admin?).to be false - - initial = "#{notification.initiating_user.last_name[0]}." - expect(command.dig(:event_payload, :initiating_user_last_name)).to eq(initial) - end - end -end diff --git a/back/engines/free/email_campaigns/spec/models/email_campaigns/campaigns/internal_comment_on_initiative_assigned_to_you_spec.rb b/back/engines/free/email_campaigns/spec/models/email_campaigns/campaigns/internal_comment_on_initiative_assigned_to_you_spec.rb deleted file mode 100644 index 83ad0da92d81..000000000000 --- a/back/engines/free/email_campaigns/spec/models/email_campaigns/campaigns/internal_comment_on_initiative_assigned_to_you_spec.rb +++ /dev/null @@ -1,19 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe EmailCampaigns::Campaigns::InternalCommentOnInitiativeAssignedToYou do - describe 'InternalCommentOnInitiativeAssignedToYou Campaign default factory' do - it 'is valid' do - expect(build(:internal_comment_on_initiative_assigned_to_you_campaign)).to be_valid - end - end - - describe '#generate_commands' do - let(:campaign) { create(:internal_comment_on_initiative_assigned_to_you_campaign) } - let(:notification) { create(:internal_comment_on_initiative_assigned_to_you) } - let!(:post_image) { create(:initiative_image, initiative: notification.post) } - - include_examples 'internal_comment_campaign_generate_commands' - end -end diff --git a/back/engines/free/email_campaigns/spec/models/email_campaigns/campaigns/internal_comment_on_initiative_you_commented_internally_on_spec.rb b/back/engines/free/email_campaigns/spec/models/email_campaigns/campaigns/internal_comment_on_initiative_you_commented_internally_on_spec.rb deleted file mode 100644 index 2db0cb4c3bd2..000000000000 --- a/back/engines/free/email_campaigns/spec/models/email_campaigns/campaigns/internal_comment_on_initiative_you_commented_internally_on_spec.rb +++ /dev/null @@ -1,19 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe EmailCampaigns::Campaigns::InternalCommentOnInitiativeYouCommentedInternallyOn do - describe 'InternalCommentOnInitiativeYouCommentedInternallyOn Campaign default factory' do - it 'is valid' do - expect(build(:internal_comment_on_initiative_you_commented_internally_on_campaign)).to be_valid - end - end - - describe '#generate_commands' do - let(:campaign) { create(:internal_comment_on_initiative_you_commented_internally_on_campaign) } - let(:notification) { create(:internal_comment_on_initiative_you_commented_internally_on) } - let!(:post_image) { create(:initiative_image, initiative: notification.post) } - - include_examples 'internal_comment_campaign_generate_commands' - end -end diff --git a/back/engines/free/email_campaigns/spec/models/email_campaigns/campaigns/internal_comment_on_unassigned_initiative_spec.rb b/back/engines/free/email_campaigns/spec/models/email_campaigns/campaigns/internal_comment_on_unassigned_initiative_spec.rb deleted file mode 100644 index 78d58f0f4aa8..000000000000 --- a/back/engines/free/email_campaigns/spec/models/email_campaigns/campaigns/internal_comment_on_unassigned_initiative_spec.rb +++ /dev/null @@ -1,19 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe EmailCampaigns::Campaigns::InternalCommentOnUnassignedInitiative do - describe 'InternalCommentOnUnassignedInitiative Campaign default factory' do - it 'is valid' do - expect(build(:internal_comment_on_unassigned_initiative_campaign)).to be_valid - end - end - - describe '#generate_commands' do - let(:campaign) { create(:internal_comment_on_unassigned_initiative_campaign) } - let(:notification) { create(:internal_comment_on_unassigned_initiative) } - let!(:post_image) { create(:initiative_image, initiative: notification.post) } - - include_examples 'internal_comment_campaign_generate_commands' - end -end diff --git a/back/engines/free/email_campaigns/spec/models/email_campaigns/campaigns/new_initiative_for_admin_spec.rb b/back/engines/free/email_campaigns/spec/models/email_campaigns/campaigns/new_initiative_for_admin_spec.rb deleted file mode 100644 index 26622ec42e84..000000000000 --- a/back/engines/free/email_campaigns/spec/models/email_campaigns/campaigns/new_initiative_for_admin_spec.rb +++ /dev/null @@ -1,35 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe EmailCampaigns::Campaigns::NewInitiativeForAdmin do - describe 'NewInitiativeForAdmin Campaign default factory' do - it 'is valid' do - expect(build(:new_initiative_for_admin_campaign)).to be_valid - end - end - - describe 'apply_recipient_filters' do - let(:campaign) { build(:new_initiative_for_admin_campaign) } - - it 'filters out normal users' do - initiative = create(:initiative) - create(:user) - admin = create(:admin) - - expect(campaign.apply_recipient_filters(activity: create(:activity, item: initiative, action: 'published')).ids).to match_array([admin.id]) - end - end - - describe 'apply_recipient_filters' do - let(:campaign) { build(:new_initiative_for_admin_campaign) } - - it 'filters out everyone if the author is admin' do - author = create(:admin) - initiative = create(:initiative, author: author) - create(:admin) - - expect(campaign.apply_recipient_filters(activity: create(:activity, item: initiative, action: 'published')).count).to eq 0 - end - end -end diff --git a/back/engines/free/email_campaigns/spec/models/email_campaigns/campaigns/official_feedback_on_initiative_you_follow_spec.rb b/back/engines/free/email_campaigns/spec/models/email_campaigns/campaigns/official_feedback_on_initiative_you_follow_spec.rb deleted file mode 100644 index 2520317e1a6e..000000000000 --- a/back/engines/free/email_campaigns/spec/models/email_campaigns/campaigns/official_feedback_on_initiative_you_follow_spec.rb +++ /dev/null @@ -1,35 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe EmailCampaigns::Campaigns::OfficialFeedbackOnInitiativeYouFollow do - describe 'OfficialFeedbackOnInitiativeYouFollow campaign default factory' do - it 'is valid' do - expect(build(:official_feedback_on_initiative_you_follow_campaign)).to be_valid - end - end - - describe '#generate_commands' do - let(:campaign) { create(:official_feedback_on_initiative_you_follow_campaign) } - let(:notification) { create(:official_feedback_on_initiative_you_follow) } - let(:notification_activity) { create(:activity, item: notification, action: 'created') } - - it 'generates a command with the desired payload and tracked content' do - command = campaign.generate_commands( - recipient: notification_activity.item.recipient, - activity: notification_activity - ).first - - expect(command).to match({ - event_payload: hash_including( - official_feedback_author_multiloc: notification.official_feedback.author_multiloc, - official_feedback_body_multiloc: notification.official_feedback.body_multiloc, - official_feedback_url: an_instance_of(String), - post_published_at: an_instance_of(String), - post_title_multiloc: notification.post.title_multiloc, - post_author_name: notification.post.author_name - ) - }) - end - end -end diff --git a/back/engines/free/email_campaigns/spec/models/email_campaigns/campaigns/status_change_on_initiative_you_follow_spec.rb b/back/engines/free/email_campaigns/spec/models/email_campaigns/campaigns/status_change_on_initiative_you_follow_spec.rb deleted file mode 100644 index ba1d6ce6345b..000000000000 --- a/back/engines/free/email_campaigns/spec/models/email_campaigns/campaigns/status_change_on_initiative_you_follow_spec.rb +++ /dev/null @@ -1,38 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe EmailCampaigns::Campaigns::StatusChangeOnInitiativeYouFollow do - describe 'StatusChangeOnInitiativeYouFollow campaign default factory' do - it 'is valid' do - expect(build(:status_change_on_initiative_you_follow_campaign)).to be_valid - end - end - - describe '#generate_commands' do - let(:campaign) { create(:status_change_on_initiative_you_follow_campaign) } - let(:notification) { create(:status_change_on_initiative_you_follow) } - let(:notification_activity) { create(:activity, item: notification, action: 'created') } - - it 'generates a command with the desired payload and tracked content' do - command = campaign.generate_commands( - recipient: notification_activity.item.recipient, - activity: notification_activity - ).first - - expect(command).to match({ - event_payload: hash_including( - post_id: notification.post.id, - post_title_multiloc: notification.post.title_multiloc, - post_body_multiloc: notification.post.body_multiloc, - post_url: an_instance_of(String), - post_images: an_instance_of(Array), - initiative_status_id: notification.post_status.id, - initiative_status_title_multiloc: notification.post_status.title_multiloc, - initiative_status_code: notification.post_status.code, - initiative_status_color: notification.post_status.color - ) - }) - end - end -end diff --git a/back/spec/acceptance/action_descriptors_spec.rb b/back/spec/acceptance/action_descriptors_spec.rb deleted file mode 100644 index 8648cb7994ce..000000000000 --- a/back/spec/acceptance/action_descriptors_spec.rb +++ /dev/null @@ -1,66 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' -require 'rspec_api_documentation/dsl' - -resource 'ActionDescriptors' do - explanation 'Describe which actions the current user is allowed to take.' - - let(:json_response) { json_parse(response_body) } - - before do - resident_header_token - header 'Content-Type', 'application/json' - end - - get 'web_api/v1/action_descriptors/initiatives' do - example_request 'Get the global action descriptors for initiatives' do - assert_status 200 - expect(json_response.dig(:data, :type)).to eq 'initiatives' - json_attributes = json_response.dig(:data, :attributes) - expect(json_attributes.values.pluck(:enabled).all?).to be true - expect(json_attributes.values.pluck(:disabled_reason).none?).to be true - end - - context 'with permissions on a phase enabled', document: false do - before do - Permissions::PermissionsUpdateService.new.update_all_permissions - Permission.find_by(permission_scope: nil, action: 'commenting_initiative') - .update!(permitted_by: 'users', groups: create_list(:group, 2)) - end - - # TODO: cleanup-after-proposals-migration - example_request 'Get the global action descriptors for initiatives' do - expect(json_response).to eq( - { - data: { - type: 'initiatives', - attributes: { - posting_initiative: { - enabled: true, - disabled_reason: nil - }, - commenting_initiative: { - enabled: false, - disabled_reason: 'user_not_in_group' - }, - reacting_initiative: { - enabled: true, - disabled_reason: nil - }, - cancelling_initiative_reactions: { - enabled: true, - disabled_reason: nil - }, - comment_reacting_initiative: { - enabled: false, - disabled_reason: 'user_not_in_group' - } - } - } - } - ) - end - end - end -end diff --git a/back/spec/acceptance/avatars_spec.rb b/back/spec/acceptance/avatars_spec.rb index 9c8446bddfb8..a7f8d7d1767e 100644 --- a/back/spec/acceptance/avatars_spec.rb +++ b/back/spec/acceptance/avatars_spec.rb @@ -91,26 +91,6 @@ end end - # TODO: cleanup-after-proposals-migration - describe do - let(:initiative) { create(:initiative) } - let(:context_type) { 'initiative' } - let(:context_id) { initiative.id } - let(:author_id) { initiative.author.id } - let!(:commenter_ids) { Array.new(2) { create(:comment, post: initiative).author.id } } - let(:limit) { 2 } - - example_request 'List random user avatars on an initiative (author and commenters)' do - assert_status 200 - json_response = json_parse(response_body) - expect(json_response[:data].size).to eq 2 - expect(json_response[:data].map { |d| d.dig(:attributes, :avatar).keys }).to all(eq %i[small medium large]) - expect(json_response[:data].flat_map { |d| d.dig(:attributes, :avatar).values }).to all(be_present) - expect(json_response[:data].pluck(:id)).to all(satisfy { |id| (commenter_ids + [author_id]).include?(id) }) - expect(json_response.dig(:meta, :total)).to eq 3 - end - end - context 'as an admin' do before { admin_header_token } diff --git a/back/spec/acceptance/comment_reactions_spec.rb b/back/spec/acceptance/comment_reactions_spec.rb index c327431875c0..b229c7dbce85 100644 --- a/back/spec/acceptance/comment_reactions_spec.rb +++ b/back/spec/acceptance/comment_reactions_spec.rb @@ -70,9 +70,9 @@ end describe do - before { @comment.update!(post: create(:initiative)) } + before { @comment.update!(post: create(:idea)) } - example 'Create a reaction on a comment of an initiative', document: false do + example 'Create a reaction on a comment of an idea', document: false do do_request assert_status 201 end diff --git a/back/spec/acceptance/followers_spec.rb b/back/spec/acceptance/followers_spec.rb index 23db18c2f4af..fead47c9d9e6 100644 --- a/back/spec/acceptance/followers_spec.rb +++ b/back/spec/acceptance/followers_spec.rb @@ -25,15 +25,14 @@ let!(:other_follow) { create(:follower, user: create(:user)) } let!(:folder_follows) { create_list(:project_folder, 2).map { |project| create(:follower, user: user, followable: project) } } let!(:idea_follows) { [create(:follower, user: user, followable: create(:idea))] } - let!(:initiative_follows) { [create(:follower, user: user, followable: create(:initiative))] } let!(:topic_follows) { [create(:follower, user: user, followable: create(:topic))] } let!(:area_follows) { [create(:follower, user: user, followable: create(:area))] } example_request 'List all followers' do assert_status 200 json_response = json_parse response_body - expect(json_response[:data].size).to eq 8 - expect(json_response[:data].pluck(:id)).to match_array (project_follows + folder_follows + idea_follows + initiative_follows + topic_follows + area_follows).map(&:id) + expect(json_response[:data].size).to eq 7 + expect(json_response[:data].pluck(:id)).to match_array (project_follows + folder_follows + idea_follows + topic_follows + area_follows).map(&:id) end describe do @@ -50,7 +49,6 @@ end end - # TODO: cleanup-after-proposals-migration get 'web_api/v1/followers/:id' do let(:id) { create(:follower, user: user).id } @@ -77,11 +75,6 @@ resource: 'ideas', factory: 'idea' }, - { - type: 'initiative', - resource: 'initiatives', - factory: 'initiative' - }, { type: 'topic', resource: 'topics', diff --git a/back/spec/acceptance/initiative_comments_spec.rb b/back/spec/acceptance/initiative_comments_spec.rb deleted file mode 100644 index e883065f17f5..000000000000 --- a/back/spec/acceptance/initiative_comments_spec.rb +++ /dev/null @@ -1,359 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' -require 'rspec_api_documentation/dsl' - -# TODO: cleanup-after-proposals-migration -resource 'Comments' do - explanation 'Comments permit users to have discussions about content (i.e. ideas).' - - before do - header 'Content-Type', 'application/json' - @initiative = create(:initiative) - end - - get 'web_api/v1/initiatives/:initiative_id/comments' do - with_options scope: :page do - parameter :number, 'Page number' - parameter :size, 'Number of top-level comments per page. The response will include 2 to 5 child comments per top-level comment, so expect to receive more' - end - parameter :sort, 'Either new, -new, likes_count or -likes_count. Defaults to -new. Only applies to the top-level comments, children are always returned chronologically.' - - describe do - before do - @c1 = create(:comment, post: @initiative) - @c2 = create(:comment, post: @initiative) - @c1sub1 = create(:comment, parent: @c2, post: @initiative) - @c1sub2 = create(:comment, parent: @c2, post: @initiative) - @c1sub3 = create(:comment, parent: @c2, post: @initiative) - @c1sub4 = create(:comment, parent: @c2, post: @initiative) - @c1sub5 = create(:comment, parent: @c2, post: @initiative) - @c3 = create(:comment, post: @initiative) - @c3sub1 = create(:comment, parent: @c3, post: @initiative) - @c3sub2 = create(:comment, parent: @c3, post: @initiative) - @c3sub3 = create(:comment, parent: @c3, post: @initiative) - @c3sub4 = create(:comment, parent: @c3, post: @initiative) - @c3sub5 = create(:comment, parent: @c3, post: @initiative) - @c3sub6 = create(:comment, parent: @c3, post: @initiative) - @c4 = create(:comment, post: @initiative) - @c4sub1 = create(:comment, parent: @c4, post: @initiative) - end - - let(:initiative_id) { @initiative.id } - let(:size) { 3 } - - example_request 'List the top-level comments of an initiative' do - expect(status).to eq(200) - json_response = json_parse(response_body) - expect(json_response[:data].size).to eq 10 - expect(json_response[:data].pluck(:id)).to eq([ - @c1, - @c2, - @c1sub1, - @c1sub2, - @c1sub3, - @c1sub4, - @c1sub5, - @c3, - @c3sub5, - @c3sub6 - ].map(&:id)) - end - end - - describe do - let(:initiative_id) { @initiative.id } - let(:sort) { '-likes_count' } - - before do - @c1, @c2, @c3 = create_list(:comment, 3, post: @initiative) - create_list(:reaction, 2, reactable: @c3) - create_list(:reaction, 3, reactable: @c2) - @c3sub1, @c3sub2 = create_list(:comment, 2, parent: @c3, post: @initiative) - create(:reaction, reactable: @c3sub2) - end - - example_request 'List the top-level comments of an initiative sorted by descending likes_count' do - expect(status).to eq(200) - json_response = json_parse(response_body) - expect(json_response[:data].size).to eq 5 - expect(json_response[:data].pluck(:id)).to eq([ - @c2, - @c3, - @c3sub1, - @c3sub2, - @c1 - ].map(&:id)) - end - end - end - - get 'web_api/v1/comments/:comment_id/children' do - explanation 'Children are always returned chronologically' - with_options scope: :page do - parameter :number, 'Page number' - parameter :size, 'Number of comments per page' - end - - before do - @c = create(:comment, post: @initiative) - @csub1 = create(:comment, parent: @c, post: @initiative) - @csub2 = create(:comment, parent: @c, post: @initiative) - @csub3 = create(:comment, parent: @c, post: @initiative) - @csub4 = create(:comment, parent: @c, post: @initiative) - @csub5 = create(:comment, parent: @c, post: @initiative) - @csub6 = create(:comment, parent: @c, post: @initiative) - @c2 = create(:comment, post: @initiative) - end - - let(:comment_id) { @c.id } - - example_request 'List the direct child comments of a comment on an initiative' do - expect(status).to eq(200) - json_response = json_parse(response_body) - expect(json_response[:data].size).to eq 6 - expect(json_response[:data].pluck(:id)).to eq([ - @csub1, - @csub2, - @csub3, - @csub4, - @csub5, - @csub6 - ].map(&:id)) - end - end - - get 'web_api/v1/initiatives/comments/as_xlsx' do - parameter :initiatives, 'Filter by a given list of initiative ids', required: false - before do - @user = create(:admin) - header_token_for @user - end - - describe do - before do - @comments = Array.new(3) do |_i| - create(:comment, post: create(:initiative)) - end - end - - example_request 'XLSX export of comments on initiatives' do - expect(status).to eq 200 - worksheet = RubyXL::Parser.parse_buffer(response_body).worksheets[0] - expect(worksheet.count).to eq(@comments.size + 1) - end - end - - describe do - before do - @comments = create_list(:comment, 4, post: create(:initiative)) - end - - let(:initiatives) { @comments.map(&:post_id) } - - example 'XLSX export by initiative ids', document: false do - do_request - expect(status).to eq 200 - worksheet = RubyXL::Parser.parse_buffer(response_body).worksheets[0] - expect(worksheet.count).to eq(initiatives.size + 1) - end - end - - describe 'when resident' do - before { resident_header_token } - - example '[error] XLSX export', document: false do - do_request - expect(status).to eq 401 - end - end - end - - get 'web_api/v1/comments/:id' do - let(:comment) { create(:comment, post: create(:initiative)) } - let(:id) { comment.id } - - example_request 'Get one comment by id' do - expect(status).to eq 200 - expect(response_data[:id]).to eq id - expect(response_data[:attributes]).to include( - dislikes_count: 0, - publication_status: 'published', - is_admin_comment: false, - anonymous: false, - author_hash: comment.author_hash - ) - end - end - - context 'when authenticated' do - before do - @user = create(:user) - header_token_for @user - end - - get 'web_api/v1/initiatives/:initiative_id/comments' do - let(:initiative_id) { @initiative.id } - - example 'List all comments of an initiative includes the user_reaction when authenticated' do - comment = create(:comment, post: @initiative) - reaction = create(:reaction, user: @user, reactable: comment) - do_request - json_response = json_parse(response_body) - expect(json_response[:data].filter_map { |d| d[:relationships][:user_reaction][:data] }.first[:id]).to eq reaction.id - expect(json_response[:included].pluck(:id)).to include reaction.id - end - end - - post 'web_api/v1/initiatives/:initiative_id/comments' do - with_options scope: :comment do - parameter :author_id, 'The user id of the user owning the comment. Signed in user by default', required: false - parameter :body_multiloc, 'Multi-locale field with the comment body', required: true - parameter :parent_id, 'The id of the comment this comment is a response to', required: false - parameter :anonymous, 'Post this comment anonymously - true/false', required: false - end - ValidationErrorHelper.new.error_fields(self, Comment) - response_field :base, "Array containing objects with signature { error: #{Permissions::InitiativePermissionsService::USER_DENIED_REASONS.values.join(' | ')} }", scope: :errors - - let(:initiative_id) { @initiative.id } - let(:comment) { build(:comment) } - let(:body_multiloc) { comment.body_multiloc } - - example_request 'Create a comment on an initiative' do - expect(response_status).to eq 201 - json_response = json_parse(response_body) - expect(json_response.dig(:data, :relationships, :author, :data, :id)).to eq @user.id - expect(json_response.dig(:data, :attributes, :body_multiloc).stringify_keys).to match body_multiloc - expect(json_response.dig(:data, :relationships, :parent, :data)).to be_nil - expect(json_response.dig(:data, :relationships, :post, :data, :id)).to eq initiative_id - expect(@initiative.reload.comments_count).to eq 1 - end - - describe 'anomymous commenting' do - let(:allow_anonymous_participation) { true } - let(:anonymous) { true } - - before do - config = AppConfiguration.instance - config.settings['initiatives']['allow_anonymous_participation'] = allow_anonymous_participation - config.save! - end - - example_request 'Create an anonymous comment on an initiative' do - assert_status 201 - expect(response_data.dig(:relationships, :author, :data, :id)).to be_nil - expect(response_data.dig(:attributes, :anonymous)).to be true - expect(response_data.dig(:attributes, :author_name)).to be_nil - end - - example 'Does not add the author as a follower', document: false do - expect { do_request }.not_to change(Follower, :count) - end - - example 'Does not log activities for the author', document: false do - expect { do_request }.not_to have_enqueued_job(LogActivityJob).with(anything, anything, @user, anything) - end - - describe 'when anonymous posting is not allowed' do - let(:allow_anonymous_participation) { false } - - example_request 'Rejects the anonymous parameter' do - assert_status 422 - json_response = json_parse response_body - expect(json_response).to include_response_error(:base, 'anonymous_participation_not_allowed') - end - end - end - end - - post 'web_api/v1/comments/:id/mark_as_deleted' do - with_options scope: :comment do - parameter :reason_code, "one of #{Notifications::CommentDeletedByAdmin::REASON_CODES}; only required for admins", required: false - parameter :other_reason, "the reason for deleting the comment, if none of the reason codes is applicable, in which case 'other' must be chosen", required: false - end - - let(:comment) { create(:comment, author: @user, post: @initiative) } - let(:id) { comment.id } - - example_request 'Mark a comment on an initiative as deleted' do - expect(response_status).to eq 202 - expect(comment.reload.publication_status).to eq('deleted') - end - end - - patch 'web_api/v1/comments/:id' do - with_options scope: :comment do - parameter :author_id, 'The user id of the user owning the comment. Signed in user by default' - parameter :body_multiloc, 'Multi-locale field with the comment body' - parameter :parent_id, 'The id of the comment this comment is a response to' - parameter :anonymous, 'Change this comment to anonymous - true/false' - end - ValidationErrorHelper.new.error_fields(self, Comment) - response_field :base, "Array containing objects with signature { error: #{Permissions::InitiativePermissionsService::USER_DENIED_REASONS.values.join(' | ')} }", scope: :errors - - let(:comment) { create(:comment, author: @user, post: @initiative) } - let(:id) { comment.id } - let(:body_multiloc) { { 'en' => "His hair is not blond, it's orange. Get your facts straight!" } } - - example_request 'Update a comment on an initiative' do - expect(response_status).to eq 200 - json_response = json_parse(response_body) - expect(json_response.dig(:data, :attributes, :body_multiloc).stringify_keys).to match body_multiloc - expect(@initiative.reload.comments_count).to eq 1 - end - - example 'Admins cannot modify a comment on an initiative', document: false do - admin_header_token - do_request - expect(comment.reload.body_multiloc).not_to eq body_multiloc - end - - describe 'anomymous commenting' do - let(:allow_anonymous_participation) { true } - let(:anonymous) { true } - - before do - config = AppConfiguration.instance - config.settings['initiatives']['allow_anonymous_participation'] = allow_anonymous_participation - config.save! - end - - example_request 'Change an comment on an initiative to anonymous' do - assert_status 200 - expect(response_data.dig(:relationships, :author, :data, :id)).to be_nil - expect(response_data.dig(:attributes, :anonymous)).to be true - expect(response_data.dig(:attributes, :author_name)).to be_nil - end - - example '[Error] Cannot update an anonymous comment' do - comment.update!(anonymous: true) - do_request - assert_status 401 - expect(json_response_body.dig(:errors, :base, 0, :error)).to eq 'Unauthorized!' - end - - example 'Does not log activities for the author and clears the author from past activities', document: false do - clear_activity = create(:activity, item: comment, user: @user) - other_item_activity = create(:activity, item: comment, user: create(:user)) - other_user_activity = create(:activity, user: @user) - - expect { do_request }.not_to have_enqueued_job(LogActivityJob).with(anything, anything, @user, anything) - expect(clear_activity.reload.user_id).to be_nil - expect(other_item_activity.reload.user_id).to be_present - expect(other_user_activity.reload.user_id).to eq @user.id - end - - describe 'when anonymous posting is not allowed' do - let(:allow_anonymous_participation) { false } - - example 'Rejects the anonymous parameter' do - do_request comment: { anonymous: true } - assert_status 422 - json_response = json_parse response_body - expect(json_response).to include_response_error(:base, 'anonymous_participation_not_allowed') - end - end - end - end - end -end diff --git a/back/spec/acceptance/initiative_files_spec.rb b/back/spec/acceptance/initiative_files_spec.rb deleted file mode 100644 index cae6b6ead8bb..000000000000 --- a/back/spec/acceptance/initiative_files_spec.rb +++ /dev/null @@ -1,76 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' -require 'rspec_api_documentation/dsl' - -# TODO: cleanup-after-proposals-migration -resource 'InitiativeFile' do - explanation 'File attachments.' - - before do - header 'Content-Type', 'application/json' - @user = create(:user) - header_token_for @user - @initiative = create(:initiative, author: @user) - create_list(:initiative_file, 2, initiative: @initiative) - end - - get 'web_api/v1/initiatives/:initiative_id/files' do - let(:initiative_id) { @initiative.id } - - example_request 'List all file attachments of an initiative' do - expect(status).to eq(200) - json_response = json_parse(response_body) - expect(json_response[:data].size).to eq 2 - end - end - - get 'web_api/v1/initiatives/:initiative_id/files/:file_id' do - let(:initiative_id) { @initiative.id } - let(:file_id) { InitiativeFile.first.id } - - example_request 'Get one file attachment of an initiative by id' do - expect(status).to eq(200) - json_response = json_parse(response_body) - expect(json_response.dig(:data, :attributes, :file)).to be_present - end - end - - post 'web_api/v1/initiatives/:initiative_id/files' do - with_options scope: :file do - parameter :file, 'The base64 encoded file', required: true - parameter :name, 'The name of the file, including the file extension', required: true - parameter :ordering, 'An integer that is used to order the files within an initiative', required: false - end - ValidationErrorHelper.new.error_fields(self, InitiativeFile) - - let(:initiative_id) { @initiative.id } - let(:file) { encode_file_as_base64('afvalkalender.pdf') } - let(:ordering) { 1 } - let(:name) { 'afvalkalender.pdf' } - - example_request 'Add a file attachment to an initiative' do - expect(response_status).to eq 201 - json_response = json_parse(response_body) - expect(json_response.dig(:data, :attributes, :file)).to be_present - expect(json_response.dig(:data, :attributes, :ordering)).to eq(1) - expect(json_response.dig(:data, :attributes, :name)).to eq(name) - end - end - - delete 'web_api/v1/initiatives/:initiative_id/files/:file_id' do - let(:initiative_id) { @initiative.id } - let(:file_id) { InitiativeFile.first.id } - - example_request 'Delete a file attachment from an initiative' do - expect(response_status).to eq 200 - expect { InitiativeFile.find(file_id) }.to raise_error(ActiveRecord::RecordNotFound) - end - end - - private - - def encode_file_as_base64(filename) - "data:application/pdf;base64,#{Base64.encode64(Rails.root.join('spec', 'fixtures', filename).read)}" - end -end diff --git a/back/spec/acceptance/initiative_images_spec.rb b/back/spec/acceptance/initiative_images_spec.rb deleted file mode 100644 index 82db8a7c4103..000000000000 --- a/back/spec/acceptance/initiative_images_spec.rb +++ /dev/null @@ -1,87 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' -require 'rspec_api_documentation/dsl' - -# TODO: cleanup-after-proposals-migration -resource 'InitiativeImage' do - explanation 'Initiatives can have mutliple images.' - - before do - header 'Content-Type', 'application/json' - @user = create(:user) - header_token_for @user - @initiative = create(:initiative, author: @user) - create_list(:initiative_image, 2, initiative: @initiative) - end - - get 'web_api/v1/initiatives/:initiative_id/images' do - let(:initiative_id) { @initiative.id } - - example_request 'List all images of an initiative' do - expect(status).to eq(200) - json_response = json_parse(response_body) - expect(json_response[:data].size).to eq 2 - end - end - - get 'web_api/v1/initiatives/:initiative_id/images/:image_id' do - let(:initiative_id) { @initiative.id } - let(:image_id) { InitiativeImage.first.id } - - example_request 'Get one image of an initiative by id' do - expect(status).to eq(200) - json_response = json_parse(response_body) - expect(json_response.dig(:data, :attributes, :versions).keys).to match %i[small medium large fb] - end - end - - post 'web_api/v1/initiatives/:initiative_id/images' do - with_options scope: :image do - parameter :image, 'The base64 encoded image', required: true - parameter :ordering, 'An integer that is used to order the images within an initiative', required: false - end - ValidationErrorHelper.new.error_fields(self, InitiativeImage) - - let(:initiative_id) { @initiative.id } - let(:image) { png_image_as_base64 'image13.png' } - let(:ordering) { 1 } - - example_request 'Add an image to an initiative' do - expect(response_status).to eq 201 - json_response = json_parse(response_body) - expect(json_response.dig(:data, :attributes, :versions).keys).to match %i[small medium large fb] - expect(json_response.dig(:data, :attributes, :ordering)).to eq(1) - end - end - - patch 'web_api/v1/initiatives/:initiative_id/images/:image_id' do - with_options scope: :image do - parameter :image, 'The base64 encoded image' - parameter :ordering, 'An integer that is used to order the images within an initiative' - end - ValidationErrorHelper.new.error_fields(self, InitiativeImage) - - let(:initiative_id) { @initiative.id } - let(:image_id) { InitiativeImage.first.id } - let(:image) { png_image_as_base64 'image14.png' } - let(:ordering) { 2 } - - example_request 'Update an image for an initiative' do - expect(response_status).to eq 200 - json_response = json_parse(response_body) - expect(json_response.dig(:data, :attributes, :versions).keys).to match %i[small medium large fb] - expect(json_response.dig(:data, :attributes, :ordering)).to eq(2) - end - end - - delete 'web_api/v1/initiatives/:initiative_id/images/:image_id' do - let(:initiative_id) { @initiative.id } - let(:image_id) { InitiativeImage.first.id } - - example_request 'Delete an image from an initiative' do - expect(response_status).to eq 200 - expect { InitiativeImage.find(image_id) }.to raise_error(ActiveRecord::RecordNotFound) - end - end -end diff --git a/back/spec/acceptance/initiative_official_feedbacks_spec.rb b/back/spec/acceptance/initiative_official_feedbacks_spec.rb deleted file mode 100644 index 2d405bbbd53e..000000000000 --- a/back/spec/acceptance/initiative_official_feedbacks_spec.rb +++ /dev/null @@ -1,132 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' -require 'rspec_api_documentation/dsl' - -# TODO: cleanup-after-proposals-migration -resource 'OfficialFeedback' do - explanation 'Official feedback is input from moderators on content (i.e. ideas), separated from comments.' - - before do - header 'Content-Type', 'application/json' - @initiative = create(:initiative) - @feedbacks = create_list(:official_feedback, 2, post: @initiative) - end - - get 'web_api/v1/initiatives/:initiative_id/official_feedback' do - with_options scope: :page do - parameter :number, 'Page number' - parameter :size, 'Number of official feedback per page' - end - - let(:initiative_id) { @initiative.id } - - example_request 'List all official feedback of an initiative' do - assert_status 200 - json_response = json_parse(response_body) - expect(json_response[:data].size).to eq 2 - expect(json_response.dig(:data, 0, :attributes, :body_multiloc)).to be_present - expect(json_response.dig(:data, 0, :attributes, :author_multiloc)).to be_present - end - end - - get 'web_api/v1/official_feedback/:id' do - let(:id) { @feedbacks.first.id } - - example_request 'Get one official feedback on an initiative by id' do - assert_status 200 - json_response = json_parse(response_body) - expect(json_response.dig(:data, :id)).to eq @feedbacks.first.id - end - end - - context 'when authenticated' do - before do - @user = create(:admin) - header_token_for @user - end - - post 'web_api/v1/initiatives/:initiative_id/official_feedback' do - with_options scope: :official_feedback do - parameter :body_multiloc, 'Multi-locale field with the feedback body', required: true - parameter :author_multiloc, 'Multi-locale field with describing the author', required: true - end - ValidationErrorHelper.new.error_fields(self, OfficialFeedback) - - let(:initiative_id) { @initiative.id } - let(:feedback) { build(:official_feedback) } - let(:body_multiloc) { feedback.body_multiloc } - let(:author_multiloc) { feedback.author_multiloc } - - example_request 'Create an official feedback on an initiative' do - assert_status 201 - json_response = json_parse(response_body) - expect(json_response.dig(:data, :relationships, :user, :data, :id)).to eq @user.id - expect(json_response.dig(:data, :attributes, :body_multiloc).stringify_keys).to match body_multiloc - expect(json_response.dig(:data, :attributes, :author_multiloc).stringify_keys).to match author_multiloc - expect(json_response.dig(:data, :relationships, :post, :data, :id)).to eq initiative_id - expect(@initiative.reload.official_feedbacks_count).to eq 3 - end - - describe do - let(:body_multiloc) { { 'en' => '' } } - - example_request '[error] Create an invalid official feedback on an initiative' do - assert_status 422 - json_response = json_parse response_body - expect(json_response).to include_response_error(:body_multiloc, 'blank') - end - end - end - - patch 'web_api/v1/official_feedback/:id' do - with_options scope: :official_feedback do - parameter :body_multiloc, 'Multi-locale field with the feedback body', required: true - parameter :author_multiloc, 'Multi-locale field with describing the author', required: true - end - ValidationErrorHelper.new.error_fields(self, OfficialFeedback) - - let(:official_feedback) { create(:official_feedback, user: @user, post: @initiative) } - let(:id) { official_feedback.id } - let(:body_multiloc) { { 'en' => "His hair is not blond, it's orange. Get your facts straight!" } } - - example_request 'Update an official feedback for an initiative' do - expect(response_status).to eq 200 - json_response = json_parse(response_body) - expect(json_response.dig(:data, :attributes, :body_multiloc).stringify_keys).to match body_multiloc - expect(@initiative.reload.official_feedbacks_count).to eq 3 - end - end - - delete 'web_api/v1/official_feedback/:id' do - let(:official_feedback) { create(:official_feedback, user: @user, post: @initiative) } - let(:id) { official_feedback.id } - example_request 'Delete an official feedback from an initiative' do - expect(response_status).to eq 200 - expect { OfficialFeedback.find(id) }.to raise_error(ActiveRecord::RecordNotFound) - expect(@initiative.reload.official_feedbacks_count).to eq 2 - end - end - end - - context 'when resident' do - before { resident_header_token } - - post 'web_api/v1/initiatives/:initiative_id/official_feedback' do - with_options scope: :official_feedback do - parameter :body_multiloc, 'Multi-locale field with the feedback body', required: true - parameter :author_multiloc, 'Multi-locale field with describing the author', required: true - end - ValidationErrorHelper.new.error_fields(self, OfficialFeedback) - - let(:initiative_id) { @initiative.id } - let(:feedback) { build(:official_feedback) } - let(:body_multiloc) { feedback.body_multiloc } - let(:author_multiloc) { feedback.author_multiloc } - - example_request '[error] Create an official feedback on an initiative' do - expect(response_status).to eq 401 - end - end - end -end diff --git a/back/spec/acceptance/initiative_reactions_spec.rb b/back/spec/acceptance/initiative_reactions_spec.rb deleted file mode 100644 index c16109130871..000000000000 --- a/back/spec/acceptance/initiative_reactions_spec.rb +++ /dev/null @@ -1,148 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' -require 'rspec_api_documentation/dsl' - -# TODO: cleanup-after-proposals-migration -resource 'Reactions' do - explanation 'Reactions are used to express agreement on content (i.e. ideas). Ideally, the city would accept the most reactiond initiatives.' - - before do - @user = create(:admin) - header_token_for @user - header 'Content-Type', 'application/json' - - @status_proposed = create(:initiative_status_proposed) - @status_expired = create(:initiative_status_expired) - @status_threshold_reached = create(:initiative_status_threshold_reached) - @status_answered = create(:initiative_status_answered) - @status_ineligible = create(:initiative_status_ineligible) - - @initiative = create(:initiative) - - @initiative.initiative_status_changes.create!( - initiative_status: @status_proposed - ) - @reactions = create_list(:reaction, 2, reactable: @initiative, mode: 'up') - end - - get 'web_api/v1/initiatives/:initiative_id/reactions' do - let(:initiative_id) { @initiative.id } - - example_request 'List all reactions of an initiative' do - assert_status 200 - json_response = json_parse(response_body) - expect(json_response[:data].size).to eq 2 - end - end - - get 'web_api/v1/reactions/:id' do - let(:id) { @reactions.first.id } - - example_request 'Get one reaction on an initiative by id' do - assert_status 200 - json_response = json_parse(response_body) - expect(json_response.dig(:data, :id)).to eq @reactions.first.id - end - end - - post 'web_api/v1/initiatives/:initiative_id/reactions' do - with_options scope: :reaction do - parameter :user_id, 'The user id of the user owning the reaction. Signed in user by default', required: false - parameter :mode, 'one of [up, down]', required: true - end - ValidationErrorHelper.new.error_fields(self, Reaction) - - disabled_reasons = Permissions::InitiativePermissionsService::USER_DENIED_REASONS.values - response_field :base, "Array containing objects with signature { error: #{disabled_reasons.join(' | ')} }", scope: :errors - - let(:initiative_id) { @initiative.id } - let(:mode) { 'up' } - - example_request 'Create a reaction to an initiative' do - assert_status 201 - json_response = json_parse(response_body) - expect(json_response.dig(:data, :relationships, :user, :data, :id)).to be_nil - expect(json_response.dig(:data, :attributes, :mode)).to eq 'up' - expect(@initiative.reload.likes_count).to eq 3 - end - - # TODO: cleanup-after-proposals-migration - example 'Reaching the voting threshold immediately triggers status change', document: false do - settings = AppConfiguration.instance.settings - settings['initiatives']['reacting_threshold'] = 3 - AppConfiguration.instance.update! settings: settings - - do_request - assert_status 201 - expect(@initiative.reload.initiative_status).to eq @status_threshold_reached - end - - example 'The first non-author reaction create action will set editing_locked to true', document: false do - expect(@initiative.reload.editing_locked).to be false - do_request - assert_status 201 - expect(@initiative.reload.editing_locked).to be true - end - end - - post 'web_api/v1/initiatives/:initiative_id/reactions/up' do - ValidationErrorHelper.new.error_fields(self, Reaction) - - disabled_reasons = Permissions::InitiativePermissionsService::USER_DENIED_REASONS.values - response_field :base, "Array containing objects with signature { error: #{disabled_reasons.join(' | ')} }", scope: :errors - - let(:initiative_id) { @initiative.id } - - example_request "Like an initiative that doesn't have your reaction yet" do - assert_status 201 - expect(@initiative.reload.likes_count).to eq 3 - expect(@initiative.reload.dislikes_count).to eq 0 - end - - example 'Like an initiative that you disliked before' do - @initiative.reactions.create(user: @user, mode: 'down') - do_request - assert_status 201 - expect(@initiative.reload.likes_count).to eq 3 - expect(@initiative.reload.dislikes_count).to eq 0 - end - - example '[error] Like an initiative that you liked before' do - @initiative.reactions.create(user: @user, mode: 'up') - do_request - assert_status 422 - json_response = json_parse response_body - expect(json_response).to include_response_error(:base, 'already_liked') - expect(@initiative.reload.likes_count).to eq 3 - expect(@initiative.reload.dislikes_count).to eq 0 - end - end - - post 'web_api/v1/initiatives/:initiative_id/reactions/down' do - ValidationErrorHelper.new.error_fields(self, Reaction) - - disabled_reasons = Permissions::InitiativePermissionsService::USER_DENIED_REASONS.values - response_field :base, "Array containing objects with signature { error: #{disabled_reasons.join(' | ')} }", scope: :errors - - let(:initiative_id) { @initiative.id } - - example_request "[error] Dislike an initiative that doesn't have your reaction yet" do - assert_status 401 - json_response = json_parse(response_body) - expect(json_response.dig(:errors, :base)).to include({ error: 'dislikes_not_supported' }) - expect(@initiative.reload.likes_count).to eq 2 - expect(@initiative.reload.dislikes_count).to eq 0 - end - end - - delete 'web_api/v1/reactions/:id' do - let(:reaction) { create(:reaction, user: @user, reactable: @initiative) } - let(:id) { reaction.id } - - example_request 'Delete a reaction from an initiative' do - assert_status 200 - expect { Reaction.find(id) }.to raise_error(ActiveRecord::RecordNotFound) - end - end -end diff --git a/back/spec/acceptance/initiative_spam_report_spec.rb b/back/spec/acceptance/initiative_spam_report_spec.rb deleted file mode 100644 index d19fee268f84..000000000000 --- a/back/spec/acceptance/initiative_spam_report_spec.rb +++ /dev/null @@ -1,84 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' -require 'rspec_api_documentation/dsl' - -# TODO: cleanup-after-proposals-migration -resource 'Spam Reports' do - explanation 'Reporting undesired content (i.e. an initiative).' - - before do - @user = create(:admin) - header_token_for @user - header 'Content-Type', 'application/json' - @initiative = create(:initiative) - @spam_reports = create_list(:spam_report, 2, spam_reportable: @initiative) - end - - get 'web_api/v1/initiatives/:initiative_id/spam_reports' do - let(:initiative_id) { @initiative.id } - - example_request 'List all spam reports of an initiative' do - expect(status).to eq(200) - json_response = json_parse(response_body) - expect(json_response[:data].size).to eq 2 - end - end - - get 'web_api/v1/spam_reports/:id' do - let(:id) { @spam_reports.first.id } - - example_request 'Get one spam report of an initiative by id' do - expect(status).to eq 200 - json_response = json_parse(response_body) - expect(json_response.dig(:data, :id)).to eq @spam_reports.first.id - end - end - - post 'web_api/v1/initiatives/:initiative_id/spam_reports' do - with_options scope: :spam_report do - parameter :user_id, 'the user id of the user owning the spam report. Signed in user by default', required: false - parameter :reason_code, 'one of [wrong_content, inappropriate, other]', required: true - parameter :other_reason, "the reason for the spam report, if none of the reason codes is applicable, in which case 'other' must be chosen", required: false - end - ValidationErrorHelper.new.error_fields(self, SpamReport) - - let(:initiative_id) { @initiative.id } - let(:reason_code) { 'inappropriate' } - - example_request 'Create a spam report for an initiative' do - expect(response_status).to eq 201 - json_response = json_parse(response_body) - expect(json_response.dig(:data, :relationships, :user, :data, :id)).to eq @user.id - expect(json_response.dig(:data, :attributes, :reason_code)).to eq 'inappropriate' - end - end - - patch 'web_api/v1/spam_reports/:id' do - with_options scope: :spam_report do - parameter :reason_code, 'one of [wrong_content, inappropriate, other]', required: true - parameter :other_reason, "the reason for the spam report, if none of the reason codes is applicable, in which case 'other' must be chosen", required: false - end - ValidationErrorHelper.new.error_fields(self, SpamReport) - - let(:spam_report) { create(:spam_report, user: @user, spam_reportable: @initiative, reason_code: 'other', other_reason: 'pagiarism') } - let(:id) { spam_report.id } - let(:reason_code) { 'inappropriate' } - - example_request 'Update a spam report for an initiative' do - expect(status).to be 200 - json_response = json_parse(response_body) - expect(json_response.dig(:data, :attributes, :reason_code)).to eq 'inappropriate' - end - end - - delete 'web_api/v1/spam_reports/:id' do - let(:spam_report) { create(:spam_report, user: @user, spam_reportable: @initiative) } - let(:id) { spam_report.id } - - example_request 'Delete a spam report from an initiative' do - expect(response_status).to eq 200 - expect { SpamReport.find(id) }.to raise_error(ActiveRecord::RecordNotFound) - end - end -end diff --git a/back/spec/acceptance/initiative_status_changes_spec.rb b/back/spec/acceptance/initiative_status_changes_spec.rb deleted file mode 100644 index 94d9819d8764..000000000000 --- a/back/spec/acceptance/initiative_status_changes_spec.rb +++ /dev/null @@ -1,227 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' -require 'rspec_api_documentation/dsl' - -# TODO: cleanup-after-proposals-migration -resource 'InitiativeStatusChange' do - explanation 'Initiative status changes allow admins to apply manual status changes on initiatives.' - - before do - header 'Content-Type', 'application/json' - @initiative = create(:initiative) - @changes = create_list(:initiative_status_change, 2, initiative: @initiative) - end - - get 'web_api/v1/initiatives/:initiative_id/initiative_status_changes' do - with_options scope: :page do - parameter :number, 'Page number' - parameter :size, 'Number of status changes per page' - end - - before { admin_header_token } - - let(:initiative_id) { @initiative.id } - - example_request 'List all status changes of an initiative' do - assert_status 200 - json_response = json_parse(response_body) - expect(json_response[:data].size).to eq 3 - expect(json_response.dig(:data, 0, :attributes, :created_at)).to be_present - end - end - - get 'web_api/v1/initiative_status_changes/:id' do - before { admin_header_token } - - let(:id) { @changes.first.id } - - example_request 'Get one status changes on an initiative by id' do - assert_status 200 - json_response = json_parse(response_body) - expect(json_response.dig(:data, :id)).to eq @changes.first.id - end - end - - context 'when authenticated' do - before do - @user = create(:admin) - header_token_for @user - - @status_review_pending = create(:initiative_status_review_pending) - @status_changes_requested = create(:initiative_status_changes_requested) - @status_proposed = create(:initiative_status_proposed) - @status_expired = create(:initiative_status_expired) - @status_threshold_reached = create(:initiative_status_threshold_reached) - @status_answered = create(:initiative_status_answered) - @status_ineligible = create(:initiative_status_ineligible) - - create( - :initiative_status_change, - initiative: @initiative, initiative_status: @status_threshold_reached - ) - end - - post 'web_api/v1/initiatives/:initiative_id/initiative_status_changes' do - with_options scope: :initiative_status_change do - parameter :initiative_status_id, 'The new initiative status', required: true - parameter :user_id, 'The user who made the status change', required: false - parameter :official_feedback_id, 'An existing official feedback can be used', required: false - end - with_options scope: %i[initiative_status_change official_feedback_attributes] do - parameter :body_multiloc, 'Multi-locale field with the feedback body', required: false - parameter :author_multiloc, 'Multi-locale field with describing the author', required: false - end - ValidationErrorHelper.new.error_fields(self, InitiativeStatusChange) - - let(:initiative_id) { @initiative.id } - let(:initiative_status_id) { @status_answered.id } - let(:feedback) { build(:official_feedback) } - let(:body_multiloc) { feedback.body_multiloc } - let(:author_multiloc) { feedback.author_multiloc } - - example_request 'Create a status change on an initiative with new feedback' do - assert_status 201 - json_response = json_parse(response_body) - expect(json_response.dig(:data, :relationships, :user, :data, :id)).to eq @user.id - expect(@initiative.reload.official_feedbacks_count).to eq 1 - end - - describe do - let(:official_feedback_id) { create(:official_feedback, post: @initiative).id } - let(:body_multiloc) { nil } - let(:author_multiloc) { nil } - - example_request 'Create a status change on an initiative using an existing feedback' do - assert_status 201 - expect(@initiative.reload.official_feedbacks_count).to eq 1 - end - end - - describe do - let(:body_multiloc) { nil } - let(:author_multiloc) { nil } - - example_request '[error] Create a status change on an initiative without feedback' do - assert_status 422 - end - end - - describe do - let(:initiative_status_id) { @status_expired.id } - - example_request '[error] Create a status change through an invalid transition' do - assert_status 422 - json_response = json_parse response_body - expect(json_response).to include_response_error(:base, 'initiative_status_transition_not_allowed') - end - end - - describe do - let(:initiative_status_id) { @status_threshold_reached.id } - - example_request '[error] Create a status change to the same status' do - assert_status 422 - json_response = json_parse response_body - expect(json_response).to include_response_error(:base, 'initiative_status_transition_without_change') - end - end - - # If the review feature is off, the initiative_status_change with code 'proposed' - # is created at the model level when the initiative is published. - # Thus, this POST request can only happen when the review feature is on. - context 'when the the status change is to the proposed status' do - let(:new_initiative) { create(:initiative) } - let!(:_initiative_status_change) do - create( - :initiative_status_change, - initiative: new_initiative, - initiative_status: @status_review_pending - ) - end - - let(:initiative_id) { new_initiative.id } - let(:initiative_status_id) { @status_proposed.id } - - example 'creation of status change record' do - expect(Initiative.find(initiative_id).editing_locked).to be false - - # results in the logging of an Initiative 'proposed' activity - expect { do_request } - .to have_enqueued_job(LogActivityJob) - .with(instance_of(Initiative), 'proposed', @user, instance_of(Integer)) - .exactly(1).times - assert_status 201 - # results in the setting of editing_locked: true - expect(Initiative.find(initiative_id).editing_locked).to be true - end - end - - context 'when the the status change is to the changes_requested status' do - let(:new_initiative) { create(:initiative) } - let!(:_initiative_status_change) do - create( - :initiative_status_change, - initiative: new_initiative, - initiative_status: @status_changes_requested - ) - end - - let(:initiative_id) { new_initiative.id } - let(:initiative_status_id) { @status_review_pending.id } - - example 'creation of status change record' do - expect(Initiative.find(initiative_id).editing_locked).to be false - - # does not result in the logging of an Initiative 'proposed' activity - expect { do_request } - .not_to have_enqueued_job(LogActivityJob) - .with(instance_of(Initiative), 'proposed', anything, anything) - assert_status 201 - # does not result in locked editing - expect(Initiative.find(initiative_id).editing_locked).to be false - end - end - end - end - - context 'when resident' do - before do - resident_header_token - - @status_proposed = create(:initiative_status_proposed) - @status_expired = create(:initiative_status_expired) - @status_threshold_reached = create(:initiative_status_threshold_reached) - @status_answered = create(:initiative_status_answered) - @status_ineligible = create(:initiative_status_ineligible) - - create( - :initiative_status_change, - initiative: @initiative, initiative_status: @status_threshold_reached - ) - end - - post 'web_api/v1/initiatives/:initiative_id/initiative_status_changes' do - with_options scope: :initiative_status_change do - parameter :initiative_status_id, 'The new initiative status', required: true - parameter :user_id, 'The user who made the status change', required: false - parameter :official_feedback_id, 'An existing official feedback can be used', required: false - end - with_options scope: %i[initiative_status_change official_feedback_attributes] do - parameter :body_multiloc, 'Multi-locale field with the feedback body', required: false - parameter :author_multiloc, 'Multi-locale field with describing the author', required: false - end - ValidationErrorHelper.new.error_fields(self, InitiativeStatusChange) - - let(:initiative_id) { @initiative.id } - let(:initiative_status_id) { @status_answered.id } - let(:feedback) { build(:official_feedback) } - let(:body_multiloc) { feedback.body_multiloc } - let(:author_multiloc) { feedback.author_multiloc } - - example_request '[error] Create an official feedback on an initiative' do - expect(response_status).to eq 401 - end - end - end -end diff --git a/back/spec/acceptance/initiative_statuses_spec.rb b/back/spec/acceptance/initiative_statuses_spec.rb deleted file mode 100644 index 1c873c524a4a..000000000000 --- a/back/spec/acceptance/initiative_statuses_spec.rb +++ /dev/null @@ -1,33 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' -require 'rspec_api_documentation/dsl' - -# TODO: cleanup-after-proposals-migration -resource 'InitiativeStatuses' do - explanation 'Initivative statuses reflect the cities attitude towards an initiative.' - - before do - header 'Content-Type', 'application/json' - @statuses = create_list(:initiative_status, 3) - end - - get 'web_api/v1/initiative_statuses' do - example_request 'List all initiative statuses' do - expect(status).to eq(200) - json_response = json_parse(response_body) - expect(json_response[:data].size).to eq 3 - expect(json_response[:data].pluck(:id)).to match_array @statuses.map(&:id) - end - end - - get 'web_api/v1/initiative_statuses/:id' do - let(:id) { @statuses.first.id } - - example_request 'Get one initiative status by id' do - expect(status).to eq 200 - json_response = json_parse(response_body) - expect(json_response.dig(:data, :id)).to eq @statuses.first.id - end - end -end diff --git a/back/spec/acceptance/initiatives_spec.rb b/back/spec/acceptance/initiatives_spec.rb deleted file mode 100644 index 509399a1c964..000000000000 --- a/back/spec/acceptance/initiatives_spec.rb +++ /dev/null @@ -1,796 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' -require 'rspec_api_documentation/dsl' - -resource 'Initiatives' do - explanation 'Proposals from citizens (but more spontaneous than ideas) to the city.' - - before do - header 'Content-Type', 'application/json' - @first_admin = create(:admin) - @initiatives = %w[published published draft published published published].map { |ps| create(:initiative, publication_status: ps, assignee: create(:admin)) } - @user = create(:user) - header_token_for @user - end - - # TODO: cleanup-after-proposals-migration - get 'web_api/v1/initiatives' do - with_options scope: :page do - parameter :number, 'Page number' - parameter :size, 'Number of initiatives per page' - end - parameter :author, 'Filter by author (user id)', required: false - parameter :publication_status, 'Filter by publication status; returns all publlished initiatives by default', required: false - parameter :topics, 'Filter by topics (OR)', required: false - parameter :areas, 'Filter by areas (OR)', required: false - parameter :initiative_status, 'Filter by status (initiative status id)', required: false - parameter :assignee, 'Filter by assignee (user id)', required: false - parameter :search, 'Filter by searching in title and body', required: false - parameter :feedback_needed, 'Filter out initiatives that need feedback', required: false - parameter :sort, "Either 'trending' (default), 'new', '-new', 'author_name', '-author_name', 'likes_count', '-likes_count', 'status', '-status', 'random'", required: false - - example_request 'List all published initiatives (default behaviour)' do - assert_status 200 - json_response = json_parse(response_body) - expect(json_response[:data].size).to eq 5 - expect(json_response[:data].map { |d| d.dig(:attributes, :publication_status) }).to all(eq 'published') - end - - example "Don't list drafts (default behaviour)", document: false do - do_request publication_status: 'draft' - - assert_status 200 - json_response = json_parse(response_body) - expect(json_response[:data].size).to eq 0 - end - - example 'List all initiatives which match one of the given topics', document: false do - t1 = create(:topic) - t2 = create(:topic) - - i1 = @initiatives[0] - i1.topics = [t1] - i1.save - i2 = @initiatives[1] - i2.topics = [t2] - i2.save - - do_request topics: [t1.id, t2.id] - json_response = json_parse(response_body) - expect(json_response[:data].size).to eq 2 - expect(json_response[:data].pluck(:id)).to match_array [i1.id, i2.id] - end - - example 'List all initiatives which match one of the given areas', document: false do - a1 = create(:area) - a2 = create(:area) - - i1 = @initiatives.first - i1.areas = [a1] - i1.save - i2 = @initiatives.second - i2.areas = [a2] - i2.save - - do_request areas: [a1.id, a2.id] - json_response = json_parse(response_body) - expect(json_response[:data].size).to eq 2 - expect(json_response[:data].pluck(:id)).to match_array [i1.id, i2.id] - end - - example 'List all initiatives for a user' do - u = create(:user) - i = create(:initiative, author: u) - - do_request author: u.id - json_response = json_parse(response_body) - expect(json_response[:data].size).to eq 1 - expect(json_response[:data][0][:id]).to eq i.id - end - - example 'List all initiatives for an initiative status', document: false do - status = create(:initiative_status) - i = create(:initiative, initiative_status: status) - - do_request initiative_status: status.id - json_response = json_parse(response_body) - expect(json_response[:data].size).to eq 1 - expect(json_response[:data][0][:id]).to eq i.id - end - - example 'List all initiatives for an assignee', document: false do - a = create(:admin) - i = create(:initiative, assignee: a) - - do_request assignee: a.id - json_response = json_parse(response_body) - expect(json_response[:data].size).to eq 1 - expect(json_response[:data][0][:id]).to eq i.id - end - - example 'List all unassigned initiatives' do - create(:initiative, assignee: create(:admin)) - initiatives = create_list(:initiative, 2, assignee: nil) - - do_request assignee: 'unassigned' - - assert_status 200 - json_response = json_parse response_body - expect(json_response[:data].size).to eq 2 - expect(json_response[:data].pluck(:id)).to match_array initiatives.map(&:id) - end - - example 'List all initiatives that need feedback', document: false do - threshold_reached = create(:initiative_status_threshold_reached) - i = create(:initiative, initiative_status: threshold_reached) - - do_request feedback_needed: true - json_response = json_parse(response_body) - expect(json_response[:data].size).to eq 1 - expect(json_response[:data][0][:id]).to eq i.id - end - - example 'Search for initiatives', document: false do - create(:user) - initiatives = [ - create(:initiative, title_multiloc: { en: 'This initiative is uniqque' }), - create(:initiative, title_multiloc: { en: 'This one origiinal' }) - ] - - do_request search: 'uniqque' - json_response = json_parse(response_body) - expect(json_response[:data].size).to eq 1 - expect(json_response[:data][0][:id]).to eq initiatives[0].id - end - - example 'List all initiatives sorted by new', document: false do - create(:user) - i1 = create(:initiative) - - do_request sort: 'new' - json_response = json_parse(response_body) - expect(json_response[:data].size).to eq 6 - expect(json_response[:data][0][:id]).to eq i1.id - end - - example 'List all initiatives by random ordering', document: false do - create(:user) - create(:initiative) - - do_request sort: 'random' - json_response = json_parse(response_body) - expect(json_response[:data].size).to eq 6 - end - - example 'List all initiatives includes the user_reaction and user_follower', document: false do - initiative = create(:initiative) - reaction = create(:reaction, reactable: initiative, user: @user) - follower = create(:follower, followable: create(:initiative), user: @user) - - do_request - json_response = json_parse(response_body) - expect(json_response[:data].filter_map { |d| d.dig(:relationships, :user_reaction, :data, :id) }.first).to eq reaction.id - expect(json_response[:data].filter_map { |d| d.dig(:relationships, :user_follower, :data, :id) }.first).to eq follower.id - expect(json_response[:included].pluck(:id)).to include reaction.id - end - end - - # TODO: cleanup-after-proposals-migration - get 'web_api/v1/initiatives/as_markers' do - before do - locations = [[51.044039, 3.716964], [50.845552, 4.357355], [50.640255, 5.571848], [50.950772, 4.308304], [51.215929, 4.422602], [50.453848, 3.952217], [-27.148983, -109.424659]] - placenames = ['Ghent', 'Brussels', 'Liège', 'Meise', 'Antwerp', 'Mons', 'Hanga Roa'] - @initiatives.each do |i| - i.location_point_geojson = { 'type' => 'Point', 'coordinates' => locations.pop } - i.title_multiloc['en'] = placenames.pop - i.publication_status = 'published' - i.save! - end - end - - with_options scope: :page do - parameter :number, 'Page number' - parameter :size, 'Number of initiatives per page' - end - parameter :author, 'Filter by author (user id)', required: false - parameter :publication_status, 'Return only initiatives with the specified publication status; returns all pusblished initiatives by default', required: false - parameter :bounding_box, 'Given an [x1,y1,x2,y2] array of doubles (x being latitude and y being longitude), the markers are filtered to only retain those within the (x1,y1)-(x2,y2) box.', required: false - parameter :topics, 'Filter by topics (OR)', required: false - parameter :areas, 'Filter by areas (OR)', required: false - parameter :assignee, 'Filter by assignee (user id)', required: false - parameter :search, 'Filter by searching in title and body', required: false - - example 'List all markers within a bounding box' do - do_request(bounding_box: '[51.208758,3.224363,50.000667,5.715281]') # Bruges-Bastogne - - assert_status 200 - json_response = json_parse(response_body) - expect(json_response[:data].size).to eq 4 - expect(json_response[:data].map { |d| d.dig(:attributes, :title_multiloc, :en) }.sort).to match %w[Brussels Liège Meise Mons].sort - end - end - - # TODO: move-old-proposals-test - get 'web_api/v1/initiatives/as_xlsx' do - before { admin_header_token } - - parameter :initiatives, 'Filter by a given list of initiative ids', required: false - - example_request 'XLSX export' do - assert_status 200 - end - - describe do - before do - @selected_initiatives = @initiatives.select(&:published?).shuffle.take 2 - end - - let(:initiatives) { @selected_initiatives.map(&:id) } - - example_request 'XLSX export by initiative ids', document: false do - assert_status 200 - worksheet = RubyXL::Parser.parse_buffer(response_body).worksheets[0] - expect(worksheet.count).to eq(@selected_initiatives.size + 1) - end - end - - describe 'when resident' do - before { resident_header_token } - - example '[error] XLSX export', document: false do - do_request - assert_status 401 - end - end - end - - # TODO: cleanup-after-proposals-migration - get 'web_api/v1/initiatives/filter_counts' do - before do - Initiative.all.each(&:destroy!) - @t1 = create(:topic) - @t2 = create(:topic) - @a1 = create(:area) - @a2 = create(:area) - @s1 = create(:initiative_status) - @s2 = create(:initiative_status) - @i1 = create(:initiative, topics: [@t1, @t2], areas: [@a1], initiative_status: @s1) - @i2 = create(:initiative, topics: [@t1], areas: [@a1, @a2], initiative_status: @s2) - @i3 = create(:initiative, topics: [@t2], areas: [], initiative_status: @s2) - @i4 = create(:initiative, topics: [], areas: [@a1], initiative_status: @s2) - - # a1 -> 3 - # a2 -> 1 - # t1 -> 2 - # t2 -> 2 - # s1 -> 1 - # s2 -> 3 - end - - parameter :topics, 'Filter by topics (OR)', required: false - parameter :areas, 'Filter by areas (OR)', required: false - parameter :author, 'Filter by author (user id)', required: false - parameter :assignee, 'Filter by assignee (user id)', required: false - parameter :initiative_status, 'Filter by status (initiative status id)', required: false - parameter :search, 'Filter by searching in title and body', required: false - parameter :publication_status, 'Return only initiatives with the specified publication status; returns all pusblished initiatives by default', required: false - - example_request 'List initiative counts per filter option' do - assert_status 200 - json_response = json_parse response_body - expect(json_response.dig(:data, :type)).to eq 'filter_counts' - - expect(json_response.dig(:data, :attributes, :initiative_status_id)[@s1.id.to_sym]).to eq 1 - expect(json_response.dig(:data, :attributes, :initiative_status_id)[@s2.id.to_sym]).to eq 3 - expect(json_response.dig(:data, :attributes, :area_id)[@a1.id.to_sym]).to eq 3 - expect(json_response.dig(:data, :attributes, :area_id)[@a2.id.to_sym]).to eq 1 - expect(json_response.dig(:data, :attributes, :topic_id)[@t1.id.to_sym]).to eq 2 - expect(json_response.dig(:data, :attributes, :topic_id)[@t2.id.to_sym]).to eq 2 - expect(json_response.dig(:data, :attributes, :total)).to eq 4 - end - - example 'List initiative counts per filter option on topic', document: false do - do_request topics: [@t1.id] - assert_status 200 - end - - example 'List initiative counts per filter option on area', document: false do - do_request areas: [@a1.id] - assert_status 200 - end - - example 'List initiative counts when also using search filtering AND sort', document: false do - do_request(search: 'uniqque', sort: 'new') - assert_status 200 - end - end - - get 'web_api/v1/initiatives/:id' do - let(:initiative) { @initiatives.first } - let(:id) { initiative.id } - - example_request 'Get one initiative by id' do - assert_status 200 - expect(response_data[:id]).to eq initiative.id - expect(response_data[:attributes]).to include( - anonymous: false, - author_hash: initiative.author_hash - ) - end - end - - get 'web_api/v1/initiatives/by_slug/:slug' do - let(:slug) { @initiatives.first.slug } - - example_request 'Get one initiative by slug' do - assert_status 200 - json_response = json_parse(response_body) - expect(json_response.dig(:data, :id)).to eq @initiatives.first.id - end - - describe do - let(:slug) { 'unexisting-initiative' } - - example '[error] Get an unexisting initiative', document: false do - do_request - assert_status 404 - end - end - end - - # TODO: cleanup-after-proposals-migration - post 'web_api/v1/initiatives' do - before do - create(:initiative_status, code: 'proposed') - end - - with_options scope: :initiative do - parameter :author_id, 'The user id of the user owning the initiative. This can only be specified by moderators and is inferred from the JWT token for residents.' - parameter :publication_status, 'Publication status', required: true, extra: "One of #{Post::PUBLICATION_STATUSES.join(',')}" - parameter :title_multiloc, 'Multi-locale field with the initiative title', required: true, extra: 'Maximum 100 characters' - parameter :body_multiloc, 'Multi-locale field with the initiative body', extra: 'Required if not draft' - parameter :location_point_geojson, 'A GeoJSON point that situates the location the initiative applies to' - parameter :location_description, 'A human readable description of the location the initiative applies to' - parameter :header_bg, 'Base64 encoded header image' - parameter :topic_ids, 'Array of ids of the associated topics' - parameter :area_ids, 'Array of ids of the associated areas' - parameter :assignee_id, 'The user id of the admin that takes ownership. Set automatically if not provided. Only allowed for admins.' - parameter :anonymous, 'Post this initiative anonymously - true/false' - parameter :cosponsor_ids, 'Array of user ids of the desired cosponsors' - end - ValidationErrorHelper.new.error_fields(self, Initiative) - - let(:initiative) { build(:initiative) } - let(:publication_status) { 'published' } - let(:title_multiloc) { initiative.title_multiloc } - let(:body_multiloc) { initiative.body_multiloc } - let(:location_point_geojson) { { type: 'Point', coordinates: [51.11520776293035, 3.921154106874878] } } - let(:location_description) { 'Stanley Road 4' } - let(:header_bg) { file_as_base64 'header.jpg', 'image/jpeg' } - let(:topic_ids) { create_list(:topic, 2).map(&:id) } - let(:area_ids) { create_list(:area, 2).map(&:id) } - let(:assignee_id) { create(:admin).id } - - describe do - before do - @user.add_role 'admin' - @user.save! - end - - example_request 'Create an initiative' do - assert_status 201 - json_response = json_parse(response_body) - expect(json_response.dig(:data, :attributes, :location_point_geojson)).to eq location_point_geojson - expect(json_response.dig(:data, :attributes, :location_description)).to eq location_description - expect(json_response.dig(:data, :relationships, :topics, :data).pluck(:id)).to match_array topic_ids - expect(json_response.dig(:data, :relationships, :areas, :data).pluck(:id)).to match_array area_ids - expect(json_response.dig(:data, :relationships, :assignee, :data, :id)).to eq assignee_id - end - - example 'Check for the automatic creation of a like by the author when an initiative is created', document: false do - do_request - json_response = json_parse(response_body) - new_initiative = Initiative.find(json_response.dig(:data, :id)) - expect(new_initiative.reactions.size).to eq 1 - expect(new_initiative.reactions[0].mode).to eq 'up' - expect(new_initiative.reactions[0].user.id).to eq @user.id - expect(json_response[:data][:attributes][:likes_count]).to eq 1 - end - - example 'Check for the automatic assignement of the default assignee', document: false do - do_request(initiative: { assignee_id: nil }) - json_response = json_parse(response_body) - expect(json_response.dig(:data, :relationships, :assignee, :data, :id)).to eq @first_admin.id - end - end - - example_group 'with permissions on phase' do - let(:group) { create(:group) } - - before do - Permissions::PermissionsUpdateService.new.update_global_permissions - Permission.find_by(permission_scope: nil, action: 'posting_initiative') - .update!(permitted_by: 'users', groups: [group]) - end - - example '[error] Not authorized to create an initiative', document: false do - do_request - assert_status 401 - end - - example 'Create an initiative (group permission)' do - group.add_member(@user).save! - do_request - assert_status 201 - end - end - - describe do - before { SettingsService.new.activate_feature! 'blocking_profanity' } - - let(:title_multiloc) { { 'nl-BE' => 'Fuck' } } - let(:body_multiloc) { { 'fr-FR' => 'Fuck' } } - - example_request '[error] Create an initiative with blocked words' do - assert_status 422 - json_response = json_parse(response_body) - title_multiloc_error = json_response - .dig(:errors, :title_multiloc)&.select { |err| err[:error] == 'includes_banned_words' } - body_multiloc_error = json_response - .dig(:errors, :body_multiloc)&.select { |err| err[:error] == 'includes_banned_words' } - expect(title_multiloc_error).to be_present - expect(body_multiloc_error).to be_present - end - end - - describe 'anomymous posting of inititatives' do - let(:allow_anonymous_participation) { true } - let(:anonymous) { true } - - before do - config = AppConfiguration.instance - config.settings['initiatives']['allow_anonymous_participation'] = allow_anonymous_participation - config.save! - end - - example_request 'Create an anonymous initiative' do - assert_status 201 - expect(response_data.dig(:relationships, :author, :data, :id)).to be_nil - expect(response_data.dig(:attributes, :anonymous)).to be true - expect(response_data.dig(:attributes, :author_name)).to be_nil - end - - example 'Does not log activities for the author', document: false do - expect { do_request }.not_to have_enqueued_job(LogActivityJob).with(anything, anything, @user, anything) - end - - example 'Does not add the author as a follower of the initiative', document: false do - expect { do_request }.not_to change(Follower, :count) - initiative_id = response_data[:id] - expect(Follower.where(followable_id: initiative_id, user: @user)).not_to exist - end - - describe 'when anonymous posting is not allowed' do - let(:allow_anonymous_participation) { false } - - example_request 'Rejects the anonymous parameter' do - assert_status 422 - json_response = json_parse response_body - expect(json_response).to include_response_error(:base, 'anonymous_participation_not_allowed') - end - end - end - - describe 'cosponsor_ids' do - let(:cosponsor) { create(:user) } - let(:cosponsor_ids) { [cosponsor.id] } - - example 'Update the cosponsors of an initiative' do - expect { do_request } - .to have_enqueued_job(LogActivityJob) - .with(instance_of(CosponsorsInitiative), 'created', @user, instance_of(Integer)) - .exactly(1).times - - assert_status 201 - json_response = json_parse(response_body) - - expect(json_response.dig(:data, :relationships, :cosponsors, :data).pluck(:id)).to match_array cosponsor_ids - end - end - end - - # TODO: cleanup-after-proposals-migration - patch 'web_api/v1/initiatives/:id' do - before do - create(:initiative_status, code: 'proposed') - @initiative = create(:initiative, author: @user) - end - - with_options scope: :initiative do - parameter :author_id, 'The user id of the user owning the initiative. This can only be specified by moderators and is inferred from the JWT token for residents.' - parameter :publication_status, "Either #{Post::PUBLICATION_STATUSES.join(', ')}" - parameter :title_multiloc, 'Multi-locale field with the initiative title', extra: 'Maximum 100 characters' - parameter :body_multiloc, 'Multi-locale field with the initiative body', extra: 'Required if not draft' - parameter :location_point_geojson, 'A GeoJSON point that situates the location the initiative applies to' - parameter :location_description, 'A human readable description of the location the initiative applies to' - parameter :header_bg, 'Base64 encoded header image' - parameter :topic_ids, 'Array of ids of the associated topics' - parameter :area_ids, 'Array of ids of the associated areas' - parameter :assignee_id, 'The user id of the admin that takes ownership. Only allowed for admins.' - parameter :anonymous, 'Post this initiative anonymously - true/false' - parameter :cosponsor_ids, 'Array of user ids of the desired cosponsors' - end - ValidationErrorHelper.new.error_fields(self, Initiative) - - describe 'published initiatives' do - let(:id) { @initiative.id } - let(:location_point_geojson) { { type: 'Point', coordinates: [51.4365635, 3.825930459] } } - let(:location_description) { 'Watkins Road 8' } - let(:header_bg) { file_as_base64 'header.jpg', 'image/jpeg' } - let(:topic_ids) { create_list(:topic, 2).map(&:id) } - let(:area_ids) { create_list(:area, 2).map(&:id) } - - describe do - let(:title_multiloc) { { 'en' => 'Changed title' } } - - example_request 'Update an initiative' do - expect(status).to be 200 - json_response = json_parse(response_body) - expect(json_response.dig(:data, :attributes, :title_multiloc, :en)).to eq 'Changed title' - expect(json_response.dig(:data, :attributes, :location_point_geojson)).to eq location_point_geojson - expect(json_response.dig(:data, :attributes, :location_description)).to eq location_description - expect(json_response.dig(:data, :relationships, :topics, :data).pluck(:id)).to match_array topic_ids - expect(json_response.dig(:data, :relationships, :areas, :data).pluck(:id)).to match_array area_ids - end - end - - describe do - let(:id) { @initiative.id } - let(:header_bg) { file_as_base64 'header.jpg', 'image/jpeg' } - - example 'The header image can be updated and the file is present', document: false do - do_request - expect(@initiative.reload.header_bg_url).to be_present - expect(@initiative.reload.header_bg.file).to be_present - end - end - - describe do - let(:id) { @initiative.id } - - example 'The header image can be removed' do - @initiative.update!(header_bg: Rails.root.join('spec/fixtures/header.jpg').open) - expect(@initiative.reload.header_bg_url).to be_present - do_request initiative: { header_bg: nil } - expect(@initiative.reload.header_bg_url).to be_nil - end - end - - describe do - let(:topic_ids) { [] } - let(:area_ids) { [] } - - example 'Remove the topics/areas', document: false do - @initiative.topics = create_list(:topic, 2) - @initiative.areas = create_list(:area, 2) - do_request - expect(status).to be 200 - json_response = json_parse(response_body) - expect(json_response.dig(:data, :relationships, :topics, :data).pluck(:id)).to match_array topic_ids - expect(json_response.dig(:data, :relationships, :areas, :data).pluck(:id)).to match_array area_ids - end - end - - describe do - let(:assignee_id) { create(:admin).id } - - example 'Changing the assignee as a non-admin does not work', document: false do - do_request - expect(status).to be 200 - json_response = json_parse(response_body) - expect(json_response.dig(:data, :relationships, :assignee)).to be_nil - end - end - - describe 'changing the author of my own initiative' do - let(:author_id) { create(:admin).id } - - example '[Error] Cannot change the author from your own id as a non-admin', document: false do - do_request - expect(@initiative.reload.author_id).not_to eq author_id - end - end - - describe 'changing the author of another authors initiative' do - let(:author_id) { @user.id } - - example '[Error] Cannot update another authors record to your own id as a non-admin', document: false do - @initiative.update!(author: create(:user)) - do_request - assert_status 401 - expect(json_response_body.dig(:errors, :base, 0, :error)).to eq 'Unauthorized!' - end - end - - describe 'updating anomymous initiatives' do - let(:allow_anonymous_participation) { true } - let(:anonymous) { true } - - before do - config = AppConfiguration.instance - config.settings['initiatives']['allow_anonymous_participation'] = allow_anonymous_participation - config.save! - end - - example_request 'Change a published initiative to anonymous' do - assert_status 200 - expect(response_data.dig(:relationships, :author, :data, :id)).to be_nil - expect(response_data.dig(:attributes, :anonymous)).to be true - expect(response_data.dig(:attributes, :author_name)).to be_nil - end - - example '[Error] Cannot update an anonymous initiative as a non-admin' do - @initiative.update!(anonymous: true) - do_request - assert_status 401 - expect(json_response_body.dig(:errors, :base, 0, :error)).to eq 'Unauthorized!' - end - - example 'Does not log activities for the author and clears the author from past activities', document: false do - clear_activity = create(:activity, item: @initiative, user: @user) - other_item_activity = create(:activity, item: @initiative, user: create(:user)) - other_user_activity = create(:activity, user: @user) - - expect { do_request }.not_to have_enqueued_job(LogActivityJob).with(anything, anything, @user, anything) - expect(clear_activity.reload.user_id).to be_nil - expect(other_item_activity.reload.user_id).to be_present - expect(other_user_activity.reload.user_id).to eq @user.id - end - - describe 'when anonymous posting is not allowed' do - let(:allow_anonymous_participation) { false } - - example_request 'Rejects the anonymous parameter' do - assert_status 422 - json_response = json_parse response_body - expect(json_response).to include_response_error(:base, 'anonymous_participation_not_allowed') - end - end - end - end - - describe 'draft initiatives' do - before do - @initiative = create(:initiative, author: @user, publication_status: 'draft') - end - - parameter :publication_status, "Either #{Post::PUBLICATION_STATUSES.join(', ')}", required: true, scope: :initiative - - let(:id) { @initiative.id } - - describe 'updating anomymous initiatives' do - let(:anonymous) { true } - - before do - config = AppConfiguration.instance - config.settings['initiatives']['allow_anonymous_participation'] = true - config.save! - end - - example_request 'Change a draft initiative to anonymous' do - assert_status 200 - expect(response_data.dig(:relationships, :author, :data, :id)).to be_nil - expect(response_data.dig(:attributes, :anonymous)).to be true - expect(response_data.dig(:attributes, :author_name)).to be_nil - end - end - - describe 'publishing an initiative' do - let(:publication_status) { 'published' } - - example_request 'Change the publication status' do - assert_status 200 - expect(response_data.dig(:attributes, :publication_status)).to eq 'published' - end - end - - describe 'changing a draft initiative of another user' do - let(:title_multiloc) { { 'en' => 'Changed title' } } - - example '[Error] Cannot update an anonymous initiative as a non-admin' do - @initiative.update!(author: create(:user)) - do_request - assert_status 401 - expect(json_response_body.dig(:errors, :base, 0, :error)).to eq 'Unauthorized!' - end - end - - describe 'cosponsor_ids' do - let(:id) { @initiative.id } - let(:cosponsor) { create(:user) } - let(:cosponsor_ids) { [cosponsor.id] } - - example 'Update the cosponsors of an initiative' do - expect { do_request } - .to have_enqueued_job(LogActivityJob) - .with(instance_of(CosponsorsInitiative), 'created', @user, instance_of(Integer)) - .exactly(1).times - - assert_status 200 - json_response = json_parse(response_body) - - expect(json_response.dig(:data, :relationships, :cosponsors, :data).pluck(:id)).to match_array cosponsor_ids - end - end - end - end - - # TODO: move-old-proposals-test - patch 'web_api/v1/initiatives/:id/accept_cosponsorship_invite' do - before do - @initiative = create(:initiative) - @cosponsors_initiative = create(:cosponsors_initiative, initiative: @initiative, user: @user) - end - - describe 'for initiative with associated cosponsor' do - let(:id) { @initiative.id } - - example 'cosponsor accepts invitation' do - expect { do_request }.to change { @cosponsors_initiative.reload.status }.from('pending').to('accepted') - assert_status 200 - json_response = json_parse(response_body) - expect(json_response.dig(:data, :attributes, :slug)).to eq @initiative.slug - end - end - end - - # TODO: cleanup-after-proposals-migration - delete 'web_api/v1/initiatives/:id' do - before do - @initiative = create(:initiative_with_topics, author: @user, publication_status: 'published') - end - - let(:id) { @initiative.id } - - example_request 'Delete an initiative' do - assert_status 200 - expect { Initiative.find(id) }.to raise_error(ActiveRecord::RecordNotFound) - end - end - - # TODO: cleanup-after-proposals-migration - get 'web_api/v1/initiatives/:id/allowed_transitions' do - before do - admin_header_token - - @initiative = create(:initiative) - threshold_reached = create(:initiative_status_threshold_reached) - create( - :initiative_status_change, - initiative: @initiative, initiative_status: threshold_reached - ) - end - - let(:id) { @initiative.id } - - example_request 'Allowed transitions' do - assert_status 200 - json_response = json_parse response_body - expect(json_response.dig(:data, :type)).to eq 'allowed_transitions' - expect(json_response).to eq({ - data: { - type: 'allowed_transitions', - attributes: { - **InitiativeStatus.where(code: 'answered').ids.to_h do |id| - [id.to_sym, { feedback_required: true }] - end, - **InitiativeStatus.where(code: 'ineligible').ids.to_h do |id| - [id.to_sym, { feedback_required: true }] - end - } - } - }) - end - end -end diff --git a/back/spec/acceptance/internal_comments_spec.rb b/back/spec/acceptance/internal_comments_spec.rb index 38db842df0d6..5387810b3ec4 100644 --- a/back/spec/acceptance/internal_comments_spec.rb +++ b/back/spec/acceptance/internal_comments_spec.rb @@ -294,277 +294,4 @@ end end end - - # TODO: cleanup-after-proposals-migration - context 'when internal comments are on an initiative' do - before { @initiative = create(:initiative) } - - context 'when an authenticated regular user' do - before do - @user = create(:user) - header_token_for @user - end - - post 'web_api/v1/initiatives/:initiative_id/internal_comments' do - with_options scope: :internal_comment do - parameter :body, 'Text field with the comment body', required: true - parameter :parent_id, 'The id of the comment this comment is a response to', required: false - end - - let(:initiative_id) { @initiative.id } - let(:internal_comment) { build(:internal_comment) } - let(:body) { internal_comment.body } - - example_request '[Unauthorized] Create an internal comment on an initiative' do - assert_status 401 - json_response = json_parse(response_body) - - expect(json_response.dig(:errors, :base)[0][:error]).to eq 'Unauthorized!' - expect(@initiative.reload.internal_comments_count).to eq 0 - end - end - end - - context 'when an authenticated admin' do - before do - @user = create(:admin) - header_token_for @user - end - - get 'web_api/v1/internal_comments/:id' do - let(:initiative) { create(:initiative) } - let(:parent) { create(:internal_comment, post: initiative) } - let(:internal_comment) { create(:internal_comment, parent: parent, post: initiative) } - let(:id) { internal_comment.id } - - example_request 'Get one internal comment by id' do - assert_status 200 - json_response = json_parse(response_body) - - expect(json_response.dig(:data, :id)).to eq id - expect(json_response.dig(:data, :type)).to eq 'internal_comment' - expect(json_response.dig(:data, :attributes)).to include(publication_status: 'published') - expect(json_response.dig(:data, :relationships)).to include( - post: { - data: { id: internal_comment.post_id, type: 'initiative' } - }, - author: { - data: { id: internal_comment.author_id, type: 'user' } - }, - parent: { - data: { id: parent.id, type: 'internal_comment' } - } - ) - expect(json_response.dig(:included, 0, :attributes)).to include( - first_name: internal_comment.author.first_name, - locale: internal_comment.author.locale - ) - end - end - - get 'web_api/v1/initiatives/:initiative_id/internal_comments' do - with_options scope: :page do - parameter :number, 'Page number' - parameter :size, 'Number of top-level comments per page. The response will include 2 to 5 child comments per top-level comment, so expect to receive more' - end - parameter :sort, 'Either new or -new. Defaults to -new. Only applies to the top-level comments, children are always returned chronologically.' - - describe do - before do - @c1 = create(:internal_comment, post: @initiative) - @c2 = create(:internal_comment, post: @initiative) - @c1sub1 = create(:internal_comment, parent: @c2, post: @initiative) - @c1sub2 = create(:internal_comment, parent: @c2, post: @initiative) - @c1sub3 = create(:internal_comment, parent: @c2, post: @initiative) - @c1sub4 = create(:internal_comment, parent: @c2, post: @initiative) - @c1sub5 = create(:internal_comment, parent: @c2, post: @initiative) - @c3 = create(:internal_comment, post: @initiative) - @c3sub1 = create(:internal_comment, parent: @c3, post: @initiative) - @c3sub2 = create(:internal_comment, parent: @c3, post: @initiative) - @c3sub3 = create(:internal_comment, parent: @c3, post: @initiative) - @c3sub4 = create(:internal_comment, parent: @c3, post: @initiative) - @c3sub5 = create(:internal_comment, parent: @c3, post: @initiative) - @c3sub6 = create(:internal_comment, parent: @c3, post: @initiative) - @c4 = create(:internal_comment, post: @initiative) - @c4sub1 = create(:internal_comment, parent: @c4, post: @initiative) - end - - let(:initiative_id) { @initiative.id } - let(:size) { 3 } - - example_request 'List the top-level internal comments of an initiative' do - assert_status 200 - json_response = json_parse(response_body) - expect(json_response[:data].size).to eq 10 - expect(json_response[:data].pluck(:id)).to eq([ - @c1, - @c2, - @c1sub1, - @c1sub2, - @c1sub3, - @c1sub4, - @c1sub5, - @c3, - @c3sub5, - @c3sub6 - ].map(&:id)) - expect(json_response[:links][:next]).to be_present - end - end - - describe do - let(:initiative_id) { @initiative.id } - let(:sort) { '-new' } - - before do - @c1 = create(:internal_comment, post: @initiative, created_at: 1.day.ago) - @c2 = create(:internal_comment, post: @initiative, created_at: 2.days.ago) - @c3 = create(:internal_comment, post: @initiative, created_at: 3.days.ago) - @c2sub1, @c2sub2 = create_list(:internal_comment, 2, parent: @c2, post: @initiative) - end - - example_request 'List the top-level internal comments of an initiative sorted by age, with oldest first' do - assert_status 200 - json_response = json_parse(response_body) - expect(json_response[:data].size).to eq 5 - expect(json_response[:data].pluck(:id)).to eq([ - @c3, - @c2, - @c2sub1, - @c2sub2, - @c1 - ].map(&:id)) - end - end - end - - get 'web_api/v1/internal_comments/:internal_comment_id/children' do - explanation 'Children are always returned chronologically' - with_options scope: :page do - parameter :number, 'Page number' - parameter :size, 'Number of internal comments per page' - end - - describe do - before do - @c = create(:internal_comment, post: @initiative) - @csub1 = create(:internal_comment, parent: @c, post: @initiative) - @csub2 = create(:internal_comment, parent: @c, post: @initiative) - @csub3 = create(:internal_comment, parent: @c, post: @initiative) - @csub4 = create(:internal_comment, parent: @c, post: @initiative) - @csub5 = create(:internal_comment, parent: @c, post: @initiative) - @csub6 = create(:internal_comment, parent: @c, post: @initiative) - @c2 = create(:internal_comment, post: @initiative) - end - - let(:internal_comment_id) { @c.id } - - example_request 'List the direct child internal comments of an internal comment on an initiative' do - assert_status 200 - json_response = json_parse(response_body) - expect(json_response[:data].size).to eq 6 - expect(json_response[:data].pluck(:id)).to eq([ - @csub1, - @csub2, - @csub3, - @csub4, - @csub5, - @csub6 - ].map(&:id)) - end - end - end - - post 'web_api/v1/initiatives/:initiative_id/internal_comments' do - with_options scope: :internal_comment do - parameter :body, 'Text field with the comment body', required: true - parameter :parent_id, 'The id of the comment this comment is a response to', required: false - end - - let(:initiative_id) { @initiative.id } - let(:internal_comment) { build(:internal_comment) } - let(:body) { internal_comment.body } - - example_request 'Create an internal comment on an initiative' do - assert_status 201 - json_response = json_parse(response_body) - - expect(json_response.dig(:data, :relationships, :author, :data, :id)).to eq @user.id - expect(json_response.dig(:data, :attributes, :body)).to match body - expect(json_response.dig(:data, :relationships, :parent, :data)).to be_nil - expect(json_response.dig(:data, :relationships, :post, :data, :id)).to eq initiative_id - expect(@initiative.reload.internal_comments_count).to eq 1 - end - - describe do - let(:parent_id) { create(:internal_comment, post: @initiative).id } - - example_request 'Create an internal comment on an internal comment' do - assert_status 201 - json_response = json_parse(response_body) - - expect(json_response.dig(:data, :relationships, :author, :data, :id)).to eq @user.id - expect(json_response.dig(:data, :attributes, :body)).to match body - expect(json_response.dig(:data, :relationships, :parent, :data, :id)).to eq parent_id - expect(json_response.dig(:data, :relationships, :post, :data, :id)).to eq initiative_id - expect(@initiative.reload.internal_comments_count).to eq 2 - end - end - - describe do - let(:body) { '' } - - example_request '[error] Create an invalid internal comment' do - assert_status 422 - json_response = json_parse response_body - expect(json_response).to include_response_error(:body, 'blank') - end - end - end - - patch 'web_api/v1/internal_comments/:id' do - with_options scope: :internal_comment do - parameter :body, 'Text field with the comment body' - parameter :parent_id, 'The id of the internal comment this internal comment is a response to' - end - - let(:internal_comment) { create(:internal_comment, author: @user, post: @initiative) } - let(:id) { internal_comment.id } - let(:body) { "His hair is not blond, it's orange. Get your facts straight!" } - - example_request "Update author's own internal comment on an initiative" do - assert_status 200 - json_response = json_parse(response_body) - expect(json_response.dig(:data, :attributes, :body)).to match body - expect(@initiative.reload.internal_comments_count).to eq 1 - end - - example "[Unauthorized] Update other admin's internal comment on an initiative" do - internal_comment.update!(author: create(:admin)) - - do_request - assert_status 401 - expect(@initiative.reload.internal_comments_count).to eq 1 - end - end - - patch 'web_api/v1/internal_comments/:id/mark_as_deleted' do - let(:internal_comment) { create(:internal_comment, author: @user, post: @initiative) } - let(:id) { internal_comment.id } - - example_request "Author marks their own internal comment as 'deleted'" do - assert_status 204 - expect(internal_comment.reload.publication_status).to eq('deleted') - end - - example "[Unauthorized] Admin (not author) marks an internal comment on an initiative as 'deleted'" do - internal_comment.update!(author: create(:admin)) - - do_request - assert_status 401 - expect(internal_comment.reload.publication_status).to eq('published') - end - end - end - end end diff --git a/back/spec/acceptance/permissions_spec.rb b/back/spec/acceptance/permissions_spec.rb index c3689c4e190e..102d6392885d 100644 --- a/back/spec/acceptance/permissions_spec.rb +++ b/back/spec/acceptance/permissions_spec.rb @@ -91,7 +91,7 @@ example_request 'List all global permissions' do assert_status 200 - expect(response_data.size).to eq 6 + expect(response_data.size).to eq 3 expect(response_data.map { |d| d.dig(:attributes, :action) }).to match_array Permission.available_actions(nil) end end @@ -123,9 +123,8 @@ end end - # TODO: posting_initiative get 'web_api/v1/permissions/:action' do - let(:action) { 'posting_initiative' } + let(:action) { 'attending_event' } example_request 'Get one global permission by action' do assert_status 200 @@ -198,7 +197,6 @@ end end - # TODO: posting_initiative patch 'web_api/v1/permissions/:action' do with_options scope: :permission do parameter :permitted_by, "Defines who is granted permission, either #{Permission::PERMITTED_BIES.join(',')}.", required: false @@ -207,7 +205,7 @@ end ValidationErrorHelper.new.error_fields(self, Permission) - let(:action) { 'reacting_initiative' } + let(:action) { 'attending_event' } let(:permitted_by) { 'users' } let(:group_ids) { create_list(:group, 3).map(&:id) } diff --git a/back/spec/acceptance/stats_initiatives_spec.rb b/back/spec/acceptance/stats_initiatives_spec.rb deleted file mode 100644 index 46dff56278da..000000000000 --- a/back/spec/acceptance/stats_initiatives_spec.rb +++ /dev/null @@ -1,98 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' -require 'rspec_api_documentation/dsl' - -def time_boundary_parameters(s) - s.parameter :start_at, 'Date defining from where results should start', required: false - s.parameter :end_at, 'Date defining till when results should go', required: false -end - -def time_series_parameters(s) - time_boundary_parameters s - s.parameter :interval, 'Either day, week, month, year', required: true -end - -def group_filter_parameter(s) - s.parameter :group, 'Group ID. Only count initiatives posted by users in the given group', required: false -end - -def topic_filter_parameter(s) - s.parameter :topic, 'Topic ID. Only count initiatives that have the given topic assigned', required: false -end - -def feedback_needed_filter_parameter(s) - s.parameter :feedback_needed, 'Only count initiatives that need feedback', required: false -end - -# TODO: cleanup-after-proposals-migration -resource 'Stats - Initiatives' do - explanation 'The various stats endpoints can be used to show certain properties of initiatives.' - header 'Content-Type', 'application/json' - - let_it_be(:now) { AppConfiguration.timezone.now } - - before { admin_header_token } - - before_all do - AppConfiguration.instance.update!(created_at: now - 3.years) - - @threshold_reached = create(:initiative_status, code: 'threshold_reached') - @initiatives_with_topics = [] - @initiatives_with_areas = [] - - begin_of_last_year = AppConfiguration.timezone.now.beginning_of_year - 1.year - - travel_to(begin_of_last_year - 1.month) do - i = create(:initiative, initiative_status: @threshold_reached) - create(:official_feedback, post: i) - end - - travel_to(begin_of_last_year + 2.months) do - @initiatives_with_topics += create_list(:initiative_with_topics, 2, initiative_status: @threshold_reached) - @initiatives_with_areas += create_list(:initiative_with_areas, 3, initiative_status: @threshold_reached) - end - - travel_to(begin_of_last_year + 5.months) do - @initiatives_with_topics += create_list(:initiative_with_topics, 3, initiative_status: @threshold_reached) - @initiatives_with_areas += create_list(:initiative_with_areas, 2, initiative_status: @threshold_reached) - create(:initiative, initiative_status: @threshold_reached) - end - end - - get 'web_api/v1/stats/initiatives_count' do - time_boundary_parameters self - group_filter_parameter self - topic_filter_parameter self - feedback_needed_filter_parameter self - - example_request 'Count all initiatives' do - assert_status 200 - json_response = json_parse response_body - expect(json_response.dig(:data, :type)).to eq 'initiatives_count' - expect(json_response.dig(:data, :attributes, :count)).to eq Initiative.published.count - end - - describe 'with feedback_needed filter' do - let(:feedback_needed) { true } - - example_request 'Count all initiatives that need feedback' do - assert_status 200 - json_response = json_parse response_body - expect(json_response.dig(:data, :type)).to eq 'initiatives_count' - expect(json_response.dig(:data, :attributes, :count)).to eq Initiative.published.count - end - - example 'Count all initiatives that need feedback for a specific assignee' do - assignee = create(:admin) - create(:initiative, initiative_status: @threshold_reached, assignee: assignee) - do_request assignee: assignee.id - - assert_status 200 - json_response = json_parse response_body - expect(json_response.dig(:data, :type)).to eq 'initiatives_count' - expect(json_response.dig(:data, :attributes, :count)).to eq 1 - end - end - end -end diff --git a/back/spec/acceptance/user_comments_spec.rb b/back/spec/acceptance/user_comments_spec.rb index 58072113a124..cf099a8e693a 100644 --- a/back/spec/acceptance/user_comments_spec.rb +++ b/back/spec/acceptance/user_comments_spec.rb @@ -21,7 +21,7 @@ describe do before do @i1 = create(:idea, published_at: Time.zone.now) - @i2 = create(:initiative, published_at: 1.day.ago) + @i2 = create(:idea, published_at: 1.day.ago) @i3 = create(:idea, published_at: 3.days.ago) @user = create(:user) @c1 = create(:comment, post: @i2, author: @user, created_at: 1.hour.ago) @@ -35,7 +35,6 @@ let(:user_id) { @user.id } let(:size) { 2 } - # TODO: posting_initiative example_request 'List the comments of a user' do expect(status).to eq(200) json_response = json_parse(response_body) diff --git a/back/spec/acceptance/users_spec.rb b/back/spec/acceptance/users_spec.rb index e1a08f72435a..16592f2e9819 100644 --- a/back/spec/acceptance/users_spec.rb +++ b/back/spec/acceptance/users_spec.rb @@ -1439,31 +1439,15 @@ end end - # TODO: cleanup-after-proposals-migration - get 'web_api/v1/users/:id/initiatives_count' do - let(:id) { @user.id } - - example 'Get the number of initiatives published by one user' do - create(:initiative, author: @user) - create(:initiative) - create(:initiative, author: @user, publication_status: 'draft') - do_request - assert_status 200 - json_response = json_parse response_body - expect(json_response.dig(:data, :type)).to eq 'initiatives_count' - expect(json_response.dig(:data, :attributes, :count)).to eq 1 - end - end - get 'web_api/v1/users/:id/comments_count' do parameter :post_type, "Count only comments of one post type. Either 'Idea' or 'Initiative'.", required: false let(:id) { @user.id } example 'Get the number of comments posted by one user' do - create(:comment, author: @user, post: create(:initiative)) create(:comment) create(:comment, author: @user, post: create(:idea)) + create(:comment, author: @user, post: create(:idea)) create(:comment, author: @user, publication_status: 'deleted') do_request expect(status).to eq 200 @@ -1471,6 +1455,8 @@ expect(json_response.dig(:data, :attributes, :count)).to eq 2 end + # TODO: cleanup-after-proposals-migration + # This test won't make sense once Comment.post_type is removed example 'Get the number of comments on ideas posted by one user' do create(:comment, author: @user, post: create(:initiative)) create(:comment, post: create(:initiative)) @@ -1482,19 +1468,6 @@ json_response = json_parse(response_body) expect(json_response.dig(:data, :attributes, :count)).to eq 2 end - - # TODO: cleanup-after-proposals-migration - example 'Get the number of comments on initiatives posted by one user' do - create(:comment, author: @user, post: create(:initiative)) - create(:comment, author: @user, post: create(:initiative)) - create(:comment, post: create(:idea)) - create(:comment, author: @user, post: create(:idea)) - create(:comment, author: @user, publication_status: 'deleted', post: create(:initiative)) - do_request post_type: 'Initiative' - expect(status).to eq 200 - json_response = json_parse(response_body) - expect(json_response.dig(:data, :attributes, :count)).to eq 2 - end end end end diff --git a/back/spec/factories/notifications.rb b/back/spec/factories/notifications.rb index 8ed70d4f7e65..214142f814c3 100644 --- a/back/spec/factories/notifications.rb +++ b/back/spec/factories/notifications.rb @@ -31,12 +31,6 @@ association :post, factory: :idea end - factory :cosponsor_of_your_initiative, parent: :notification, class: 'Notifications::CosponsorOfYourInitiative' do - association :post, factory: :initiative - cosponsors_initiative - initiating_user - end - factory :cosponsor_of_your_idea, parent: :notification, class: 'Notifications::CosponsorOfYourIdea' do association :post, factory: :idea cosponsorship @@ -67,30 +61,6 @@ association :post, factory: :idea end - factory :internal_comment_on_initiative_assigned_to_you, - parent: :notification, - class: 'Notifications::InternalComments::InternalCommentOnInitiativeAssignedToYou' do - initiating_user - internal_comment - association :post, factory: :initiative - end - - factory :internal_comment_on_initiative_you_commented_internally_on, - parent: :notification, - class: 'Notifications::InternalComments::InternalCommentOnInitiativeYouCommentedInternallyOn' do - initiating_user - internal_comment - association :post, factory: :initiative - end - - factory :internal_comment_on_unassigned_initiative, - parent: :notification, - class: 'Notifications::InternalComments::InternalCommentOnUnassignedInitiative' do - initiating_user - internal_comment - association :post, factory: :initiative - end - factory :internal_comment_on_unassigned_unmoderated_idea, parent: :notification, class: 'Notifications::InternalComments::InternalCommentOnUnassignedUnmoderatedIdea' do @@ -114,39 +84,17 @@ initiating_user end - factory :initiative_assigned_to_you, parent: :notification, class: 'Notifications::InitiativeAssignedToYou' do - initiating_user - association :post, factory: :initiative - end - - factory :initiative_marked_as_spam, parent: :notification, class: 'Notifications::InitiativeMarkedAsSpam' do - association :post, factory: :initiative - spam_report - initiating_user - end - factory :invite_accepted, parent: :notification, class: 'Notifications::InviteAccepted' do initiating_user invite end - factory :invitation_to_cosponsor_initiative, parent: :notification, class: 'Notifications::InvitationToCosponsorInitiative' do - association :post, factory: :initiative - cosponsors_initiative - initiating_user - end - factory :invitation_to_cosponsor_idea, parent: :notification, class: 'Notifications::InvitationToCosponsorIdea' do association :post, factory: :idea cosponsorship initiating_user end - factory :initiative_resubmitted_for_review, parent: :notification, class: 'Notifications::InitiativeResubmittedForReview' do - association :post, factory: :initiative - association :post_status, factory: :initiative_status, code: 'review_pending' - end - factory :comment_on_idea_you_follow, parent: :notification, class: 'Notifications::CommentOnIdeaYouFollow' do initiating_user comment @@ -154,12 +102,6 @@ project end - factory :comment_on_initiative_you_follow, parent: :notification, class: 'Notifications::CommentOnInitiativeYouFollow' do - initiating_user - comment - association :post, factory: :initiative - end - factory :mention_in_comment, parent: :notification, class: 'Notifications::MentionInComment' do initiating_user comment @@ -185,12 +127,6 @@ project end - factory :official_feedback_on_initiative_you_follow, parent: :notification, class: 'Notifications::OfficialFeedbackOnInitiativeYouFollow' do - initiating_user - official_feedback - association :post, factory: :initiative - end - factory :project_folder_moderation_rights_received, parent: :notification, class: 'Notifications::ProjectFolderModerationRightsReceived' do initiating_user project_folder @@ -236,16 +172,8 @@ end end - factory :status_change_on_initiative_you_follow, parent: :notification, class: 'Notifications::StatusChangeOnInitiativeYouFollow' do - association :post, factory: :initiative - association :post_status, factory: :initiative_status - before(:create) do |notification| - notification.post.initiative_status_changes.create!(initiative_status: notification.post_status) - end - end - factory :threshold_reached_for_admin, parent: :notification, class: 'Notifications::ThresholdReachedForAdmin' do - association :post, factory: :initiative + association :post, factory: :idea association :post_status, factory: :proposals_status end diff --git a/back/spec/finders/initiatives_finder_spec.rb b/back/spec/finders/initiatives_finder_spec.rb deleted file mode 100644 index aa4990b2041e..000000000000 --- a/back/spec/finders/initiatives_finder_spec.rb +++ /dev/null @@ -1,195 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -# TODO: cleanup-after-proposals-migration -describe InitiativesFinder do - subject(:finder) { described_class.new(params, **options) } - - let(:record_ids) { finder.find_records.pluck(:id) } - let(:options) { {} } - let(:params) { {} } - - before { create_list(:initiative, 3, assignee: create(:admin)) } - - context 'without passing params' do - it 'returns all initiatives' do - expect(finder.find_records.count).to eq Initiative.count - end - end - - describe '#find_records' do - let(:normal_user) { create(:user) } - - let(:options) do - { - scope: InitiativePolicy::Scope.new(normal_user, Initiative).resolve - } - end - - before do - users = User.all - Initiative.first.cosponsors << users[0] - Initiative.first.cosponsors << users[1] - end - - it 'does not return duplicate records' do - expect(record_ids).to match_array Initiative.all.pluck(:id).uniq - end - end - - describe '#topics_condition' do - let(:topic_ids) { Topic.limit(3).pluck(:id) } - - before do - params[:topics] = topic_ids - end - - it 'filters by topics' do - expect(record_ids).to match_array Initiative.with_some_topics(topic_ids) - end - end - - describe '#areas_condition' do - let(:area_ids) { Area.limit(3).pluck(:id) } - - before do - params[:areas] = area_ids - end - - it 'filters by areas' do - expect(record_ids).to match_array Initiative.with_some_areas(area_ids) - end - end - - describe '#initiative_status_condition' do - let(:initiative_status_id) { InitiativeStatus.first.id } - - before do - params[:initiative_status] = initiative_status_id - end - - it 'filters by initiative_status' do - filtered_ids = Initiative - .left_outer_joins(:initiative_initiative_status) - .where(initiative_initiative_statuses: { initiative_status_id: initiative_status_id }) - .pluck(:id) - expect(record_ids).to match_array filtered_ids - end - end - - describe '#assignee_condition' do - let(:assignee) { create(:admin) } - let!(:unassigned_initiatives) { create_list(:initiative, 2, assignee: nil) } - let!(:assigned_initiatives) { create_list(:initiative, 3, assignee: assignee) } - - describe 'filtering on an assignee ID' do - let(:params) { { assignee: assignee.id } } - - it 'returns the correct records' do - expect(record_ids).to match_array assigned_initiatives.map(&:id) - end - end - - describe 'filtering on unassigned' do - let(:params) { { assignee: 'unassigned' } } - - it 'returns the correct records' do - expect(record_ids).to match_array unassigned_initiatives.map(&:id) - end - end - end - - describe '#feedback_needed_condition' do - let(:initiative_status) { create(:initiative_status) } - let(:feedback_needed_initiative_status) { create(:initiative_status, code: 'threshold_reached') } - - context 'when true' do - before do - create(:initiative, initiative_status: initiative_status) - create(:initiative, initiative_status: feedback_needed_initiative_status) - params[:feedback_needed] = true - end - - it 'filters by feedback needed' do - expect(record_ids).to match_array Initiative.feedback_needed.pluck(:id) - end - end - - context 'when false' do - before do - create(:initiative, initiative_status: initiative_status) - create(:initiative, initiative_status: feedback_needed_initiative_status) - params[:feedback_needed] = false - end - - it 'filters by feedback not needed' do - expect(record_ids).to match_array Initiative.no_feedback_needed.pluck(:id) - end - end - end - - describe '#author_condition' do - let(:author) { create(:user) } - - before do - create(:initiative, author_id: author.id) - params[:author] = author.id - end - - it 'filters by author' do - expect(record_ids).to match_array Initiative.where(author_id: author.id).pluck(:id) - end - end - - describe '#search_condition' do - let(:slug) { 'slug-1' } - let(:expected_record_ids) { Initiative.search_by_all(slug).pluck(:id) } - - before do - params[:search] = slug - end - - it 'returns the correct records' do - create(:initiative, slug: slug) - expect(record_ids).to match_array expected_record_ids - end - end - - describe '#publication_status_condition' do - let(:publication_status) { :published } - - before do - create(:initiative, publication_status: publication_status) - params[:publication_status] = publication_status - end - - it 'filters by publication_status' do - expect(record_ids).to match_array Initiative.where(publication_status: publication_status).pluck(:id) - end - end - - describe '#bounding_box_condition' do - let(:bounding_box) { '[51.208758, 3.224363, 50.000667, 5.715281]' } - - before do - params[:bounding_box] = bounding_box - end - - it 'filters by bounding_box' do - expect(record_ids).to match_array Initiative.with_bounding_box(bounding_box).pluck(:id) - end - end - - describe '#initiatives_condition' do - let(:ids) { Initiative.limit(2).pluck(:id) } - - before do - params[:initiatives] = ids - end - - it 'filters by initiatives' do - expect(record_ids).to match_array Initiative.where(id: ids).pluck(:id) - end - end -end diff --git a/back/spec/jobs/log_activity_job_spec.rb b/back/spec/jobs/log_activity_job_spec.rb index 476920f7b05f..ada3362cf134 100644 --- a/back/spec/jobs/log_activity_job_spec.rb +++ b/back/spec/jobs/log_activity_job_spec.rb @@ -178,23 +178,18 @@ Notifications::CommentMarkedAsSpam, Notifications::CommentOnYourComment, Notifications::CommentOnIdeaYouFollow, - Notifications::CommentOnInitiativeYouFollow, Notifications::IdeaMarkedAsSpam, - Notifications::InitiativeAssignedToYou, - Notifications::InitiativeMarkedAsSpam, Notifications::InviteAccepted, Notifications::MarkedAsSpam, Notifications::MentionInComment, Notifications::MentionInOfficialFeedback, Notifications::OfficialFeedbackOnIdeaYouFollow, - Notifications::OfficialFeedbackOnInitiativeYouFollow, Notifications::ProjectFolderModerationRightsReceived, Notifications::ProjectModerationRightsReceived, Notifications::ProjectPhaseStarted, Notifications::ProjectPhaseUpcoming, Notifications::ProjectPublished, Notifications::StatusChangeOnIdeaYouFollow, - Notifications::StatusChangeOnInitiativeYouFollow, Notifications::ThresholdReachedForAdmin, OfficialFeedback, Phase, diff --git a/back/spec/models/initiative_spec.rb b/back/spec/models/initiative_spec.rb index 246532cc8fd1..09bd170b0a04 100644 --- a/back/spec/models/initiative_spec.rb +++ b/back/spec/models/initiative_spec.rb @@ -266,13 +266,13 @@ expect(User.find(cosponsor1.id)).to be_present end - it 'removes cosponsors_initiative even when an associated notifcation exists' do - cosponsor2 = create(:user) - create(:invitation_to_cosponsor_initiative, cosponsors_initiative: cosponsors_initiative) - initiative.update!(cosponsor_ids: [cosponsor2.id]) + # it 'removes cosponsors_initiative even when an associated notifcation exists' do + # cosponsor2 = create(:user) + # create(:invitation_to_cosponsor_initiative, cosponsors_initiative: cosponsors_initiative) + # initiative.update!(cosponsor_ids: [cosponsor2.id]) - expect(initiative.reload.cosponsors).to match_array [cosponsor2] - end + # expect(initiative.reload.cosponsors).to match_array [cosponsor2] + # end it 'can add and remove cosponsors_initiatives at the same time' do cosponsor2 = create(:user) diff --git a/back/spec/models/notification_spec.rb b/back/spec/models/notification_spec.rb index 362fe4225a52..3c94a8a4ba06 100644 --- a/back/spec/models/notification_spec.rb +++ b/back/spec/models/notification_spec.rb @@ -132,14 +132,6 @@ expect(described_class.count).to eq(count - 1) end - it 'deleting an initiative status also deletes notifications requiring that initiative status' do - initiative_status = create(:initiative_status) - create(:status_change_on_initiative_you_follow, post_status: initiative_status) - count = described_class.count - initiative_status.destroy! - expect(described_class.count).to eq(count - 1) - end - it 'deleting an invite also deletes notifications requiring that invite' do invite = create(:invite) create(:invite_accepted, invite: invite) diff --git a/back/spec/models/notifications/comment_on_initiative_you_follow_spec.rb b/back/spec/models/notifications/comment_on_initiative_you_follow_spec.rb deleted file mode 100644 index 41152605f43c..000000000000 --- a/back/spec/models/notifications/comment_on_initiative_you_follow_spec.rb +++ /dev/null @@ -1,33 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe Notifications::CommentOnInitiativeYouFollow do - describe 'make_notifications_on' do - it 'makes a notification on created comment activity' do - initiative = create(:initiative) - follower = create(:follower, followable: initiative) - comment = create(:comment, post: initiative) - activity = create(:activity, item: comment, action: 'created') - - notifications = described_class.make_notifications_on activity - expect(notifications.first).to have_attributes( - recipient_id: follower.user_id, - initiating_user_id: comment.author_id, - post_id: initiative.id, - comment_id: comment.id - ) - end - - it 'does not make a notification when follow feature is turned off' do - initiative = create(:initiative) - create(:follower, followable: initiative) - comment = create(:comment, post: initiative) - activity = create(:activity, item: comment, action: 'created') - - SettingsService.new.deactivate_feature! 'follow' - notifications = described_class.make_notifications_on activity - expect(notifications).to be_empty - end - end -end diff --git a/back/spec/models/notifications/cosponsor_of_your_initiative_spec.rb b/back/spec/models/notifications/cosponsor_of_your_initiative_spec.rb deleted file mode 100644 index 2eedc91b14dc..000000000000 --- a/back/spec/models/notifications/cosponsor_of_your_initiative_spec.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe Notifications::CosponsorOfYourInitiative do - describe 'cosponsor_of_your_initiative' do - it 'makes a notification on cosponsorship_accepted cosponsors_initiative activity' do - initiative = create(:initiative, author: create(:user)) - cosponsors_initiative = create(:cosponsors_initiative, initiative: initiative, status: 'accepted') - activity = create( - :activity, - item: cosponsors_initiative, - user_id: cosponsors_initiative.user_id, - action: 'cosponsorship_accepted' - ) - - notifications = described_class.make_notifications_on activity - expect(notifications.first).to have_attributes( - recipient_id: initiative.author_id, - initiating_user: cosponsors_initiative.user, - post: initiative - ) - end - end -end diff --git a/back/spec/models/notifications/initiative_resubmitted_for_review_spec.rb b/back/spec/models/notifications/initiative_resubmitted_for_review_spec.rb deleted file mode 100644 index dd930fb9da21..000000000000 --- a/back/spec/models/notifications/initiative_resubmitted_for_review_spec.rb +++ /dev/null @@ -1,21 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe Notifications::InitiativeResubmittedForReview do - describe '.make_notifications_on' do - it 'makes a notification on changed_status activity, when status changes from changes_requested to review_pending' do - assignee = create(:admin) - initiative = create(:initiative, assignee: assignee) - create(:initiative_status_change, initiative: initiative, initiative_status: create(:initiative_status, code: 'changes_requested')) - create(:initiative_status_change, initiative: initiative, initiative_status: create(:initiative_status, code: 'review_pending')) - activity = create(:activity, item: initiative, action: 'changed_status') - - notifications = described_class.make_notifications_on activity - expect(notifications.first).to have_attributes( - recipient_id: assignee.id, - post_id: initiative.id - ) - end - end -end diff --git a/back/spec/models/notifications/internal_comments/internal_comment_on_initiative_assigned_to_you_spec.rb b/back/spec/models/notifications/internal_comments/internal_comment_on_initiative_assigned_to_you_spec.rb deleted file mode 100644 index 265a2994e526..000000000000 --- a/back/spec/models/notifications/internal_comments/internal_comment_on_initiative_assigned_to_you_spec.rb +++ /dev/null @@ -1,74 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe Notifications::InternalComments::InternalCommentOnInitiativeAssignedToYou do - describe 'make_notifications_on' do - let(:assignee) { create(:admin) } - let(:initiative) { create(:initiative, assignee: assignee) } - let(:internal_comment) { create(:internal_comment, post: initiative) } - - context 'when an assignee should receive this notification' do - let(:activity) { create(:activity, item: internal_comment, action: 'created') } - - it 'makes a notification on created internal comment activity' do - notifications = described_class.make_notifications_on activity - expect(notifications.first).to have_attributes( - recipient_id: assignee.id, - initiating_user_id: internal_comment.author_id, - post_id: initiative.id, - internal_comment_id: internal_comment.id - ) - end - end - - shared_examples 'no notification created' do - it 'does not make a notification on created internal comment activity' do - notifications_count = described_class.count - activity = create(:activity, item: internal_comment, action: 'created') - notifications = described_class.make_notifications_on activity - - expect(notifications.count).to eq notifications_count - end - end - - context 'when the recipient is the internal comment author' do - let(:internal_comment) { create(:internal_comment, author: assignee, post: initiative) } - - it_behaves_like 'no notification created' - end - - context "when the internal comment is a comment on the assignee's internal comment" do - let(:parent_internal_comment) { create(:internal_comment, post: initiative, author: assignee) } - let(:internal_comment) { create(:internal_comment, parent: parent_internal_comment, post: initiative) } - - # Don't create this notification if the Activity (internal comment created) - # should lead to a InternalCommentOnYourInternalComment notification to the recipient. - it_behaves_like 'no notification created' - end - - context 'when the internal comment contains a mention of the assignee' do - let(:internal_comment) do - create( - :internal_comment, - :with_mentions, - mentioned_users: [assignee], - post: initiative - ) - end - - # Don't create this notification if the Activity (internal comment created) - # should lead to a MentionInInternalComment notification to the recipient. - it_behaves_like 'no notification created' - end - - context 'when the internal comment is on an idea the assignee is assigned to' do - let(:idea) { create(:idea, assignee: assignee) } - let(:internal_comment) { create(:internal_comment, post: idea) } - - # Don't create this notification if the Activity (internal comment created) - # should lead to a InternalCommentOnIdeaAssignedToYou notification to the recipient. - it_behaves_like 'no notification created' - end - end -end diff --git a/back/spec/models/notifications/internal_comments/internal_comment_on_initiative_you_commented_internally_on_spec.rb b/back/spec/models/notifications/internal_comments/internal_comment_on_initiative_you_commented_internally_on_spec.rb deleted file mode 100644 index 7019c7dfd4ea..000000000000 --- a/back/spec/models/notifications/internal_comments/internal_comment_on_initiative_you_commented_internally_on_spec.rb +++ /dev/null @@ -1,96 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe Notifications::InternalComments::InternalCommentOnInitiativeYouCommentedInternallyOn do - describe 'make_notifications_on' do - let(:initiative) { create(:initiative) } - let!(:commenter) { create(:admin) } - let!(:_internal_comment2) { create(:internal_comment, post: initiative, author: commenter) } - let(:internal_comment) { create(:internal_comment, post: initiative) } - - context 'when admin has internally commented on initiative and should receive this notification' do - let(:activity) { create(:activity, item: internal_comment, action: 'created') } - - it 'makes a notification on created internal comment activity' do - notifications = described_class.make_notifications_on activity - expect(notifications.first).to have_attributes( - recipient_id: commenter.id, - initiating_user_id: internal_comment.author_id, - internal_comment_id: internal_comment.id, - post_id: initiative.id, - post_type: 'Initiative', - project_id: nil - ) - end - end - - context 'when recipient is moderator and should receive this notification' do - let(:initiative2) { create(:initiative) } - let!(:project_moderator) { create(:project_moderator) } - let(:internal_comment) { create(:internal_comment, post: initiative2) } - let!(:_internal_comment2) { create(:internal_comment, post: initiative2, author: project_moderator) } - let(:activity) { create(:activity, item: internal_comment, action: 'created') } - - it 'makes a notification on created internal comment activity' do - notifications = described_class.make_notifications_on activity - expect(notifications.first).to have_attributes( - recipient_id: project_moderator.id, - initiating_user_id: internal_comment.author_id, - internal_comment_id: internal_comment.id, - post_id: initiative2.id, - post_type: 'Initiative', - project_id: nil - ) - end - end - - shared_examples 'no notification created' do - it 'does not make a notification on created internal comment activity' do - notifications_count = described_class.count - activity = create(:activity, item: internal_comment, action: 'created') - notifications = described_class.make_notifications_on activity - - expect(notifications.count).to eq notifications_count - end - end - - context 'when the recipient is the internal comment author' do - let(:internal_comment) { create(:internal_comment, author: commenter, post: initiative) } - - it_behaves_like 'no notification created' - end - - context "when the internal comment is a comment on the recipient's internal comment" do - let(:parent_internal_comment) { create(:internal_comment, post: initiative, author: commenter) } - let(:internal_comment) { create(:internal_comment, parent: parent_internal_comment, post: initiative) } - - # Don't create this notification if the Activity (internal comment created) - # should lead to a InternalCommentOnYourInternalComment notification to the recipient. - it_behaves_like 'no notification created' - end - - context 'when the internal comment contains a mention of the recipient' do - let(:internal_comment) do - create( - :internal_comment, - :with_mentions, - mentioned_users: [commenter], - post: initiative - ) - end - - # Don't create this notification if the Activity (internal comment created) - # should lead to a MentionInInternalComment notification to the recipient. - it_behaves_like 'no notification created' - end - - context 'when the internal comment is on an initiative the recipient is assigned to' do - let(:initiative) { create(:initiative, assignee: commenter) } - - # Don't create this notification if the Activity (internal comment created) - # should lead to a InternalCommentOnInitiativeAssignedToYou notification to the recipient. - it_behaves_like 'no notification created' - end - end -end diff --git a/back/spec/models/notifications/internal_comments/internal_comment_on_unassigned_initiative_spec.rb b/back/spec/models/notifications/internal_comments/internal_comment_on_unassigned_initiative_spec.rb deleted file mode 100644 index fd327838a428..000000000000 --- a/back/spec/models/notifications/internal_comments/internal_comment_on_unassigned_initiative_spec.rb +++ /dev/null @@ -1,79 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe Notifications::InternalComments::InternalCommentOnUnassignedInitiative do - describe 'make_notifications_on' do - let(:initiative) { create(:initiative) } - let!(:admin) { create(:admin) } - let(:internal_comment) { create(:internal_comment, post: initiative) } - - context 'when an admin should receive this notification' do - let(:activity) { create(:activity, item: internal_comment, action: 'created') } - - it 'makes a notification on created internal comment activity' do - notifications = described_class.make_notifications_on activity - expect(notifications.first).to have_attributes( - recipient_id: admin.id, - initiating_user_id: internal_comment.author_id, - post_id: initiative.id, - internal_comment_id: internal_comment.id - ) - end - end - - shared_examples 'no notification created' do - it 'does not make a notification on created internal comment activity' do - notifications_count = described_class.count - activity = create(:activity, item: internal_comment, action: 'created') - notifications = described_class.make_notifications_on activity - - expect(notifications.count).to eq notifications_count - end - end - - context 'when the recipient is the internal comment author' do - let(:internal_comment) { create(:internal_comment, author: admin, post: initiative) } - - it_behaves_like 'no notification created' - end - - context 'when someone is assigned to the initiative' do - let(:initiative) { create(:initiative, assignee: create(:admin)) } - - it_behaves_like 'no notification created' - end - - context "when the internal comment is a comment on the recipient's internal comment" do - let(:parent_internal_comment) { create(:internal_comment, post: initiative, author: admin) } - let(:internal_comment) { create(:internal_comment, parent: parent_internal_comment, post: initiative) } - - # Don't create this notification if the Activity (internal comment created) - # should lead to a InternalCommentOnYourInternalComment notification to the recipient. - it_behaves_like 'no notification created' - end - - context 'when the internal comment contains a mention of the recipient' do - let(:internal_comment) do - create( - :internal_comment, - :with_mentions, - mentioned_users: [admin], - post: initiative - ) - end - - # Don't create this notification if the Activity (internal comment created) - # should lead to a MentionInInternalComment notification to the recipient. - it_behaves_like 'no notification created' - end - - context 'when the recipient has already commented internally on the initiative' do - let!(:_other_internal_comment) { create(:internal_comment, post: initiative, author: admin) } - - # Don't create this notification if the Activity (internal comment created) - # should lead to a InternalCommentOnInitiativeYouCommentedInternallyOn notification to the recipient. - it_behaves_like 'no notification created' - end - end -end diff --git a/back/spec/models/notifications/internal_comments/internal_comment_on_unassigned_unmoderated_idea_spec.rb b/back/spec/models/notifications/internal_comments/internal_comment_on_unassigned_unmoderated_idea_spec.rb index 97ba9401f488..b9507ebb54fc 100644 --- a/back/spec/models/notifications/internal_comments/internal_comment_on_unassigned_unmoderated_idea_spec.rb +++ b/back/spec/models/notifications/internal_comments/internal_comment_on_unassigned_unmoderated_idea_spec.rb @@ -87,7 +87,7 @@ let!(:_other_internal_comment) { create(:internal_comment, post: idea, author: admin) } # Don't create this notification if the Activity (internal comment created) - # should lead to a InternalCommentOnInitiativeYouCommentedInternallyOn notification to the recipient. + # should lead to a InternalCommentOnIdeaYouCommentedInternallyOn notification to the recipient. it_behaves_like 'no notification created' end end diff --git a/back/spec/models/notifications/invitation_to_cosponsor_initiative_spec.rb b/back/spec/models/notifications/invitation_to_cosponsor_initiative_spec.rb deleted file mode 100644 index 7b930878aeae..000000000000 --- a/back/spec/models/notifications/invitation_to_cosponsor_initiative_spec.rb +++ /dev/null @@ -1,20 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe Notifications::InvitationToCosponsorInitiative do - describe 'invitation_to_cosponsor_initiative' do - it 'makes a notification on created cosponsors_initiative activity' do - initiative = create(:initiative, author: create(:user)) - cosponsors_initiative = create(:cosponsors_initiative, initiative: initiative) - activity = create(:activity, item: cosponsors_initiative, user_id: initiative.author.id, action: 'created') - - notifications = described_class.make_notifications_on activity - expect(notifications.first).to have_attributes( - recipient_id: cosponsors_initiative.user_id, - initiating_user: cosponsors_initiative.initiative.author, - post: initiative - ) - end - end -end diff --git a/back/spec/models/notifications/official_feedback_on_initiative_you_follow_spec.rb b/back/spec/models/notifications/official_feedback_on_initiative_you_follow_spec.rb deleted file mode 100644 index ec9a89ab10d1..000000000000 --- a/back/spec/models/notifications/official_feedback_on_initiative_you_follow_spec.rb +++ /dev/null @@ -1,62 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe Notifications::OfficialFeedbackOnInitiativeYouFollow do - describe 'make_notifications_on' do - it 'generates exactly one notification for each follower of the initiative' do - initiative = create(:initiative) - follower1 = create(:follower, followable: initiative) - create(:follower) - follower3 = create(:follower, followable: initiative) - - official_feedback = create(:official_feedback, post: initiative) - activity = create(:activity, item: official_feedback, action: :created) - - notifications = described_class.make_notifications_on(activity) - expect(notifications.map(&:recipient_id)).to contain_exactly follower1.user_id, follower3.user_id - end - - it "doesn't generate notifications for the initiating user" do - initiative = create(:initiative) - follower = create(:follower, followable: initiative) - - official_feedback = create(:official_feedback, post: initiative, user: follower.user) - activity = create(:activity, item: official_feedback, action: :created) - - notifications = described_class.make_notifications_on(activity) - expect(notifications).to eq [] - end - - it "doesn't generate notifications when there was a status change for the same official feedback" do - initiative = create(:initiative) - create(:follower, followable: initiative) - official_feedback = create(:official_feedback, post: initiative) - create(:initiative_status_change, initiative: initiative, official_feedback: official_feedback) - activity = create(:activity, item: official_feedback, action: :created) - - notifications = described_class.make_notifications_on(activity) - expect(notifications).to eq [] - end - - it "doesn't generate notifications when the post is an idea" do - idea = create(:idea) - official_feedback = create(:official_feedback, post: idea) - activity = create(:activity, item: official_feedback, action: :created) - - notifications = described_class.make_notifications_on(activity) - expect(notifications).to eq [] - end - - it 'does not make a notification when follow feature is turned off' do - initiative = create(:initiative) - create(:follower, followable: initiative) - official_feedback = create(:official_feedback, post: initiative) - activity = create(:activity, item: official_feedback, action: :created) - - SettingsService.new.deactivate_feature! 'follow' - notifications = described_class.make_notifications_on activity - expect(notifications).to be_empty - end - end -end diff --git a/back/spec/models/notifications/status_change_on_initiative_you_follow_spec.rb b/back/spec/models/notifications/status_change_on_initiative_you_follow_spec.rb deleted file mode 100644 index 5ff07d281cb0..000000000000 --- a/back/spec/models/notifications/status_change_on_initiative_you_follow_spec.rb +++ /dev/null @@ -1,39 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe Notifications::StatusChangeOnInitiativeYouFollow do - describe 'make_notifications_on' do - it 'generates exactly one notification for each follower of the initiative' do - initiative = create(:initiative) - follower1 = create(:follower, followable: initiative) - create(:follower) - follower3 = create(:follower, followable: initiative) - - activity = create(:activity, item: initiative, action: 'changed_status') - - notifications = described_class.make_notifications_on activity - expect(notifications.map(&:recipient_id)).to contain_exactly follower1.user_id, follower3.user_id - end - - it "doesn't generate notifications for the initiating user" do - initiative = create(:initiative) - follower = create(:follower, followable: initiative) - - activity = create(:activity, item: initiative, action: 'changed_status', user: follower.user) - - notifications = described_class.make_notifications_on(activity) - expect(notifications).to eq [] - end - - it 'does not make a notification when follow feature is turned off' do - initiative = create(:initiative) - create(:follower, followable: initiative) - activity = create(:activity, item: initiative, action: 'changed_status') - - SettingsService.new.deactivate_feature! 'follow' - notifications = described_class.make_notifications_on activity - expect(notifications).to be_empty - end - end -end diff --git a/back/spec/policies/follower_policy_spec.rb b/back/spec/policies/follower_policy_spec.rb index e02fc71ade7e..91b1d13c9e7a 100644 --- a/back/spec/policies/follower_policy_spec.rb +++ b/back/spec/policies/follower_policy_spec.rb @@ -196,46 +196,6 @@ end end - context 'for an initiative' do - let(:followable) { create(:initiative) } - - context 'for a resident' do - let(:user) { create(:user) } - - it { is_expected.not_to permit(:show) } - it { is_expected.not_to permit(:create) } - it { is_expected.not_to permit(:destroy) } - - it 'does not index the follower' do - expect(scope.resolve.size).to eq 0 - end - end - - context 'for the following user' do - let(:user) { following_user } - - it { is_expected.to permit(:show) } - it { is_expected.to permit(:create) } - it { is_expected.to permit(:destroy) } - - it 'indexes the follower' do - expect(scope.resolve.size).to eq 1 - end - end - - context 'for an admin' do - let(:user) { create(:admin) } - - it { is_expected.not_to permit(:show) } - it { is_expected.not_to permit(:create) } - it { is_expected.not_to permit(:destroy) } - - it 'does not index the follower' do - expect(scope.resolve.size).to eq 0 - end - end - end - context 'for a topic' do let(:followable) { create(:topic) } diff --git a/back/spec/policies/initiative_comment_policy_spec.rb b/back/spec/policies/initiative_comment_policy_spec.rb deleted file mode 100644 index 1181e481d36d..000000000000 --- a/back/spec/policies/initiative_comment_policy_spec.rb +++ /dev/null @@ -1,176 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -describe InitiativeCommentPolicy do - subject { described_class.new(user, comment) } - - let(:scope) { InitiativeCommentPolicy::Scope.new(user, initiative.comments) } - - context 'on comment on initiative with proposed status' do - let(:initiative) { create(:initiative) } - - let!(:_initiative_status_change) do - create( - :initiative_status_change, - initiative: initiative, - initiative_status: create(:initiative_status_proposed) - ) - end - - let!(:comment) { create(:comment, post: initiative) } - - context 'for a visitor' do - let(:user) { nil } - - it { is_expected.to permit(:show) } - it { is_expected.not_to permit(:create) } - it { is_expected.not_to permit(:update) } - it { is_expected.not_to permit(:destroy) } - - it 'indexes the comment' do - expect(scope.resolve.size).to eq 1 - end - end - - context 'for a user who is not the author of the comment' do - let(:user) { create(:user) } - - it { is_expected.to permit(:show) } - it { is_expected.not_to permit(:create) } - it { is_expected.not_to permit(:update) } - it { is_expected.not_to permit(:destroy) } - - it 'indexes the comment' do - expect(scope.resolve.size).to eq 1 - end - end - - context 'for a user who is the author of the comment' do - let(:user) { comment.author } - - it { is_expected.to permit(:show) } - it { is_expected.to permit(:create) } - it { is_expected.to permit(:update) } - it { is_expected.not_to permit(:destroy) } - - it 'indexes the comment' do - expect(scope.resolve.size).to eq 1 - end - end - - context 'for blocked comment author' do - let(:user) { create(:user, block_end_at: 5.days.from_now) } - let(:comment) { create(:comment, author: user, post: initiative) } - - it_behaves_like 'policy for blocked user' - end - - context 'for an admin' do - let(:user) { create(:admin) } - - it { is_expected.to permit(:show) } - it { is_expected.to permit(:create) } - it { is_expected.to permit(:update) } - it { is_expected.not_to permit(:destroy) } - - it 'indexes the comment' do - expect(scope.resolve.size).to eq 1 - end - end - end - - context 'on comment on initiative with review_pending status' do - let(:initiative) { create(:initiative) } - - let!(:_initiative_status_change) do - create( - :initiative_status_change, - initiative: initiative, - initiative_status: create(:initiative_status_review_pending) - ) - end - - let!(:comment) { create(:comment, post: initiative, author: initiative.author) } - - context 'for a visitor' do - let(:user) { nil } - - it { is_expected.not_to permit(:show) } - it { is_expected.not_to permit(:create) } - it { is_expected.not_to permit(:update) } - it { is_expected.not_to permit(:destroy) } - - it 'does not index the comment' do - expect(scope.resolve.size).to eq 0 - end - end - - context 'for a user who is not the author of the initiative or the comment' do - let(:user) { create(:user) } - - it { is_expected.not_to permit(:show) } - it { is_expected.not_to permit(:create) } - it { is_expected.not_to permit(:update) } - it { is_expected.not_to permit(:destroy) } - - it 'does not index the comment' do - expect(scope.resolve.size).to eq 0 - end - end - - context 'for a user who is the author of the initiative and the comment' do - let(:user) { initiative.author } - - it { is_expected.to permit(:show) } - it { is_expected.to permit(:create) } - it { is_expected.to permit(:update) } - it { is_expected.not_to permit(:destroy) } - - it 'indexes the comment' do - expect(scope.resolve.size).to eq 1 - end - end - - context 'for blocked comment author' do - let(:user) { create(:user, block_end_at: 5.days.from_now) } - let(:comment) { create(:comment, author: user, post: initiative) } - - it { is_expected.not_to permit(:show) } - it { is_expected.not_to permit(:create) } - it { is_expected.not_to permit(:update) } - it { is_expected.not_to permit(:destroy) } - - it 'does not index the comment' do - expect(scope.resolve.size).to eq 0 - end - end - - context 'for an admin' do - let(:user) { create(:admin) } - - it { is_expected.to permit(:show) } - it { is_expected.to permit(:create) } - it { is_expected.to permit(:update) } - it { is_expected.not_to permit(:destroy) } - - it 'indexes the comment' do - expect(scope.resolve.size).to eq 1 - end - end - - context 'for a user who is cosponsor of the initiative' do - let(:user) { create(:user) } - let!(:cosponsors_initiative) { create(:cosponsors_initiative, user: user, initiative: initiative) } - - it { is_expected.to permit(:show) } - it { is_expected.not_to permit(:create) } - it { is_expected.not_to permit(:update) } - it { is_expected.not_to permit(:destroy) } - - it 'indexes the initiative' do - expect(scope.resolve.size).to eq 1 - end - end - end -end diff --git a/back/spec/policies/initiative_policy_spec.rb b/back/spec/policies/initiative_policy_spec.rb deleted file mode 100644 index 19c8f38c8814..000000000000 --- a/back/spec/policies/initiative_policy_spec.rb +++ /dev/null @@ -1,417 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -# TODO: cleanup-after-proposals-migration -describe InitiativePolicy do - subject(:policy) { described_class.new(user, initiative) } - - let(:scope) { InitiativePolicy::Scope.new(user, Initiative) } - let(:author) { create(:user) } - - # We need to consider 4 main scenarios: - # 1. When review feature is NOT active, and - # 2. When review feature is active - # a). When initiative has review_pending or review_rejected status - # b). when initiative has status other than review_pending or review_rejected - # 1a + 1b + 2a + 2b = 4 scenarios - # - # We avoid testing initiatives with 'custom' status, as it is not used and not adequately considered in the codebase. - - # ------------------- 1. Review feature is NOT active ------------------- - - context 'when review feature is NOT fully active' do - it 'is not active' do - expect(Initiative.review_required?).to be false - end - - # For statuses with REVIEW_CODES, we only show/index initiatives with review statuses to the author and admins. - context 'for an initiative with status review_pending' do - let!(:initiative) do - create(:initiative, author: author, initiative_status: create(:initiative_status_review_pending)) - end - - context 'for an admin' do - let(:user) { create(:admin) } - - it { is_expected.to permit(:show) } - it { is_expected.to permit(:by_slug) } - it { is_expected.to permit(:create) } - it { is_expected.to permit(:update) } - it { is_expected.to permit(:destroy) } - it { is_expected.not_to permit(:accept_cosponsorship_invite) } - - it 'indexes the initiative' do - expect(scope.resolve.size).to eq 1 - end - end - - context 'for a user who is not author of the initiative' do - let(:user) { create(:user) } - - it { is_expected.not_to permit(:show) } - it { is_expected.not_to permit(:by_slug) } - it { is_expected.not_to permit(:create) } - it { is_expected.not_to permit(:update) } - it { is_expected.not_to permit(:destroy) } - it { is_expected.not_to permit(:accept_cosponsorship_invite) } - - it 'does not index the initiative' do - expect(scope.resolve.size).to eq 0 - end - end - - context 'for a user who is author of the initiative' do - let(:user) { author } - - it { is_expected.to permit(:show) } - it { is_expected.to permit(:by_slug) } - it { is_expected.to permit(:create) } - it { is_expected.to permit(:update) } - it { is_expected.to permit(:destroy) } - it { is_expected.not_to permit(:accept_cosponsorship_invite) } - - it 'indexes the initiative' do - expect(scope.resolve.size).to eq 1 - end - end - - context 'for a user who is cosponsor of the initiative' do - let(:user) { create(:user) } - let!(:cosponsors_initiative) { create(:cosponsors_initiative, user: user, initiative: initiative) } - - it { is_expected.to permit(:show) } - it { is_expected.to permit(:by_slug) } - it { is_expected.not_to permit(:create) } - it { is_expected.not_to permit(:update) } - it { is_expected.not_to permit(:destroy) } - it { is_expected.to permit(:accept_cosponsorship_invite) } - - it 'indexes the initiative' do - expect(scope.resolve.size).to eq 1 - end - end - - context 'for a visitor' do - let(:user) { nil } - - it { is_expected.not_to permit(:show) } - it { is_expected.not_to permit(:by_slug) } - it { expect { policy.create? }.to raise_error(Pundit::NotAuthorizedError) } - it { expect { policy.update? }.to raise_error(Pundit::NotAuthorizedError) } - it { expect { policy.destroy? }.to raise_error(Pundit::NotAuthorizedError) } - it { is_expected.not_to permit(:accept_cosponsorship_invite) } - - it 'does not index the initiative' do - expect(scope.resolve.size).to eq 0 - end - end - end - - context 'for an initiative with proposed status' do - let!(:initiative) do - create(:initiative, author: author, initiative_status: create(:initiative_status_proposed)) - end - - context 'for an admin' do - let(:user) { create(:admin) } - - it { is_expected.to permit(:show) } - it { is_expected.to permit(:by_slug) } - it { is_expected.to permit(:create) } - it { is_expected.to permit(:update) } - it { is_expected.to permit(:destroy) } - it { is_expected.not_to permit(:accept_cosponsorship_invite) } - - it 'indexes the initiative' do - expect(scope.resolve.size).to eq 1 - end - end - - context 'for a user who is not author of the initiative' do - let(:user) { create(:user) } - - it { is_expected.to permit(:show) } - it { is_expected.to permit(:by_slug) } - it { is_expected.not_to permit(:create) } - it { is_expected.not_to permit(:update) } - it { is_expected.not_to permit(:destroy) } - it { is_expected.not_to permit(:accept_cosponsorship_invite) } - - it 'indexes the initiative' do - expect(scope.resolve.size).to eq 1 - end - end - - context 'for a user who is author of the initiative' do - let(:user) { author } - - it { is_expected.to permit(:show) } - it { is_expected.to permit(:by_slug) } - it { is_expected.to permit(:create) } - it { is_expected.to permit(:update) } - it { is_expected.to permit(:destroy) } - it { is_expected.not_to permit(:accept_cosponsorship_invite) } - - it 'indexes the initiative' do - expect(scope.resolve.size).to eq 1 - end - end - - context 'for a user who is cosponsor of the initiative' do - let(:user) { create(:user) } - let!(:cosponsors_initiative) { create(:cosponsors_initiative, user: user, initiative: initiative) } - - it { is_expected.to permit(:show) } - it { is_expected.to permit(:by_slug) } - it { is_expected.not_to permit(:create) } - it { is_expected.not_to permit(:update) } - it { is_expected.not_to permit(:destroy) } - it { is_expected.to permit(:accept_cosponsorship_invite) } - - it 'indexes the initiative' do - expect(scope.resolve.size).to eq 1 - end - end - - context 'for a visitor' do - let(:user) { nil } - - it { is_expected.to permit(:show) } - it { is_expected.to permit(:by_slug) } - it { expect { policy.create? }.to raise_error(Pundit::NotAuthorizedError) } - it { expect { policy.update? }.to raise_error(Pundit::NotAuthorizedError) } - it { expect { policy.destroy? }.to raise_error(Pundit::NotAuthorizedError) } - it { is_expected.not_to permit(:accept_cosponsorship_invite) } - - it 'indexes the initiative' do - expect(scope.resolve.size).to eq 1 - end - end - end - end - - # ------------------- 2. Review feature IS active ---------------------- - - context 'when review feature IS fully active' do - before do - SettingsService.new.activate_feature! 'initiative_review' - - configuration = AppConfiguration.instance - configuration.settings['initiatives']['require_review'] = true - configuration.save! - end - - it 'is active' do - expect(Initiative.review_required?).to be true - end - - # For statuses with REVIEW_CODES, we only show initiatives with review statuses to the author and admins. - context 'for an initiative with review_pending status' do - let!(:initiative) do - create(:initiative, author: author, initiative_status: create(:initiative_status_review_pending)) - end - - context 'for an admin' do - let(:user) { create(:admin) } - - it { is_expected.to permit(:show) } - it { is_expected.to permit(:by_slug) } - it { is_expected.to permit(:create) } - it { is_expected.to permit(:update) } - it { is_expected.to permit(:destroy) } - it { is_expected.not_to permit(:accept_cosponsorship_invite) } - - it 'indexes the initiative' do - expect(scope.resolve.size).to eq 1 - end - end - - context 'for a user who is not author of the initiative' do - let(:user) { create(:user) } - - it { is_expected.not_to permit(:show) } - it { is_expected.not_to permit(:by_slug) } - it { is_expected.not_to permit(:create) } - it { is_expected.not_to permit(:update) } - it { is_expected.not_to permit(:destroy) } - it { is_expected.not_to permit(:accept_cosponsorship_invite) } - - it 'does not index the initiative' do - expect(scope.resolve.size).to eq 0 - end - end - - # Author can still see their own initiative if review feature is active, - # even when status in InitiativeStatus::REVIEW_CODES. - context 'for a user who is author of the initiative' do - let(:user) { author } - - it { is_expected.to permit(:show) } - it { is_expected.to permit(:by_slug) } - it { is_expected.to permit(:create) } - it { is_expected.to permit(:update) } - it { is_expected.to permit(:destroy) } - it { is_expected.not_to permit(:accept_cosponsorship_invite) } - - it 'indexes the initiative' do - expect(scope.resolve.size).to eq 1 - end - end - - # Cosponsor can still see an initiative thaye are a cosponsor of if review feature is active, - # even when status in InitiativeStatus::REVIEW_CODES. - context 'for a user who is cosponsor of the initiative' do - let(:user) { create(:user) } - let!(:cosponsors_initiative) { create(:cosponsors_initiative, user: user, initiative: initiative) } - - it { is_expected.to permit(:show) } - it { is_expected.to permit(:by_slug) } - it { is_expected.not_to permit(:create) } - it { is_expected.not_to permit(:update) } - it { is_expected.not_to permit(:destroy) } - it { is_expected.to permit(:accept_cosponsorship_invite) } - - it 'indexes the initiative' do - expect(scope.resolve.size).to eq 1 - end - end - - context 'for a visitor' do - let(:user) { nil } - - it { is_expected.not_to permit(:show) } - it { is_expected.not_to permit(:by_slug) } - it { expect { policy.create? }.to raise_error(Pundit::NotAuthorizedError) } - it { expect { policy.update? }.to raise_error(Pundit::NotAuthorizedError) } - it { expect { policy.destroy? }.to raise_error(Pundit::NotAuthorizedError) } - it { is_expected.not_to permit(:accept_cosponsorship_invite) } - - it 'does not index the initiative' do - expect(scope.resolve.size).to eq 0 - end - end - end - - context 'for an initiative with proposed status' do - let!(:initiative) do - create(:initiative, author: author, initiative_status: create(:initiative_status_proposed)) - end - - context 'for an admin' do - let(:user) { create(:admin) } - - it { is_expected.to permit(:show) } - it { is_expected.to permit(:by_slug) } - it { is_expected.to permit(:create) } - it { is_expected.to permit(:update) } - it { is_expected.to permit(:destroy) } - it { is_expected.not_to permit(:accept_cosponsorship_invite) } - - it 'indexes the initiative' do - expect(scope.resolve.size).to eq 1 - end - end - - context 'for a user who is not author of the initiative' do - let(:user) { create(:user) } - - it { is_expected.to permit(:show) } - it { is_expected.to permit(:by_slug) } - it { is_expected.not_to permit(:create) } - it { is_expected.not_to permit(:update) } - it { is_expected.not_to permit(:destroy) } - it { is_expected.not_to permit(:accept_cosponsorship_invite) } - - it 'does not index the initiative' do - expect(scope.resolve.size).to eq 1 - end - end - - # Author can see initiative, but not edit, if review feature is active, - # and when status in InitiativeStatus::NOT_REVIEW_CODES - context 'for a user who is author of the initiative' do - let(:user) { author } - - it { is_expected.to permit(:show) } - it { is_expected.to permit(:by_slug) } - it { is_expected.to permit(:create) } - it { is_expected.to permit(:update) } # TODO: This is confusing, as editing_locked should be true when status proposed! - it { is_expected.to permit(:destroy) } - it { is_expected.not_to permit(:accept_cosponsorship_invite) } - - it 'indexes the initiative' do - expect(scope.resolve.size).to eq 1 - end - end - - context 'for a user who is cosponsor of the initiative' do - let(:user) { create(:user) } - let!(:cosponsors_initiative) { create(:cosponsors_initiative, user: user, initiative: initiative) } - - it { is_expected.to permit(:show) } - it { is_expected.to permit(:by_slug) } - it { is_expected.not_to permit(:create) } - it { is_expected.not_to permit(:update) } - it { is_expected.not_to permit(:destroy) } - it { is_expected.to permit(:accept_cosponsorship_invite) } - - it 'indexes the initiative' do - expect(scope.resolve.size).to eq 1 - end - end - - context 'for a visitor' do - let(:user) { nil } - - it { is_expected.to permit(:show) } - it { is_expected.to permit(:by_slug) } - it { expect { policy.create? }.to raise_error(Pundit::NotAuthorizedError) } - it { expect { policy.update? }.to raise_error(Pundit::NotAuthorizedError) } - it { expect { policy.destroy? }.to raise_error(Pundit::NotAuthorizedError) } - it { is_expected.not_to permit(:accept_cosponsorship_invite) } - - it 'does not index the initiative' do - expect(scope.resolve.size).to eq 1 - end - end - end - end - - # Nobody can edit initiative if editing is locked, including admins and authors - context 'when editing is locked for the initiative' do - let!(:initiative) do - create(:initiative, author: author, editing_locked: true, initiative_status: create(:initiative_status_proposed)) - end - - context 'for an admin' do - let(:user) { create(:admin) } - - it { is_expected.to permit(:show) } - it { is_expected.to permit(:by_slug) } - it { is_expected.to permit(:create) } - it { is_expected.to permit(:update) } - it { is_expected.to permit(:destroy) } - it { is_expected.not_to permit(:accept_cosponsorship_invite) } - - it 'indexes the initiative' do - expect(scope.resolve.size).to eq 1 - end - end - - context 'for a user who is author of the initiative' do - let(:user) { author } - - it { is_expected.to permit(:show) } - it { is_expected.to permit(:by_slug) } - it { is_expected.to permit(:create) } - it { is_expected.not_to permit(:update) } - it { is_expected.to permit(:destroy) } - it { is_expected.not_to permit(:accept_cosponsorship_invite) } - - it 'indexes the initiative' do - expect(scope.resolve.size).to eq 1 - end - end - end -end diff --git a/back/spec/policies/initiative_reaction_policy_spec.rb b/back/spec/policies/initiative_reaction_policy_spec.rb deleted file mode 100644 index c6cc7bb1844c..000000000000 --- a/back/spec/policies/initiative_reaction_policy_spec.rb +++ /dev/null @@ -1,74 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -describe InitiativeReactionPolicy do - subject(:policy) { described_class.new(user, reaction) } - - let(:scope) { InitiativeReactionPolicy::Scope.new(user, Reaction) } - let(:reactable) { create(:initiative) } - let!(:reaction) { create(:reaction, reactable: reactable) } - - context 'for a visitor' do - let(:user) { nil } - - it { is_expected.not_to permit(:show) } - it { is_expected.not_to permit(:create) } - it { is_expected.not_to permit(:up) } - it { expect { policy.down? }.to raise_error(Pundit::NotAuthorizedError) } - it { is_expected.not_to permit(:destroy) } - - it 'should not index the reaction' do - expect(scope.resolve.size).to eq 0 - end - end - - context 'for a mortal user on a reaction of another user' do - let(:user) { create(:user) } - - it { is_expected.not_to permit(:show) } - it { is_expected.not_to permit(:create) } - it { is_expected.not_to permit(:up) } - it { expect { policy.down? }.to raise_error(Pundit::NotAuthorizedError) } - it { is_expected.not_to permit(:destroy) } - - it 'should not index the reaction' do - expect(scope.resolve.size).to eq 0 - end - end - - context 'for a mortal user who owns the reaction' do - let(:user) { reaction.user } - - it { is_expected.to permit(:show) } - it { is_expected.to permit(:create) } - it { is_expected.to permit(:up) } - it { expect { policy.down? }.to raise_error(Pundit::NotAuthorizedError) } - it { is_expected.to permit(:destroy) } - - it 'should index the reaction' do - expect(scope.resolve.size).to eq 1 - end - end - - context 'for blocked reaction owner' do - let(:user) { create(:user, block_end_at: 5.days.from_now) } - let(:reaction) { create(:reaction, user: user, reactable: reactable) } - - it_behaves_like 'policy for blocked user reaction', down_authorized: false - end - - context 'for an admin' do - let(:user) { create(:admin) } - - it { is_expected.to permit(:show) } - it { is_expected.not_to permit(:create) } - it { is_expected.not_to permit(:up) } - it { expect { policy.down? }.to raise_error(Pundit::NotAuthorizedError) } - it { is_expected.not_to permit(:destroy) } - - it 'should index the reaction' do - expect(scope.resolve.size).to eq 1 - end - end -end diff --git a/back/spec/policies/initiative_status_policy_spec.rb b/back/spec/policies/initiative_status_policy_spec.rb deleted file mode 100644 index dd63b5b6d68e..000000000000 --- a/back/spec/policies/initiative_status_policy_spec.rb +++ /dev/null @@ -1,81 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -# TODO: cleanup-after-proposals-migration -describe InitiativeStatusPolicy do - describe InitiativeStatusPolicy::Scope do - subject(:scope) { described_class.new(user, InitiativeStatus).resolve } - - def create_initiative_status_change(status, initiative: build(:initiative)) - create(:initiative_status_change, initiative: initiative, initiative_status: status) - end - - context 'with default statuses' do - let!(:status_review_pending) { create(:initiative_status_review_pending) } - let!(:status_changes_requested) { create(:initiative_status_changes_requested) } - let!(:status_proposed) { create(:initiative_status_proposed) } - let!(:status_expired) { create(:initiative_status_expired) } - let!(:status_threshold_reached) { create(:initiative_status_threshold_reached) } - let!(:status_answered) { create(:initiative_status_answered) } - let!(:status_ineligible) { create(:initiative_status_ineligible) } - - let!(:default_codes) { InitiativeStatus::CODES - ['custom'] } - let!(:not_review_codes) { InitiativeStatus::NOT_REVIEW_CODES - ['custom'] } - - context 'when admin' do - let(:user) { create(:admin) } - - context 'when review feature is not fully activated' do - it 'does not return review statuses if no review initiative exists' do - expect(scope.pluck(:code)).to match_array not_review_codes - end - - it 'returns all statuses if at least one review_pending initiative exists' do - create_initiative_status_change(status_review_pending) - expect(scope.pluck(:code)).to match_array default_codes - end - - it 'returns all statuses if at least one changes_requested initiative exists' do - create_initiative_status_change(status_changes_requested) - expect(scope.pluck(:code)).to match_array default_codes - end - end - - context 'when review feature is fully activated' do - before do - SettingsService.new.activate_feature! 'initiative_review' - - configuration = AppConfiguration.instance - configuration.settings['initiatives']['require_review'] = true - configuration.save! - end - - it 'returns all initiative statuses' do - expect(scope.pluck(:code)).to match_array default_codes - end - end - end - - context 'when normal user' do - let(:user) { create(:user) } - - it 'does not return review statuses if user authored no review initiatives' do - create_initiative_status_change(status_review_pending) - create_initiative_status_change(status_changes_requested) - expect(scope.pluck(:code)).to match_array not_review_codes - end - - it 'returns review_pending status if user authored an review_pending initiative' do - create_initiative_status_change(status_review_pending, initiative: build(:initiative, author: user)) - expect(scope.pluck(:code)).to match_array not_review_codes + ['review_pending'] - end - - it 'returns changes_requested status if user authored an changes_requested initiative' do - create_initiative_status_change(status_changes_requested, initiative: build(:initiative, author: user)) - expect(scope.pluck(:code)).to match_array not_review_codes + ['changes_requested'] - end - end - end - end -end diff --git a/back/spec/requests/rack_attack_spec.rb b/back/spec/requests/rack_attack_spec.rb index c46ac9a86fc1..d99535eb6693 100644 --- a/back/spec/requests/rack_attack_spec.rb +++ b/back/spec/requests/rack_attack_spec.rb @@ -248,16 +248,16 @@ it 'limits search requests from same IP to 15 in 20 seconds' do freeze_time do 15.times do - get '/web_api/v1/initiatives?search=some-random-search-term' + get '/web_api/v1/ideas?search=some-random-search-term' end expect(status).to eq(200) # OK - get '/web_api/v1/initiatives?search=some-random-search-term' + get '/web_api/v1/ideas?search=some-random-search-term' expect(status).to eq(429) # Too many requests end travel_to(20.seconds.from_now) do - get '/web_api/v1/initiatives?search=some-random-search-term' + get '/web_api/v1/ideas?search=some-random-search-term' expect(status).to eq(200) # OK end end diff --git a/back/spec/serializers/web_api/v1/initiative_serializer_spec.rb b/back/spec/serializers/web_api/v1/initiative_serializer_spec.rb deleted file mode 100644 index 62f5ef698f69..000000000000 --- a/back/spec/serializers/web_api/v1/initiative_serializer_spec.rb +++ /dev/null @@ -1,105 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -describe WebApi::V1::InitiativeSerializer do - context "with 'abbreviated user names' enabled" do - before { SettingsService.new.activate_feature! 'abbreviated_user_names' } - - let(:jane) { create(:user, first_name: 'Jane', last_name: 'Doe') } - let(:john) { create(:user, first_name: 'John', last_name: 'Smith') } - let(:admin) { create(:admin, first_name: 'Thomas', last_name: 'Anderson') } - - it 'should abbreviate the author name' do - jane_initiative = create(:initiative, author: jane) - last_name = described_class - .new(jane_initiative, params: { current_user: john }) - .serializable_hash - .dig(:data, :attributes, :author_name) - expect(last_name).to eq 'Jane D.' - end - - it 'should not abbreviate user names for admins' do - jane_initiative = create(:initiative, author: jane) - last_name = described_class - .new(jane_initiative, params: { current_user: admin }) - .serializable_hash - .dig(:data, :attributes, :author_name) - expect(last_name).to eq 'Jane Doe' - - admin_initiative = create(:initiative, author: admin) - last_name = described_class - .new(admin_initiative, params: { current_user: john }) - .serializable_hash - .dig(:data, :attributes, :author_name) - expect(last_name).to eq 'Thomas Anderson' - end - end - - context 'when serializing internal comments count of initiative' do - let(:initiative) { create(:initiative) } - - before do - create_list(:internal_comment, 2, post: initiative) - initiative.reload - end - - context 'when current user is nil (visitor)' do - it 'should not include internal comments count' do - expect(internal_comments_count_for_current_user(initiative, nil)).to be_nil - end - end - - context 'when current user is regular user' do - it 'should not include internal comments count' do - expect(internal_comments_count_for_current_user(initiative, create(:user))).to be_nil - end - end - - context 'when current user is admin' do - it 'should include internal comments count' do - expect(internal_comments_count_for_current_user(initiative, create(:admin))).to eq 2 - end - end - end - - context 'when cosponsors of initiative exist' do - let(:initiative) { create(:initiative) } - let(:current_user) { create(:user) } - let(:cosponsor) { create(:user) } - let(:name_service) { UserDisplayNameService.new(AppConfiguration.instance, current_user) } - let(:cosponsor_display_name) { name_service.display_name!(cosponsor) } - let!(:_cosponsorship) { create(:cosponsors_initiative, initiative: initiative, user: cosponsor) } - - it 'should include cosponsorships' do - expect(cosponsorships(initiative, current_user).first[:user_id]).to eq cosponsor.id - expect(cosponsorships(initiative, current_user).first[:name]).to eq cosponsor_display_name - end - - it 'should include cosponsors' do - expect(cosponsors(initiative, current_user).size).to eq 1 - expect(cosponsors(initiative, current_user).first[:id]).to eq cosponsor.id - end - end - - def internal_comments_count_for_current_user(initiative, current_user) - described_class - .new(initiative, params: { current_user: current_user }) - .serializable_hash - .dig(:data, :attributes, :internal_comments_count) - end - - def cosponsors(initiative, current_user) - described_class - .new(initiative, params: { current_user: current_user }) - .serializable_hash - .dig(:data, :relationships, :cosponsors, :data) - end - - def cosponsorships(initiative, current_user) - described_class - .new(initiative, params: { current_user: current_user }) - .serializable_hash - .dig(:data, :attributes, :cosponsorships) - end -end diff --git a/back/spec/serializers/web_api/v1/notifications_spec.rb b/back/spec/serializers/web_api/v1/notifications_spec.rb index b5a12b4d3c9e..7e889d37d62d 100644 --- a/back/spec/serializers/web_api/v1/notifications_spec.rb +++ b/back/spec/serializers/web_api/v1/notifications_spec.rb @@ -43,13 +43,6 @@ def expect_serializer_to_hide_name(user1, user2, admin, notification_factory_nam ) end - it 'serializes CommentOnInitiativeYouFollow correctly' do - expect_serializer_to_hide_name( - john, jane, admin, :comment_on_initiative_you_follow, - WebApi::V1::Notifications::CommentOnInitiativeYouFollowSerializer - ) - end - it 'serializes CommentOnIdeaYouFollow correctly' do expect_serializer_to_hide_name( john, jane, admin, :comment_on_idea_you_follow, @@ -57,10 +50,10 @@ def expect_serializer_to_hide_name(user1, user2, admin, notification_factory_nam ) end - it 'serializes InitiativeAssignedToYouSerializer correctly' do + it 'serializes InternalCommentOnIdeaAssignedToYouSerializer correctly' do expect_serializer_to_hide_name( - john, jane, admin, :initiative_assigned_to_you, - WebApi::V1::Notifications::InitiativeAssignedToYouSerializer + john, jane, admin, :internal_comment_on_idea_assigned_to_you, + WebApi::V1::Notifications::InternalCommentOnIdeaAssignedToYouSerializer ) end diff --git a/back/spec/services/permissions/initiatives_permissions_service_spec.rb b/back/spec/services/permissions/initiatives_permissions_service_spec.rb deleted file mode 100644 index 0ff55870247f..000000000000 --- a/back/spec/services/permissions/initiatives_permissions_service_spec.rb +++ /dev/null @@ -1,38 +0,0 @@ -require 'rails_helper' - -# TODO: cleanup-after-proposals-migration -describe Permissions::InitiativePermissionsService do - let(:service) { described_class.new(user) } - - before { SettingsService.new.activate_feature! 'user_confirmation' } - - # NOTE: Most of the logic here is tested in the parent class tests - BasePermissionsService - describe '"posting_initiative" denied_reason_for_action' do - let(:action) { 'posting_initiative' } - let(:permission) { Permission.find_by(permission_scope: nil, action: action) } - let(:user) { create(:user) } - - before { Permissions::PermissionsUpdateService.new.update_global_permissions } - - it 'returns nil when action is allowed' do - groups = create_list(:group, 2) - groups.first.add_member(user).save! - permission.update!(permitted_by: 'users', group_ids: groups.map(&:id)) - expect(service.denied_reason_for_action(action)).to be_nil - end - - context 'when the user is not signed in' do - let(:user) { nil } - - it 'returns `user_not_signed_in` when user needs to be signed in' do - permission.update!(permitted_by: 'users') - expect(service.denied_reason_for_action(action)).to eq 'user_not_signed_in' - end - end - - it 'returns `user_not_in_group` when user is not in authorized groups' do - permission.update!(permitted_by: 'users', group_ids: create_list(:group, 2).map(&:id)) - expect(service.denied_reason_for_action(action)).to eq 'user_not_in_group' - end - end -end diff --git a/back/spec/services/side_fx_comment_service_spec.rb b/back/spec/services/side_fx_comment_service_spec.rb index b0d696856d99..6f6a50d83d0b 100644 --- a/back/spec/services/side_fx_comment_service_spec.rb +++ b/back/spec/services/side_fx_comment_service_spec.rb @@ -34,7 +34,7 @@ expectation.to enqueue_job(LogActivityJob).with(comment, 'mentioned', user, created_at, payload: { mentioned_user: u2.id }, project_id: project_id) end - it 'creates a follower' do + it 'creates the expected follower records' do project = create(:project) folder = create(:project_folder, projects: [project]) idea = create(:idea, project: project) @@ -47,14 +47,23 @@ expect(user.follows.pluck(:followable_id)).to contain_exactly idea.id, project.id, folder.id end - it 'does not create a follower if the user already follows the post' do - initiative = create(:initiative) - comment = create(:comment, post: initiative) - create(:follower, followable: initiative, user: user) + it 'does not create new follower records for followable items user already follows' do + project = create(:project) + folder = create(:project_folder, projects: [project]) + idea = create(:idea, project: project) + comment = create(:comment, post: idea) - expect do - service.after_create comment, user - end.not_to change(Follower, :count) + create(:follower, followable: idea, user: user) + create(:follower, followable: project, user: user) + create(:follower, followable: folder, user: user) + n_idea_followers = idea.followers.count + n_project_followers = project.followers.count + n_folder_followers = folder.followers.count + + service.after_create comment, user + expect(idea.reload.followers.count).to eq n_idea_followers + expect(project.reload.followers.count).to eq n_project_followers + expect(folder.reload.followers.count).to eq n_folder_followers end end diff --git a/back/spec/services/side_fx_initiative_service_spec.rb b/back/spec/services/side_fx_initiative_service_spec.rb deleted file mode 100644 index 8efc2568ce1e..000000000000 --- a/back/spec/services/side_fx_initiative_service_spec.rb +++ /dev/null @@ -1,204 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -# TODO: cleanup-after-proposals-migration -describe SideFxInitiativeService do - let(:service) { described_class.new } - let(:user) { create(:user) } - - describe '#after_update' do - it "logs a 'changed' action job when the initiative has changed" do - initiative = create(:initiative) - initiative.update!(title_multiloc: { en: 'something else' }) - expect { service.after_update(initiative, user, _cosponsor_ids = []) } - .to enqueue_job(LogActivityJob).with(initiative, 'changed', any_args).exactly(1).times - end - - it "logs a 'changed_title' action job when the title has changed" do - initiative = create(:initiative) - old_initiative_title = initiative.title_multiloc - initiative.update!(title_multiloc: { en: 'changed' }) - - expect { service.after_update(initiative, user, _cosponsor_ids = []) } - .to enqueue_job(LogActivityJob).with( - initiative, - 'changed_title', - any_args, - payload: { change: [old_initiative_title, initiative.title_multiloc] } - ).exactly(1).times - end - - it "logs a 'changed_body' action job when the body has changed" do - initiative = create(:initiative) - old_initiative_body = initiative.body_multiloc - initiative.update!(body_multiloc: { en: 'changed' }) - - expect { service.after_update(initiative, user, _cosponsor_ids = []) } - .to enqueue_job(LogActivityJob).with( - initiative, - 'changed_body', - any_args, - payload: { change: [old_initiative_body, initiative.body_multiloc] } - ) - end - - context 'when initiative status is changes_requested' do - let(:initiative) do - create(:initiative_status_review_pending) - changes_requested = create(:initiative_status_changes_requested) - create(:initiative, initiative_status: changes_requested, author: user) - end - - it 'changes initiative status to review_pending' do - service.after_update(initiative, user, _cosponsor_ids = []) - expect(initiative.reload.initiative_status.code).to eq 'review_pending' - end - end - - context 'when update results in new cosponsors' do - let(:initiative) { create(:initiative, author: user) } - let(:cosponsor1) { create(:user) } - let(:cosponsor2) { create(:user) } - - it "logs CosponsorsInitiative 'created' activity jobs" do - initiative.update!(cosponsor_ids: [cosponsor1.id, cosponsor2.id]) - cosponsors_initiatives = - CosponsorsInitiative.where(initiative: initiative).where(user_id: [cosponsor1.id, cosponsor2.id]) - - expect { service.after_update(initiative, user, _old_cosponsor_ids = []) } - .to enqueue_job(LogActivityJob) - .with(cosponsors_initiatives[0], 'created', user, cosponsors_initiatives[0].created_at.to_i) - .exactly(1).times - .and enqueue_job(LogActivityJob) - .with(cosponsors_initiatives[1], 'created', user, cosponsors_initiatives[1].created_at.to_i) - .exactly(1).times - end - end - end - - describe '#after_accept_cosponsorship_invite' do - let(:initiative) { create(:initiative) } - let(:cosponsors_initiative) { create(:cosponsors_initiative, status: 'accepted', user: user, initiative: initiative) } - - it 'logs a cosponsorship_accepted activity job' do - expect { service.after_accept_cosponsorship_invite(cosponsors_initiative, user) } - .to enqueue_job(LogActivityJob) - .with( - cosponsors_initiative, - 'cosponsorship_accepted', - user, - cosponsors_initiative.updated_at.to_i, - payload: { change: %w[pending accepted] } - ) - .exactly(1).times - end - - it 'creates a follower' do - expect do - service.after_accept_cosponsorship_invite cosponsors_initiative, user - end.to change(Follower, :count).from(0).to(1) - - expect(user.follows.pluck(:followable_id)).to contain_exactly initiative.id - end - end - - describe 'after_create' do - it "logs a 'published' action job when publication_state is published" do - initiative = create(:initiative, publication_status: 'published', author: user) - - expect { service.after_create(initiative, user) } - .to enqueue_job(LogActivityJob) - .with(initiative, 'published', user, initiative.created_at.to_i) - .exactly(1).times - end - - it "doesn't log a 'published' action job when publication_state is draft" do - initiative = create(:initiative, publication_status: 'draft') - expect { service.after_create(initiative, user) } - .not_to enqueue_job(LogActivityJob) - end - - it 'creates a follower' do - initiative = create(:initiative) - - expect do - service.after_create initiative, user - end.to change(Follower, :count).from(0).to(1) - - expect(user.follows.pluck(:followable_id)).to contain_exactly initiative.id - end - - it 'creates a reaction (vote) for an author who can react (vote)' do - initiative = create(:initiative) - - expect do - service.after_create initiative, user - end.to change(Reaction, :count).from(0).to(1) - - expect(initiative.reactions.pluck(:user_id)).to contain_exactly initiative.author.id - end - - it 'does not create a reaction (vote) for an author who cannot react (vote)' do - initiative = create(:initiative) - - create( - :permission, - permission_scope: nil, - action: 'reacting_initiative', - permitted_by: 'users', - groups: [create(:group)] - ) - - expect do - service.after_create initiative, user - end.not_to change(Reaction, :count) - end - - context "when initiative has status 'proposed'" do - let(:initiative) { create(:initiative) } - let!(:initiative_status_change) do - create( - :initiative_status_change, - initiative: initiative, - initiative_status: create(:initiative_status_proposed) - ) - end - - it 'logs a proposed activity job' do - expect { service.after_create(initiative, user) } - .to enqueue_job(LogActivityJob) - .with(initiative, 'proposed', user, initiative.updated_at.to_i) - .exactly(1).times - end - end - - context "when initiative has status 'review_pending'" do - let(:initiative) { create(:initiative) } - let!(:initiative_status_change) do - create( - :initiative_status_change, - initiative: initiative, - initiative_status: create(:initiative_status_review_pending) - ) - end - - it "doesn't log a proposed activity job" do - expect { service.after_update(initiative, user, _cosponsor_ids = []) } - .not_to enqueue_job(LogActivityJob) - .with(instance_of(Initiative), 'proposed', anything, anything) - end - end - end - - describe 'after_destroy' do - it "logs a 'deleted' action job when the initiative is destroyed" do - initiative = create(:initiative) - freeze_time do - frozen_initiative = initiative.destroy - expect { service.after_destroy(frozen_initiative, user) } - .to enqueue_job(LogActivityJob).exactly(1).times - end - end - end -end diff --git a/back/spec/services/side_fx_initiative_status_change_service_spec.rb b/back/spec/services/side_fx_initiative_status_change_service_spec.rb deleted file mode 100644 index 31509bbf8de0..000000000000 --- a/back/spec/services/side_fx_initiative_status_change_service_spec.rb +++ /dev/null @@ -1,45 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -# TODO: cleanup-after-proposals-migration -describe SideFxInitiativeStatusChangeService do - let(:service) { described_class.new } - let(:user) { create(:user) } - let(:initiative) { create(:initiative, author: user) } - - describe '#after_create' do - context "when status change is to 'proposed'" do - let!(:initiative_status_change) do - create( - :initiative_status_change, - initiative: initiative, - initiative_status: create(:initiative_status_proposed) - ) - end - - it 'logs a proposed activity job' do - expect { service.after_create(initiative_status_change, user) } - .to enqueue_job(LogActivityJob) - .with(initiative_status_change.initiative, 'proposed', user, instance_of(Integer)) - .exactly(1).times - end - end - - context "when status change is to 'review_pending'" do - let!(:initiative_status_change) do - create( - :initiative_status_change, - initiative: initiative, - initiative_status: create(:initiative_status_review_pending) - ) - end - - it "doesn't log a proposed activity job" do - expect { service.after_create(initiative_status_change, user) } - .not_to enqueue_job(LogActivityJob) - .with(instance_of(Initiative), 'proposed', anything, anything) - end - end - end -end