diff --git a/.erb-lint.yml b/.erb-lint.yml
index 59e7c35e4..5e68e12e6 100644
--- a/.erb-lint.yml
+++ b/.erb-lint.yml
@@ -40,4 +40,3 @@ linters:
- '**/app/views/pages/onboarding.html.erb'
- '**/app/views/partnerships/show.html.erb'
- '**/app/views/partnerships/index.html.erb'
- - '**/app/views/pro_memberships/show.html.erb'
diff --git a/app/assets/javascripts/initializers/initializeSettings.js b/app/assets/javascripts/initializers/initializeSettings.js
index 3fe0c4149..37d216c41 100644
--- a/app/assets/javascripts/initializers/initializeSettings.js
+++ b/app/assets/javascripts/initializers/initializeSettings.js
@@ -1,12 +1,10 @@
-'use strict';
-
/* global timestampToLocalDateTime */
function initializeSettings() {
// highlights organization secret on click
const settingsOrgSecret = document.getElementById('settings-org-secret');
if (settingsOrgSecret) {
- settingsOrgSecret.addEventListener('click', event => {
+ settingsOrgSecret.addEventListener('click', (event) => {
event.target.select();
});
}
@@ -29,20 +27,4 @@ function initializeSettings() {
timeOptions,
);
}
-
- // asks for confirmation on activating pro membership
- const createProForm = document.getElementById('new_pro_membership');
- if (createProForm) {
- createProForm.addEventListener('submit', event => {
- event.preventDefault();
-
- // eslint-disable-next-line no-alert
- if (window.confirm('Are you sure?')) {
- event.target.submit();
- return true;
- }
-
- return false;
- });
- }
}
diff --git a/app/controllers/admin/pro_memberships_controller.rb b/app/controllers/admin/pro_memberships_controller.rb
deleted file mode 100644
index 4d4dadc0d..000000000
--- a/app/controllers/admin/pro_memberships_controller.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-module Admin
- class ProMembershipsController < Admin::ApplicationController
- # To customize the behavior of this controller,
- # you can overwrite any of the RESTful actions. For example:
- #
- # def index
- # super
- # @resources = ProMembership.
- # page(params[:page]).
- # per(10)
- # end
-
- # Define a custom finder by overriding the `find_resource` method:
- # def find_resource(param)
- # ProMembership.find_by!(slug: param)
- # end
-
- # See https://administrate-prototype.herokuapp.com/customizing_controller_actions
- # for more information
- end
-end
diff --git a/app/controllers/pro_memberships_controller.rb b/app/controllers/pro_memberships_controller.rb
deleted file mode 100644
index 802fd884c..000000000
--- a/app/controllers/pro_memberships_controller.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-class ProMembershipsController < ApplicationController
- before_action :authenticate_user!, except: %i[show]
- before_action :load_pro_membership, only: %i[update]
- after_action :verify_authorized, except: %i[show]
-
- def show
- @user = current_user
- @pro_membership = current_user&.pro_membership
- end
-
- def create
- authorize ProMembership
-
- if ProMemberships::Creator.call(current_user)
- flash[:settings_notice] = "You are now a Pro!"
- else
- flash[:error] = "You don't have enough credits!"
- end
-
- redirect_to user_settings_path("pro-membership")
- end
-
- def update
- raise Pundit::NotAuthorizedError, "You don't have a Pro Membership" unless @pro_membership
-
- authorize @pro_membership
-
- if @pro_membership.update(update_params)
- flash[:settings_notice] = "Your membership has been updated!"
- else
- flash[:error] = "An error has occurred while updating your membership!"
- end
-
- redirect_to user_settings_path("pro-membership")
- end
-
- private
-
- def load_pro_membership
- @pro_membership = current_user.pro_membership
- end
-
- def update_params
- params.require(:pro_membership).permit(:auto_recharge)
- end
-end
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index 2fed8d243..782d6174f 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -253,8 +253,6 @@ class UsersController < ApplicationController
handle_integrations_tab
when "billing"
handle_billing_tab
- when "pro-membership"
- handle_pro_membership_tab
when "account"
handle_account_tab
when "response-templates"
@@ -323,10 +321,6 @@ class UsersController < ApplicationController
@customer = Payments::Customer.get(stripe_code) if stripe_code.present?
end
- def handle_pro_membership_tab
- @pro_membership = current_user.pro_membership
- end
-
def handle_account_tab
community_name = ApplicationConfig["COMMUNITY_NAME"]
@email_body = <<~HEREDOC
diff --git a/app/dashboards/dashboard_manifest.rb b/app/dashboards/dashboard_manifest.rb
index 83eac66d6..1b419b0f8 100644
--- a/app/dashboards/dashboard_manifest.rb
+++ b/app/dashboards/dashboard_manifest.rb
@@ -29,7 +29,6 @@ class DashboardManifest
html_variant_trials
html_variant_successes
sponsorships
- pro_memberships
classified_listing_categories
].freeze
# DASHBOARDS = [
diff --git a/app/dashboards/pro_membership_dashboard.rb b/app/dashboards/pro_membership_dashboard.rb
deleted file mode 100644
index 43d16ee87..000000000
--- a/app/dashboards/pro_membership_dashboard.rb
+++ /dev/null
@@ -1,67 +0,0 @@
-require "administrate/base_dashboard"
-
-class ProMembershipDashboard < Administrate::BaseDashboard
- # ATTRIBUTE_TYPES
- # a hash that describes the type of each of the model's fields.
- #
- # Each different type represents an Administrate::Field object,
- # which determines how the attribute is displayed
- # on pages throughout the dashboard.
- ATTRIBUTE_TYPES = {
- user: Field::BelongsTo,
- id: Field::Number,
- status: Field::String,
- expires_at: Field::DateTime,
- expiration_notification_at: Field::DateTime,
- expiration_notifications_count: Field::Number,
- auto_recharge: Field::Boolean,
- created_at: Field::DateTime,
- updated_at: Field::DateTime
- }.freeze
-
- # COLLECTION_ATTRIBUTES
- # an array of attributes that will be displayed on the model's index page.
- #
- # By default, it's limited to four items to reduce clutter on index pages.
- # Feel free to add, remove, or rearrange items.
- COLLECTION_ATTRIBUTES = %i[
- user
- id
- status
- expires_at
- auto_recharge
- ].freeze
-
- # SHOW_PAGE_ATTRIBUTES
- # an array of attributes that will be displayed on the model's show page.
- SHOW_PAGE_ATTRIBUTES = %i[
- user
- id
- status
- expires_at
- expiration_notification_at
- expiration_notifications_count
- auto_recharge
- created_at
- updated_at
- ].freeze
-
- # FORM_ATTRIBUTES
- # an array of attributes that will be displayed
- # on the model's form (`new` and `edit`) pages.
- FORM_ATTRIBUTES = %i[
- user
- status
- expires_at
- expiration_notification_at
- expiration_notifications_count
- auto_recharge
- ].freeze
-
- # Overwrite this method to customize how pro memberships are displayed
- # across all pages of the admin dashboard.
- #
- # def display_resource(pro_membership)
- # "ProMembership ##{pro_membership.id}"
- # end
-end
diff --git a/app/decorators/user_decorator.rb b/app/decorators/user_decorator.rb
index 1e1998ce5..635eda1f8 100644
--- a/app/decorators/user_decorator.rb
+++ b/app/decorators/user_decorator.rb
@@ -53,7 +53,6 @@ class UserDecorator < ApplicationDecorator
body_class = [
config_theme.tr("_", "-"),
"#{config_font.tr('_', '-')}-article-body",
- "pro-status-#{pro?}",
"trusted-status-#{trusted}",
"#{config_navbar.tr('_', '-')}-navbar-config",
]
diff --git a/app/labor/article_suggester.rb b/app/labor/article_suggester.rb
index 535b84a8a..8bdc2872e 100644
--- a/app/labor/article_suggester.rb
+++ b/app/labor/article_suggester.rb
@@ -31,7 +31,7 @@ class ArticleSuggester
Article.published.where(featured: true).
where.not(id: ids_to_ignore).
order("hotness_score DESC").
- includes(user: [:pro_membership]).
+ includes(:user).
offset(rand(0..offsets[1])).
first(max)
end
@@ -40,7 +40,7 @@ class ArticleSuggester
Article.published.tagged_with(article.tag_list, any: true).
where.not(id: article.id).
order("hotness_score DESC").
- includes(user: [:pro_membership]).
+ includes(:user).
offset(rand(0..offsets[0])).
first(max)
end
diff --git a/app/mailers/pro_membership_mailer.rb b/app/mailers/pro_membership_mailer.rb
deleted file mode 100644
index 08e4b4008..000000000
--- a/app/mailers/pro_membership_mailer.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-class ProMembershipMailer < ApplicationMailer
- default from: -> { email_from("Pro Memberships") }
-
- def expiring_membership(pro_membership, expiration_date)
- @pro_membership = pro_membership
- @user = pro_membership.user
- @days = (Time.current.to_date..expiration_date.to_date).count - 1
- @date = expiration_date.to_date
- mail(to: @user.email, subject: "Your Pro Membership will expire in #{@days} days!")
- end
-end
diff --git a/app/models/article.rb b/app/models/article.rb
index 99673ab1b..5b6f108eb 100644
--- a/app/models/article.rb
+++ b/app/models/article.rb
@@ -551,8 +551,7 @@ class Article < ApplicationRecord
username: object.username,
slug: object == organization ? object.slug : object.username,
profile_image_90: object.profile_image_90,
- profile_image_url: object.profile_image_url,
- pro: object == user ? user.pro? : false # organizations can't be pro users
+ profile_image_url: object.profile_image_url
}
end
diff --git a/app/models/organization.rb b/app/models/organization.rb
index 51f8fd164..0994d4fe7 100644
--- a/app/models/organization.rb
+++ b/app/models/organization.rb
@@ -99,10 +99,6 @@ class Organization < ApplicationRecord
credits.unspent.size >= num_credits_needed
end
- def pro?
- false
- end
-
def banned
false
end
diff --git a/app/models/pro_membership.rb b/app/models/pro_membership.rb
deleted file mode 100644
index c35f2e300..000000000
--- a/app/models/pro_membership.rb
+++ /dev/null
@@ -1,61 +0,0 @@
-class ProMembership < ApplicationRecord
- STATUSES = %w[active expired].freeze
- MONTHLY_COST = 5
- MONTHLY_COST_USD = 25
-
- belongs_to :user
-
- validates :user, :status, :expiration_notifications_count, presence: true
- validates :user, uniqueness: true
- validates :expires_at, presence: true, on: :save
- validates :status, inclusion: { in: STATUSES }
-
- scope :active, -> { where(status: :active) }
- scope :expired, -> { where(status: :expired) }
-
- before_create :set_expiration_date
- after_save :resave_user_articles
- after_save :bust_cache
-
- def expired?
- expires_at <= Time.current
- end
-
- def active?
- expires_at > Time.current
- end
-
- def expire!
- update!(expires_at: Time.current, status: :expired)
- end
-
- def renew!
- update!(
- expires_at: 1.month.from_now,
- status: :active,
- expiration_notification_at: nil,
- expiration_notifications_count: 0,
- )
- end
-
- private
-
- def set_expiration_date
- self.expires_at = 1.month.from_now
- end
-
- # if the membership is new or the user flips from expired to active and viceversa,
- # we need to resave all of the user's articles to make sure that the pro details
- # that are cached with them are refreshed
- def resave_user_articles
- if saved_change_to_created_at? ||
- saved_change_to_expires_at? ||
- saved_change_to_status?
- Users::ResaveArticlesWorker.perform_async(user.id)
- end
- end
-
- def bust_cache
- Rails.cache.delete("user-#{user.id}/has_pro_membership")
- end
-end
diff --git a/app/models/user.rb b/app/models/user.rb
index 858cf819c..cf22987a0 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -92,7 +92,6 @@ class User < ApplicationRecord
has_many :webhook_endpoints, class_name: "Webhook::Endpoint", foreign_key: :user_id, inverse_of: :user, dependent: :delete_all
has_one :counters, class_name: "UserCounter", dependent: :destroy
- has_one :pro_membership, dependent: :destroy
mount_uploader :profile_image, ProfileImageUploader
@@ -292,8 +291,8 @@ class User < ApplicationRecord
end
def pro?
- Rails.cache.fetch("user-#{id}/has_pro_membership", expires_in: 200.hours) do
- pro_membership&.active? || has_role?(:pro)
+ Rails.cache.fetch("user-#{id}/has_pro_role", expires_in: 200.hours) do
+ has_role?(:pro)
end
end
diff --git a/app/policies/pro_membership_policy.rb b/app/policies/pro_membership_policy.rb
deleted file mode 100644
index e1816f4d1..000000000
--- a/app/policies/pro_membership_policy.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-class ProMembershipPolicy < ApplicationPolicy
- def create?
- user.pro_membership.nil? && !user.has_role?(:pro)
- end
-
- def update?
- user.pro_membership.present?
- end
-end
diff --git a/app/services/pro_memberships/biller.rb b/app/services/pro_memberships/biller.rb
deleted file mode 100644
index 2399cb30e..000000000
--- a/app/services/pro_memberships/biller.rb
+++ /dev/null
@@ -1,118 +0,0 @@
-module ProMemberships
- # Bills pro memberships that expire today
- class Biller
- def self.call(*args)
- new(*args).call
- end
-
- def call
- relation = ProMembership.includes(user: [:credits]).
- where("DATE(expires_at) = ?", Time.zone.today)
- relation.find_each do |membership|
- user = membership.user
- cost = ProMembership::MONTHLY_COST
-
- if user.enough_credits?(cost)
- renew_membership(membership, cost)
- elsif membership.auto_recharge
- if user.stripe_id_code
- charge_user_and_renew_membership(membership, cost)
- else
- notify_admins(user, "auto recharge error: missing Stripe customer ID!")
- end
- else
- expire_membership(membership)
- notify_admins(user, "pro membership expired!")
- end
- end
- end
-
- private
-
- def renew_membership(membership, cost)
- ActiveRecord::Base.transaction do
- user = membership.user
-
- membership.renew!
-
- success = Credits::Buyer.call(
- purchaser: user,
- purchase: membership,
- cost: cost,
- )
-
- unless success
- notify_admins("#{user.name}'s pro membership could not be renewed with enough credits!")
- raise ActiveRecord::Rollback
- end
-
- chat_channel = ChatChannel.find_by(slug: "pro-members")
- if chat_channel && !chat_channel.chat_channel_memberships.exists?(user_id: user.id)
- chat_channel.add_users(user)
- end
-
- success
- end
- rescue StandardError => e
- Rails.logger.error(e)
- notify_admins(membership.user, "error: #{e.message}")
- end
-
- def expire_membership(membership)
- ActiveRecord::Base.transaction do
- user = membership.user
-
- membership.expire!
-
- chat_channel = ChatChannel.find_by(slug: "pro-members")
- chat_channel&.remove_user(user)
-
- user.save
- end
- rescue StandardError => e
- Rails.logger.error(e)
- notify_admins(membership.user, "error: #{e.message}")
- end
-
- def recharge(membership, cost)
- user = membership.user
-
- # charge customer for credits
- customer = Payments::Customer.get(user.stripe_id_code)
- Payments::Customer.charge(
- customer: customer,
- amount: ProMembership::MONTHLY_COST_USD,
- description: "Purchase of #{cost} credits.",
- )
-
- # add credits
- credits = Array.new(cost) { Credit.new(user: user, cost: 1) }
- user.credits << credits
- user.save!
- end
-
- def charge_user_and_renew_membership(membership, cost)
- user = membership.user
- recharge(membership, cost)
- renew_membership(membership, cost)
- rescue Payments::PaymentsError => e
- Rails.logger.error(e)
- notify_admins(user, "payment error: #{e.message}")
- rescue ActiveRecord::RecordInvalid => e
- Rails.logger.error(e)
- notify_admins(user, "credits creation error: #{e.message}")
- rescue StandardError => e
- Rails.logger.error(e)
- notify_admins(user, "error: #{e.message}")
- end
-
- def notify_admins(user, message)
- Slack::Messengers::Worker.perform_async(
- message: "ProMemberships::Biller: #{user.username}: #{message}",
- channel: "pro-memberships",
- username: "pro-memberships",
- icon_emoji: ":fire:",
- )
- end
- end
-end
diff --git a/app/services/pro_memberships/creator.rb b/app/services/pro_memberships/creator.rb
deleted file mode 100644
index 2195342fc..000000000
--- a/app/services/pro_memberships/creator.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-module ProMemberships
- class Creator
- def initialize(user)
- @user = user
- end
-
- def self.call(*args)
- new(*args).call
- end
-
- def call
- if purchase_pro_membership
- channel = ChatChannel.find_by(slug: "pro-members")
- channel&.add_users(user)
-
- true
- else
- false
- end
- end
-
- private
-
- attr_reader :user
-
- def purchase_pro_membership
- cost = ProMembership::MONTHLY_COST
- return false unless user.enough_credits?(cost)
-
- ActiveRecord::Base.transaction do
- pro_membership = ProMembership.create!(user: user)
- Credits::Buyer.call(
- purchaser: user,
- purchase: pro_membership,
- cost: cost,
- )
- end
- end
- end
-end
diff --git a/app/services/pro_memberships/expiration_notifier.rb b/app/services/pro_memberships/expiration_notifier.rb
deleted file mode 100644
index 8b4853ed6..000000000
--- a/app/services/pro_memberships/expiration_notifier.rb
+++ /dev/null
@@ -1,47 +0,0 @@
-module ProMemberships
- # Notifies Pro members with insufficient credits of the impeding expiration
- class ExpirationNotifier
- def initialize(expiration_date)
- @expiration_date = expiration_date
- end
-
- def self.call(*args)
- new(*args).call
- end
-
- def call
- count = 0
-
- # NOTE: naive implementation because this is supposed to be called every 24hrs
- relation = ProMembership.
- includes(user: [:credits]).
- where("DATE(expires_at) = ?", expiration_date).
- where(auto_recharge: false)
- relation.find_each do |membership|
- next if membership.user.enough_credits?(ProMembership::MONTHLY_COST)
-
- # NOTE: maybe we should "deliver_later" and update the flags there
- ProMembershipMailer.expiring_membership(membership, expiration_date).deliver_now
-
- membership.expiration_notification_at = Time.current
- membership.increment(:expiration_notifications_count)
- membership.save!
-
- Slack::Messengers::Worker.perform_async(
- message: "#{membership.user.name}'s pro membership expires on #{expiration_date}",
- channel: "pro-memberships",
- username: "pro-memberships",
- icon_emoji: ":fire:",
- )
-
- count += 1
- end
-
- count
- end
-
- private
-
- attr_reader :expiration_date
- end
-end
diff --git a/app/services/suggester/articles/boosted.rb b/app/services/suggester/articles/boosted.rb
index 91d1c7f00..21d7e2c6f 100644
--- a/app/services/suggester/articles/boosted.rb
+++ b/app/services/suggester/articles/boosted.rb
@@ -10,7 +10,7 @@ module Suggester
end
def suggest
- base_articles = Article.includes(user: [:pro_membership]).
+ base_articles = Article.includes(:user).
includes(:organization).
where.not(id: not_ids, organization_id: nil).
cached_tagged_with(tag)
diff --git a/app/services/suggester/articles/classic.rb b/app/services/suggester/articles/classic.rb
index 71ee2930d..ef7f8d499 100644
--- a/app/services/suggester/articles/classic.rb
+++ b/app/services/suggester/articles/classic.rb
@@ -24,7 +24,7 @@ module Suggester
tag_name = tag_names.sample
Rails.cache.fetch("classic-article-for-tag-#{tag_name}}", expires_in: 90.minutes) do
Article.published.cached_tagged_with(tag_name).
- includes(user: [:pro_membership]).
+ includes(:user).
limited_column_select.
where(featured: true).
where("positive_reactions_count > ?", MIN_REACTION_COUNT).
diff --git a/app/services/suggester/articles/high_quality.rb b/app/services/suggester/articles/high_quality.rb
index c985be3ee..e3237a1a0 100644
--- a/app/services/suggester/articles/high_quality.rb
+++ b/app/services/suggester/articles/high_quality.rb
@@ -9,7 +9,7 @@ module Suggester
def suggest(num)
Article.published.where(featured: true).
- includes(user: [:pro_membership]).
+ includes(:user).
limited_column_select.
where("positive_reactions_count > ?", MIN_HQ_REACTION_COUNT).
where.not(id: @not_ids).
diff --git a/app/services/users/delete_activity.rb b/app/services/users/delete_activity.rb
index d153bd82c..33332ca09 100644
--- a/app/services/users/delete_activity.rb
+++ b/app/services/users/delete_activity.rb
@@ -21,7 +21,6 @@ module Users
user.page_views.delete_all
user.poll_skips.delete_all
user.poll_votes.delete_all
- user.pro_membership.delete if user.pro_membership.present?
user.rating_votes.delete_all
user.response_templates.delete_all
user.classified_listings.destroy_all
diff --git a/app/views/additional_content_boxes/_article_content_area.html.erb b/app/views/additional_content_boxes/_article_content_area.html.erb
index 66640bcf8..33fa0f834 100644
--- a/app/views/additional_content_boxes/_article_content_area.html.erb
+++ b/app/views/additional_content_boxes/_article_content_area.html.erb
@@ -5,7 +5,6 @@
<%= article.user.name %>
- <%= render "shared/pro_checkmark" if article.user.pro? %>
diff --git a/app/views/additional_content_boxes/_article_followable_area.html.erb b/app/views/additional_content_boxes/_article_followable_area.html.erb
index 2b3ff0e97..4710472a2 100644
--- a/app/views/additional_content_boxes/_article_followable_area.html.erb
+++ b/app/views/additional_content_boxes/_article_followable_area.html.erb
@@ -30,7 +30,6 @@
"
data-details="<%= followable&.slug if classification == "boosted" %>__PROFILE"><%= followable.name %>
- <%= render "shared/pro_checkmark" if followable.pro? %>
<% end %>
diff --git a/app/views/articles/_single_story.html.erb b/app/views/articles/_single_story.html.erb
index 95e30a4b8..94ac6f2bd 100644
--- a/app/views/articles/_single_story.html.erb
+++ b/app/views/articles/_single_story.html.erb
@@ -32,7 +32,6 @@
<%= story.cached_user.name %>
- <%= render "shared/pro_checkmark" if story.cached_user.pro %>
<% if story.cached_organization && !@organization_article_index %>
diff --git a/app/views/articles/_sticky_nav.html.erb b/app/views/articles/_sticky_nav.html.erb
index 8ff9bab73..6d5acc60c 100644
--- a/app/views/articles/_sticky_nav.html.erb
+++ b/app/views/articles/_sticky_nav.html.erb
@@ -41,7 +41,6 @@
diff --git a/app/views/articles/show.html.erb b/app/views/articles/show.html.erb
index d10e6a0f5..eb18c9587 100644
--- a/app/views/articles/show.html.erb
+++ b/app/views/articles/show.html.erb
@@ -205,7 +205,7 @@
<% end %>
-<% stick_nav_cache_key = "sticky-sidebar-#{@article.id}-#{(@organization || @user).profile_updated_at}-#{(@organization || @user).last_article_at}-#{@variant_number}-#{(@organization || @user).pro?}" %>
+<% stick_nav_cache_key = "sticky-sidebar-#{@article.id}-#{(@organization || @user).profile_updated_at}-#{(@organization || @user).last_article_at}-#{@variant_number}-#{@user.pro?}" %>
<% cache(stick_nav_cache_key, expires_in: 50.hours) do %>
<%= render "articles/sticky_nav" %>
<% end %>
diff --git a/app/views/credits/_ledger_pro_membership.html.erb b/app/views/credits/_ledger_pro_membership.html.erb
deleted file mode 100644
index a1569a383..000000000
--- a/app/views/credits/_ledger_pro_membership.html.erb
+++ /dev/null
@@ -1,3 +0,0 @@
-
- Pro Membership
-
diff --git a/app/views/mailers/pro_membership_mailer/expiring_membership.html.erb b/app/views/mailers/pro_membership_mailer/expiring_membership.html.erb
deleted file mode 100644
index f466f9f7d..000000000
--- a/app/views/mailers/pro_membership_mailer/expiring_membership.html.erb
+++ /dev/null
@@ -1,8 +0,0 @@
-<% credits_url = app_url(credits_path) %>
-<% pro_membership_url = app_url(user_settings_path("pro-membership")) %>
-
-Hi <%= @user.name %>, your Pro Membership will expire in <%= @days %> days on <%= @date.to_s(:long) %>!
-
-Make sure you have enough credits for its renewal: <%= credits_url %>.
-
-You can also review your Pro Membership status.
diff --git a/app/views/mailers/pro_membership_mailer/expiring_membership.text.erb b/app/views/mailers/pro_membership_mailer/expiring_membership.text.erb
deleted file mode 100644
index 6677145f1..000000000
--- a/app/views/mailers/pro_membership_mailer/expiring_membership.text.erb
+++ /dev/null
@@ -1,8 +0,0 @@
-<% credits_url = app_url(credits_path) %>
-<% pro_membership_url = app_url(user_settings_path("pro-membership")) %>
-
-Hi <%= @user.name %>, your Pro Membership will expire in <%= @days %> days on <%= @date.to_s(:long) %>!
-
-Make sure you have enough credits for its renewal: <%= credits_url %>
-
-You can also review your Pro Membership status: <%= pro_membership_url %>
diff --git a/app/views/pro_memberships/show.html.erb b/app/views/pro_memberships/show.html.erb
deleted file mode 100644
index 73180a26b..000000000
--- a/app/views/pro_memberships/show.html.erb
+++ /dev/null
@@ -1,51 +0,0 @@
-<% title "Pro Membership" %>
-
-
-
-
- <% if flash[:notice].present? %>
-
- <%= flash[:notice] %>
-
- <% elsif flash[:error].present? %>
-
- ERROR
- <%= flash[:error] %>
-
- <% end %>
-
-
" /> Like a Pro
-
- Pro is coming soon. This page is a work in progress. Since we're open source and transparently working on this, no sense in keeping it private eh?
-
-
- The copy on this page is a total draft. No judgment yet pls. ๐
-
-
๐ Access the Bleeding Edge
-
A pro membership gives you access to bleeding edge features that may some day scale to general availability. However, we would rather launch great new features to a core dedicated userbase that is most interested than to delay until things are scalable to the whole community.
-
Current Bleeding Edge Features
-
- - Pro Analytics: Deeper insights into the performance of your posts, traffic sources, etc.
- - Searchable Pro History: Instant access to a repository of your entire reading historyโfor your own heightened reference.
-
-
๐ Access the Exclusive Areas
-
A pro membership gives you access to small pro groups that don't necessarily scale. Your pro membership immediately gives you access to Editorial Groups in <%= community_name %> Connect. These are chat channels dedicated to peer-to-peer review of technical blog posts to help you make the most of your <%= community_name %> presence.
-
๐ค Access the Trust-based features
-
While pro memberships will not give you preferential treatment in the feed algorithm (that would be a no-no), you will get skin-in-the-game based access to trust-based features where we can place your posts more quickly and effectively in areas where spam would otherwise inhibit us.
-
๐ Always Improving
-
Because this will be the landing destination for experimental and low scale features, you will always have the best and most interesting <%= community_name %> features. We also plan to connect for exclusive deals on new and upcoming features on other platforms.
-
- <% if @user %>
-
- <% if @user.pro? %>
- <%= link_to "View your Pro Membership", user_settings_path("pro-membership") %>
- <% else %>
- <%= link_to "Become a Pro member", user_settings_path("pro-membership") %>
- <% end %>
-
- <% end %>
-
diff --git a/app/views/shared/_pro_checkmark.html.erb b/app/views/shared/_pro_checkmark.html.erb
deleted file mode 100644
index e12643e26..000000000
--- a/app/views/shared/_pro_checkmark.html.erb
+++ /dev/null
@@ -1 +0,0 @@
-<% # placeholder for future "pro indicator" %>
diff --git a/app/views/users/_pro_membership.html.erb b/app/views/users/_pro_membership.html.erb
deleted file mode 100644
index a9dd5ea83..000000000
--- a/app/views/users/_pro_membership.html.erb
+++ /dev/null
@@ -1,66 +0,0 @@
-Pro Membership
-
-<% if @user.pro? %>
- <% if @pro_membership %>
- <%= form_for(@pro_membership) do |f| %>
-
- - Status
- -
- <% if @pro_membership.active? %>
- <%= image_tag("checkmark-green.svg", class: "icon-img", alt: "check mark", title: "active", width: 25) %>
- <% else %>
- expired
- <% end %>
-
- - Expiration date
- -
-
-
- - Top up from credit card?
- -
-
- <%= f.check_box :auto_recharge %>
-
-
-
- <%= f.submit "SUBMIT", class: "cta" %>
- <% end %>
- <% else %>
-
- - Status
- -
- <%= image_tag("checkmark-green.svg", class: "icon-img", alt: "check mark", title: "active", width: 25) %>
-
- - Expiration date
- -
- Never
-
-
- <% end %>
-<% else %>
-
- <%= link_to "Pro Membership", pro_membership_path %>
- is <%= ProMembership::MONTHLY_COST %> credits per month.
-
-
- <% available_credits = @user.credits.unspent.size %>
- <% if available_credits >= ProMembership::MONTHLY_COST %>
- You currently have <%= available_credits %> available credits.
-
- <%= form_for(ProMembership.new(user: @user)) do |f| %>
- <%= f.submit("Become a Pro member", class: "cta") %>
- <% end %>
- <% elsif available_credits.positive? %>
- You currently have 4 credits, you need <%= ProMembership::MONTHLY_COST %> to become a Pro member.
- Purchase credits
- <% else %>
- You currently have no credits, you need <%= ProMembership::MONTHLY_COST %> to become a Pro member.
- Purchase credits
- <% end %>
-<% end %>
-
-
- You can find additional info on the <%= link_to "Pro Membership page", pro_membership_path %>.
-
diff --git a/app/views/users/show.html.erb b/app/views/users/show.html.erb
index f3f22aa55..95cbfd147 100644
--- a/app/views/users/show.html.erb
+++ b/app/views/users/show.html.erb
@@ -36,7 +36,7 @@
- <%= @user.name %><%= render "shared/pro_checkmark", style: nil, width: nil %>
+ <%= @user.name %>
diff --git a/config/routes.rb b/config/routes.rb
index b14b72d76..0010ad160 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -223,12 +223,10 @@ Rails.application.routes.draw do
resources :partnerships, only: %i[index create show], param: :option
resources :display_ad_events, only: [:create]
resources :badges, only: [:index]
- resource :pro_membership, path: :pro, only: %i[show create update]
resources :user_blocks, param: :blocked_id, only: %i[show create destroy]
resources :podcasts, only: %i[new create]
resources :article_approvals, only: %i[create]
resources :video_chats, only: %i[show]
- resolve("ProMembership") { [:pro_membership] } # see https://guides.rubyonrails.org/routing.html#using-resolve
namespace :followings, defaults: { format: :json } do
get :users
get :tags
diff --git a/lib/tasks/pro_memberships.rake b/lib/tasks/pro_memberships.rake
deleted file mode 100644
index e3c800f1e..000000000
--- a/lib/tasks/pro_memberships.rake
+++ /dev/null
@@ -1,19 +0,0 @@
-# All of these tasks need to be scheduled daily
-namespace :pro_memberships do
- desc "Notify pro users with insufficient credits that their membership is about to expire in a week"
- task notify_expirations_one_week_before: :environment do
- num_notified = ProMemberships::ExpirationNotifier.call(1.week.from_now)
- Rails.logger.info("Notified #{num_notified} Pro users...")
- end
-
- desc "Notify pro users with insufficient credits that their membership is about to expire in a day"
- task notify_expirations_one_day_before: :environment do
- num_notified = ProMemberships::ExpirationNotifier.call(1.day.from_now)
- Rails.logger.info("Notified #{num_notified} Pro users...")
- end
-
- desc "Bill pro users and optionally charge their cards"
- task bill_users: :environment do
- ProMemberships::Biller.call
- end
-end
diff --git a/spec/decorators/user_decorator_spec.rb b/spec/decorators/user_decorator_spec.rb
index be3ff434f..9ffbe19dc 100644
--- a/spec/decorators/user_decorator_spec.rb
+++ b/spec/decorators/user_decorator_spec.rb
@@ -108,7 +108,7 @@ RSpec.describe UserDecorator, type: :decorator do
describe "#config_body_class" do
it "creates proper body class with defaults" do
expected_result = %W[
- default default-article-body pro-status-#{user.pro?}
+ default default-article-body
trusted-status-#{user.trusted} #{user.config_navbar}-navbar-config
].join(" ")
expect(user.decorate.config_body_class).to eq(expected_result)
@@ -117,7 +117,7 @@ RSpec.describe UserDecorator, type: :decorator do
it "creates proper body class with sans serif config" do
user.config_font = "sans_serif"
expected_result = %W[
- default sans-serif-article-body pro-status-#{user.pro?}
+ default sans-serif-article-body
trusted-status-#{user.trusted} #{user.config_navbar}-navbar-config
].join(" ")
expect(user.decorate.config_body_class).to eq(expected_result)
@@ -126,7 +126,7 @@ RSpec.describe UserDecorator, type: :decorator do
it "creates proper body class with night theme" do
user.config_theme = "night_theme"
expected_result = %W[
- night-theme default-article-body pro-status-#{user.pro?}
+ night-theme default-article-body
trusted-status-#{user.trusted} #{user.config_navbar}-navbar-config
].join(" ")
expect(user.decorate.config_body_class).to eq(expected_result)
@@ -135,7 +135,7 @@ RSpec.describe UserDecorator, type: :decorator do
it "creates proper body class with pink theme" do
user.config_theme = "pink_theme"
expected_result = %W[
- pink-theme default-article-body pro-status-#{user.pro?}
+ pink-theme default-article-body
trusted-status-#{user.trusted} #{user.config_navbar}-navbar-config
].join(" ")
expect(user.decorate.config_body_class).to eq(expected_result)
@@ -144,7 +144,7 @@ RSpec.describe UserDecorator, type: :decorator do
it "creates proper body class with minimal light theme" do
user.config_theme = "minimal_light_theme"
expected_result = %W[
- minimal-light-theme default-article-body pro-status-#{user.pro?}
+ minimal-light-theme default-article-body
trusted-status-#{user.trusted} #{user.config_navbar}-navbar-config
].join(" ")
expect(user.decorate.config_body_class).to eq(expected_result)
@@ -153,7 +153,7 @@ RSpec.describe UserDecorator, type: :decorator do
it "works with static navbar" do
user.config_navbar = "static"
expected_result = %W[
- default default-article-body pro-status-#{user.pro?}
+ default default-article-body
trusted-status-#{user.trusted} static-navbar-config
].join(" ")
expect(user.decorate.config_body_class).to eq(expected_result)
@@ -162,21 +162,11 @@ RSpec.describe UserDecorator, type: :decorator do
context "when user with roles" do
let(:user) { create(:user) }
- it "creates proper body class with pro user" do
- user.add_role(:pro)
-
- expected_result = %W[
- default default-article-body pro-status-true
- trusted-status-#{user.trusted} default-navbar-config
- ].join(" ")
- expect(user.decorate.config_body_class).to eq(expected_result)
- end
-
it "creates proper body class with trusted user" do
user.add_role(:trusted)
- expected_result = %W[
- default default-article-body pro-status-#{user.pro?}
+ expected_result = %w[
+ default default-article-body
trusted-status-true default-navbar-config
].join(" ")
expect(user.decorate.config_body_class).to eq(expected_result)
diff --git a/spec/factories/users.rb b/spec/factories/users.rb
index c299adcf7..d95667437 100644
--- a/spec/factories/users.rb
+++ b/spec/factories/users.rb
@@ -124,12 +124,6 @@ FactoryBot.define do
end
end
- trait :with_pro_membership do
- after(:create) do |user|
- create(:pro_membership, user: user)
- end
- end
-
trait :tag_moderator do
after(:create) do |user|
tag = create(:tag)
diff --git a/spec/mailers/previews/pro_membership_mailer_preview.rb b/spec/mailers/previews/pro_membership_mailer_preview.rb
deleted file mode 100644
index b0ddb8519..000000000
--- a/spec/mailers/previews/pro_membership_mailer_preview.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-# Preview all emails at http://localhost:3000/rails/mailers/pro_membership_mailer
-class ProMembershipMailerPreview < ActionMailer::Preview
- def expiring_membership
- pro_membership = ProMembership.find_or_create_by(user: User.last)
- ProMembershipMailer.expiring_membership(pro_membership, 1.week.from_now)
- end
-end
diff --git a/spec/mailers/pro_membership_mailer_spec.rb b/spec/mailers/pro_membership_mailer_spec.rb
deleted file mode 100644
index 0056ab991..000000000
--- a/spec/mailers/pro_membership_mailer_spec.rb
+++ /dev/null
@@ -1,69 +0,0 @@
-require "rails_helper"
-
-RSpec.describe ProMembershipMailer, type: :mailer do
- let(:pro_membership) { build_stubbed(:pro_membership) }
- let(:user) { pro_membership.user }
-
- describe "#expiring_membership" do
- it "works correctly" do
- Timecop.freeze(Time.current) do
- email = described_class.expiring_membership(pro_membership, 1.week.from_now)
-
- expect(email.subject).to eq("Your Pro Membership will expire in 7 days!")
- expect(email.to).to eq([user.email])
- expect(email.from).to eq([SiteConfig.email_addresses[:default]])
- expect(email["from"].value).to eq("#{ApplicationConfig['COMMUNITY_NAME']} Pro Memberships <#{SiteConfig.email_addresses[:default]}>")
- end
- end
-
- context "when generating the plain text email" do
- it "includes the user's name" do
- email = described_class.expiring_membership(pro_membership, 1.week.from_now)
- expect(email.text_part.body).to include(user.name)
- end
-
- it "includes the expiration date" do
- Timecop.freeze(Time.current) do
- expiration_date = 1.week.from_now
- email = described_class.expiring_membership(pro_membership, expiration_date)
- expect(email.text_part.body).to include(expiration_date.to_date.to_s(:long))
- end
- end
-
- it "includes credits path" do
- email = described_class.expiring_membership(pro_membership, 1.week.from_now)
- expect(email.text_part.body).to include(credits_path)
- end
-
- it "includes pro membership path" do
- email = described_class.expiring_membership(pro_membership, 1.week.from_now)
- expect(email.text_part.body).to include(user_settings_path("pro-membership"))
- end
- end
-
- context "when generating the html email" do
- it "includes the user's name" do
- email = described_class.expiring_membership(pro_membership, 1.week.from_now)
- expect(email.html_part.body).to include(user.name)
- end
-
- it "includes the expiration date" do
- Timecop.freeze(Time.current) do
- expiration_date = 1.week.from_now
- email = described_class.expiring_membership(pro_membership, expiration_date)
- expect(email.html_part.body).to include(expiration_date.to_date.to_s(:long))
- end
- end
-
- it "includes credits path" do
- email = described_class.expiring_membership(pro_membership, 1.week.from_now)
- expect(email.html_part.body).to include(CGI.escape(credits_path))
- end
-
- it "includes pro membership path" do
- email = described_class.expiring_membership(pro_membership, 1.week.from_now)
- expect(email.html_part.body).to include(CGI.escape(user_settings_path("pro-membership")))
- end
- end
- end
-end
diff --git a/spec/models/article_spec.rb b/spec/models/article_spec.rb
index 2737cf32a..996e7b55e 100644
--- a/spec/models/article_spec.rb
+++ b/spec/models/article_spec.rb
@@ -663,7 +663,6 @@ RSpec.describe Article, type: :model do
expect(article.cached_user.slug).to eq(article.user.username)
expect(article.cached_user.profile_image_90).to eq(article.user.profile_image_90)
expect(article.cached_user.profile_image_url).to eq(article.user.profile_image_url)
- expect(article.cached_user.pro).to eq(article.user.pro?)
end
it "assigns cached_organization on save" do
@@ -673,7 +672,6 @@ RSpec.describe Article, type: :model do
expect(article.cached_organization.slug).to eq(article.organization.slug)
expect(article.cached_organization.profile_image_90).to eq(article.organization.profile_image_90)
expect(article.cached_organization.profile_image_url).to eq(article.organization.profile_image_url)
- expect(article.cached_organization.pro).to be(false)
end
end
diff --git a/spec/models/organization_spec.rb b/spec/models/organization_spec.rb
index 84901e3a1..9e0b175c5 100644
--- a/spec/models/organization_spec.rb
+++ b/spec/models/organization_spec.rb
@@ -270,12 +270,6 @@ RSpec.describe Organization, type: :model do
end
end
- describe "#pro?" do
- it "always returns false" do
- expect(build(:organization).pro?).to be(false)
- end
- end
-
describe "#decoarated" do
it "returns not fully banished" do
expect(organization.decorate.fully_banished?).to eq(false)
diff --git a/spec/models/pro_membership_spec.rb b/spec/models/pro_membership_spec.rb
deleted file mode 100644
index dce3c306e..000000000
--- a/spec/models/pro_membership_spec.rb
+++ /dev/null
@@ -1,138 +0,0 @@
-require "rails_helper"
-
-RSpec.describe ProMembership, type: :model do
- it { is_expected.to belong_to(:user) }
- it { is_expected.to validate_presence_of(:user) }
- it { is_expected.to validate_presence_of(:status) }
- it { is_expected.to validate_presence_of(:expires_at).on(:save) }
- it { is_expected.to validate_presence_of(:expiration_notifications_count) }
- it { is_expected.to validate_inclusion_of(:status).in_array(ProMembership::STATUSES) }
- it { is_expected.to have_db_index(:status) }
- it { is_expected.to have_db_index(:expires_at) }
-
- describe "constants" do
- it "has the correct values for constants" do
- expect(ProMembership::STATUSES).to eq(%w[active expired])
- expect(ProMembership::MONTHLY_COST).to eq(5)
- expect(ProMembership::MONTHLY_COST_USD).to eq(25)
- end
- end
-
- describe "defaults" do
- it "has the correct defaults" do
- pm = described_class.new
- expect(pm.status).to eq("active")
- expect(pm.expires_at).to be(nil)
- expect(pm.expiration_notification_at).to be(nil)
- expect(pm.expiration_notifications_count).to eq(0)
- expect(pm.auto_recharge).to be(false)
- end
- end
-
- describe "creation" do
- it "sets expires_at to a month from now" do
- Timecop.freeze(Time.current) do
- pm = described_class.create!(user: create(:user))
- expect(pm.expires_at.to_i).to eq(1.month.from_now.to_i)
- end
- end
- end
-
- describe "#expired?" do
- it "returns false if expires_at is in the future" do
- Timecop.freeze(Time.current) do
- pm = build(:pro_membership, expires_at: 5.seconds.from_now)
- expect(pm.expired?).to be(false)
- end
- end
-
- it "returns true if expires_at is in the past" do
- Timecop.freeze(Time.current) do
- pm = build(:pro_membership, expires_at: 5.seconds.ago)
- expect(pm.expired?).to be(true)
- end
- end
- end
-
- describe "#active?" do
- it "returns true if expires_at is in the future" do
- Timecop.freeze(Time.current) do
- pm = build(:pro_membership, expires_at: 5.seconds.from_now)
- expect(pm.active?).to be(true)
- end
- end
-
- it "returns false if expires_at is in the past" do
- Timecop.freeze(Time.current) do
- pm = build(:pro_membership, expires_at: 5.seconds.ago)
- expect(pm.active?).to be(false)
- end
- end
- end
-
- describe "#expire!" do
- let(:pro_membership) { create(:pro_membership) }
-
- it "sets expires_at to the current time" do
- Timecop.freeze(Time.current) do
- pro_membership.expire!
- expect(pro_membership.reload.expires_at.to_i).to eq(Time.current.to_i)
- end
- end
-
- it "sets status to expired" do
- Timecop.freeze(Time.current) do
- pro_membership.expire!
- expect(pro_membership.reload.status).to eq("expired")
- end
- end
-
- it "makes the membership expired" do
- Timecop.freeze(Time.current) do
- pro_membership.expire!
- expect(pro_membership.reload.expired?).to be(true)
- end
- end
- end
-
- describe "#renew!" do
- let(:pro_membership) { create(:pro_membership) }
-
- it "sets expires_at to a month from now" do
- Timecop.freeze(Time.current) do
- pro_membership.renew!
- expect(pro_membership.reload.expires_at.to_i).to eq(1.month.from_now.to_i)
- end
- end
-
- it "sets status to active" do
- Timecop.freeze(Time.current) do
- pro_membership.renew!
- expect(pro_membership.reload.status).to eq("active")
- end
- end
-
- it "sets expiration_notification_at to nil" do
- Timecop.freeze(Time.current) do
- pro_membership.update_column(:expiration_notification_at, Time.current)
- pro_membership.renew!
- expect(pro_membership.reload.expiration_notification_at).to be(nil)
- end
- end
-
- it "sets expiration_notifications_count to 0" do
- Timecop.freeze(Time.current) do
- pro_membership.update_column(:expiration_notifications_count, 1)
- pro_membership.renew!
- expect(pro_membership.reload.expiration_notifications_count).to eq(0)
- end
- end
-
- it "makes the membership active" do
- Timecop.freeze(Time.current) do
- pro_membership.renew!
- expect(pro_membership.reload.active?).to be(true)
- end
- end
- end
-end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index a764e14de..f10f385fc 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -141,7 +141,6 @@ RSpec.describe User, type: :model do
end
it { is_expected.to have_one(:counters).class_name("UserCounter").dependent(:destroy) }
- it { is_expected.to have_one(:pro_membership).dependent(:destroy) }
it { is_expected.not_to allow_value("#xyz").for(:bg_color_hex) }
it { is_expected.not_to allow_value("#xyz").for(:text_color_hex) }
it { is_expected.not_to allow_value("AcMe_1%").for(:username) }
@@ -975,7 +974,7 @@ RSpec.describe User, type: :model do
describe "theming properties" do
it "creates proper body class with defaults" do
- expect(user.decorate.config_body_class).to eq("default default-article-body pro-status-#{user.pro?} trusted-status-#{user.trusted} #{user.config_navbar}-navbar-config")
+ expect(user.decorate.config_body_class).to eq("default default-article-body trusted-status-#{user.trusted} #{user.config_navbar}-navbar-config")
end
it "determines dark theme if night theme" do
@@ -995,22 +994,22 @@ RSpec.describe User, type: :model do
it "creates proper body class with sans serif config" do
user.config_font = "sans_serif"
- expect(user.decorate.config_body_class).to eq("default sans-serif-article-body pro-status-#{user.pro?} trusted-status-#{user.trusted} #{user.config_navbar}-navbar-config")
+ expect(user.decorate.config_body_class).to eq("default sans-serif-article-body trusted-status-#{user.trusted} #{user.config_navbar}-navbar-config")
end
it "creates proper body class with open dyslexic config" do
user.config_font = "open_dyslexic"
- expect(user.decorate.config_body_class).to eq("default open-dyslexic-article-body pro-status-#{user.pro?} trusted-status-#{user.trusted} #{user.config_navbar}-navbar-config")
+ expect(user.decorate.config_body_class).to eq("default open-dyslexic-article-body trusted-status-#{user.trusted} #{user.config_navbar}-navbar-config")
end
it "creates proper body class with night theme" do
user.config_theme = "night_theme"
- expect(user.decorate.config_body_class).to eq("night-theme default-article-body pro-status-#{user.pro?} trusted-status-#{user.trusted} #{user.config_navbar}-navbar-config")
+ expect(user.decorate.config_body_class).to eq("night-theme default-article-body trusted-status-#{user.trusted} #{user.config_navbar}-navbar-config")
end
it "creates proper body class with pink theme" do
user.config_theme = "pink_theme"
- expect(user.decorate.config_body_class).to eq("pink-theme default-article-body pro-status-#{user.pro?} trusted-status-#{user.trusted} #{user.config_navbar}-navbar-config")
+ expect(user.decorate.config_body_class).to eq("pink-theme default-article-body trusted-status-#{user.trusted} #{user.config_navbar}-navbar-config")
end
end
@@ -1068,19 +1067,6 @@ RSpec.describe User, type: :model do
user.add_role(:pro)
expect(user.pro?).to be(true)
end
-
- it "returns true if the user has an active pro membership" do
- user.pro_membership = build(:pro_membership, status: "active")
- expect(user.pro?).to be(true)
- end
-
- it "returns false if the user has an expired pro membership" do
- Timecop.freeze(Time.current) do
- membership = create(:pro_membership, user: user)
- membership.expire!
- expect(user.pro?).to be(false)
- end
- end
end
describe "#enough_credits?" do
diff --git a/spec/requests/credits_spec.rb b/spec/requests/credits_spec.rb
index 199f49256..f809ba4bd 100644
--- a/spec/requests/credits_spec.rb
+++ b/spec/requests/credits_spec.rb
@@ -96,19 +96,6 @@ RSpec.describe "Credits", type: :request do
expect(response.body).to include("Purchase history")
expect(response.body).to include("Miscellaneous items")
end
-
- it "shows a pro membership purchase" do
- pro_membership = create(:pro_membership, user: user)
- purchase_params = { user: user, purchase_type: pro_membership.class.name, purchase_id: pro_membership.id }
- create(:credit, params.merge(purchase_params))
-
- sign_in user
- get credits_path
-
- expect(response.body).to include("Purchase history")
- expect(response.body).to include("Pro Membership")
- expect(response.body).to include(pro_membership_path)
- end
end
end
diff --git a/spec/requests/dashboard_spec.rb b/spec/requests/dashboard_spec.rb
index fecf861c6..694f7bf3f 100644
--- a/spec/requests/dashboard_spec.rb
+++ b/spec/requests/dashboard_spec.rb
@@ -216,15 +216,6 @@ RSpec.describe "Dashboards", type: :request do
end
end
- context "when user has a pro membership" do
- it "shows page properly" do
- create(:pro_membership, user: user)
- sign_in user
- get "/dashboard/pro"
- expect(response.body).to include("pro")
- end
- end
-
context "when user has pro permission and is an org admin" do
it "shows page properly" do
org = create :organization
diff --git a/spec/requests/pro_memberships_spec.rb b/spec/requests/pro_memberships_spec.rb
deleted file mode 100644
index 34b62ec38..000000000
--- a/spec/requests/pro_memberships_spec.rb
+++ /dev/null
@@ -1,173 +0,0 @@
-require "rails_helper"
-
-RSpec.describe "Pro Memberships", type: :request do
- describe "GET /pro" do
- it "returns Pro landing page" do
- get pro_membership_path
- expect(response.body).to include("Like a Pro")
- end
- end
-
- describe "POST /pro" do
- let(:user) { create(:user) }
-
- context "when the user is not logged in" do
- it "redirects to the sign up page" do
- post pro_membership_path
- expect(response).to redirect_to(sign_up_path)
- end
- end
-
- context "when the user is logged in and already has a pro memberships" do
- before do
- sign_in user
- end
-
- it "does not authorize creation if it has an active membership" do
- create(:pro_membership, user: user)
- expect do
- post pro_membership_path
- end.to raise_error(Pundit::NotAuthorizedError)
- end
-
- it "does not authorize creation if it has an expired membership" do
- Timecop.freeze(Time.current) do
- membership = create(:pro_membership, user: user)
- membership.expire!
-
- expect do
- post pro_membership_path
- end.to raise_error(Pundit::NotAuthorizedError)
- end
- end
-
- it "does not authorize creation if the user as a pro role" do
- user.add_role(:pro)
- expect do
- post pro_membership_path
- end.to raise_error(Pundit::NotAuthorizedError)
- end
- end
-
- context "when the user is logged in without a pro membership and enough credits" do
- before do
- sign_in user
- create_list(:credit, ProMembership::MONTHLY_COST, user: user)
- end
-
- it "creates an active pro membership" do
- expect do
- post pro_membership_path
- end.to change(ProMembership, :count).by(1)
- expect(user.reload.pro_membership.active?).to be(true)
- end
-
- it "buys the pro membership with the correct amount of credits" do
- expect do
- post pro_membership_path
- end.to change(user.credits.spent, :count).by(ProMembership::MONTHLY_COST)
- end
-
- it "enqueues a job to bust the user's cache" do
- ActiveJob::Base.queue_adapter.enqueued_jobs.clear # make sure it hasn't been previously queued
- sidekiq_assert_enqueued_with(job: Users::BustCacheWorker, args: [user.id]) do
- post pro_membership_path
- end
- end
-
- it "enqueues a job to bust the user's articles caches" do
- sidekiq_assert_enqueued_with(
- job: Users::ResaveArticlesWorker,
- args: [user.id],
- queue: "medium_priority",
- ) do
- post pro_membership_path
- end
- end
-
- it "adds the user to the pro members channel" do
- create(:chat_channel, channel_type: "invite_only", slug: "pro-members")
- post pro_membership_path
- expect(user.reload.chat_channels.exists?(slug: "pro-members")).to be(true)
- end
-
- it "redirects to the pro membership settings page with a notice" do
- post pro_membership_path
- expect(response).to redirect_to(user_settings_path("pro-membership"))
- expect(flash[:settings_notice]).to eq("You are now a Pro!")
- end
- end
-
- context "when the user is logged in without a pro membership and not enough credits" do
- before do
- sign_in user
- end
-
- it "does not create an active pro membership" do
- expect do
- post pro_membership_path
- end.to change(ProMembership, :count).by(0)
- end
-
- it "does not subtract credits" do
- expect do
- post pro_membership_path
- end.to change(user.credits.spent, :count).by(0)
- end
-
- it "redirects to the pro membership settings page with an error message" do
- post pro_membership_path
- expect(response).to redirect_to(user_settings_path("pro-membership"))
- expect(flash[:error]).to eq("You don't have enough credits!")
- end
- end
- end
-
- describe "PUT /pro" do
- let(:user) { create(:user) }
-
- context "when the user is not logged in" do
- it "redirects to the sign up page" do
- put pro_membership_path
- expect(response).to redirect_to(sign_up_path)
- end
- end
-
- context "when the user is logged in without a pro membership" do
- before do
- sign_in user
- end
-
- it "does not authorize the operation" do
- expect do
- put pro_membership_path
- end.to raise_error(Pundit::NotAuthorizedError)
- end
- end
-
- context "when the user is logged in with a pro membership" do
- before do
- sign_in user
- end
-
- it "works correctly" do
- create(:pro_membership, user: user)
- put pro_membership_path, params: { pro_membership: { auto_recharge: true } }
- expect(flash[:settings_notice]).to eq("Your membership has been updated!")
- expect(response).to redirect_to(user_settings_path("pro-membership"))
- end
-
- it "activates auto recharge" do
- pro_membership = create(:pro_membership, user: user)
- put pro_membership_path, params: { pro_membership: { auto_recharge: true } }
- expect(pro_membership.reload.auto_recharge).to be(true)
- end
-
- it "deactivates auto recharge" do
- pro_membership = create(:pro_membership, user: user, auto_recharge: true)
- put pro_membership_path, params: { pro_membership: { auto_recharge: false } }
- expect(pro_membership.reload.auto_recharge).to be(false)
- end
- end
- end
-end
diff --git a/spec/services/pro_memberships/biller_spec.rb b/spec/services/pro_memberships/biller_spec.rb
deleted file mode 100644
index 1fd6c1701..000000000
--- a/spec/services/pro_memberships/biller_spec.rb
+++ /dev/null
@@ -1,257 +0,0 @@
-require "rails_helper"
-
-RSpec.describe ProMemberships::Biller, type: :service do
- def format_date(datetime)
- # PostgreSQL DATE(..) function uses UTC.
- datetime.utc.to_date.iso8601
- end
-
- context "when there are expiring memberships with enough credits" do
- let(:pro_membership) { create(:pro_membership) }
- let(:user) { pro_membership.user }
-
- before do
- create_list(:credit, ProMembership::MONTHLY_COST, user: user)
- end
-
- it "renews the membership" do
- Timecop.travel(format_date(pro_membership.expires_at)) do
- described_class.call
- pro_membership.reload
- expect(pro_membership.expires_at.to_i).to eq(1.month.from_now.to_i)
- expect(pro_membership.status).to eq("active")
- end
- end
-
- it "subtracts the correct amount of credits" do
- Timecop.travel(format_date(pro_membership.expires_at)) do
- expect do
- described_class.call
- end.to change(user.credits.spent, :size).by(ProMembership::MONTHLY_COST)
- end
- end
-
- it "adds the user back to the pro members chat channel" do
- create(:chat_channel, slug: "pro-members", channel_type: "invite_only")
-
- Timecop.travel(format_date(pro_membership.expires_at)) do
- described_class.call
- expect(user.reload.chat_channels.exists?(slug: "pro-members")).to be(true)
- end
- end
-
- it "does not fail if the user is already in the pro members chat channel" do
- cc = create(:chat_channel, slug: "pro-members", channel_type: "invite_only")
- cc.add_users(user)
-
- allow(Rails.logger).to receive(:error)
- Timecop.travel(format_date(pro_membership.expires_at)) do
- described_class.call
- expect(Rails.logger).not_to have_received(:error)
- expect(user.reload.chat_channels.exists?(slug: "pro-members")).to be(true)
- end
- end
-
- it "enqueues a job to bust the users caches" do
- ActiveJob::Base.queue_adapter.enqueued_jobs.clear # make sure it hasn't been previously queued
- Timecop.travel(format_date(pro_membership.expires_at)) do
- sidekiq_assert_enqueued_with(job: Users::BustCacheWorker, args: [user.id]) do
- described_class.call
- end
- end
- end
-
- it "enqueues a job to bust the users articles caches" do
- Timecop.travel(format_date(pro_membership.expires_at)) do
- sidekiq_assert_enqueued_with(
- job: Users::ResaveArticlesWorker,
- args: [user.id],
- queue: "medium_priority",
- ) do
- described_class.call
- end
- end
- end
-
- context "when an error occurs" do
- it "does not renew the membership" do
- Timecop.travel(format_date(pro_membership.expires_at)) do
- pro_membership.expire!
- allow(Credits::Buyer).to receive(:call).and_raise(StandardError)
- described_class.call
- expect(pro_membership.expires_at.to_i).to be(Time.current.to_i)
- expect(pro_membership.status).to eq("expired")
- end
- end
-
- it "does not subtract credits" do
- Timecop.travel(format_date(pro_membership.expires_at)) do
- allow(Credits::Buyer).to receive(:call).and_raise(StandardError)
- expect do
- described_class.call
- end.to change(user.credits.spent, :size).by(0)
- end
- end
-
- it "notifies the admins about the error" do
- Timecop.travel(format_date(pro_membership.expires_at)) do
- allow(Credits::Buyer).to receive(:call).and_raise(StandardError)
- sidekiq_assert_enqueued_with(job: Slack::Messengers::Worker) do
- described_class.call
- end
- end
- end
- end
- end
-
- context "when there are expiring memberships with insufficient credits" do
- let(:pro_membership) { create(:pro_membership) }
- let(:user) { pro_membership.user }
-
- it "expires the membership" do
- Timecop.travel(format_date(pro_membership.expires_at)) do
- described_class.call
- pro_membership.reload
- expect(pro_membership.expired?).to be(true)
- expect(pro_membership.status).to eq("expired")
- end
- end
-
- it "removes the user from the pro members chat channel" do
- cc = create(:chat_channel, slug: "pro-members", channel_type: "invite_only")
- cc.add_users(user)
-
- Timecop.travel(format_date(pro_membership.expires_at)) do
- described_class.call
- expect(user.reload.chat_channels.exists?(slug: "pro-members")).to be(false)
- end
- end
-
- it "notifies the admins about the expiration" do
- Timecop.travel(format_date(pro_membership.expires_at)) do
- sidekiq_assert_enqueued_with(job: Slack::Messengers::Worker) do
- described_class.call
- end
- end
- end
-
- it "enqueues a job to bust the users caches" do
- ActiveJob::Base.queue_adapter.enqueued_jobs.clear # make sure it hasn't been previously queued
- Timecop.travel(format_date(pro_membership.expires_at)) do
- sidekiq_assert_enqueued_with(job: Users::BustCacheWorker, args: [user.id]) do
- described_class.call
- end
- end
- end
-
- it "enqueues a job to bust the users articles caches" do
- Timecop.travel(format_date(pro_membership.expires_at)) do
- sidekiq_assert_enqueued_with(
- job: Users::ResaveArticlesWorker,
- args: [user.id],
- queue: "medium_priority",
- ) do
- described_class.call
- end
- end
- end
- end
-
- context "when there are expiring memberships with insufficient credits and auto recharge" do
- let(:pro_membership) { create(:pro_membership, auto_recharge: true) }
- let(:user) { pro_membership.user }
-
- context "when the user has an associated customer" do
- before do
- StripeMock.start
- customer = Payments::Customer.create
- user.update_columns(stripe_id_code: customer.id)
- end
-
- after do
- StripeMock.stop
- end
-
- it "charges the customer" do
- customer = Payments::Customer.get(user.stripe_id_code)
- allow(Payments::Customer).to receive(:charge)
- Timecop.travel(format_date(pro_membership.expires_at)) do
- described_class.call
- end
-
- expect(Payments::Customer).to have_received(:charge).with(
- customer: customer,
- amount: ProMembership::MONTHLY_COST_USD,
- description: "Purchase of 5 credits.",
- )
- end
-
- it "adds the correct amount of credits" do
- Timecop.travel(format_date(pro_membership.expires_at)) do
- # we cannot use "expect.to change" because of how activerecord-import works
- old_num_credits = user.credits.size
- described_class.call
- expect(user.reload.credits.size).to be(old_num_credits + ProMembership::MONTHLY_COST)
- end
- end
-
- it "renews the membership" do
- Timecop.travel(format_date(pro_membership.expires_at)) do
- described_class.call
- pro_membership.reload
- expect(pro_membership.expires_at.to_i).to eq(1.month.from_now.to_i)
- expect(pro_membership.status).to eq("active")
- end
- end
-
- it "spends the correct amount of credits" do
- Timecop.travel(format_date(pro_membership.expires_at)) do
- # we cannot use "expect.to change" because of how activerecord-import works
- old_num_credits = user.reload.credits.spent.size
- described_class.call
- expect(user.reload.credits.spent.size).to eq(old_num_credits + ProMembership::MONTHLY_COST)
- end
- end
-
- it "enqueues a job to bust the users caches" do
- ActiveJob::Base.queue_adapter.enqueued_jobs.clear # make sure it hasn't been previously queued
- Timecop.travel(format_date(pro_membership.expires_at)) do
- sidekiq_assert_enqueued_with(job: Users::BustCacheWorker, args: [user.id]) do
- described_class.call
- end
- end
- end
-
- it "enqueues a job to bust the users articles caches" do
- Timecop.travel(format_date(pro_membership.expires_at)) do
- sidekiq_assert_enqueued_with(
- job: Users::ResaveArticlesWorker,
- args: [user.id],
- queue: "medium_priority",
- ) do
- described_class.call
- end
- end
- end
- end
-
- context "when the user has no associated customer" do
- it "notifies the admins about the problem" do
- allow(user).to receive(:stripe_id_code).and_return(nil)
- Timecop.travel(format_date(pro_membership.expires_at)) do
- sidekiq_assert_enqueued_with(job: Slack::Messengers::Worker) do
- described_class.call
- end
- end
- end
-
- it "does not change the number of credits" do
- Timecop.travel(format_date(pro_membership.expires_at)) do
- expect do
- described_class.call
- end.to change(user.credits, :size).by(0)
- end
- end
- end
- end
-end
diff --git a/spec/services/pro_memberships/expiration_notifier_spec.rb b/spec/services/pro_memberships/expiration_notifier_spec.rb
deleted file mode 100644
index fa1a36b5a..000000000
--- a/spec/services/pro_memberships/expiration_notifier_spec.rb
+++ /dev/null
@@ -1,66 +0,0 @@
-require "rails_helper"
-
-RSpec.describe ProMemberships::ExpirationNotifier, type: :service do
- context "when there are expiring memberships with enough credits" do
- let(:pro_membership) { create(:pro_membership) }
- let(:user) { pro_membership.user }
-
- it "does not deliver an email to the user" do
- create_list(:credit, ProMembership::MONTHLY_COST, user: user)
- Timecop.travel(pro_membership.expires_at - 1.week) do
- assert_emails 0 do
- described_class.call(1.week.from_now)
- end
- end
- end
- end
-
- context "when there are expiring memberships with insufficient credits" do
- let(:pro_membership) { create(:pro_membership) }
-
- it "delivers an email to the user" do
- Timecop.travel(pro_membership.expires_at - 1.week) do
- assert_emails 1 do
- described_class.call(1.week.from_now)
- end
- end
- end
-
- it "sets the expiration notification datetime" do
- Timecop.freeze(pro_membership.expires_at - 1.week) do
- described_class.call(1.week.from_now)
- expect(pro_membership.reload.expiration_notification_at.to_i).to eq(Time.current.to_i)
- end
- end
-
- it "increments the notifications count" do
- Timecop.travel(pro_membership.expires_at - 1.week) do
- expect(pro_membership.expiration_notifications_count).to eq(0)
- described_class.call(1.week.from_now)
- expect(pro_membership.reload.expiration_notifications_count).to eq(1)
- end
- end
-
- it "enqueus a slack bot ping job" do
- Timecop.travel(pro_membership.expires_at - 1.week) do
- sidekiq_assert_enqueued_with(job: Slack::Messengers::Worker) do
- described_class.call(1.week.from_now)
- end
- end
- end
- end
-
- context "when there are expiring memberships with insufficient credits and auto recharge" do
- let(:pro_membership) { create(:pro_membership) }
- let(:user) { pro_membership.user }
-
- it "does not deliver an email to the user" do
- pro_membership.update_columns(auto_recharge: true)
- Timecop.travel(pro_membership.expires_at - 1.week) do
- assert_emails 0 do
- described_class.call(1.week.from_now)
- end
- end
- end
- end
-end
diff --git a/spec/system/pro_memberships/user_creates_a_pro_membership_spec.rb b/spec/system/pro_memberships/user_creates_a_pro_membership_spec.rb
deleted file mode 100644
index 23e2025cb..000000000
--- a/spec/system/pro_memberships/user_creates_a_pro_membership_spec.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-require "rails_helper"
-
-RSpec.describe "Creates a Pro Membership", type: :system do
- let(:user) { create(:user) }
-
- context "when signed in as a regular user with enough credits" do
- before do
- create_list(:credit, 5, user: user)
- sign_in user
- end
-
- it "makes the user become a pro" do
- visit "/settings/pro-membership"
- label = "Become a Pro member"
- click_on label
-
- expect(page).to have_content("You are now a Pro!")
- end
- end
-end
diff --git a/spec/system/pro_memberships/user_visits_pro_page_spec.rb b/spec/system/pro_memberships/user_visits_pro_page_spec.rb
deleted file mode 100644
index 89a5dc8f5..000000000
--- a/spec/system/pro_memberships/user_visits_pro_page_spec.rb
+++ /dev/null
@@ -1,69 +0,0 @@
-require "rails_helper"
-
-RSpec.describe "Visits Pro Memberships page", type: :system do
- let(:user) { create(:user) }
-
- context "when not signed in" do
- it "does not say you are a member" do
- visit "/pro"
- expect(page).not_to have_content("View your Pro Membership")
- end
-
- it "does not ask to become a pro member" do
- visit "/pro"
-
- expect(page).not_to have_content("Become a Pro member")
- end
- end
-
- context "when signed in as a regular user" do
- before do
- sign_in user
- end
-
- it "does not say you are a member" do
- visit "/pro"
- expect(page).not_to have_content("View your Pro Membership")
- end
-
- it "asks to become a pro member" do
- visit "/pro"
-
- expect(page).to have_content("Become a Pro member")
- end
- end
-
- context "when signed in as as user with a pro role" do
- before do
- user.add_role(:pro)
- sign_in user
- end
-
- it "says you are a member" do
- visit "/pro"
- expect(page).to have_content("View your Pro Membership")
- end
-
- it "does not ask to become a pro member" do
- visit "/pro"
- expect(page).not_to have_content("Become a Pro member")
- end
- end
-
- context "when signed in as as user with a pro membership" do
- before do
- create(:pro_membership, user: user, expires_at: 1.month.from_now)
- sign_in user
- end
-
- it "says you are a member" do
- visit "/pro"
- expect(page).to have_content("View your Pro Membership")
- end
-
- it "does not ask to become a pro member" do
- visit "/pro"
- expect(page).not_to have_content("Become a Pro member")
- end
- end
-end
diff --git a/spec/system/pro_memberships/user_visits_settings_pro_membership_page_spec.rb b/spec/system/pro_memberships/user_visits_settings_pro_membership_page_spec.rb
deleted file mode 100644
index 309bbd330..000000000
--- a/spec/system/pro_memberships/user_visits_settings_pro_membership_page_spec.rb
+++ /dev/null
@@ -1,90 +0,0 @@
-require "rails_helper"
-
-RSpec.describe "Visits Pro Membership settings page", type: :system do
- let(:user) { create(:user) }
- let(:cost) { ProMembership::MONTHLY_COST }
-
- context "when signed in as a regular user" do
- before do
- sign_in user
- end
-
- it "contains the correct info if you have no credits" do
- visit "/settings/pro-membership"
-
- expect(page).to have_content("Pro Membership is #{cost} credits per month")
- expect(page).to have_content("You currently have no credits, you need #{cost} to become a Pro member")
- expect(page).to have_link("Purchase credits", href: "/credits/purchase")
- expect(page).to have_link("Pro Membership page", href: "/pro")
- end
-
- it "contains the correct info if you have some credits but not enough" do
- create(:credit, user: user)
-
- visit "/settings/pro-membership"
- expect(page).to have_content("Pro Membership is #{cost} credits per month")
- expect(page).to have_content(
- "You currently have #{cost - 1} credits, you need #{cost} to become a Pro member",
- )
- expect(page).to have_link("Purchase credits", href: "/credits/purchase")
- expect(page).to have_link("Pro Membership page", href: "/pro")
- end
-
- it "contains the correct info if you have enough credits" do
- create_list(:credit, cost, user: user)
-
- visit "/settings/pro-membership"
- expect(page).to have_content("Pro Membership is #{cost} credits per month")
- expect(page).to have_content("You currently have #{user.credits.unspent.size} available credits")
- expect(page).not_to have_link("Purchase credits", href: "/credits/purchase")
- expect(page).to have_selector("input[type=submit][value='Become a Pro member']")
- expect(page).to have_link("Pro Membership page", href: "/pro")
- end
- end
-
- context "when signed in as as user with a pro role" do
- before do
- user.add_role(:pro)
- sign_in user
- end
-
- it "shows the status of the membership", js: true, percy: true do
- visit "/settings/pro-membership"
-
- Percy.snapshot(page, name: "Settings: /settings/pro-membership renders for pro role")
-
- expect(page).to have_content("Status")
- expect(page).to have_content("Expiration date")
- expect(page).to have_content("Never")
- expect(page).to have_link("Pro Membership page", href: "/pro")
- end
- end
-
- context "when signed in as as user with a pro membership" do
- before do
- create(:pro_membership, user: user, expires_at: 1.month.from_now)
- sign_in user
- end
-
- it "shows the status of the membership", js: true, percy: true do
- visit "/settings/pro-membership"
-
- Percy.snapshot(page, name: "Settings: /settings/pro-membership renders for pro membership")
-
- expect(page).to have_content("Status")
- expect(page).to have_content("Expiration date")
- expect(page).to have_content(user.pro_membership.expires_at.to_date.to_s(:long))
- expect(page).to have_content("Top up from credit card?")
- expect(page).to have_selector("input[type=submit]")
- expect(page).to have_link("Pro Membership page", href: "/pro")
- end
-
- it "updates the auto recharge" do
- visit "/settings/pro-membership"
-
- check "pro_membership[auto_recharge]"
- click_on "SUBMIT"
- expect(page).to have_content("Your membership has been updated!")
- end
- end
-end