Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions app/assets/javascripts/admin/adapters/embedding.js.es6
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import RestAdapter from 'discourse/adapters/rest';

export default RestAdapter.extend({
pathFor() {
return "/admin/customize/embedding";
}
});
63 changes: 63 additions & 0 deletions app/assets/javascripts/admin/components/embeddable-host.js.es6
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { bufferedProperty } from 'discourse/mixins/buffered-content';
import computed from 'ember-addons/ember-computed-decorators';
import { on, observes } from 'ember-addons/ember-computed-decorators';
import { popupAjaxError } from 'discourse/lib/ajax-error';

export default Ember.Component.extend(bufferedProperty('host'), {
editToggled: false,
tagName: 'tr',
categoryId: null,

editing: Ember.computed.or('host.isNew', 'editToggled'),

@on('didInsertElement')
@observes('editing')
_focusOnInput() {
Ember.run.schedule('afterRender', () => { this.$('.host-name').focus(); });
},

@computed('buffered.host', 'host.isSaving')
cantSave(host, isSaving) {
return isSaving || Ember.isEmpty(host);
},

actions: {
edit() {
this.set('categoryId', this.get('host.category.id'));
this.set('editToggled', true);
},

save() {
if (this.get('cantSave')) { return; }

const props = this.get('buffered').getProperties('host');
props.category_id = this.get('categoryId');

const host = this.get('host');
host.save(props).then(() => {
host.set('category', Discourse.Category.findById(this.get('categoryId')));
this.set('editToggled', false);
}).catch(popupAjaxError);
},

delete() {
bootbox.confirm(I18n.t('admin.embedding.confirm_delete'), (result) => {
if (result) {
this.get('host').destroyRecord().then(() => {

@cubic-dev-ai cubic-dev-ai Bot Feb 13, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Missing error handling on destroyRecord(). The save() action properly uses .catch(popupAjaxError) but the delete action does not handle promise rejection. If the server returns an error, the user gets no feedback and a stale UI state.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/assets/javascripts/admin/components/embeddable-host.js.es6, line 46:

<comment>Missing error handling on `destroyRecord()`. The `save()` action properly uses `.catch(popupAjaxError)` but the `delete` action does not handle promise rejection. If the server returns an error, the user gets no feedback and a stale UI state.</comment>

<file context>
@@ -0,0 +1,63 @@
+    delete() {
+      bootbox.confirm(I18n.t('admin.embedding.confirm_delete'), (result) => {
+        if (result) {
+          this.get('host').destroyRecord().then(() => {
+            this.sendAction('deleteHost', this.get('host'));
+          });
</file context>
Fix with Cubic

this.sendAction('deleteHost', this.get('host'));
});
}
});
},

cancel() {
const host = this.get('host');
if (host.get('isNew')) {
this.sendAction('deleteHost', host);
} else {
this.rollbackBuffer();
this.set('editToggled', false);
}
}
}
});
18 changes: 18 additions & 0 deletions app/assets/javascripts/admin/controllers/admin-embedding.js.es6
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export default Ember.Controller.extend({
embedding: null,

actions: {
saveChanges() {
this.get('embedding').update({});

@cubic-dev-ai cubic-dev-ai Bot Feb 13, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Bug: update({}) is called with an empty object, so no data is actually sent to the server. The embeddable hosts data should be serialized and passed. Additionally, the returned promise is ignored — the user receives no success/failure feedback.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/assets/javascripts/admin/controllers/admin-embedding.js.es6, line 6:

<comment>Bug: `update({})` is called with an empty object, so no data is actually sent to the server. The embeddable hosts data should be serialized and passed. Additionally, the returned promise is ignored — the user receives no success/failure feedback.</comment>

<file context>
@@ -0,0 +1,18 @@
+
+  actions: {
+    saveChanges() {
+      this.get('embedding').update({});
+    },
+
</file context>
Fix with Cubic

},

addHost() {
const host = this.store.createRecord('embeddable-host');
this.get('embedding.embeddable_hosts').pushObject(host);
},

deleteHost(host) {
this.get('embedding.embeddable_hosts').removeObject(host);

@cubic-dev-ai cubic-dev-ai Bot Feb 13, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Bug: deleteHost only removes the host from the local array but never deletes it on the server. If the host was previously persisted, it will reappear after page reload. Following the codebase pattern (e.g., admin-api.js.es6 revokeKey), a server-side destroy call should be made before or alongside the local removal.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/assets/javascripts/admin/controllers/admin-embedding.js.es6, line 15:

<comment>Bug: `deleteHost` only removes the host from the local array but never deletes it on the server. If the host was previously persisted, it will reappear after page reload. Following the codebase pattern (e.g., `admin-api.js.es6` `revokeKey`), a server-side destroy call should be made before or alongside the local removal.</comment>

<file context>
@@ -0,0 +1,18 @@
+    },
+
+    deleteHost(host) {
+      this.get('embedding.embeddable_hosts').removeObject(host);
+    }
+  }
</file context>
Fix with Cubic

}
}
});
9 changes: 9 additions & 0 deletions app/assets/javascripts/admin/routes/admin-embedding.js.es6
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export default Ember.Route.extend({
model() {
return this.store.find('embedding');
},

setupController(controller, model) {
controller.set('embedding', model);
}
});
1 change: 1 addition & 0 deletions app/assets/javascripts/admin/routes/admin-route-map.js.es6
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export default {
this.resource('adminUserFields', { path: '/user_fields' });
this.resource('adminEmojis', { path: '/emojis' });
this.resource('adminPermalinks', { path: '/permalinks' });
this.resource('adminEmbedding', { path: '/embedding' });
});
this.route('api');

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{{#if editing}}
<td>
{{input value=buffered.host placeholder="example.com" enter="save" class="host-name"}}
</td>
<td>
{{category-chooser value=categoryId}}
</td>
<td>
{{d-button icon="check" action="save" class="btn-primary" disabled=cantSave}}
{{d-button icon="times" action="cancel" class="btn-danger" disabled=host.isSaving}}
</td>
{{else}}
<td>{{host.host}}</td>
<td>{{category-badge host.category}}</td>
<td>
{{d-button icon="pencil" action="edit"}}
{{d-button icon="trash-o" action="delete" class='btn-danger'}}
</td>
{{/if}}
1 change: 1 addition & 0 deletions app/assets/javascripts/admin/templates/customize.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
{{nav-item route='adminUserFields' label='admin.user_fields.title'}}
{{nav-item route='adminEmojis' label='admin.emoji.title'}}
{{nav-item route='adminPermalinks' label='admin.permalink.title'}}
{{nav-item route='adminEmbedding' label='admin.embedding.title'}}
{{/admin-nav}}

<div class="admin-container">
Expand Down
15 changes: 15 additions & 0 deletions app/assets/javascripts/admin/templates/embedding.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{{#if embedding.embeddable_hosts}}
<table>
<tr>
<th style='width: 50%'>{{i18n "admin.embedding.host"}}</th>
<th style='width: 30%'>{{i18n "admin.embedding.category"}}</th>
<th style='width: 20%'>&nbsp;</th>
</tr>
{{#each embedding.embeddable_hosts as |host|}}
{{embeddable-host host=host deleteHost="deleteHost"}}
{{/each}}
</table>
{{/if}}

{{d-button label="admin.embedding.add_host" action="addHost" icon="plus" class="btn-primary"}}

4 changes: 2 additions & 2 deletions app/assets/javascripts/discourse/adapters/rest.js.es6
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const ADMIN_MODELS = ['plugin', 'site-customization'];
const ADMIN_MODELS = ['plugin', 'site-customization', 'embeddable-host'];

export function Result(payload, responseJson) {
this.payload = payload;
Expand All @@ -19,7 +19,7 @@ function rethrow(error) {
export default Ember.Object.extend({

basePath(store, type) {
if (ADMIN_MODELS.indexOf(type) !== -1) { return "/admin/"; }
if (ADMIN_MODELS.indexOf(type.replace('_', '-')) !== -1) { return "/admin/"; }

@cubic-dev-ai cubic-dev-ai Bot Feb 13, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: String.replace with a string pattern only replaces the first occurrence. Use a regex with the global flag (/_/g) to ensure all underscores are converted to hyphens. This currently works by coincidence (each model has only one underscore), but will silently fail for any future model name with multiple underscores.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/assets/javascripts/discourse/adapters/rest.js.es6, line 22:

<comment>`String.replace` with a string pattern only replaces the **first** occurrence. Use a regex with the global flag (`/_/g`) to ensure all underscores are converted to hyphens. This currently works by coincidence (each model has only one underscore), but will silently fail for any future model name with multiple underscores.</comment>

<file context>
@@ -19,7 +19,7 @@ function rethrow(error) {
 
   basePath(store, type) {
-    if (ADMIN_MODELS.indexOf(type) !== -1) { return "/admin/"; }
+    if (ADMIN_MODELS.indexOf(type.replace('_', '-')) !== -1) { return "/admin/"; }
     return "/";
   },
</file context>
Suggested change
if (ADMIN_MODELS.indexOf(type.replace('_', '-')) !== -1) { return "/admin/"; }
if (ADMIN_MODELS.indexOf(type.replace(/_/g, '-')) !== -1) { return "/admin/"; }
Fix with Cubic

return "/";
},

Expand Down
18 changes: 14 additions & 4 deletions app/assets/javascripts/discourse/models/store.js.es6
Original file line number Diff line number Diff line change
Expand Up @@ -189,14 +189,24 @@ export default Ember.Object.extend({
_hydrateEmbedded(type, obj, root) {
const self = this;
Object.keys(obj).forEach(function(k) {
const m = /(.+)\_id$/.exec(k);
const m = /(.+)\_id(s?)$/.exec(k);
if (m) {
const subType = m[1];
const hydrated = self._lookupSubType(subType, type, obj[k], root);
if (hydrated) {
obj[subType] = hydrated;

if (m[2]) {
const hydrated = obj[k].map(function(id) {

@cubic-dev-ai cubic-dev-ai Bot Feb 13, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Missing null/undefined guard on obj[k] before calling .map(). If a property ending in _ids has a null or non-array value, this will throw a TypeError at runtime. Add a guard to check that obj[k] is an array before mapping.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/assets/javascripts/discourse/models/store.js.es6, line 197:

<comment>Missing null/undefined guard on `obj[k]` before calling `.map()`. If a property ending in `_ids` has a null or non-array value, this will throw a TypeError at runtime. Add a guard to check that `obj[k]` is an array before mapping.</comment>

<file context>
@@ -189,14 +189,24 @@ export default Ember.Object.extend({
-          obj[subType] = hydrated;
+
+        if (m[2]) {
+          const hydrated = obj[k].map(function(id) {
+            return self._lookupSubType(subType, type, id, root);
+          });
</file context>
Fix with Cubic

return self._lookupSubType(subType, type, id, root);
});
obj[self.pluralize(subType)] = hydrated || [];

@cubic-dev-ai cubic-dev-ai Bot Feb 13, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: delete obj[k] is unconditional in the plural branch, unlike the singular branch which only deletes when hydration succeeds. This loses the original IDs even when _lookupSubType returns undefined for entries. Also, hydrated || [] is dead code since .map() always returns an array. Consider filtering out undefined results and only deleting obj[k] if hydration produced valid results.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/assets/javascripts/discourse/models/store.js.es6, line 200:

<comment>`delete obj[k]` is unconditional in the plural branch, unlike the singular branch which only deletes when hydration succeeds. This loses the original IDs even when `_lookupSubType` returns `undefined` for entries. Also, `hydrated || []` is dead code since `.map()` always returns an array. Consider filtering out undefined results and only deleting `obj[k]` if hydration produced valid results.</comment>

<file context>
@@ -189,14 +189,24 @@ export default Ember.Object.extend({
+          const hydrated = obj[k].map(function(id) {
+            return self._lookupSubType(subType, type, id, root);
+          });
+          obj[self.pluralize(subType)] = hydrated || [];
           delete obj[k];
+        } else {
</file context>
Fix with Cubic

delete obj[k];
} else {
const hydrated = self._lookupSubType(subType, type, obj[k], root);
if (hydrated) {
obj[subType] = hydrated;
delete obj[k];
}
}

}
});
},
Expand Down
34 changes: 34 additions & 0 deletions app/controllers/admin/embeddable_hosts_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
class Admin::EmbeddableHostsController < Admin::AdminController

before_filter :ensure_logged_in, :ensure_staff

def create
save_host(EmbeddableHost.new)
end

def update
host = EmbeddableHost.where(id: params[:id]).first

@cubic-dev-ai cubic-dev-ai Bot Feb 13, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: No nil check after lookup — if the EmbeddableHost is not found, save_host(host) will raise a NoMethodError on nil. Use find (which raises ActiveRecord::RecordNotFound and returns 404) or add an explicit nil guard.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/controllers/admin/embeddable_hosts_controller.rb, line 10:

<comment>No nil check after lookup — if the `EmbeddableHost` is not found, `save_host(host)` will raise a `NoMethodError` on nil. Use `find` (which raises `ActiveRecord::RecordNotFound` and returns 404) or add an explicit nil guard.</comment>

<file context>
@@ -0,0 +1,34 @@
+  end
+
+  def update
+    host = EmbeddableHost.where(id: params[:id]).first
+    save_host(host)
+  end
</file context>
Fix with Cubic

save_host(host)
end

def destroy
host = EmbeddableHost.where(id: params[:id]).first
host.destroy
render json: success_json
end

protected

def save_host(host)
host.host = params[:embeddable_host][:host]
host.category_id = params[:embeddable_host][:category_id]
host.category_id = SiteSetting.uncategorized_category_id if host.category_id.blank?

if host.save
render_serialized(host, EmbeddableHostSerializer, root: 'embeddable_host', rest_serializer: true)
else
render_json_error(host)
end
end

end
21 changes: 21 additions & 0 deletions app/controllers/admin/embedding_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
class Admin::EmbeddingController < Admin::AdminController

before_filter :ensure_logged_in, :ensure_staff, :fetch_embedding

@cubic-dev-ai cubic-dev-ai Bot Feb 13, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: :ensure_logged_in and :ensure_staff are already declared in the parent Admin::AdminController and will be inherited. Only :fetch_embedding needs to be added here.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/controllers/admin/embedding_controller.rb, line 3:

<comment>`:ensure_logged_in` and `:ensure_staff` are already declared in the parent `Admin::AdminController` and will be inherited. Only `:fetch_embedding` needs to be added here.</comment>

<file context>
@@ -0,0 +1,21 @@
+class Admin::EmbeddingController < Admin::AdminController
+
+  before_filter :ensure_logged_in, :ensure_staff, :fetch_embedding
+
+  def show
</file context>
Fix with Cubic


def show
render_serialized(@embedding, EmbeddingSerializer, root: 'embedding', rest_serializer: true)
end

def update

@cubic-dev-ai cubic-dev-ai Bot Feb 13, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Bug: update action does not process any parameters or persist changes — it just re-renders the current state, making it a no-op identical to show. The action should read permitted params, update the relevant records (e.g., EmbeddableHost attributes), and then render the result.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/controllers/admin/embedding_controller.rb, line 9:

<comment>Bug: `update` action does not process any parameters or persist changes — it just re-renders the current state, making it a no-op identical to `show`. The action should read permitted params, update the relevant records (e.g., `EmbeddableHost` attributes), and then render the result.</comment>

<file context>
@@ -0,0 +1,21 @@
+    render_serialized(@embedding, EmbeddingSerializer, root: 'embedding', rest_serializer: true)
+  end
+
+  def update
+    render_serialized(@embedding, EmbeddingSerializer, root: 'embedding', rest_serializer: true)
+  end
</file context>
Fix with Cubic

render_serialized(@embedding, EmbeddingSerializer, root: 'embedding', rest_serializer: true)
end

protected

def fetch_embedding
@embedding = OpenStruct.new({
id: 'default',
embeddable_hosts: EmbeddableHost.all.order(:host)
})
end
end
3 changes: 1 addition & 2 deletions app/controllers/embed_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,7 @@ def count
def ensure_embeddable

if !(Rails.env.development? && current_user.try(:admin?))
raise Discourse::InvalidAccess.new('embeddable hosts not set') if SiteSetting.embeddable_hosts.blank?
raise Discourse::InvalidAccess.new('invalid referer host') unless SiteSetting.allows_embeddable_host?(request.referer)
raise Discourse::InvalidAccess.new('invalid referer host') unless EmbeddableHost.host_allowed?(request.referer)
end

response.headers['X-Frame-Options'] = "ALLOWALL"
Expand Down
24 changes: 24 additions & 0 deletions app/models/embeddable_host.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
class EmbeddableHost < ActiveRecord::Base
validates_format_of :host, :with => /\A[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(:[0-9]{1,5})?(\/.*)?\Z/i
belongs_to :category

before_validation do
self.host.sub!(/^https?:\/\//, '')

@cubic-dev-ai cubic-dev-ai Bot Feb 13, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Potential NoMethodError if host is nil. The before_validation callback calls self.host.sub! without a nil guard. If a record is saved without setting host, this will crash before validation can reject it.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/models/embeddable_host.rb, line 6:

<comment>Potential `NoMethodError` if `host` is nil. The `before_validation` callback calls `self.host.sub!` without a nil guard. If a record is saved without setting `host`, this will crash before validation can reject it.</comment>

<file context>
@@ -0,0 +1,24 @@
+  belongs_to :category
+
+  before_validation do
+    self.host.sub!(/^https?:\/\//, '')
+    self.host.sub!(/\/.*$/, '')
+  end
</file context>
Fix with Cubic

self.host.sub!(/\/.*$/, '')
end

def self.record_for_host(host)
uri = URI(host) rescue nil
return false unless uri.present?

host = uri.host
return false unless host.present?

where("lower(host) = ?", host).first

@cubic-dev-ai cubic-dev-ai Bot Feb 13, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Bug: case-insensitive lookup is broken because the query parameter is not downcased. lower(host) lowercases the column value but the bound parameter retains its original casing from uri.host, so mixed-case URLs will fail to match.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/models/embeddable_host.rb, line 17:

<comment>Bug: case-insensitive lookup is broken because the query parameter is not downcased. `lower(host)` lowercases the column value but the bound parameter retains its original casing from `uri.host`, so mixed-case URLs will fail to match.</comment>

<file context>
@@ -0,0 +1,24 @@
+    host = uri.host
+    return false unless host.present?
+
+    where("lower(host) = ?", host).first
+  end
+
</file context>
Fix with Cubic

end

def self.host_allowed?(host)
record_for_host(host).present?
end

end
14 changes: 0 additions & 14 deletions app/models/site_setting.rb
Original file line number Diff line number Diff line change
Expand Up @@ -68,20 +68,6 @@ def self.anonymous_menu_items
@anonymous_menu_items ||= Set.new Discourse.anonymous_filters.map(&:to_s)
end

def self.allows_embeddable_host?(host)
return false if embeddable_hosts.blank?
uri = URI(host) rescue nil
return false unless uri.present?

host = uri.host
return false unless host.present?

!!embeddable_hosts.split("\n").detect {|h| h.sub(/^https?\:\/\//, '') == host }

hosts = embeddable_hosts.split("\n").map {|h| (URI(h).host rescue nil) || h }
!!hosts.detect {|h| h == host}
end

def self.anonymous_homepage
top_menu_items.map { |item| item.name }
.select { |item| anonymous_menu_items.include?(item) }
Expand Down
2 changes: 1 addition & 1 deletion app/models/topic.rb
Original file line number Diff line number Diff line change
Expand Up @@ -866,7 +866,7 @@ def has_topic_embed?
end

def expandable_first_post?
SiteSetting.embeddable_hosts.present? && SiteSetting.embed_truncate? && has_topic_embed?
SiteSetting.embed_truncate? && has_topic_embed?
end

TIME_TO_FIRST_RESPONSE_SQL ||= <<-SQL
Expand Down
4 changes: 3 additions & 1 deletion app/models/topic_embed.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,14 @@ def self.import(user, url, title, contents)
# If there is no embed, create a topic, post and the embed.
if embed.blank?
Topic.transaction do
eh = EmbeddableHost.record_for_host(url)

creator = PostCreator.new(user,
title: title,
raw: absolutize_urls(url, contents),
skip_validations: true,
cook_method: Post.cook_methods[:raw_html],
category: SiteSetting.embed_category)
category: eh.try(:category_id))
post = creator.create
if post.present?
TopicEmbed.create!(topic_id: post.topic_id,
Expand Down
16 changes: 16 additions & 0 deletions app/serializers/embeddable_host_serializer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
class EmbeddableHostSerializer < ApplicationSerializer
attributes :id, :host, :category_id

def id
object.id
end

def host
object.host
end

def category_id
object.category_id
end
end

8 changes: 8 additions & 0 deletions app/serializers/embedding_serializer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
class EmbeddingSerializer < ApplicationSerializer
attributes :id
has_many :embeddable_hosts, serializer: EmbeddableHostSerializer, embed: :ids

def id
object.id
end
end
8 changes: 8 additions & 0 deletions config/locales/client.en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2491,6 +2491,14 @@ en:
image: "Image"
delete_confirm: "Are you sure you want to delete the :%{name}: emoji?"

embedding:
confirm_delete: "Are you sure you want to delete that host?"
title: "Embedding"
host: "Allowed Hosts"
edit: "edit"
category: "Post to Category"
add_host: "Add Host"

permalink:
title: "Permalinks"
url: "URL"
Expand Down
2 changes: 0 additions & 2 deletions config/locales/server.en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1164,13 +1164,11 @@ en:
autohighlight_all_code: "Force apply code highlighting to all preformatted code blocks even when they didn't explicitly specify the language."
highlighted_languages: "Included syntax highlighting rules. (Warning: including too many langauges may impact performance) see: https://highlightjs.org/static/demo/ for a demo"

embeddable_hosts: "Host(s) that can embed the comments from this Discourse forum. Hostname only, do not begin with http://"
feed_polling_enabled: "EMBEDDING ONLY: Whether to embed a RSS/ATOM feed as posts."
feed_polling_url: "EMBEDDING ONLY: URL of RSS/ATOM feed to embed."
embed_by_username: "Discourse username of the user who creates the embedded topics."
embed_username_key_from_feed: "Key to pull discourse username from feed."
embed_truncate: "Truncate the embedded posts."
embed_category: "Category of embedded topics."
embed_post_limit: "Maximum number of posts to embed."
embed_whitelist_selector: "CSS selector for elements that are allowed in embeds."
embed_blacklist_selector: "CSS selector for elements that are removed from embeds."
Expand Down
Loading