(비권장) 테마 또는 플러그인에서 Discourse 템플릿 오버라이드

이상적으로는 Discourse를 테마/플러그인으로 커스텀할 때 CSS, JavaScript 플러그인 API, 또는 플러그인 아웃렛을 사용해야 합니다. 이 중 어느 것도 사용 사례에 맞지 않는다면, Discourse 코어에 PR을 열거나 Meta의 Development 토픽에서 대화를 시작해 주세요. 커스텀을 더 쉽게 만들기 위해 새로운 아웃렛/API를 추가하는 것에 대해 항상 논의하는 것을 환영합니다.

모든 다른 옵션을 다 시도해 보았음에도 불구하고 템플릿 오버라이드가 필요한 경우가 있습니다. 이 기법은 테마/플러그인에서 어떤 Ember 컴포넌트나 라우트의 전체 템플릿을 오버라이드할 수 있게 해줍니다.

:rotating_light: 이것은 Discourse를 커스텀하는 권장되지 않는 방법입니다. Discourse 코어의 일상적인 변경 사항은 결국 템플릿 오버라이드와 충돌할 것이며, 포럼 렌더링 시 치명적인 오류를 일으킬 수 있습니다.

이 접근 방식을 결정했다면, 회귀를 감지하기 위한 충분한 자동화 테스트와 QA 프로세스를 갖추고 있는지 확인하세요. 템플릿 오버라이드가 포함된 테마/플러그인을 배포하는 경우, 포럼 관리자가 해당 테마/플러그인이 내포하는 안정성 위험을 인지하고 있는지 반드시 확인해야 합니다.

:rotating_light: :rotating_light: :rotating_light: 2023년 10월 업데이트: 새로운 기능에 대해 Discourse는 점점 더 Ember의 .gjs 파일 형식으로 작성된 컴포넌트 사용으로 이동하고 있습니다. 이러한 컴포넌트의 템플릿은 인라인으로 정의되며, 테마/플러그인에 의해 오버라이드될 수 없습니다.

앞으로 모든 템플릿 커스텀은 플러그인 아웃렛을 사용하여 수행해야 합니다.

가까운 미래에 이것이 깨질 것이라는 것을 알고 있지만, 그래도 문서를 보여주세요

컴포넌트 템플릿 오버라이드

Ember 컴포넌트 템플릿(즉, Discourse 코어의 components/* 아래에 있는 모든 것)를 오버라이드하려면, 테마/플러그인에서 동일한 이름을 가진 .hbs 파일을 생성해야 합니다. 예를 들어, Discourse 코어의 badge-button 컴포넌트 템플릿을 오버라이드하려면, 테마/플러그인의 다음 위치에 템플릿 파일을 생성해야 합니다:

:art: {theme}/javascripts/discourse/templates/components/badge-button.hbs

:electric_plug: {plugin}/assets/javascripts/discourse/templates/components/badge-button.hbs

오버라이드는 항상 /templates 디렉터리 안에 중첩되어야 하며, 코어 컴포넌트가 ‘동반(colocated)’ 템플릿을 가지고 있더라도 마찬가지입니다.

라우트 템플릿 오버라이드

라우트 템플릿 오버라이드(즉, templates/* 아래에 있는 모든 비-컴포넌트 템플릿)는 컴포넌트와 동일한 방식으로 작동합니다. 테마/플러그인에서 동일한 이름을 가진 템플릿을 생성합니다. 예를 들어, 코어의 discovery.hbs를 오버라이드하려면 다음과 같은 파일을 생성해야 합니다.

:art: {theme}/javascripts/discourse/templates/discovery.hbs

:electric_plug: {plugin}/assets/javascripts/discourse/templates/discovery.hbs

여러 테마/플러그인 간의 상호작용

여러 설치된 테마/플러그인이 동일한 템플릿을 오버라이드하는 경우, '승자’는 다음 목록에서 가장 낮은 순위 번호를 가진 것입니다:

  1. 테마 오버라이드 (가장 높은 테마 'id’가 승리)
  2. 플러그인 오버라이드 (가장 최신 알파벳 순 플러그인 이름이 승리)
  3. 코어

이 우선 순위는 또한 테마에서 플러그인 템플릿을 오버라이드할 수 있음을 의미합니다. 기술적으로는 다른 테마에서 테마 템플릿을, 다른 플러그인에서 플러그인 템플릿을 오버라이드할 수도 있지만, 플러그인 이름과 테마 id에 대한 의존성 때문에 동작이 의도치 않을 수 있습니다.

이것이 어떻게 작동하나요?

Discourse는 DiscourseTemplateMap 클래스에서 템플릿을 조립하고 우선순위를 부여합니다. 동반(colocated) 컴포넌트 템플릿의 경우, 해당 정보는 앱 초기화 동안 코어 템플릿 연관성을 대체하는 데 사용됩니다. 모든 다른 템플릿의 경우, 이 맵은 런타임의 리졸버에 의해 올바른 템플릿을 가져오는 데 사용됩니다.


이 문서는 버전 관리됩니다 - github에서 변경 사항을 제안하세요.

17개의 좋아요

And what about mobile templates? What is the directory structure to rewrite templates from core

It should work exactly the same - you match the name of the core template. So if it has /mobile, include that in your override.

I try to rewrite mobile login.hbs template and it doesn’t work Imgur: The magic of the Internet, am I right with the path?

The full path isn’t visible in your screenshot as far as I can see. Please can you paste it here as text.

themeroot/javascripts/mobile/modal/login.hbs

You’re missing discourse/templates from your path

So in your case, it would be {theme}/javascripts/discourse/templates/mobile/modal/login.hbs

2개의 좋아요

Is this still the case?

I’m a bit sad the ability to override a lot of code is being removed.

It makes sense to replace the bespoke Widget system, to some extent, but that gave us the ability to hook into existing code at multiple levels, reducing a lot of breaking change risk as we could target just small blocks in clever ways that would allow us to:

  • add features
  • not disturb anything else.

I’ve just had to remove TWO significant features from Discourse Journal, for example, that were based on fine grain overrides to widgets because the only way to have recreated them in Glimmer is via a pair of Template overrides (including an attempt to change a .gjs file) which is apparently no longer supported.

Even if this was supported, we would be left with overriding bigger stretches of code than under the widget framework, with an associated increase in risk of core changes conflicting with the overrides.

This isn’t healthy for the extensibility of the platform.

Can anything be done about it?

7개의 좋아요

Yeah I hear you - there were some nice things about widget extensibility APIs.

But the flip side is that it’s been incredibly difficult for us to modify ANY of the widget-based UI in core, because we have no idea what random methods/decorations people might be introducing. That’s why widget customisations have seemed relatively stable - we’ve been too scared to touch the core implementations.

Our solution for this going forward is Wrapper Plugin Outlets. These allow themes and plugins to optionally override very small chunks of templates with their own implementation.

For example, see how Chat conditionally overrides the home-logo with a custom component. That works for the existing widget-based header, and the new glimmer-based header (coming soon! :tm:)

We’re generally happy to accept PRs to add new wrapper outlets in various places. If you’re unsure about a particular use-case, please feel free to open a Development topic with details!

10개의 좋아요

OK that’s sounds like a way forward, thank you.

I’ll need to digest the implications of that and adjust to a strategy along those lines.

Appreciate the response!

6개의 좋아요