From 1e2fc4b05bb5ea640f85140310705fcdb6456797 Mon Sep 17 00:00:00 2001 From: Alteras1 <42795314+Alteras1@users.noreply.github.com.> Date: Sun, 25 May 2025 15:48:28 -0700 Subject: [PATCH] Add ASCII slug for topic function Uses AnyAscii for Ascii generation --- lib/discourse_modifications/topic_slug.rb | 22 +++++++ plugin.rb | 7 +++ .../topic_slug_spec.rb | 62 +++++++++++++++++++ 3 files changed, 91 insertions(+) create mode 100644 lib/discourse_modifications/topic_slug.rb create mode 100644 spec/lib/discourse_modifications/topic_slug_spec.rb diff --git a/lib/discourse_modifications/topic_slug.rb b/lib/discourse_modifications/topic_slug.rb new file mode 100644 index 0000000..47f5663 --- /dev/null +++ b/lib/discourse_modifications/topic_slug.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +require 'any_ascii' + +module ::DiscourseModifications + class TopicSlug + + # Converts a topic title into a slug, removing any emoji strings and normalizing the text. + # This is primarily adds the unicode normalization step to the existing slug generation process + # for ASCII encoding. + def self.slug_for_topic(topic, slug, title) + return slug unless (SiteSetting.slug_generation_method || :ascii).to_sym == :ascii + + string = title.gsub(/:([\w\-+]+(?::t\d)?):/, "") + string = AnyAscii.transliterate(string) + string = Slug.ascii_generator(string) + string = Slug.prettify_slug(string, max_length: Slug::MAX_LENGTH) + + string.blank? || Slug.slug_is_only_numbers?(string) ? "topic-#{topic.id}" : string + end + end +end \ No newline at end of file diff --git a/plugin.rb b/plugin.rb index f92d62c..38e051c 100644 --- a/plugin.rb +++ b/plugin.rb @@ -8,6 +8,8 @@ # url: TODO # required_version: 2.7.0 +gem 'any_ascii', '0.3.2' + enabled_site_setting :discourse_modifications_enabled module ::DiscourseModifications @@ -18,4 +20,9 @@ module ::DiscourseModifications after_initialize do # Code which should run after Rails has finished booting + + # Note: if this file gets too large, consider moving code into separate files in the lib directory + # and applying a Initializer pattern to load them. + + Topic.slug_computed_callbacks << ::DiscourseModifications::TopicSlug.method(:slug_for_topic) end diff --git a/spec/lib/discourse_modifications/topic_slug_spec.rb b/spec/lib/discourse_modifications/topic_slug_spec.rb new file mode 100644 index 0000000..ea5dcdd --- /dev/null +++ b/spec/lib/discourse_modifications/topic_slug_spec.rb @@ -0,0 +1,62 @@ +# frozen_string_literal: true + +RSpec.configure { |c| c.filter_run_when_matching :focus } + +RSpec.describe DiscourseModifications::TopicSlug do + let(:topic) { Fabricate(:topic, id: 42) } + + describe ".slug_for_topic" do + before do + # Default to ascii slug generation + allow(SiteSetting).to receive(:slug_generation_method).and_return("ascii") + end + + it "removes emoji codes from the title" do + title = "Hello :smile: World" + slug = "hello-world" + expect(described_class.slug_for_topic(topic, slug, title)).to eq("hello-world") + end + + it "unicode normalizes the title" do + title = "Café" + slug = "cafe" + expect(described_class.slug_for_topic(topic, slug, title)).to eq("cafe") + end + + it "returns the original slug if slug_generation_method is not ascii" do + allow(SiteSetting).to receive(:slug_generation_method).and_return("encoded") + expect(described_class.slug_for_topic(topic, "original-slug", "Some Title")).to eq("original-slug") + end + + it "returns a fallback slug if the result is blank" do + title = ":smile:" + slug = "" + expect(described_class.slug_for_topic(topic, slug, title)).to eq("topic-42") + end + + it "returns a fallback slug if the result is only numbers" do + title = "123456" + slug = "123456" + expect(described_class.slug_for_topic(topic, slug, title)).to eq("topic-42") + end + + it "truncates the slug to the max length" do + long_title = "a" * (Slug::MAX_LENGTH + 10) + slug = "a" * (Slug::MAX_LENGTH + 10) + result = described_class.slug_for_topic(topic, slug, long_title) + expect(result.length).to eq(Slug::MAX_LENGTH) + end + + it "handles titles with multiple emoji codes" do + title = "Hello :smile: World :rocket:" + slug = "hello-world" + expect(described_class.slug_for_topic(topic, slug, title)).to eq("hello-world") + end + + it "handles titles with emoji codes with t modifier" do + title = "Hello :wave:t2: World" + slug = "hello-world" + expect(described_class.slug_for_topic(topic, slug, title)).to eq("hello-world") + end + end +end \ No newline at end of file