FEATURE: Add new notification for admin problems (#21287)

Add new notification for admin problems to replace old PM-based flow.
This commit is contained in:
Bianca Nenciu 2023-05-03 18:35:22 +02:00 committed by GitHub
parent 076def505e
commit cc18a99105
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 101 additions and 97 deletions

View File

@ -1,5 +1,6 @@
import NotificationTypeBase from "discourse/lib/notification-types/base"; import NotificationTypeBase from "discourse/lib/notification-types/base";
import AdminProblems from "discourse/lib/notification-types/admin-problems";
import BookmarkReminder from "discourse/lib/notification-types/bookmark-reminder"; import BookmarkReminder from "discourse/lib/notification-types/bookmark-reminder";
import Custom from "discourse/lib/notification-types/custom"; import Custom from "discourse/lib/notification-types/custom";
import GrantedBadge from "discourse/lib/notification-types/granted-badge"; import GrantedBadge from "discourse/lib/notification-types/granted-badge";
@ -27,6 +28,7 @@ const CLASS_FOR_TYPE = {
membership_request_consolidated: MembershipRequestConsolidated, membership_request_consolidated: MembershipRequestConsolidated,
moved_post: MovedPost, moved_post: MovedPost,
new_features: NewFeatures, new_features: NewFeatures,
admin_problems: AdminProblems,
watching_first_post: WatchingFirstPost, watching_first_post: WatchingFirstPost,
}; };

View File

@ -0,0 +1,21 @@
import NotificationTypeBase from "discourse/lib/notification-types/base";
import getURL from "discourse-common/lib/get-url";
import I18n from "I18n";
export default class extends NotificationTypeBase {
get label() {
return null;
}
get description() {
return I18n.t("notifications.admin_problems");
}
get linkHref() {
return getURL("/admin");
}
get icon() {
return "gift";
}
}

View File

@ -0,0 +1,36 @@
# frozen_string_literal: true
module Jobs
class AdminProblems < ::Jobs::Scheduled
every 30.minutes
def execute(args)
Notification
.where(notification_type: Notification.types[:admin_problems])
.where("created_at < ?", 7.days.ago)
.destroy_all
return if !persistent_problems?
notified_user_ids =
Notification.where(notification_type: Notification.types[:admin_problems]).pluck(:user_id)
users = Group[:admins].users.where.not(id: notified_user_ids)
users.each do |user|
Notification.create!(
notification_type: Notification.types[:admin_problems],
user_id: user.id,
data: "{}",
)
end
end
private
def persistent_problems?
problems_started_at = AdminDashboardData.problems_started_at
problems_started_at && problems_started_at < 2.days.ago
end
end
end

View File

@ -1,27 +0,0 @@
# frozen_string_literal: true
module Jobs
class DashboardStats < ::Jobs::Scheduled
every 30.minutes
def execute(args)
if persistent_problems?
# If there have been problems reported on the dashboard for a while,
# send a message to admins no more often than once per week.
group_message =
GroupMessage.new(Group[:admins].name, :dashboard_problems, limit_once_per: 7.days.to_i)
Topic.transaction do
group_message.delete_previous!
group_message.create
end
end
end
private
def persistent_problems?
problems_started_at = AdminDashboardData.problems_started_at
problems_started_at && problems_started_at < 2.days.ago
end
end
end

View File

@ -152,6 +152,7 @@ class Notification < ActiveRecord::Base
question_answer_user_commented: 35, # Used by https://github.com/discourse/discourse-question-answer question_answer_user_commented: 35, # Used by https://github.com/discourse/discourse-question-answer
watching_category_or_tag: 36, watching_category_or_tag: 36,
new_features: 37, new_features: 37,
admin_problems: 38,
following: 800, # Used by https://github.com/discourse/discourse-follow following: 800, # Used by https://github.com/discourse/discourse-follow
following_created_topic: 801, # Used by https://github.com/discourse/discourse-follow following_created_topic: 801, # Used by https://github.com/discourse/discourse-follow
following_replied: 802, # Used by https://github.com/discourse/discourse-follow following_replied: 802, # Used by https://github.com/discourse/discourse-follow

View File

@ -2580,6 +2580,7 @@ en:
reaction_2: "<span>%{username}, %{username2}</span> %{description}" reaction_2: "<span>%{username}, %{username2}</span> %{description}"
votes_released: "%{description} - completed" votes_released: "%{description} - completed"
new_features: "New features available!" new_features: "New features available!"
admin_problems: "New advice on your site dashboard"
dismiss_confirmation: dismiss_confirmation:
body: body:
default: default:

View File

@ -3538,16 +3538,6 @@ en:
subject_template: "Downloading remote images disabled" subject_template: "Downloading remote images disabled"
text_body_template: "The `download_remote_images_to_local` setting was disabled because the disk space limit at `download_remote_images_threshold` was reached." text_body_template: "The `download_remote_images_to_local` setting was disabled because the disk space limit at `download_remote_images_threshold` was reached."
dashboard_problems:
title: "Dashboard Problems"
subject_template: "New advice on your site dashboard"
text_body_template: |
We have some new advice and recommendations for you based on your current site settings.
[Visit your site dashboard](%{base_url}/admin) to see it.
If nothing is visible on your dashboard, another staff member may have already acted on this advice. A list of staff actions can be found in your [Staff Action Logs](%{base_url}/admin/logs/staff_action_logs).
new_user_of_the_month: new_user_of_the_month:
title: "You're a New User of the Month!" title: "You're a New User of the Month!"
subject_template: "You're a New User of the Month!" subject_template: "You're a New User of the Month!"

View File

@ -0,0 +1,37 @@
# frozen_string_literal: true
RSpec.describe ::Jobs::AdminProblems do
fab!(:admin) { Fabricate(:admin) }
it "creates notification when problems persist for at least 2 days" do
Discourse.redis.setex(AdminDashboardData.problems_started_key, 14.days.to_i, Time.zone.now.to_s)
expect { described_class.new.execute({}) }.not_to change { Notification.count }
Discourse.redis.setex(AdminDashboardData.problems_started_key, 14.days.to_i, 3.days.ago)
expect { described_class.new.execute({}) }.to change { Notification.count }.by(1)
end
it "does not replace old notification created in last 7 days" do
Discourse.redis.setex(AdminDashboardData.problems_started_key, 14.days.to_i, 3.days.ago)
expect { described_class.new.execute({}) }.to change { Notification.count }.by(1)
old_notification = Notification.last
expect { described_class.new.execute({}) }.not_to change { Notification.count }
new_notification = Notification.last
expect(old_notification.id).to equal(new_notification.id)
end
it "replace old notification created more than 7 days ago" do
Discourse.redis.setex(AdminDashboardData.problems_started_key, 14.days.to_i, 13.days.ago)
freeze_time 10.days.ago do
expect { described_class.new.execute({}) }.to change { Notification.count }.by(1)
end
old_notification = Notification.last
expect { described_class.new.execute({}) }.not_to change { Notification.count }
new_notification = Notification.last
expect(old_notification.id).not_to equal(new_notification.id)
end
end

View File

@ -1,60 +0,0 @@
# frozen_string_literal: true
RSpec.describe ::Jobs::DashboardStats do
let(:group_message) do
GroupMessage.new(Group[:admins].name, :dashboard_problems, limit_once_per: 7.days.to_i)
end
def clear_recently_sent!
# We won't immediately create new PMs due to the limit_once_per option, reset the value for testing purposes.
Discourse.redis.del(group_message.sent_recently_key)
end
after { clear_recently_sent! }
it "creates group message when problems are persistent for 2 days" do
Discourse.redis.setex(AdminDashboardData.problems_started_key, 14.days.to_i, Time.zone.now.to_s)
expect { described_class.new.execute({}) }.not_to change { Topic.count }
Discourse.redis.setex(AdminDashboardData.problems_started_key, 14.days.to_i, 3.days.ago)
expect { described_class.new.execute({}) }.to change { Topic.count }.by(1)
end
it "replaces old message" do
Discourse.redis.setex(AdminDashboardData.problems_started_key, 14.days.to_i, 3.days.ago)
expect { described_class.new.execute({}) }.to change { Topic.count }.by(1)
old_topic = Topic.last
clear_recently_sent!
new_topic = described_class.new.execute({}).topic
expect(old_topic.reload.deleted_at.present?).to eq(true)
expect(new_topic.reload.deleted_at).to be_nil
expect(new_topic.title).to eq(old_topic.title)
end
it "respects the sent_recently? check when deleting previous message" do
Discourse.redis.setex(AdminDashboardData.problems_started_key, 14.days.to_i, 3.days.ago)
expect { described_class.new.execute({}) }.to change { Topic.count }.by(1)
expect { described_class.new.execute({}) }.not_to change { Topic.count }
end
it "duplicates message if previous one has replies" do
Discourse.redis.setex(AdminDashboardData.problems_started_key, 14.days.to_i, 3.days.ago)
expect { described_class.new.execute({}) }.to change { Topic.count }.by(1)
clear_recently_sent!
_reply_1 = Fabricate(:post, topic: Topic.last)
expect { described_class.new.execute({}) }.to change { Topic.count }.by(1)
end
it "duplicates message if previous was 3 months ago" do
freeze_time 3.months.ago do
Discourse.redis.setex(AdminDashboardData.problems_started_key, 14.days.to_i, 3.days.ago)
expect { described_class.new.execute({}) }.to change { Topic.count }.by(1)
clear_recently_sent!
end
expect { described_class.new.execute({}) }.to change { Topic.count }.by(1)
end
end

View File

@ -41,6 +41,9 @@
"new_features": { "new_features": {
"type": "integer" "type": "integer"
}, },
"admin_problems": {
"type": "integer"
},
"moved_post": { "moved_post": {
"type": "integer" "type": "integer"
}, },