FEATURE: Extend embeddable hosts with Individual tags and author assignments (#26868)
* FEATURE: Extend embeddable hosts with tags and author assignments
This commit is contained in:
parent
0e9451e93f
commit
63b7a36fac
|
@ -26,6 +26,25 @@
|
||||||
class="small"
|
class="small"
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
|
<td class="editing-input">
|
||||||
|
<div class="label">{{i18n "admin.embedding.tags"}}</div>
|
||||||
|
<TagChooser
|
||||||
|
@tags={{this.tags}}
|
||||||
|
@everyTag={{true}}
|
||||||
|
@excludeSynonyms={{true}}
|
||||||
|
@unlimitedTagCount={{true}}
|
||||||
|
@onChange={{fn (mut this.tags)}}
|
||||||
|
@options={{hash filterPlaceholder="category.tags_placeholder"}}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td class="editing-input">
|
||||||
|
<div class="label">{{i18n "admin.embedding.user"}}</div>
|
||||||
|
<UserChooser
|
||||||
|
@value={{this.user}}
|
||||||
|
@onChange={{action "onUserChange"}}
|
||||||
|
@options={{hash maximum=1 excludeCurrentUser=false}}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
<td class="editing-controls">
|
<td class="editing-controls">
|
||||||
<DButton
|
<DButton
|
||||||
@icon="check"
|
@icon="check"
|
||||||
|
@ -55,6 +74,12 @@
|
||||||
<div class="label">{{i18n "admin.embedding.category"}}</div>
|
<div class="label">{{i18n "admin.embedding.category"}}</div>
|
||||||
{{category-badge this.category allowUncategorized=true}}
|
{{category-badge this.category allowUncategorized=true}}
|
||||||
</td>
|
</td>
|
||||||
|
<td>
|
||||||
|
{{this.tags}}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{this.user}}
|
||||||
|
</td>
|
||||||
<td class="controls">
|
<td class="controls">
|
||||||
<DButton @icon="pencil-alt" @action={{this.edit}} />
|
<DButton @icon="pencil-alt" @action={{this.edit}} />
|
||||||
<DButton @icon="far-trash-alt" @action={{this.delete}} class="btn-danger" />
|
<DButton @icon="far-trash-alt" @action={{this.delete}} class="btn-danger" />
|
||||||
|
|
|
@ -18,6 +18,8 @@ export default class EmbeddableHost extends Component.extend(
|
||||||
editToggled = false;
|
editToggled = false;
|
||||||
categoryId = null;
|
categoryId = null;
|
||||||
category = null;
|
category = null;
|
||||||
|
tags = null;
|
||||||
|
user = null;
|
||||||
|
|
||||||
@or("host.isNew", "editToggled") editing;
|
@or("host.isNew", "editToggled") editing;
|
||||||
|
|
||||||
|
@ -29,6 +31,8 @@ export default class EmbeddableHost extends Component.extend(
|
||||||
const category = Category.findById(categoryId);
|
const category = Category.findById(categoryId);
|
||||||
|
|
||||||
this.set("category", category);
|
this.set("category", category);
|
||||||
|
this.set("tags", host.tags || []);
|
||||||
|
this.set("user", host.user);
|
||||||
}
|
}
|
||||||
|
|
||||||
@discourseComputed("buffered.host", "host.isSaving")
|
@discourseComputed("buffered.host", "host.isSaving")
|
||||||
|
@ -40,7 +44,10 @@ export default class EmbeddableHost extends Component.extend(
|
||||||
edit() {
|
edit() {
|
||||||
this.set("editToggled", true);
|
this.set("editToggled", true);
|
||||||
}
|
}
|
||||||
|
@action
|
||||||
|
onUserChange(user) {
|
||||||
|
this.set("user", user);
|
||||||
|
}
|
||||||
@action
|
@action
|
||||||
save() {
|
save() {
|
||||||
if (this.cantSave) {
|
if (this.cantSave) {
|
||||||
|
@ -53,6 +60,9 @@ export default class EmbeddableHost extends Component.extend(
|
||||||
"class_name"
|
"class_name"
|
||||||
);
|
);
|
||||||
props.category_id = this.category.id;
|
props.category_id = this.category.id;
|
||||||
|
props.tags = this.tags;
|
||||||
|
props.user =
|
||||||
|
Array.isArray(this.user) && this.user.length > 0 ? this.user[0] : null;
|
||||||
|
|
||||||
const host = this.host;
|
const host = this.host;
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,18 @@
|
||||||
{{#if this.embedding.embeddable_hosts}}
|
{{#if this.embedding.embeddable_hosts}}
|
||||||
<table class="embedding grid">
|
<table class="embedding grid">
|
||||||
<thead>
|
<thead>
|
||||||
<th style="width: 30%">{{i18n "admin.embedding.host"}}</th>
|
<th style="width: 18%">{{i18n "admin.embedding.host"}}</th>
|
||||||
<th style="width: 30%">{{i18n "admin.embedding.allowed_paths"}}</th>
|
<th style="width: 18%">{{i18n "admin.embedding.allowed_paths"}}</th>
|
||||||
<th style="width: 30%">{{i18n "admin.embedding.category"}}</th>
|
<th style="width: 18%">{{i18n "admin.embedding.category"}}</th>
|
||||||
|
<th style="width: 18%">{{i18n "admin.embedding.tags"}}</th>
|
||||||
|
{{#if this.embedding.embed_by_username}}
|
||||||
|
<th style="width: 18%">{{i18n
|
||||||
|
"admin.embedding.post_author"
|
||||||
|
author=this.embedding.embed_by_username
|
||||||
|
}}</th>
|
||||||
|
{{else}}
|
||||||
|
<th style="width: 18%">{{i18n "admin.embedding.post_author"}}</th>
|
||||||
|
{{/if}}
|
||||||
<th style="width: 10%"> </th>
|
<th style="width: 10%"> </th>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
|
|
@ -878,6 +878,10 @@ table.permalinks {
|
||||||
margin-bottom: 2em;
|
margin-bottom: 2em;
|
||||||
table.grid {
|
table.grid {
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
|
.tag-chooser,
|
||||||
|
.user-chooser {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
tr td {
|
tr td {
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
max-width: 25vw;
|
max-width: 25vw;
|
||||||
|
|
|
@ -28,7 +28,18 @@ class Admin::EmbeddableHostsController < Admin::AdminController
|
||||||
host.category_id = params[:embeddable_host][:category_id]
|
host.category_id = params[:embeddable_host][:category_id]
|
||||||
host.category_id = SiteSetting.uncategorized_category_id if host.category_id.blank?
|
host.category_id = SiteSetting.uncategorized_category_id if host.category_id.blank?
|
||||||
|
|
||||||
|
username = params[:embeddable_host][:user]
|
||||||
|
|
||||||
|
if username.blank?
|
||||||
|
host.user = nil
|
||||||
|
else
|
||||||
|
host.user = User.find_by_username(username)
|
||||||
|
end
|
||||||
|
|
||||||
|
ActiveRecord::Base.transaction do
|
||||||
if host.save
|
if host.save
|
||||||
|
manage_tags(host, params[:embeddable_host][:tags]&.map(&:strip))
|
||||||
|
|
||||||
changes = host.saved_changes if action == :update
|
changes = host.saved_changes if action == :update
|
||||||
StaffActionLogger.new(current_user).log_embeddable_host(
|
StaffActionLogger.new(current_user).log_embeddable_host(
|
||||||
host,
|
host,
|
||||||
|
@ -43,6 +54,30 @@ class Admin::EmbeddableHostsController < Admin::AdminController
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
render_json_error(host)
|
render_json_error(host)
|
||||||
|
raise ActiveRecord::Rollback
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def manage_tags(host, tags)
|
||||||
|
if tags.blank?
|
||||||
|
host.tags.clear
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
existing_tags = Tag.where(name: tags).index_by(&:name)
|
||||||
|
tags_to_associate = []
|
||||||
|
|
||||||
|
tags.each do |tag_name|
|
||||||
|
tag = existing_tags[tag_name] || Tag.create(name: tag_name)
|
||||||
|
if tag.persisted?
|
||||||
|
tags_to_associate << tag
|
||||||
|
else
|
||||||
|
host.errors.add(:tags, "Failed to create or find tag: #{tag_name}")
|
||||||
|
raise ActiveRecord::Rollback
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
host.tags = tags_to_associate
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
|
@ -3,6 +3,9 @@
|
||||||
class EmbeddableHost < ActiveRecord::Base
|
class EmbeddableHost < ActiveRecord::Base
|
||||||
validate :host_must_be_valid
|
validate :host_must_be_valid
|
||||||
belongs_to :category
|
belongs_to :category
|
||||||
|
belongs_to :user, optional: true
|
||||||
|
has_many :embeddable_host_tags
|
||||||
|
has_many :tags, through: :embeddable_host_tags
|
||||||
after_destroy :reset_embedding_settings
|
after_destroy :reset_embedding_settings
|
||||||
|
|
||||||
before_validation do
|
before_validation do
|
||||||
|
@ -81,4 +84,5 @@ end
|
||||||
# updated_at :datetime not null
|
# updated_at :datetime not null
|
||||||
# class_name :string
|
# class_name :string
|
||||||
# allowed_paths :string
|
# allowed_paths :string
|
||||||
|
# user_id :integer
|
||||||
#
|
#
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class EmbeddableHostTag < ActiveRecord::Base
|
||||||
|
belongs_to :embeddable_host
|
||||||
|
belongs_to :tag
|
||||||
|
|
||||||
|
validates :embeddable_host_id, presence: true
|
||||||
|
validates :tag_id, presence: true
|
||||||
|
validates :embeddable_host_id, uniqueness: { scope: :tag_id }
|
||||||
|
end
|
||||||
|
|
||||||
|
# == Schema Information
|
||||||
|
#
|
||||||
|
# Table name: embeddable_host_tags
|
||||||
|
#
|
||||||
|
# id :bigint not null, primary key
|
||||||
|
# embeddable_host_id :integer not null
|
||||||
|
# tag_id :integer not null
|
||||||
|
# created_at :datetime not null
|
||||||
|
# updated_at :datetime not null
|
||||||
|
#
|
||||||
|
# Indexes
|
||||||
|
#
|
||||||
|
# index_embeddable_host_tags_on_embeddable_host_id (embeddable_host_id)
|
||||||
|
# index_embeddable_host_tags_on_embeddable_host_id_and_tag_id (embeddable_host_id,tag_id) UNIQUE
|
||||||
|
# index_embeddable_host_tags_on_tag_id (tag_id)
|
||||||
|
#
|
|
@ -57,6 +57,9 @@ class Tag < ActiveRecord::Base
|
||||||
has_many :synonyms, class_name: "Tag", foreign_key: "target_tag_id", dependent: :destroy
|
has_many :synonyms, class_name: "Tag", foreign_key: "target_tag_id", dependent: :destroy
|
||||||
has_many :sidebar_section_links, as: :linkable, dependent: :delete_all
|
has_many :sidebar_section_links, as: :linkable, dependent: :delete_all
|
||||||
|
|
||||||
|
has_many :embeddable_host_tags
|
||||||
|
has_many :embeddable_hosts, through: :embeddable_host_tags
|
||||||
|
|
||||||
before_save :sanitize_description
|
before_save :sanitize_description
|
||||||
|
|
||||||
after_save :index_search
|
after_save :index_search
|
||||||
|
|
|
@ -60,7 +60,10 @@ class TopicEmbed < ActiveRecord::Base
|
||||||
# If there is no embed, create a topic, post and the embed.
|
# If there is no embed, create a topic, post and the embed.
|
||||||
if embed.blank?
|
if embed.blank?
|
||||||
Topic.transaction do
|
Topic.transaction do
|
||||||
eh = EmbeddableHost.record_for_url(url)
|
if eh = EmbeddableHost.record_for_url(url)
|
||||||
|
tags = eh.tags.presence || tags
|
||||||
|
user = eh.user.presence || user
|
||||||
|
end
|
||||||
|
|
||||||
cook_method ||=
|
cook_method ||=
|
||||||
if SiteSetting.embed_support_markdown
|
if SiteSetting.embed_support_markdown
|
||||||
|
@ -99,6 +102,11 @@ class TopicEmbed < ActiveRecord::Base
|
||||||
absolutize_urls(url, contents)
|
absolutize_urls(url, contents)
|
||||||
post = embed.post
|
post = embed.post
|
||||||
|
|
||||||
|
if eh = EmbeddableHost.record_for_url(url)
|
||||||
|
tags = eh.tags.presence || tags
|
||||||
|
user = eh.user.presence || user
|
||||||
|
end
|
||||||
|
|
||||||
# Update the topic if it changed
|
# Update the topic if it changed
|
||||||
if post&.topic
|
if post&.topic
|
||||||
if post.user != user
|
if post.user != user
|
||||||
|
@ -113,8 +121,16 @@ class TopicEmbed < ActiveRecord::Base
|
||||||
post.reload
|
post.reload
|
||||||
end
|
end
|
||||||
|
|
||||||
if (content_sha1 != embed.content_sha1) || (title && title != post&.topic&.title)
|
existing_tag_names = post.topic.tags.pluck(:name).sort
|
||||||
|
incoming_tag_names = Array(tags).map(&:name).sort
|
||||||
|
|
||||||
|
tags_changed = existing_tag_names != incoming_tag_names
|
||||||
|
|
||||||
|
if (content_sha1 != embed.content_sha1) || (title && title != post&.topic&.title) ||
|
||||||
|
tags_changed
|
||||||
changes = { raw: absolutize_urls(url, contents) }
|
changes = { raw: absolutize_urls(url, contents) }
|
||||||
|
|
||||||
|
changes[:tags] = incoming_tag_names if SiteSetting.tagging_enabled && tags_changed
|
||||||
changes[:title] = title if title.present?
|
changes[:title] = title if title.present?
|
||||||
|
|
||||||
post.revise(user, changes, skip_validations: true, bypass_rate_limiter: true)
|
post.revise(user, changes, skip_validations: true, bypass_rate_limiter: true)
|
||||||
|
|
|
@ -141,6 +141,7 @@ class User < ActiveRecord::Base
|
||||||
belongs_to :uploaded_avatar, class_name: "Upload"
|
belongs_to :uploaded_avatar, class_name: "Upload"
|
||||||
|
|
||||||
has_many :sidebar_section_links, dependent: :delete_all
|
has_many :sidebar_section_links, dependent: :delete_all
|
||||||
|
has_many :embeddable_hosts
|
||||||
|
|
||||||
delegate :last_sent_email_address, to: :email_logs
|
delegate :last_sent_email_address, to: :email_logs
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,17 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class EmbeddableHostSerializer < ApplicationSerializer
|
class EmbeddableHostSerializer < ApplicationSerializer
|
||||||
TO_SERIALIZE = %i[id host allowed_paths class_name category_id]
|
TO_SERIALIZE = %i[id host allowed_paths class_name category_id tags user]
|
||||||
|
|
||||||
attributes *TO_SERIALIZE
|
attributes *TO_SERIALIZE
|
||||||
|
|
||||||
TO_SERIALIZE.each { |attr| define_method(attr) { object.public_send(attr) } }
|
TO_SERIALIZE.each { |attr| define_method(attr) { object.public_send(attr) } }
|
||||||
|
|
||||||
|
def user
|
||||||
|
object.user&.username
|
||||||
|
end
|
||||||
|
|
||||||
|
def tags
|
||||||
|
object.tags.map(&:name)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6816,6 +6816,8 @@ en:
|
||||||
allowed_paths: "Path Allowlist"
|
allowed_paths: "Path Allowlist"
|
||||||
edit: "edit"
|
edit: "edit"
|
||||||
category: "Post to Category"
|
category: "Post to Category"
|
||||||
|
tags: "Topic Tags"
|
||||||
|
post_author: "Post Author - Defaults to %{author}"
|
||||||
add_host: "Add Host"
|
add_host: "Add Host"
|
||||||
settings: "Embedding Settings"
|
settings: "Embedding Settings"
|
||||||
crawling_settings: "Crawler Settings"
|
crawling_settings: "Crawler Settings"
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
class AddUserIdToEmbeddableHosts < ActiveRecord::Migration[7.0]
|
||||||
|
def change
|
||||||
|
add_column :embeddable_hosts, :user_id, :integer
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,15 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
class CreateEmbeddableHostTags < ActiveRecord::Migration[7.0]
|
||||||
|
def change
|
||||||
|
create_table :embeddable_host_tags do |t|
|
||||||
|
t.integer :embeddable_host_id, null: false
|
||||||
|
t.integer :tag_id, null: false
|
||||||
|
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
|
||||||
|
add_index :embeddable_host_tags, :embeddable_host_id
|
||||||
|
add_index :embeddable_host_tags, :tag_id
|
||||||
|
add_index :embeddable_host_tags, %i[embeddable_host_id tag_id], unique: true
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,6 @@
|
||||||
|
#frozen_string_literal: true
|
||||||
|
|
||||||
|
Fabricator(:embeddable_host_tag) do
|
||||||
|
embeddable_host
|
||||||
|
tag
|
||||||
|
end
|
|
@ -0,0 +1,58 @@
|
||||||
|
#frozen_string_literal: true
|
||||||
|
|
||||||
|
RSpec.describe EmbeddableHostTag, type: :model do
|
||||||
|
describe "associations" do
|
||||||
|
it "belongs to an embeddable_host" do
|
||||||
|
expect(described_class.reflect_on_association(:embeddable_host).macro).to eq(:belongs_to)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "belongs to a tag" do
|
||||||
|
expect(described_class.reflect_on_association(:tag).macro).to eq(:belongs_to)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "validations" do
|
||||||
|
subject { Fabricate(:embeddable_host_tag) }
|
||||||
|
|
||||||
|
it { is_expected.to validate_presence_of(:embeddable_host_id) }
|
||||||
|
it { is_expected.to validate_presence_of(:tag_id) }
|
||||||
|
it { is_expected.to validate_uniqueness_of(:embeddable_host_id).scoped_to(:tag_id) }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "functionality" do
|
||||||
|
context "when creating valid associations" do
|
||||||
|
let(:embeddable_host) { Fabricate(:embeddable_host) }
|
||||||
|
let(:tag) { Fabricate(:tag) }
|
||||||
|
|
||||||
|
it "successfully creates an embeddable_host_tag with valid inputs" do
|
||||||
|
host_tag = EmbeddableHostTag.new(embeddable_host: embeddable_host, tag: tag)
|
||||||
|
expect(host_tag.save).to be true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when attempting to create duplicate associations" do
|
||||||
|
let(:embeddable_host) { Fabricate(:embeddable_host) }
|
||||||
|
let(:tag) { Fabricate(:tag) }
|
||||||
|
|
||||||
|
before { EmbeddableHostTag.create!(embeddable_host: embeddable_host, tag: tag) }
|
||||||
|
|
||||||
|
it "prevents duplicate embeddable_host_tag from being saved" do
|
||||||
|
duplicate_host_tag = EmbeddableHostTag.new(embeddable_host: embeddable_host, tag: tag)
|
||||||
|
expect(duplicate_host_tag.valid?).to be false
|
||||||
|
expect(duplicate_host_tag.errors[:embeddable_host_id]).to include("has already been taken")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with missing fields" do
|
||||||
|
it "fails to save without an embeddable_host" do
|
||||||
|
host_tag = EmbeddableHostTag.new(tag: Fabricate(:tag))
|
||||||
|
expect(host_tag.valid?).to be false
|
||||||
|
end
|
||||||
|
|
||||||
|
it "fails to save without a tag" do
|
||||||
|
host_tag = EmbeddableHostTag.new(embeddable_host: Fabricate(:embeddable_host))
|
||||||
|
expect(host_tag.valid?).to be false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -299,6 +299,53 @@ RSpec.describe TopicEmbed do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "with specified user and tags" do
|
||||||
|
fab!(:tag1) { Fabricate(:tag, name: "interesting") }
|
||||||
|
fab!(:tag2) { Fabricate(:tag, name: "article") }
|
||||||
|
|
||||||
|
let!(:new_user) { Fabricate(:user) }
|
||||||
|
let(:tags) { [tag1.name, tag2.name] }
|
||||||
|
let(:imported_post) { TopicEmbed.import(new_user, url, title, contents, tags: tags) }
|
||||||
|
|
||||||
|
it "assigns the specified user as the author" do
|
||||||
|
expect(imported_post.user).to eq(new_user)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "associates the specified tags with the topic" do
|
||||||
|
expect(imported_post.topic.tags).to contain_exactly(tag1, tag2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when updating an existing post with new tags and a different user" do
|
||||||
|
fab!(:tag1) { Fabricate(:tag, name: "interesting") }
|
||||||
|
fab!(:tag2) { Fabricate(:tag, name: "article") }
|
||||||
|
|
||||||
|
let!(:admin) { Fabricate(:admin) }
|
||||||
|
let!(:new_admin) { Fabricate(:admin) }
|
||||||
|
let(:tags) { [tag1.name, tag2.name] }
|
||||||
|
|
||||||
|
before { SiteSetting.tagging_enabled = true }
|
||||||
|
|
||||||
|
it "updates the user and adds new tags" do
|
||||||
|
original_post = TopicEmbed.import(admin, url, title, contents)
|
||||||
|
|
||||||
|
expect(original_post.user).to eq(admin)
|
||||||
|
expect(original_post.topic.tags).to be_empty
|
||||||
|
|
||||||
|
embeddable_host.update!(
|
||||||
|
tags: [tag1, tag2],
|
||||||
|
user: new_admin,
|
||||||
|
category: category,
|
||||||
|
host: "eviltrout.com",
|
||||||
|
)
|
||||||
|
|
||||||
|
edited_post = TopicEmbed.import(admin, url, title, contents)
|
||||||
|
|
||||||
|
expect(edited_post.user).to eq(new_admin)
|
||||||
|
expect(edited_post.topic.tags).to match_array([tag1, tag2])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "embedded content truncation" do
|
describe "embedded content truncation" do
|
||||||
MAX_LENGTH_BEFORE_TRUNCATION = 100
|
MAX_LENGTH_BEFORE_TRUNCATION = 100
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,68 @@ RSpec.describe Admin::EmbeddableHostsController do
|
||||||
).exists?,
|
).exists?,
|
||||||
).to eq(true)
|
).to eq(true)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "creates an embeddable host with associated tags" do
|
||||||
|
tag1 = Fabricate(:tag, name: "tag1")
|
||||||
|
tag2 = Fabricate(:tag, name: "tag2")
|
||||||
|
|
||||||
|
post "/admin/embeddable_hosts.json",
|
||||||
|
params: {
|
||||||
|
embeddable_host: {
|
||||||
|
host: "example.com",
|
||||||
|
tags: %w[tag1 tag2],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(response.status).to eq(200)
|
||||||
|
expect(EmbeddableHost.last.tags).to contain_exactly(tag1, tag2)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "updates an embeddable host with associated tags" do
|
||||||
|
tag1 = Fabricate(:tag, name: "newTag1")
|
||||||
|
tag2 = Fabricate(:tag, name: "newTag2")
|
||||||
|
|
||||||
|
put "/admin/embeddable_hosts/#{embeddable_host.id}.json",
|
||||||
|
params: {
|
||||||
|
embeddable_host: {
|
||||||
|
host: "updated-example.com",
|
||||||
|
tags: %w[newTag1 newTag2],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(response.status).to eq(200)
|
||||||
|
expect(EmbeddableHost.find(embeddable_host.id).tags).to contain_exactly(tag1, tag2)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "creates an embeddable host with an associated author" do
|
||||||
|
user = Fabricate(:user, username: "johndoe")
|
||||||
|
|
||||||
|
post "/admin/embeddable_hosts.json",
|
||||||
|
params: {
|
||||||
|
embeddable_host: {
|
||||||
|
host: "example.com",
|
||||||
|
user: "johndoe",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(response.status).to eq(200)
|
||||||
|
expect(EmbeddableHost.last.user).to eq(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "updates an embeddable host with a new author" do
|
||||||
|
new_user = Fabricate(:user, username: "johndoe")
|
||||||
|
|
||||||
|
put "/admin/embeddable_hosts/#{embeddable_host.id}.json",
|
||||||
|
params: {
|
||||||
|
embeddable_host: {
|
||||||
|
host: "updated-example.com",
|
||||||
|
user: "johndoe",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(response.status).to eq(200)
|
||||||
|
expect(EmbeddableHost.find(embeddable_host.id).user).to eq(new_user)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
shared_examples "embeddable host creation not allowed" do
|
shared_examples "embeddable host creation not allowed" do
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
RSpec.describe "Admin EmbeddableHost Management", type: :system do
|
||||||
|
fab!(:admin)
|
||||||
|
fab!(:author) { Fabricate(:admin) }
|
||||||
|
fab!(:category)
|
||||||
|
fab!(:category2) { Fabricate(:category) }
|
||||||
|
fab!(:tag)
|
||||||
|
fab!(:tag2) { Fabricate(:tag) }
|
||||||
|
|
||||||
|
before { sign_in(admin) }
|
||||||
|
|
||||||
|
it "allows admin to add and edit embeddable hosts" do
|
||||||
|
visit "/admin/customize/embedding"
|
||||||
|
|
||||||
|
find("button.btn-icon-text", text: "Add Host").click
|
||||||
|
within find("tr.ember-view") do
|
||||||
|
find('input[placeholder="example.com"]').set("awesome-discourse-site.local")
|
||||||
|
find('input[placeholder="/blog/.*"]').set("/blog/.*")
|
||||||
|
|
||||||
|
category_chooser = PageObjects::Components::SelectKit.new(".category-chooser")
|
||||||
|
category_chooser.expand
|
||||||
|
category_chooser.select_row_by_name(category.name)
|
||||||
|
|
||||||
|
tag_chooser = PageObjects::Components::SelectKit.new(".tag-chooser")
|
||||||
|
tag_chooser.expand
|
||||||
|
tag_chooser.select_row_by_name(tag.name)
|
||||||
|
|
||||||
|
find(".user-chooser").click
|
||||||
|
find(".select-kit-body .select-kit-filter input").fill_in with: author.username
|
||||||
|
find(".select-kit-body", text: author.username).click
|
||||||
|
end
|
||||||
|
find("td.editing-controls .btn.btn-primary").click
|
||||||
|
expect(page).to have_content("awesome-discourse-site.local")
|
||||||
|
expect(page).to have_content("/blog/.*")
|
||||||
|
expect(page).not_to have_content("#{tag.name},#{tag2.name}")
|
||||||
|
expect(page).to have_content("#{tag.name}")
|
||||||
|
|
||||||
|
# Editing
|
||||||
|
|
||||||
|
find(".embeddable-hosts tr:first-child .controls svg.d-icon-pencil-alt").find(
|
||||||
|
:xpath,
|
||||||
|
"..",
|
||||||
|
).click
|
||||||
|
|
||||||
|
within find(".embeddable-hosts tr:first-child.ember-view") do
|
||||||
|
find('input[placeholder="example.com"]').set("updated-example.com")
|
||||||
|
find('input[placeholder="/blog/.*"]').set("/updated-blog/.*")
|
||||||
|
|
||||||
|
category_chooser = PageObjects::Components::SelectKit.new(".category-chooser")
|
||||||
|
category_chooser.expand
|
||||||
|
category_chooser.select_row_by_name(category2.name)
|
||||||
|
|
||||||
|
tag_chooser = PageObjects::Components::SelectKit.new(".tag-chooser")
|
||||||
|
tag_chooser.expand
|
||||||
|
tag_chooser.select_row_by_name(tag2.name)
|
||||||
|
end
|
||||||
|
|
||||||
|
find("td.editing-controls .btn.btn-primary").click
|
||||||
|
expect(page).to have_content("updated-example.com")
|
||||||
|
expect(page).to have_content("/updated-blog/.*")
|
||||||
|
expect(page).to have_content("#{tag.name},#{tag2.name}")
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue