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(() => {
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({});
},

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

deleteHost(host) {
this.get('embedding.embeddable_hosts').removeObject(host);
}
Comment on lines +14 to +16

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider destroying the host record instead of just removing from collection.

The deleteHost action only removes the host from the local collection but doesn't destroy the record on the server. This could lead to data inconsistency.

    deleteHost(host) {
-      this.get('embedding.embeddable_hosts').removeObject(host);
+      host.destroy().then(() => {
+        this.get('embedding.embeddable_hosts').removeObject(host);
+      }).catch(error => {
+        // Handle error appropriately
+        console.error('Failed to delete host:', error);
+      });
    }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In app/assets/javascripts/admin/controllers/admin-embedding.js.es6 around lines
14 to 16, the deleteHost method currently only removes the host from the local
embeddable_hosts collection without destroying the record on the server. To fix
this, update the deleteHost method to call the destroyRecord() method on the
host object, which will delete the record on the server and then remove it from
the collection to maintain data consistency.

}
});
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/"; }
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) {
return self._lookupSubType(subType, type, id, root);
});
obj[self.pluralize(subType)] = hydrated || [];
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
save_host(host)
end

def destroy
host = EmbeddableHost.where(id: params[:id]).first
host.destroy
render json: success_json
end
Comment on lines +9 to +18

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add error handling for missing records.

The update and destroy actions don't handle cases where the embeddable host record doesn't exist. Using .first returns nil for missing records, which could lead to NoMethodError when calling methods on nil.

Apply this diff to add proper error handling:

  def update
    host = EmbeddableHost.where(id: params[:id]).first
+   return render_json_error("Not found", status: 404) unless host
    save_host(host)
  end

  def destroy
    host = EmbeddableHost.where(id: params[:id]).first
+   return render_json_error("Not found", status: 404) unless host
    host.destroy
    render json: success_json
  end

Alternatively, use .find instead of .where(...).first to automatically raise ActiveRecord::RecordNotFound:

  def update
-   host = EmbeddableHost.where(id: params[:id]).first
+   host = EmbeddableHost.find(params[:id])
    save_host(host)
  end

  def destroy
-   host = EmbeddableHost.where(id: params[:id]).first
+   host = EmbeddableHost.find(params[:id])
    host.destroy
    render json: success_json
  end
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def update
host = EmbeddableHost.where(id: params[:id]).first
save_host(host)
end
def destroy
host = EmbeddableHost.where(id: params[:id]).first
host.destroy
render json: success_json
end
def update
host = EmbeddableHost.where(id: params[:id]).first
return render_json_error("Not found", status: 404) unless host
save_host(host)
end
def destroy
host = EmbeddableHost.where(id: params[:id]).first
return render_json_error("Not found", status: 404) unless host
host.destroy
render json: success_json
end
🤖 Prompt for AI Agents
In app/controllers/admin/embeddable_hosts_controller.rb around lines 9 to 18,
the update and destroy actions fetch the embeddable host using
`.where(...).first`, which returns nil if the record is missing, causing
potential NoMethodError when calling methods on nil. Replace `.where(id:
params[:id]).first` with `.find(params[:id])` to let ActiveRecord automatically
raise RecordNotFound exceptions for missing records, or add explicit nil checks
and handle the error by rendering an appropriate error response before
proceeding.


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
Comment on lines +22 to +32

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider using strong parameters for better security.

The save_host method handles success/error responses appropriately, but directly accessing params[:embeddable_host][:host] without strong parameters could be a security risk.

Apply this diff to use strong parameters:

  protected

    def save_host(host)
-     host.host = params[:embeddable_host][:host]
-     host.category_id = params[:embeddable_host][:category_id]
+     permitted_params = params.require(:embeddable_host).permit(:host, :category_id)
+     host.host = permitted_params[:host]
+     host.category_id = permitted_params[: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

Or more concisely using assign_attributes:

    def save_host(host)
+     permitted_params = params.require(:embeddable_host).permit(:host, :category_id)
-     host.host = params[:embeddable_host][:host]
-     host.category_id = params[:embeddable_host][:category_id]
+     host.assign_attributes(permitted_params)
      host.category_id = SiteSetting.uncategorized_category_id if host.category_id.blank?
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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
def save_host(host)
permitted_params = params.require(:embeddable_host).permit(:host, :category_id)
host.host = permitted_params[:host]
host.category_id = permitted_params[: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
🤖 Prompt for AI Agents
In app/controllers/admin/embeddable_hosts_controller.rb around lines 22 to 32,
the save_host method directly accesses params without using strong parameters,
which is a security risk. Define a private method to whitelist the allowed
parameters from params[:embeddable_host], then use host.assign_attributes with
these strong parameters instead of directly assigning each attribute. This will
ensure only permitted attributes are assigned and improve security.


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

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

def update
render_serialized(@embedding, EmbeddingSerializer, root: 'embedding', rest_serializer: true)
end
Comment on lines +9 to +11

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Update action doesn't perform any updates.

The update action renders the same data as show without performing any actual updates. This seems incorrect for a PUT endpoint.

Consider implementing actual update logic or removing this action if updates aren't needed:

  def update
+    # Add update logic here if needed, e.g.:
+    # @embedding.some_property = params[:some_param]
+    # @embedding.save
    render_serialized(@embedding, EmbeddingSerializer, root: 'embedding', rest_serializer: true)
  end

Or if no updates are needed, consider removing the update route and action entirely.

🤖 Prompt for AI Agents
In app/controllers/admin/embedding_controller.rb around lines 9 to 11, the
update action currently does not perform any update operation and just renders
the existing embedding data. To fix this, implement the update logic by applying
permitted parameters to update the @embedding object and handle success or
failure responses accordingly. If updating embeddings is not required, remove
the update action and its corresponding route to avoid confusion and incorrect
API behavior.


protected

def fetch_embedding
@embedding = OpenStruct.new({
id: 'default',
embeddable_hosts: EmbeddableHost.all.order(:host)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Database query lacks error handling.

The EmbeddableHost.all.order(:host) query could potentially fail or return unexpected results.

Consider adding error handling and possibly scoping:

-      embeddable_hosts: EmbeddableHost.all.order(:host)
+      embeddable_hosts: EmbeddableHost.all.order(:host) rescue []

Or better yet, move this to a service method with proper error handling.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
embeddable_hosts: EmbeddableHost.all.order(:host)
embeddable_hosts: EmbeddableHost.all.order(:host) rescue []
🤖 Prompt for AI Agents
In app/controllers/admin/embedding_controller.rb at line 18, the database query
EmbeddableHost.all.order(:host) lacks error handling which could cause unhandled
exceptions or unexpected results. Refactor this by moving the query into a
dedicated service method that wraps the call in error handling logic, such as
rescuing potential exceptions and returning a safe fallback or logging the
error. Then call this service method from the controller instead of directly
querying the model.

})
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?:\/\//, '')
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
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))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Verify fallback behavior when no embeddable host is found.

The safe navigation eh.try(:category_id) will return nil when no embeddable host record exists. Ensure that PostCreator handles nil category appropriately, or consider providing a fallback to a default category.


🏁 Script executed:

#!/bin/bash
# Description: Check how PostCreator handles nil category parameter
ast-grep --pattern 'class PostCreator
  $$$
  def initialize($_, $options)
    $$$
  end
  $$$'

Length of output: 97


🏁 Script executed:

#!/bin/bash
# Locate the PostCreator class and inspect its initialize method
rg -n "class PostCreator" -A30

Length of output: 2423


🏁 Script executed:

#!/bin/bash
# Extract initialize method and inspect category handling in PostCreator
rg -n "def initialize" -A20 lib/post_creator.rb
rg -n ":category" -A5 -B5 lib/post_creator.rb

Length of output: 782


🏁 Script executed:

#!/bin/bash
# Inspect PostCreator#create to see how category is handled
rg -n "def create" -A30 lib/post_creator.rb
rg -n "opts\\[:category" -A5 lib/post_creator.rb

Length of output: 3211


🏁 Script executed:

#!/bin/bash
# Inspect the TopicEmbed class and the use of eh.try(:category_id)
rg -n "class TopicEmbed" -A200 app/models/topic_embed.rb
rg -n "eh.try" -A20 app/models/topic_embed.rb

Length of output: 6907


🏁 Script executed:

#!/bin/bash
# Locate and inspect the TopicCreator class to see how :category is handled
rg -l "class TopicCreator" -g "*.rb"
rg -n "class TopicCreator" -A200
rg -n "opts\[:category" -A5

Length of output: 13259


Ensure a valid category fallback for embedded topics

Currently in app/models/topic_embed.rb:43 we pass category: eh.try(:category_id). When eh is nil, this yields nil and TopicCreator.find_category ends up returning nil, so no category_id is set on the new Topic (it silently falls back to the DB default). Unless that default is guaranteed to be your intended “uncategorized” category, you should explicitly provide one.

• In app/models/topic_embed.rb (around line 43), update:

- creator = PostCreator.new(user,
-                           title: title,
-                           raw: absolutize_urls(url, contents),
-                           skip_validations: true,
-                           cook_method: Post.cook_methods[:raw_html],
-                           category: eh.try(:category_id))
+ creator = PostCreator.new(user,
+                           title: title,
+                           raw: absolutize_urls(url, contents),
+                           skip_validations: true,
+                           cook_method: Post.cook_methods[:raw_html],
+                           category: eh.try(:category_id) || SiteSetting.default_category)

• Or, in lib/topic_creator.rb (inside find_category), after determining category, add a fallback:

def find_category
  # existing logic…
  category = if @opts[:category].is_a?(Integer) || @opts[:category] =~ /^\d+$/
               Category.find_by(id: @opts[:category])
             else
               Category.find_by(name_lower: @opts[:category].try(:downcase))
             end

  category || Category.find_by(id: SiteSetting.default_category)
end

This ensures embedded topics always land in a valid category.

🤖 Prompt for AI Agents
In app/models/topic_embed.rb at line 43, the code passes category:
eh.try(:category_id), which can be nil if eh is nil, causing
TopicCreator.find_category to return nil and no category_id to be set on the new
Topic. To fix this, either update the call in topic_embed.rb to provide a valid
fallback category id when eh is nil, or modify the find_category method in
lib/topic_creator.rb to return a fallback category by adding a line that returns
Category.find_by(id: SiteSetting.default_category) if the initially found
category is nil, ensuring embedded topics always have a valid category.

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