Discourse 코드베이스를 지속적으로 개선하기 위한 노력의 일환으로, 레거시 “위젯” 렌더링 시스템의 사용을 제거하고 Glimmer 컴포넌트로 대체하고 있습니다.
최근에 게시물 메뉴를 현대화했으며, 이제 glimmer_post_menu_mode 설정을 통해 Discourse에서 사용할 수 있습니다.
이 설정은 세 가지 값을 허용합니다:
disabled: 레거시 “위젯” 시스템 사용auto: 현재 플러그인과 테마의 호환성을 감지합니다. 호환되지 않는 항목이 있으면 레거시 시스템을 사용하고, 그렇지 않으면 새 메뉴를 사용합니다.enabled: 새 메뉴를 사용합니다. 호환되지 않는 플러그인이나 테마가 있는 경우 사이트가 손상될 수 있습니다.
공식 플러그인은 이미 새 메뉴와 호환되도록 업데이트되었지만, 여전히 새 메뉴와 호환되지 않는 서드파티 플러그인, 테마 또는 테마 컴포넌트가 있는 경우 업그레이드가 필요합니다.
브라우저 콘솔에 호환성 문제의 원인을 식별하는 경고가 출력됩니다.
롤아웃 타임라인
이는 변경될 수 있는 대략적인 추정치입니다
2024년 4분기:
코어 구현 완료
공식 플러그인 업데이트
Meta에서 활성화
glimmer_post_menu_mode기본값이auto로 설정; 콘솔 비권장(deprecation) 메시지 활성화
업그레이드 조언 게시
2025년 1분기:
서드파티 플러그인과 테마 업데이트 필요
비권장 메시지 시작, 남은 문제에 대해 관리자 경고 배너 트리거
새 게시물 메뉴 기본 활성화
2025년 2분기
4월 1일 - 기능 플래그 설정 및 레거시 코드 제거
나에게 어떤 의미가 되나요?
플러그인이나 테마에서 게시물 메뉴를 커스터마이징하기 위해 ‘위젯’ API를 사용하는 경우, 새 버전과 호환되도록 업데이트해야 합니다.
새 게시물 메뉴를 어떻게 시험해 볼 수 있나요?
최신 버전의 Discourse에서는 호환되지 않는 플러그인이나 테마가 없으면 새 게시물 메뉴가 활성화됩니다.
호환되지 않는 확장 기능이 설치된 경우, 관리자로 enabled로 설정을 변경하여 새 메뉴 사용을 강제할 수 있습니다. 설치된 커스터마이징에 따라 사이트가 손상될 수 있으므로 신중하게 사용하세요.
이 자동 시스템이 예상대로 작동하지 않는 드문 경우, 위의 설정을 사용하여 이 '자동 기능 플래그’를 일시적으로 오버라이드할 수 있습니다. 이 작업이 필요한 경우 이 주제에서 알려주세요.
플러그인과 테마를 업데이트해야 하나요?\n
아래와 같은 커스터마이징을 수행하는 플러그인이나 테마가 있는 경우 업데이트가 필요합니다:
-
다음 위젯에서
decorateWidget,changeWidgetSetting,reopenWidget또는attachWidgetAction을 사용하는 경우:post-menupost-user-tip-shimsmall-user-list
-
다음 API 메서드 중 하나를 사용하는 경우:
addPostMenuButtonremovePostMenuButtonreplacePostMenuButton
위의 커스터마이징 중 하나를 수행하는 확장 기능이 있는 경우, 주제 페이지에 액세스할 때 업그레이드가 필요한 플러그인 또는 컴포넌트를 식별하는 경고가 콘솔에 출력됩니다.
비권장 ID는:
discourse.post-menu-widget-overrides
인스턴스에서 여러 테마를 사용하는 경우, 경고는 활성 플러그인과 현재 사용 중인 테마 및 테마 컴포넌트에 대해서만 출력되므로 모든 테마를 확인하시기 바랍니다.
대체 방안은 무엇인가요?
게시물 메뉴를 커스터마이징하기 위한 새로운 API로 값 변환기 post-menu-buttons를 도입했습니다.
이 값 변환기는 버튼의 추가, 교체, 제거 또는 재배열을 허용하는 DAG 객체를 제공합니다. 또한 메뉴와 연관된 게시물, 표시되는 게시물의 상태, 항목의 더 쉬운 배치에 사용할 수 있는 버튼 키와 같은 컨텍스트 정보를 제공합니다.
DAG API는 .add 및 .replace와 같이 새 버튼 정의가 필요한 경우 Ember 컴포넌트를 수신하도록 기대합니다.
각 커스터마이징은 다르지만, 가장 일반적인 사용 사례에 대한 지침은 다음과 같습니다:
addPostMenuButton
변경 전:
withPluginApi("1.34.0", (api) => {
api.addPostMenuButton("solved", (attrs) => {
if (attrs.can_accept_answer) {
const isOp = currentUser?.id === attrs.topicCreatedById;
return {
action: "acceptAnswer",
icon: "far-check-square",
className: "unaccepted",
title: "solved.accept_answer",
label: isOp ? "solved.solution" : null,
position: attrs.topic_accepted_answer ? "second-last-hidden" : "first",
};
}
});
});
변경 후:
아래 예제는 Ember의 Template Tag Format (gjs)를 사용합니다.
// components/solved-accept-answer-button.gjs
import Component from "@glimmer/component";
import { action } from "@ember/object";
import { inject as service } from "@ember/service";
import DButton from "discourse/components/d-button";
import { ajax } from "discourse/lib/ajax";
import { popupAjaxError } from "discourse/lib/ajax-error";
export default class SolvedAcceptAnswerButton extends Component {
// 버튼이 즉시 표시될지, '더 보기' 버튼 뒤에 숨겨질지 나타냅니다
static hidden(args) {
return args.post.topic_accepted_answer;
}
...
<template>
<DButton
class="post-action-menu__solved-unaccepted unaccepted"
...attributes
@action={{this.acceptAnswer}}
@icon="far-check-square"
@label={{if this.showLabel "solved.solution"}}
@title="solved.accept_answer"
/>
</template>
}
// initializer.js
import SolvedAcceptAnswerButton from "../components/solved-accept-answer-button";
...
withPluginApi("1.34.0", (api) => {
api.registerValueTransformer(
"post-menu-buttons",
({
value: dag,
context: {
post,
firstButtonKey, // 첫 번째 버튼의 키
secondLastHiddenButtonKey, // 두 번째로 마지막 숨겨진 버튼의 키
lastHiddenButtonKey, // 마지막 숨겨진 버튼의 키
},
}) => {
dag.add(
"solved",
SolvedAcceptAnswerButton,
post.topic_accepted_answer
? {
before: lastHiddenButtonKey,
after: secondLastHiddenButtonKey,
}
: {
before: [
"assign", // assign 플러그인이 추가한 버튼
firstButtonKey,
],
}
);
}
);
});
버튼 스타일링
위의 예제와 같이 컴포넌트에
...attributes를 포함하는 것이 좋습니다.컴포넌트
DButton또는DMenu의 사용과 결합하면 보일러플레이트 클래스를 처리하여 버튼이 게시물 메뉴의 다른 버튼과 동일한 형식을 따르도록 보장합니다.추가적인 형식은 사용자 정의 클래스를 사용하여 지정할 수 있습니다.
replacePostMenuButton
- 변경 전:
withPluginApi("1.34.0", (api) => {
api.replacePostMenuButton("like", {
name: "discourse-reactions-actions",
buildAttrs: (widget) => {
return { post: widget.findAncestorModel() };
},
shouldRender: (widget) => {
const post = widget.findAncestorModel();
return post && !post.deleted_at;
},
});
});
- 변경 후:
import ReactionsActionButton from "../components/discourse-reactions-actions-button";
...
withPluginApi("1.34.0", (api) => {
api.registerValueTransformer(
"post-menu-buttons",
({ value: dag, context: { buttonKeys } }) => {
// ReactionsActionButton은 새로운 버튼 컴포넌트입니다
dag.replace(buttonKeys.LIKE, ReactionsActionButton);
}
);
});
removePostMenuButton
- 변경 전:
withPluginApi("1.34.0", (api) => {
api.removePostMenuButton('like', (attrs, state, siteSettings, settings, currentUser) => {
if (attrs.post_number === 1) {
return true;
}
});
});
- 변경 후:
withPluginApi("1.34.0", (api) => {
api.registerValueTransformer(
"post-menu-buttons",
({ value: dag, context: { post, buttonKeys } }) => {
if (post.post_number === 1) {
dag.delete(buttonKeys.LIKE);
}
}
);
});
다른 커스터마이징은 어떻게 하나요?
도입한 새 API를 사용하여 커스터마이징을 달성할 수 없는 경우, 토론을 위해 새로운 개발 주제를 생성하여 알려주세요.
저는 플러그인/테마 개발자입니다. 전환 기간 동안 구버전과 새 게시물 메뉴를 모두 지원하는 테마/플러그인을 어떻게 업데이트하나요?
플러그인에서 구버전과 새 버전의 게시물 메뉴를 모두 지원하기 위해 아래 패턴을 사용했습니다:
function customizePostMenu(api) {
const transformerRegistered = api.registerValueTransformer(
"post-menu-buttons",
({ value: dag, context }) => {
// 새 게시물 메뉴 커스터마이징
...
}
);
const silencedKey =
transformerRegistered && "discourse.post-menu-widget-overrides";
withSilencedDeprecations(silencedKey, () => customizeWidgetPostMenu(api));
}
function customizeWidgetPostMenu(api) {
// 여기 레거시 "위젯" 코드 커스터마이징
...
}
export default {
name: "my-plugin",
initialize(container) {
withPluginApi("1.34.0", customizePostMenu);
}
};
더 많은 예제
새 API 사용법에 대한 예제는 공식 플러그인을 확인해 보세요: