FEATURE: Featured topic for user profile & card (#8461)
This commit is contained in:
parent
b5236591e9
commit
14cb386f1e
|
@ -1,5 +1,6 @@
|
|||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import { alias, or, and } from "@ember/object/computed";
|
||||
import { propertyEqual } from "discourse/lib/computed";
|
||||
import Component from "@ember/component";
|
||||
import { getTopicFooterButtons } from "discourse/lib/register-topic-footer-button";
|
||||
|
||||
|
@ -9,6 +10,11 @@ export default Component.extend({
|
|||
// Allow us to extend it
|
||||
layoutName: "components/topic-footer-buttons",
|
||||
|
||||
topicFeaturedOnProfile: propertyEqual(
|
||||
"topic.id",
|
||||
"currentUser.featured_topic.id"
|
||||
),
|
||||
|
||||
@discourseComputed("topic.isPrivateMessage")
|
||||
canArchive(isPM) {
|
||||
return this.siteSettings.enable_personal_messages && isPM;
|
||||
|
@ -58,5 +64,19 @@ export default Component.extend({
|
|||
|
||||
@discourseComputed("topic.message_archived")
|
||||
archiveLabel: archived =>
|
||||
archived ? "topic.move_to_inbox.title" : "topic.archive_message.title"
|
||||
archived ? "topic.move_to_inbox.title" : "topic.archive_message.title",
|
||||
|
||||
@discourseComputed(
|
||||
"topic.user_id",
|
||||
"topic.isPrivateMessage",
|
||||
"topic.category.read_restricted"
|
||||
)
|
||||
showToggleFeatureOnProfileButton(userId, isPm, restricted) {
|
||||
return (
|
||||
this.siteSettings.allow_featured_topic_on_user_profiles &&
|
||||
userId === this.currentUser.get("id") &&
|
||||
!restricted &&
|
||||
!isPm
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -50,6 +50,11 @@ export default Component.extend(CardContentsBase, CanCheckEmails, CleansUp, {
|
|||
// If inside a topic
|
||||
topicPostCount: null,
|
||||
|
||||
showFeaturedTopic: and(
|
||||
"user.featured_topic",
|
||||
"siteSettings.allow_featured_topic_on_user_profiles"
|
||||
),
|
||||
|
||||
@discourseComputed("user.staff")
|
||||
staff: isStaff => (isStaff ? "staff" : ""),
|
||||
|
||||
|
|
|
@ -679,6 +679,19 @@ export default Controller.extend(bufferedProperty("model"), {
|
|||
}
|
||||
},
|
||||
|
||||
toggleFeaturedOnProfile() {
|
||||
if (!this.currentUser) return;
|
||||
|
||||
if (
|
||||
this.currentUser.featured_topic &&
|
||||
this.currentUser.featured_topic.id !== this.model.id
|
||||
) {
|
||||
bootbox.confirm(I18n.t("topic.remove_from_profile.warning"), result => {
|
||||
if (result) return this._performToggleFeaturedOnProfile();
|
||||
});
|
||||
} else return this._performToggleFeaturedOnProfile();
|
||||
},
|
||||
|
||||
jumpToIndex(index) {
|
||||
this._jumpToIndex(index);
|
||||
},
|
||||
|
@ -1070,6 +1083,10 @@ export default Controller.extend(bufferedProperty("model"), {
|
|||
}
|
||||
},
|
||||
|
||||
_performToggleFeaturedOnProfile() {
|
||||
this.model.toggleFeaturedOnProfile(this.currentUser).catch(popupAjaxError);
|
||||
},
|
||||
|
||||
_jumpToIndex(index) {
|
||||
const postStream = this.get("model.postStream");
|
||||
|
||||
|
|
|
@ -61,6 +61,11 @@ export default Controller.extend(CanCheckEmails, {
|
|||
"hasReceivedWarnings"
|
||||
),
|
||||
|
||||
showFeaturedTopic: and(
|
||||
"model.featured_topic",
|
||||
"siteSettings.allow_featured_topic_on_user_profiles"
|
||||
),
|
||||
|
||||
@discourseComputed("model.suspended", "currentUser.staff")
|
||||
isNotSuspendedOrIsStaff(suspended, isStaff) {
|
||||
return !suspended || isStaff;
|
||||
|
|
|
@ -166,5 +166,34 @@ export default {
|
|||
return this.site.mobileView;
|
||||
}
|
||||
});
|
||||
|
||||
registerTopicFooterButton({
|
||||
dependentKeys: ["currentUser.featured_topic"],
|
||||
id: "toggle-feature-on-profile",
|
||||
icon: "id-card",
|
||||
priority: 300,
|
||||
label() {
|
||||
return this.topicFeaturedOnProfile
|
||||
? "topic.remove_from_profile.title"
|
||||
: "topic.feature_on_profile.title";
|
||||
},
|
||||
title() {
|
||||
return this.topicFeaturedOnProfile
|
||||
? "topic.remove_from_profile.help"
|
||||
: "topic.feature_on_profile.help";
|
||||
},
|
||||
classNames() {
|
||||
return this.topicFeaturedOnProfile
|
||||
? ["feature-on-profile", "featured-on-profile"]
|
||||
: ["feature-on-profile"];
|
||||
},
|
||||
action: "toggleFeaturedOnProfile",
|
||||
displayed() {
|
||||
return this.showToggleFeatureOnProfileButton;
|
||||
},
|
||||
dropdown() {
|
||||
return this.site.mobileView;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
@ -444,6 +444,21 @@ const Topic = RestModel.extend({
|
|||
});
|
||||
},
|
||||
|
||||
toggleFeaturedOnProfile(user) {
|
||||
const removing = user.get("featured_topic.id") === this.id;
|
||||
const path = removing ? "clear-featured-topic" : "feature-topic";
|
||||
return ajax(`/u/${user.username}/${path}`, {
|
||||
type: "PUT",
|
||||
data: { topic_id: this.id }
|
||||
})
|
||||
.then(() => {
|
||||
const featuredTopic = removing ? null : this;
|
||||
user.set("featured_topic", featuredTopic);
|
||||
return;
|
||||
})
|
||||
.catch(popupAjaxError);
|
||||
},
|
||||
|
||||
createGroupInvite(group) {
|
||||
return ajax(`/t/${this.id}/invite-group`, {
|
||||
type: "POST",
|
||||
|
|
|
@ -10,13 +10,13 @@
|
|||
<div class="card-row second-row">
|
||||
<div class="animated-placeholder placeholder-animation"></div>
|
||||
</div>
|
||||
<div class="card-row third-row">
|
||||
<div class="card-row">
|
||||
<div class="animated-placeholder placeholder-animation"></div>
|
||||
</div>
|
||||
<div class="card-row fourth-row">
|
||||
<div class="card-row">
|
||||
<div class="animated-placeholder placeholder-animation"></div>
|
||||
</div>
|
||||
<div class="card-row sixth-row">
|
||||
<div class="card-row">
|
||||
<div class="animated-placeholder placeholder-animation"></div>
|
||||
</div>
|
||||
{{else}}
|
||||
|
@ -140,8 +140,17 @@
|
|||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if showFeaturedTopic}}
|
||||
<div class="card-row">
|
||||
<div class="featured-topic">
|
||||
<span class="desc">{{i18n 'user.featured_topic'}}</span>
|
||||
{{#link-to "topic" user.featured_topic.slug user.featured_topic.id }}{{user.featured_topic.fancy_title}}{{/link-to}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if hasLocationOrWebsite}}
|
||||
<div class="card-row third-row">
|
||||
<div class="card-row">
|
||||
<div class="location-and-website">
|
||||
{{#if user.location}}
|
||||
<span class='location'>{{d-icon "map-marker-alt"}}
|
||||
|
@ -163,7 +172,7 @@
|
|||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div class="card-row fourth-row">
|
||||
<div class="card-row">
|
||||
{{#unless user.profile_hidden}}
|
||||
<div class="metadata">
|
||||
{{#if user.last_posted_at}}
|
||||
|
@ -203,7 +212,7 @@
|
|||
</div>
|
||||
|
||||
{{#if publicUserFields}}
|
||||
<div class="card-row fifth-row">
|
||||
<div class="card-row">
|
||||
<div class="public-user-fields">
|
||||
{{#each publicUserFields as |uf|}}
|
||||
{{#if uf.value}}
|
||||
|
@ -220,7 +229,7 @@
|
|||
{{plugin-outlet name="user-card-before-badges" args=(hash user=user)}}
|
||||
|
||||
{{#if showBadges}}
|
||||
<div class="card-row sixth-row">
|
||||
<div class="card-row">
|
||||
{{#if user.featured_user_badges}}
|
||||
<div class="badge-section">
|
||||
{{#each user.featured_user_badges as |ub|}}
|
||||
|
|
|
@ -57,6 +57,18 @@
|
|||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if model.featured_topic}}
|
||||
<div class="control-group">
|
||||
<label class="control-label">{{i18n 'user.featured_topic'}}</label>
|
||||
<label class="control-label">
|
||||
{{#link-to "topic" model.featured_topic.slug model.featured_topic.id}}{{model.featured_topic.fancy_title}}{{/link-to}}
|
||||
</label>
|
||||
<div class='instructions'>
|
||||
{{i18n 'user.change_featured_topic.instructions'}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{plugin-outlet name="user-preferences-profile" args=(hash model=model save=(action "save"))}}
|
||||
|
||||
{{plugin-outlet name="user-custom-preferences" args=(hash model=model)}}
|
||||
|
|
|
@ -313,7 +313,8 @@
|
|||
toggleArchiveMessage=(action "toggleArchiveMessage")
|
||||
editFirstPost=(action "editFirstPost")
|
||||
deferTopic=(action "deferTopic")
|
||||
replyToPost=(action "replyToPost")}}
|
||||
replyToPost=(action "replyToPost")
|
||||
toggleFeaturedOnProfile=(action "toggleFeaturedOnProfile")}}
|
||||
{{else}}
|
||||
<div id="topic-footer-buttons">
|
||||
{{d-button icon="reply" class="btn-primary pull-right" action=(route-action "showLogin") label="topic.reply.title"}}
|
||||
|
|
|
@ -92,6 +92,14 @@
|
|||
{{/if}}
|
||||
{{plugin-outlet name="user-post-names" args=(hash model=model)}}
|
||||
</div>
|
||||
|
||||
{{#if showFeaturedTopic}}
|
||||
<h3 class="featured-topic">
|
||||
<span>{{i18n 'user.featured_topic'}}</span>
|
||||
{{#link-to "topic" model.featured_topic.slug model.featured_topic.id}}{{model.featured_topic.fancy_title}}{{/link-to}}
|
||||
</h3>
|
||||
{{/if}}
|
||||
|
||||
<h3 class="location-and-website">
|
||||
{{#if model.location}}<div class="user-profile-location">{{d-icon "map-marker-alt"}} {{model.location}}</div>{{/if}}
|
||||
{{#if model.website_name}}
|
||||
|
|
|
@ -181,8 +181,18 @@ $avatar_margin: -50px; // negative margin makes avatars extend above cards
|
|||
margin-top: 0.5em;
|
||||
}
|
||||
}
|
||||
// featured topic
|
||||
.featured-topic {
|
||||
.desc {
|
||||
color: $primary-high;
|
||||
}
|
||||
a {
|
||||
color: $primary;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
// location and website
|
||||
.third-row {
|
||||
.location-and-website {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
@ -209,15 +219,12 @@ $avatar_margin: -50px; // negative margin makes avatars extend above cards
|
|||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
// custom user fields
|
||||
.fifth-row {
|
||||
.public-user-fields {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// badges
|
||||
.sixth-row {
|
||||
.badge-section {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
|
@ -234,7 +241,6 @@ $avatar_margin: -50px; // negative margin makes avatars extend above cards
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// styles for group cards only
|
||||
#group-card {
|
||||
|
|
|
@ -37,7 +37,6 @@
|
|||
// styles for user cards only
|
||||
#user-card {
|
||||
// badges
|
||||
.sixth-row {
|
||||
.badge-section {
|
||||
.user-badge {
|
||||
display: block;
|
||||
|
@ -49,4 +48,3 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -451,6 +451,9 @@ nav.post-controls {
|
|||
.bookmark.bookmarked .d-icon-bookmark {
|
||||
color: $tertiary;
|
||||
}
|
||||
.feature-on-profile.featured-on-profile .d-icon-id-card {
|
||||
color: $tertiary;
|
||||
}
|
||||
}
|
||||
|
||||
#topic-footer-button {
|
||||
|
|
|
@ -5,5 +5,10 @@
|
|||
color: $tertiary;
|
||||
}
|
||||
}
|
||||
&.featured-on-profile {
|
||||
.d-icon {
|
||||
color: $tertiary;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,7 +50,6 @@ $avatar_width: 120px;
|
|||
// styles for user cards only
|
||||
#user-card {
|
||||
// badges
|
||||
.sixth-row {
|
||||
.badge-section {
|
||||
flex-wrap: wrap;
|
||||
> span {
|
||||
|
@ -75,7 +74,6 @@ $avatar_width: 120px;
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// mobile card cloak
|
||||
.card-cloak {
|
||||
|
|
|
@ -11,13 +11,14 @@ class UsersController < ApplicationController
|
|||
:enable_second_factor_totp, :disable_second_factor, :list_second_factors,
|
||||
:update_second_factor, :create_second_factor_backup, :select_avatar,
|
||||
:notification_level, :revoke_auth_token, :register_second_factor_security_key,
|
||||
:create_second_factor_security_key
|
||||
:create_second_factor_security_key, :feature_topic, :clear_featured_topic
|
||||
]
|
||||
|
||||
skip_before_action :check_xhr, only: [
|
||||
:show, :badges, :password_reset, :update, :account_created,
|
||||
:activate_account, :perform_account_activation, :user_preferences_redirect, :avatar,
|
||||
:my_redirect, :toggle_anon, :admin_login, :confirm_admin, :email_login, :summary
|
||||
:my_redirect, :toggle_anon, :admin_login, :confirm_admin, :email_login, :summary,
|
||||
:feature_topic, :clear_featured_topic
|
||||
]
|
||||
|
||||
before_action :second_factor_check_confirmed_password, only: [
|
||||
|
@ -1403,6 +1404,22 @@ class UsersController < ApplicationController
|
|||
render json: success_json
|
||||
end
|
||||
|
||||
def feature_topic
|
||||
user = fetch_user_from_params
|
||||
topic = Topic.find(params[:topic_id].to_i)
|
||||
|
||||
raise Discourse::InvalidAccess.new unless topic && guardian.can_feature_topic?(user, topic)
|
||||
user.user_profile.update(featured_topic_id: topic.id)
|
||||
render json: success_json
|
||||
end
|
||||
|
||||
def clear_featured_topic
|
||||
user = fetch_user_from_params
|
||||
guardian.ensure_can_edit!(user)
|
||||
user.user_profile.update(featured_topic_id: nil)
|
||||
render json: success_json
|
||||
end
|
||||
|
||||
HONEYPOT_KEY ||= 'HONEYPOT_KEY'
|
||||
CHALLENGE_KEY ||= 'CHALLENGE_KEY'
|
||||
|
||||
|
@ -1457,7 +1474,8 @@ class UsersController < ApplicationController
|
|||
:dismissed_banner_key,
|
||||
:profile_background_upload_url,
|
||||
:card_background_upload_url,
|
||||
:primary_group_id
|
||||
:primary_group_id,
|
||||
:featured_topic_id
|
||||
]
|
||||
|
||||
editable_custom_fields = User.editable_user_custom_fields(by_staff: current_user.try(:staff?))
|
||||
|
@ -1532,4 +1550,5 @@ class UsersController < ApplicationController
|
|||
challenge: secure_session["staged-webauthn-challenge-#{user.id}"]
|
||||
}
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -127,6 +127,7 @@ class Topic < ActiveRecord::Base
|
|||
has_many :invites, through: :topic_invites, source: :invite
|
||||
has_many :topic_timers, dependent: :destroy
|
||||
has_many :reviewables
|
||||
has_many :user_profiles
|
||||
|
||||
has_one :user_warning
|
||||
has_one :first_post, -> { where post_number: 1 }, class_name: 'Post'
|
||||
|
@ -238,6 +239,8 @@ class Topic < ActiveRecord::Base
|
|||
after_update do
|
||||
if saved_changes[:category_id] && self.tags.present?
|
||||
CategoryTagStat.topic_moved(self, *saved_changes[:category_id])
|
||||
elsif saved_changes[:category_id] && self.category&.read_restricted?
|
||||
UserProfile.remove_featured_topic_from_all_profiles(self)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -50,6 +50,7 @@ class TopicConverter
|
|||
|
||||
add_allowed_users
|
||||
update_post_uploads_secure_status
|
||||
UserProfile.remove_featured_topic_from_all_profiles(@topic)
|
||||
|
||||
Jobs.enqueue(:topic_action_converter, topic_id: @topic.id)
|
||||
Jobs.enqueue(:delete_inaccessible_notifications, topic_id: @topic.id)
|
||||
|
|
|
@ -10,6 +10,7 @@ class UserProfile < ActiveRecord::Base
|
|||
belongs_to :card_background_upload, class_name: "Upload"
|
||||
belongs_to :profile_background_upload, class_name: "Upload"
|
||||
belongs_to :granted_title_badge, class_name: "Badge"
|
||||
belongs_to :featured_topic, class_name: 'Topic'
|
||||
|
||||
validates :bio_raw, length: { maximum: 3000 }
|
||||
validates :website, url: true, allow_blank: true, if: Proc.new { |c| c.new_record? || c.website_changed? }
|
||||
|
@ -145,6 +146,9 @@ class UserProfile < ActiveRecord::Base
|
|||
self.errors.add :base, (I18n.t('user.website.domain_not_allowed', domains: allowed_domains.split('|').join(", "))) unless allowed_domains.split('|').include?(domain)
|
||||
end
|
||||
|
||||
def self.remove_featured_topic_from_all_profiles(topic)
|
||||
where(featured_topic_id: topic.id).update_all(featured_topic_id: nil)
|
||||
end
|
||||
end
|
||||
|
||||
# == Schema Information
|
||||
|
|
|
@ -45,7 +45,8 @@ class CurrentUserSerializer < BasicUserSerializer
|
|||
:second_factor_enabled,
|
||||
:ignored_users,
|
||||
:title_count_mode,
|
||||
:timezone
|
||||
:timezone,
|
||||
:featured_topic
|
||||
|
||||
def groups
|
||||
object.visible_groups.pluck(:id, :name).map { |id, name| { id: id, name: name.downcase } }
|
||||
|
@ -217,4 +218,8 @@ class CurrentUserSerializer < BasicUserSerializer
|
|||
def second_factor_enabled
|
||||
object.totp_enabled? || object.security_keys_enabled?
|
||||
end
|
||||
|
||||
def featured_topic
|
||||
object.user_profile.featured_topic
|
||||
end
|
||||
end
|
||||
|
|
|
@ -83,7 +83,8 @@ class UserSerializer < BasicUserSerializer
|
|||
:second_factor_remaining_backup_codes,
|
||||
:associated_accounts,
|
||||
:profile_background_upload_url,
|
||||
:card_background_upload_url
|
||||
:card_background_upload_url,
|
||||
:featured_topic
|
||||
|
||||
has_one :invited_by, embed: :object, serializer: BasicUserSerializer
|
||||
has_many :groups, embed: :object, serializer: BasicGroupSerializer
|
||||
|
@ -484,4 +485,7 @@ class UserSerializer < BasicUserSerializer
|
|||
object.card_background_upload&.url
|
||||
end
|
||||
|
||||
def featured_topic
|
||||
object.user_profile.featured_topic
|
||||
end
|
||||
end
|
||||
|
|
|
@ -848,6 +848,7 @@ en:
|
|||
enable_quoting: "Enable quote reply for highlighted text"
|
||||
enable_defer: "Enable defer to mark topics unread"
|
||||
change: "change"
|
||||
featured_topic: "Featured Topic"
|
||||
moderator: "{{user}} is a moderator"
|
||||
admin: "{{user}} is an admin"
|
||||
moderator_tooltip: "This user is a moderator"
|
||||
|
@ -1045,6 +1046,10 @@ en:
|
|||
title: "User Card Background"
|
||||
instructions: "Background images will be centered and have a default width of 590px."
|
||||
|
||||
change_featured_topic:
|
||||
title: "Featured Topic"
|
||||
instructions: "To change this, either navigate to the topic to remove it as the featured topic, or feature a different topic."
|
||||
|
||||
email:
|
||||
title: "Email"
|
||||
primary: "Primary Email"
|
||||
|
@ -1997,6 +2002,14 @@ en:
|
|||
defer:
|
||||
help: "Mark as unread"
|
||||
title: "Defer"
|
||||
feature_on_profile:
|
||||
help: "Add a link to this topic on your user card and profile"
|
||||
title: "Feature On Profile"
|
||||
remove_from_profile:
|
||||
warning: "Your profile already has a featured topic. If you continue, this topic will replace the existing topic."
|
||||
help: "Remove the link to this topic on your user profile"
|
||||
title: "Remove From Profile"
|
||||
|
||||
list: "Topics"
|
||||
new: "new topic"
|
||||
unread: "unread"
|
||||
|
|
|
@ -1951,6 +1951,8 @@ en:
|
|||
|
||||
hide_user_profiles_from_public: "Disable user cards, user profiles and user directory for anonymous users."
|
||||
|
||||
allow_featured_topic_on_user_profiles: "Allow users to feature a link to a topic on their user card and profile."
|
||||
|
||||
show_inactive_accounts: "Allow logged in users to browse profiles of inactive accounts."
|
||||
|
||||
hide_suspension_reasons: "Don't display suspension reasons publically on user profiles."
|
||||
|
|
|
@ -476,6 +476,8 @@ Discourse::Application.routes.draw do
|
|||
get "#{root_path}/:username/deleted-posts" => "users#show", constraints: { username: RouteFormat.username }
|
||||
get "#{root_path}/:username/topic-tracking-state" => "users#topic_tracking_state", constraints: { username: RouteFormat.username }
|
||||
get "#{root_path}/:username/profile-hidden" => "users#profile_hidden"
|
||||
put "#{root_path}/:username/feature-topic" => "users#feature_topic", constraints: { username: RouteFormat.username }
|
||||
put "#{root_path}/:username/clear-featured-topic" => "users#clear_featured_topic", constraints: { username: RouteFormat.username }
|
||||
end
|
||||
|
||||
get "user-badges/:username.json" => "user_badges#username", constraints: { username: RouteFormat.username }, defaults: { format: :json }
|
||||
|
|
|
@ -560,6 +560,9 @@ users:
|
|||
hide_user_profiles_from_public:
|
||||
default: false
|
||||
client: true
|
||||
allow_featured_topic_on_user_profiles:
|
||||
default: true
|
||||
client: true
|
||||
show_inactive_accounts:
|
||||
default: false
|
||||
user_website_domains_whitelist:
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddFeaturedTopicIdToUserProfiles < ActiveRecord::Migration[6.0]
|
||||
def change
|
||||
add_column :user_profiles, :featured_topic_id, :integer
|
||||
end
|
||||
end
|
|
@ -123,4 +123,11 @@ module UserGuardian
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
def can_feature_topic?(user, topic)
|
||||
return false if !SiteSetting.allow_featured_topic_on_user_profiles?
|
||||
return false if !is_me?(user) && !is_staff?
|
||||
return false if topic.read_restricted_category? || topic.private_message?
|
||||
topic.user_id === user.id
|
||||
end
|
||||
end
|
||||
|
|
|
@ -77,6 +77,7 @@ class PostDestroyer
|
|||
WebHook.enqueue_post_hooks(:post_destroyed, @post, payload)
|
||||
|
||||
if is_first_post
|
||||
UserProfile.remove_featured_topic_from_all_profiles(@topic)
|
||||
UserActionManager.topic_destroyed(topic)
|
||||
DiscourseEvent.trigger(:topic_destroyed, topic, @user)
|
||||
WebHook.enqueue_topic_hooks(:topic_destroyed, topic, topic_payload)
|
||||
|
|
|
@ -125,6 +125,7 @@ module SvgSprite
|
|||
"heading",
|
||||
"heart",
|
||||
"home",
|
||||
"id-card",
|
||||
"info-circle",
|
||||
"italic",
|
||||
"key",
|
||||
|
|
|
@ -822,4 +822,14 @@ describe PostDestroyer do
|
|||
expect(@reviewable_reply.reload.status).to eq Reviewable.statuses[:approved]
|
||||
end
|
||||
end
|
||||
|
||||
describe "featured topics for user_profiles" do
|
||||
fab!(:user) { Fabricate(:user) }
|
||||
|
||||
it 'clears the user_profiles featured_topic column' do
|
||||
user.user_profile.update(featured_topic: post.topic)
|
||||
PostDestroyer.new(admin, post).destroy
|
||||
expect(user.user_profile.reload.featured_topic).to eq(nil)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -221,5 +221,15 @@ describe TopicConverter do
|
|||
expect(topic.reload.archetype).to eq("private_message")
|
||||
end
|
||||
end
|
||||
|
||||
context 'user_profiles with newly converted PM as featured topic' do
|
||||
it "sets all matching user_profile featured topic ids to nil" do
|
||||
author.user_profile.update(featured_topic: topic)
|
||||
topic.convert_to_private_message(admin)
|
||||
|
||||
expect(author.user_profile.reload.featured_topic).to eq(nil)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2503,4 +2503,17 @@ describe Topic do
|
|||
expect(topic.access_topic_via_group).to eq(open_group)
|
||||
end
|
||||
end
|
||||
|
||||
describe "#after_update" do
|
||||
fab!(:topic) { Fabricate(:topic, user: user) }
|
||||
fab!(:category) { Fabricate(:category_with_definition, read_restricted: true) }
|
||||
|
||||
it "removes the topic as featured from user profiles if new category is read_restricted" do
|
||||
user.user_profile.update(featured_topic: topic)
|
||||
expect(user.user_profile.featured_topic).to eq(topic)
|
||||
|
||||
topic.update(category: category)
|
||||
expect(user.user_profile.reload.featured_topic).to eq(nil)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3831,4 +3831,96 @@ describe UsersController do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#feature_topic' do
|
||||
fab!(:topic) { Fabricate(:topic) }
|
||||
fab!(:other_user) { Fabricate(:user) }
|
||||
fab!(:private_message) { Fabricate(:private_message_topic, user: other_user) }
|
||||
fab!(:category) { Fabricate(:category_with_definition) }
|
||||
|
||||
describe "site setting enabled" do
|
||||
before do
|
||||
SiteSetting.allow_featured_topic_on_user_profiles = true
|
||||
end
|
||||
|
||||
it 'requires the user to be logged in' do
|
||||
put "/u/#{user.username}/feature-topic.json", params: { topic_id: topic.id }
|
||||
expect(response.status).to eq(403)
|
||||
end
|
||||
|
||||
it 'returns an error if the the current user does not have access' do
|
||||
sign_in(user)
|
||||
topic.update(user_id: other_user.id)
|
||||
put "/u/#{user.username}/feature-topic.json", params: { topic_id: topic.id }
|
||||
expect(response.status).to eq(403)
|
||||
end
|
||||
|
||||
it 'returns an error if the user did not create the topic' do
|
||||
sign_in(user)
|
||||
topic.update(user_id: other_user.id)
|
||||
put "/u/#{other_user.username}/feature-topic.json", params: { topic_id: topic.id }
|
||||
expect(response.status).to eq(403)
|
||||
end
|
||||
|
||||
it 'returns an error if the topic is a PM' do
|
||||
sign_in(other_user)
|
||||
put "/u/#{other_user.username}/feature-topic.json", params: { topic_id: private_message.id }
|
||||
expect(response.status).to eq(403)
|
||||
end
|
||||
|
||||
it "returns an error if the topic's category is read_restricted" do
|
||||
sign_in(user)
|
||||
category.set_permissions({})
|
||||
topic.update(category_id: category.id)
|
||||
put "/u/#{other_user.username}/feature-topic.json", params: { topic_id: topic.id }
|
||||
expect(response.status).to eq(403)
|
||||
end
|
||||
|
||||
it 'sets the user_profiles featured_topic correctly' do
|
||||
sign_in(user)
|
||||
topic.update(user_id: user.id)
|
||||
put "/u/#{user.username}/feature-topic.json", params: { topic_id: topic.id }
|
||||
expect(response.status).to eq(200)
|
||||
expect(user.user_profile.featured_topic).to eq topic
|
||||
end
|
||||
|
||||
describe "site setting disabled" do
|
||||
before do
|
||||
SiteSetting.allow_featured_topic_on_user_profiles = false
|
||||
end
|
||||
|
||||
it "does not allow setting featured_topic for user_profiles" do
|
||||
sign_in(user)
|
||||
topic.update(user_id: user.id)
|
||||
put "/u/#{user.username}/feature-topic.json", params: { topic_id: topic.id }
|
||||
expect(response.status).to eq(403)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#clear_featured_topic' do
|
||||
fab!(:topic) { Fabricate(:topic) }
|
||||
fab!(:other_user) { Fabricate(:user) }
|
||||
|
||||
it 'requires the user to be logged in' do
|
||||
put "/u/#{user.username}/clear-featured-topic.json"
|
||||
expect(response.status).to eq(403)
|
||||
end
|
||||
|
||||
it 'returns an error if the the current user does not have access' do
|
||||
sign_in(user)
|
||||
topic.update(user_id: other_user.id)
|
||||
put "/u/#{other_user.username}/clear-featured-topic.json"
|
||||
expect(response.status).to eq(403)
|
||||
end
|
||||
|
||||
it 'clears the user_profiles featured_topic correctly' do
|
||||
sign_in(user)
|
||||
topic.update(user: user)
|
||||
put "/u/#{user.username}/clear-featured-topic.json"
|
||||
expect(response.status).to eq(200)
|
||||
expect(user.user_profile.featured_topic).to eq nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -23,18 +23,13 @@ RSpec.describe WebHookUserSerializer do
|
|||
|
||||
it 'should only include the required keys' do
|
||||
count = serializer.as_json.keys.count
|
||||
difference = count - 44
|
||||
difference = count - 45
|
||||
|
||||
expect(difference).to eq(0), lambda {
|
||||
message = ""
|
||||
|
||||
if difference < 0
|
||||
message << "#{difference * -1} key(s) have been removed from this serializer."
|
||||
else
|
||||
message << "#{difference} key(s) have been added to this serializer."
|
||||
end
|
||||
|
||||
message << "\nPlease verify if those key(s) are required as part of the web hook's payload."
|
||||
message = (difference < 0 ?
|
||||
"#{difference * -1} key(s) have been removed from this serializer." :
|
||||
"#{difference} key(s) have been added to this serializer.") +
|
||||
"\nPlease verify if those key(s) are required as part of the web hook's payload."
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue