Skip to content

DEV: Refactor themes controller to services part 1 #29631

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed

Conversation

martin-brennan
Copy link
Contributor

@martin-brennan martin-brennan commented Nov 6, 2024

This commit starts to introduce services to replace actions in the
ThemesController. We will start with the low langing fruit:

  • Create
  • Destroy
  • BulkDestroy
  • GetTranslations

Endpoints like #import and #update will be much harder.
Also, the https://github.com/discourse/discourse-theme-creator plugin
overrides some of these controller actions directly, so we need
to be careful.

Copy link
Contributor

@Flink Flink left a comment

Choose a reason for hiding this comment

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

I know this is still a draft, but I left some comments, some are just me thinking out loud, I’ll be glad to get your feedback 😁

private

def ban_in_allowlist_mode
Theme.allowed_remote_theme_ids.nil?
Copy link
Contributor

Choose a reason for hiding this comment

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

This can’t be an empty array? It looks like it should return an array 😅 We could maybe use #blank? instead of #nil? just to be sure?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's weird, sometimes it's nil and sometimes it's an empty array...but yes I think blank could work here

def self.allowed_remote_theme_ids
return nil if GlobalSetting.allowed_theme_repos.blank?
get_set_cache "allowed_remote_theme_ids" do
urls = GlobalSetting.allowed_theme_repos.split(",").map(&:strip)
Theme.joins(:remote_theme).where("remote_themes.remote_url in (?)", urls).pluck(:id)
end
end

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Annoyingly this return nil seems to be important, if Theme.joins(:remote_theme).where("remote_themes.remote_url in (?)", urls).pluck(:id) returns an [] we don't care for the purposes of other existing code. Not sure if that's intentional or not, don't want to touch it for now

@Flink
Copy link
Contributor

Flink commented Nov 19, 2024

@martin-brennan ok, so now that #29660 has been merged, we can do this (just tested locally, did not a lot of tests though, but it’s working):

  transaction do
    try(Theme::InvalidFieldTargetError, Theme::InvalidFieldTypeError) do
      model :theme, :create_theme
    end
    step :update_default_theme
    step :log_theme_change
  end

  def create_theme(params:)
    Theme.create(params.slice(:name, :user_id, :user_selectable, :color_scheme_id, :component)) do |theme|
      params.theme_fields.to_a.each { |field| theme.set_field(**field.symbolize_keys) }
    end
  end

Instead of having 3 steps with their own error logic/handling, we just use one and everything’s covered 😁

In the controller, I see that everything’s matched through on_failure, but if we want more granular control, we can use on_model_errors (meaning the Theme model has errors on it, its creation failed for whatever reason) and on_exceptions (meaning #set_field failed at some points. I don’t think the model will be available at that point, though).

@martin-brennan
Copy link
Contributor Author

Nice that's great @Flink thanks for letting me know, will do some further refactors with this now 👍 Also I need to go and make service specs for all the services I've added 😅 The functionality is covered by controller specs but I still want the low level ones too

@Flink
Copy link
Contributor

Flink commented Nov 21, 2024

Great! Let me know if you need a hand or when you want another review 😁

Copy link

This pull request has been automatically marked as stale because it has been open for 60 days with no activity. To keep it open, remove the stale tag, push code, or add a comment. Otherwise, it will be closed in 14 days.

@github-actions github-actions bot added the Stale label Jan 21, 2025
@github-actions github-actions bot closed this Feb 4, 2025
@martin-brennan martin-brennan reopened this Feb 4, 2025
@github-actions github-actions bot removed the Stale label Feb 5, 2025
Copy link

github-actions bot commented Apr 6, 2025

This pull request has been automatically marked as stale because it has been open for 60 days with no activity. To keep it open, remove the stale tag, push code, or add a comment. Otherwise, it will be closed in 14 days.

@github-actions github-actions bot added the Stale label Apr 6, 2025
@github-actions github-actions bot closed this Apr 20, 2025
@martin-brennan martin-brennan marked this pull request as ready for review April 22, 2025 04:20
@martin-brennan
Copy link
Contributor Author

@Flink okay it's been a few months 😅 But this is finally done now I think, can you please re-review?

@martin-brennan martin-brennan requested a review from Flink April 22, 2025 04:36
@martin-brennan
Copy link
Contributor Author

@Flink another bump here, can you please take a look?

@Flink
Copy link
Contributor

Flink commented May 9, 2025

@Flink another bump here, can you please take a look?

Yes sorry, completely forgot, as you're on vacation I thought it wasn't that urgent 😅
Will review first thing on Monday 🙂

@martin-brennan
Copy link
Contributor Author

@Flink it's okay, not super urgent, as long as it's in your queue somewhere, I just couldn't tell whether you got the first ping, I thought you might have been on vacation when I did it :)

@Flink
Copy link
Contributor

Flink commented May 9, 2025

@Flink it's okay, not super urgent, as long as it's in your queue somewhere, I just couldn't tell whether you got the first ping, I thought you might have been on vacation when I did it :)

No worries, it's definitely in my queue 😉

Copy link
Contributor

@Flink Flink left a comment

Choose a reason for hiding this comment

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

Great to see some more services 👍 😁

Nothing too bad in my comments, mainly suggestions/improvements.

policy :ensure_remote_themes_are_not_allowlisted

transaction do
try(Theme::InvalidFieldTargetError, Theme::InvalidFieldTypeError) do
Copy link
Contributor

Choose a reason for hiding this comment

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

Following a recent change to the model step (you’ll need to rebase this PR against main, though), we don’t need this try block anymore. If an exception is raised inside a model step, the step will fail and can be matched with the on_model_not_found outcome.
In the controller, it seems we weren’t catching this exception, but I guess we should probably match the on_model_not_found outcome.

end

it "clears the existing default theme" do
existing_default = Fabricate(:theme)
Copy link
Contributor

Choose a reason for hiding this comment

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

We could extract this in a before block.

it "clears the existing default theme" do
existing_default = Fabricate(:theme)
existing_default.set_default!
expect(existing_default.default?).to eq(true)
Copy link
Contributor

Choose a reason for hiding this comment

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

I don’t think this expectation is really useful, #set_default! is tested elsewhere and should be working, so that’s not the responsibility of this spec to ensure that.


expect(result).to be_a_success
expect(result.theme.default?).to eq(true)
expect(existing_default.reload.default?).to eq(false)
Copy link
Contributor

@Flink Flink May 12, 2025

Choose a reason for hiding this comment

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

Maybe we could probably check directly the change happens as expected:

expect { result }.to change { existing_default.reload.default? }.to false

Then we could verify that the new theme is indeed marked as being the default one.

awesomerobot and others added 7 commits June 10, 2025 11:14
As seen in the advanced search filters... iOS doesn't show placeholders
for the date input, so we have a CSS adjustment for this, but it doesn't
clear when a value is present.

This update adds a class when a value is present so the placeholder can
be cleared.

Before:

![image](https://github.com/user-attachments/assets/37edad10-886b-41d7-b412-b303be351603)


After:

![image](https://github.com/user-attachments/assets/fee713ab-09f9-49fb-9fda-96bd72a260e1)


Also fixed extra left margin by removing a class.
These lines of code would output "Period - Site name" as the page title
for the crawlier view only for Top routes. This is too specific and
unnecessary, since the top route isn't highly prioritized for crawlers.
Without this, the page title defaults to "Site name" for these routes.

One major advantage of this change is that when the homepage is set to
the Top route, the page title for crawlers on the homepage is now "Site
name", which is a lot better.
This might be useful to revert changes made by plugins when they're
disabled for example.
)

Previously it was relying on the default Modal behavior, which is to
close **after** the next route transition.
Bumps [zeitwerk](https://github.com/fxn/zeitwerk) from 2.7.2 to 2.7.3.
- [Changelog](https://github.com/fxn/zeitwerk/blob/main/CHANGELOG.md)
- [Commits](fxn/zeitwerk@v2.7.2...v2.7.3)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [rails-dom-testing](https://github.com/rails/rails-dom-testing)
from 2.2.0 to 2.3.0.
- [Release notes](https://github.com/rails/rails-dom-testing/releases)
-
[Commits](rails/rails-dom-testing@v2.2.0...v2.3.0)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
dependabot bot and others added 24 commits June 10, 2025 11:15
…l group (#33089)

Bumps the babel group with 1 update:
[@babel/standalone](https://github.com/babel/babel/tree/HEAD/packages/babel-standalone).


Updates `@babel/standalone` from 7.27.5 to 7.27.6
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
-
[Commits](https://github.com/babel/babel/commits/v7.27.6/packages/babel-standalone)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [terser](https://github.com/terser/terser) from 5.40.0 to 5.41.0.
- [Changelog](https://github.com/terser/terser/blob/master/CHANGELOG.md)
- [Commits](terser/terser@v5.40.0...v5.41.0)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [@swc/core](https://github.com/swc-project/swc) from 1.11.29 to
1.11.31.
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
-
[Commits](swc-project/swc@v1.11.29...v1.11.31)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…33098)

This brings the search_icon header search mode to parity with the
search_field mode. We don't want to show either of these if the welcome
banner is showing, since it has a search field, this is redundant.

If you scroll the page and the welcome banner is hidden, then we
show the header search icon.

This commit also cleans up some code related to the page-search
shortcut, which we no longer use, including limiting showing search
on topic only if there are > 20 posts.
Bumps [rubocop-ast](https://github.com/rubocop/rubocop-ast) from 1.45.0
to 1.45.1.
- [Release notes](https://github.com/rubocop/rubocop-ast/releases)
-
[Changelog](https://github.com/rubocop/rubocop-ast/blob/master/CHANGELOG.md)
-
[Commits](rubocop/rubocop-ast@v1.45.0...v1.45.1)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [rbtrace](https://github.com/tmm1/rbtrace) from 0.5.1 to 0.5.2.
- [Changelog](https://github.com/tmm1/rbtrace/blob/master/CHANGELOG)
- [Commits](https://github.com/tmm1/rbtrace/commits)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…and work around long-url parsing issue.
Previously we would check the request for a matching CDN hostname before
applying the `Access-Control-Allow-Origin` header. That logic requires
the CDN to include its public-facing hostname in the `Host` header,
which is not always the case.

Since we are only running this `apply_cdn_headers` before_action on
publicly-accessible asset routes, we can simplify things so that the
`Access-Control-Allow-Origin: *` header is always included. That will
make CDN config requirements much more relaxed.

At the moment, this is primarily relevant to the HighlightJsController
routes, which are loaded using native JS `type=module`. But in the near
future, we plan to expand our use of `type=module` to more critical JS
assets like translations and themes.

Also drops the `Access-Control-Allow-Methods` header from these
responses. That isn't needed for `GET` and `HEAD` requests.
For empty chats, a dummy message is created with `id=null` (an
unsaved Ember model). 

The timer or placeholder's display is determined by the message ID's presence now (not the `createdAt` prop).
We will be moving towards `type="module"` for all of Discourse's JS
bundles in the near future. This commit makes a start by applying the
change to translation bundles.
We will be moving towards `type="module"` for all of Discourse's JS
bundles in the near future. This commit makes a start by applying the
change to translation bundles.

This was previously merged, but the lack of `apply_cdn_headers` on the
locales controller led to CORS errors on sites with CDNs.
The moment locale files expect `this.` to be the window object. In a
type=module, `this` is `undefined`. This commit wraps the
moment-timezone definitions in an IIFE to resolve that.

Also adds a system spec to prevent future moment-timezone regressions.

Followup to a2b0c19
…33131)

Non-colocated `.hbs` files in themes and plugins should be treated as
classic components, not glimmer components.

This regressed in cb6cc8d

Also adds Ember's `component-template-resolving` deprecation to our list
of deprecations which trigger admin warnings.
Removing some bottom spacing + smaller font-size for mobile
Previously on iOS the chat would explicitly not scroll down to the bottom when a new message was added.
)

Currently, the first two rows returned by `DiscourseDB#query_array` are
silently dropped during the column size check in
`DiscourseDB#load_mapping`. This happens because the rows object, while
an enumerator, isn't fully compliant, it doesn't rewind during
introspection. As a result, calls like `#first`, `#peek`, or `#any?`
advance the iterator.

Ideally, we’d fix this by updating the `query_array` enumeration
implementation. However, customizing the enumerator to be fully
compliant would likely introduce unnecessary perf overhead for all use
cases. So, this fix works around that limitation by building the map a
little differently.
)

Introduced a map to manage scheduled/debounced timers, ensuring they are
canceled when the component is destroyed. This improves memory
management and prevents potential issues with lingering timers after
destruction. Updated relevant methods to utilize this new tracking
mechanism.
@martin-brennan martin-brennan requested a review from a team as a code owner June 10, 2025 01:17
@github-actions github-actions bot added chat PRs which include a change to Chat plugin i18n PRs which update English locale files or i18n related code migrations-tooling PR which includes changes to migrations tooling labels Jun 10, 2025
@martin-brennan
Copy link
Contributor Author

@Flink I absolutely boned the merge/rebase here, continuing in #33141 . Thanks for the review comments, will close this PR and address over there.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
chat PRs which include a change to Chat plugin i18n PRs which update English locale files or i18n related code migrations-tooling PR which includes changes to migrations tooling
Development

Successfully merging this pull request may close these issues.