Skip to content
Draft
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
3 changes: 3 additions & 0 deletions confluence-mdx/bin/reverse_sync/list_patcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,13 @@ def _regenerate_list_from_parent(
'old_plain_text': parent.xhtml_plain_text,
'new_inner_xhtml': new_inner,
}

# <ol start="N"> 속성 변경 감지: 시작 번호가 달라지면 ol_start 포함
old_start = _get_ordered_list_start(change.old_block.content)
new_start = _get_ordered_list_start(change.new_block.content)
if old_start is not None and new_start is not None and old_start != new_start:
patch['ol_start'] = new_start

return [patch]


Expand Down
12 changes: 12 additions & 0 deletions confluence-mdx/bin/reverse_sync/patch_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,18 @@ def _mark_used(block_id: str, m: BlockMapping):
# strategy == 'direct'
_mark_used(mapping.block_id, mapping)

# 헤딩의 경우: 레벨과 텍스트가 동일하면 XHTML을 변경하지 않음
# (### foo → ### foo 같은 prefix 공백 차이만 있는 변경은 XHTML에 전파하지 않음)
if change.old_block.type == 'heading':
old_content = change.old_block.content.strip()
new_content = change.new_block.content.strip()
old_level = len(old_content) - len(old_content.lstrip('#'))
new_level = len(new_content) - len(new_content.lstrip('#'))
old_text = old_content.lstrip('#').strip()
new_text = new_content.lstrip('#').strip()
if old_level == new_level and old_text == new_text:
continue

# 멱등성 체크: push 후 XHTML이 이미 업데이트된 경우 건너뜀
# (old != xhtml 이고 new == xhtml → 이미 적용된 변경)
if (collapse_ws(old_plain) != collapse_ws(mapping.xhtml_plain_text)
Expand Down
9 changes: 9 additions & 0 deletions confluence-mdx/bin/reverse_sync/roundtrip_verifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,14 @@ def _normalize_trailing_blank_lines(text: str) -> str:
return stripped + '\n' if stripped else text


def _normalize_blank_line_after_blockquote(text: str) -> str:
"""blockquote(>) 줄 바로 다음의 단일 빈 줄을 제거한다.

Forward converter가 blockquote 다음에 빈 줄을 추가하는 경우가 있으므로,
비교 시 제거한다.
"""
return re.sub(r'^(>[ \t]*.+)\n\n', r'\1\n', text, flags=re.MULTILINE)


def _apply_minimal_normalizations(text: str) -> str:
"""항상 적용하는 최소 정규화 (strict/lenient 모드 공통).
Expand All @@ -80,6 +88,7 @@ def _apply_minimal_normalizations(text: str) -> str:
text = _normalize_table_cell_padding(text)
text = _strip_first_heading(text)
text = text.lstrip('\n')
text = _normalize_blank_line_after_blockquote(text)
text = _normalize_trailing_blank_lines(text)
return text

Expand Down
2 changes: 2 additions & 0 deletions confluence-mdx/bin/reverse_sync/xhtml_patcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ def patch_xhtml(xhtml: str, patches: List[Dict[str, str]]) -> str:
if current_plain_with_emoticons.strip() != old_text.strip():
continue
_replace_inner_html(element, patch['new_inner_xhtml'])
# <ol start="N"> 속성 변경 적용
if 'ol_start' in patch and isinstance(element, Tag) and element.name == 'ol':
new_start = patch['ol_start']
if new_start == 1:
Expand All @@ -90,6 +91,7 @@ def patch_xhtml(xhtml: str, patches: List[Dict[str, str]]) -> str:
if current_plain_with_emoticons.strip() != old_text.strip():
continue
_apply_text_changes(element, old_text, new_text)
# <ol start="N"> 속성 변경 적용 (텍스트 전이 경로)
if 'ol_start' in patch and isinstance(element, Tag) and element.name == 'ol':
new_start = patch['ol_start']
if new_start == 1:
Expand Down
5 changes: 5 additions & 0 deletions confluence-mdx/bin/text_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,13 @@ def normalize_mdx_to_plain(content: str, block_type: str) -> str:
lambda m: m.group(2) + m.group(1).capitalize(),
s,
)
s_before_html = s
s = re.sub(r'<[^>]+/?>', '', s)
s = html_module.unescape(s)
# HTML 태그 제거로 인해 생긴 trailing space 아티팩트 제거
# (링크 텍스트의 genuine trailing space는 보존)
if s.endswith(' ') and not s_before_html.endswith(' '):
s = s.rstrip()
if s.strip():
parts.append(s)
return '\n'.join(parts)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ $

1. `docker-compose.yml` , `.env` 등 Compose 를 위한 설정파일을 내려 받기
* 기존의 `compose-env` 라는 환경변수 설정 파일의 이름이 `.env` 로 바뀌었습니다. `compose-env` 는 설정파일 생성을 위한 template 으로 사용됩니다.
2. `.env` 에 환경변수 값을 설정하기
2. `.env` 에 환경변수 값을 설정하기
* 각 환경변수에 대한 설명은 [컨테이너 환경변수](../container-environment-variables) 문서를 참고해주세요.
3. docker image 내려받기
* `docker.io/querypie/` registry 에서 image 를 내려받습니다. 따라서, Harbor login 과정이 필요하지 않습니다.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ Event Callback Integration 상세페이지
1. Administrator &gt; General &gt; System &gt; Integrations 메뉴로 이동합니다.
2. Event Callback 타일을 클릭하여 상세 페이지로 이동합니다.
3. 생성된 Event Callback 목록에서 삭제하려는 항목의 좌측 체크박스를 체크합니다.
4. 목록 좌측 상단에서 `Delete` 버튼을 클릭합니다.<br/>
4. 목록 좌측 상단에서 `Delete` 버튼을 클릭합니다. <br/>
<figure data-layout="center" data-align="center">
<img src="/administrator-manual/general/system/integrations/integrating-with-event-callback/Screenshot-2025-08-25-at-6.14.27-PM.png" alt="Screenshot-2025-08-25-at-6.14.27-PM.png" width="736" />
</figure>
Expand Down
101 changes: 101 additions & 0 deletions confluence-mdx/tests/reverse-sync/1267007528/mapping.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
lost_info:
filenames:
- normalized: Screenshot-2025-08-25-at-6.04.50-PM.png
original: Screenshot 2025-08-25 at 6.04.50 PM.png
- normalized: Screenshot-2025-08-25-at-6.06.50-PM.png
original: Screenshot 2025-08-25 at 6.06.50 PM.png
- normalized: Screenshot-2025-08-25-at-6.14.27-PM.png
original: Screenshot 2025-08-25 at 6.14.27 PM.png
images:
- raw: <ac:image ac:align="center" ac:custom-width="true" ac:layout="center" ac:original-height="1262"
ac:original-width="2560" ac:width="760"><ri:attachment ri:filename="Screenshot
2025-08-25 at 6.04.50 PM.png" ri:version-at-save="1"></ri:attachment><ac:caption><p>Event
Callback Integration 상세페이지</p></ac:caption></ac:image>
src: /administrator-manual/general/system/integrations/integrating-with-event-callback/Screenshot-2025-08-25-at-6.04.50-PM.png
- raw: <ac:image ac:align="center" ac:custom-width="true" ac:layout="center" ac:original-height="803"
ac:original-width="645" ac:width="427"><ri:attachment ri:filename="Screenshot
2025-08-25 at 6.06.50 PM.png" ri:version-at-save="1"></ri:attachment><ac:caption><p>Event
Callback 설정 팝업 다이얼로그</p></ac:caption><ac:adf-mark color="#091e4224" key="border"
size="2"></ac:adf-mark></ac:image>
src: /administrator-manual/general/system/integrations/integrating-with-event-callback/Screenshot-2025-08-25-at-6.06.50-PM.png
- raw: <ac:image ac:align="center" ac:custom-width="true" ac:layout="center" ac:original-height="540"
ac:original-width="2256" ac:width="736"><ri:attachment ri:filename="Screenshot
2025-08-25 at 6.14.27 PM.png" ri:version-at-save="1"></ri:attachment><ac:adf-mark
color="#091e4224" key="border" size="2"></ac:adf-mark></ac:image>
src: /administrator-manual/general/system/integrations/integrating-with-event-callback/Screenshot-2025-08-25-at-6.14.27-PM.png
mappings:
- mdx_blocks:
- 4
mdx_line_end: 8
mdx_line_start: 8
xhtml_type: heading
xhtml_xpath: h2[1]
- mdx_blocks:
- 6
mdx_line_end: 11
mdx_line_start: 10
xhtml_type: paragraph
xhtml_xpath: p[1]
- mdx_blocks: []
xhtml_type: paragraph
xhtml_xpath: p[2]
- mdx_blocks:
- 9
mdx_line_end: 14
mdx_line_start: 14
xhtml_type: heading
xhtml_xpath: h2[2]
- mdx_blocks:
- 11
mdx_line_end: 18
mdx_line_start: 16
xhtml_type: list
xhtml_xpath: ol[1]
- mdx_blocks:
- 13
mdx_line_end: 25
mdx_line_start: 20
xhtml_type: html_block
xhtml_xpath: ac:image[1]
- mdx_blocks:
- 15
mdx_line_end: 46
mdx_line_start: 27
xhtml_type: list
xhtml_xpath: ol[2]
- mdx_blocks: []
xhtml_type: paragraph
xhtml_xpath: p[3]
- mdx_blocks:
- 18
mdx_line_end: 49
mdx_line_start: 49
xhtml_type: list
xhtml_xpath: ol[3]
- mdx_blocks:
- 20
mdx_line_end: 51
mdx_line_start: 51
xhtml_type: heading
xhtml_xpath: h2[3]
- mdx_blocks:
- 22
mdx_line_end: 56
mdx_line_start: 53
xhtml_type: list
xhtml_xpath: ol[4]
- mdx_blocks:
- 24
mdx_line_end: 58
mdx_line_start: 58
xhtml_type: heading
xhtml_xpath: h2[4]
- mdx_blocks:
- 26
mdx_line_end: 67
mdx_line_start: 60
xhtml_type: list
xhtml_xpath: ol[5]
mdx_file: page.mdx
source_page_id: '1267007528'
version: 3
6 changes: 3 additions & 3 deletions confluence-mdx/tests/reverse-sync/1453588486/improved.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ Admin &gt; General &gt; System &gt; Integrations 의 Authentication 하위에
</figure>

* Admin &gt; General &gt; System &gt; Integrations의 Authentication 하위에 있는 OAuth Client Application 항목을 선택합니다.
* `+Add` 버튼을 누르고 등록할 애플리케이션 정보를 입력한 뒤 `Save` 버튼을 누릅니다.
* `+Add` 버튼을 누르고 등록할 애플리케이션 정보를 입력한 뒤 `Save` 버튼을 누릅니다.
* **Name** : 식별이 용이한 이름을 입력합니다.
* **Redirect URI** : 인증 과정이 끝난 후 돌려보낼 Client URL을 입력합니다.
* **Access Token Timeout (Minutes)** : 기본값은 60, 최소값은 5입니다.
* **Refresh Token Timeout (Minutes)** : 기본값은 1440, 최소값은 5입니다.
* **Access Token Timeout (Minutes)** : 기본값은 60, 최소값은 5입니다.
* **Refresh Token Timeout (Minutes)** : 기본값은 1440, 최소값은 5입니다.

<figure data-layout="center" data-align="center">
<img src="/administrator-manual/general/system/integrations/oauth-client-application/image-20251009-043210.png" alt="Add OAuth Client Application" width="760" />
Expand Down
128 changes: 128 additions & 0 deletions confluence-mdx/tests/reverse-sync/1453588486/mapping.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
lost_info:
images:
- raw: <ac:image ac:align="center" ac:alt="image-20251009-041209.png" ac:custom-width="true"
ac:layout="center" ac:original-height="903" ac:original-width="1355" ac:width="760"><ri:attachment
ri:filename="image-20251009-041209.png" ri:version-at-save="1"></ri:attachment><ac:caption><p>Admin
&gt; General &gt; System &gt; Integrations 의 Authentication 하위에 있는 OAuth Client
Application</p></ac:caption><ac:adf-mark color="#091e4224" key="border" size="2"></ac:adf-mark></ac:image>
src: /administrator-manual/general/system/integrations/oauth-client-application/image-20251009-041209.png
- raw: <ac:image ac:align="center" ac:alt="image-20251009-043210.png" ac:custom-width="true"
ac:layout="center" ac:original-height="908" ac:original-width="1365" ac:width="760"><ri:attachment
ri:filename="image-20251009-043210.png" ri:version-at-save="1"></ri:attachment><ac:caption><p>Add
OAuth Client Application</p></ac:caption></ac:image>
src: /administrator-manual/general/system/integrations/oauth-client-application/image-20251009-043210.png
- raw: <ac:image ac:align="center" ac:alt="image-20251009-043334.png" ac:custom-width="true"
ac:layout="center" ac:original-height="908" ac:original-width="1365" ac:width="760"><ri:attachment
ri:filename="image-20251009-043334.png" ri:version-at-save="1"></ri:attachment><ac:caption><p>Client
ID 및 Client Secret</p></ac:caption></ac:image>
src: /administrator-manual/general/system/integrations/oauth-client-application/image-20251009-043334.png
- raw: <ac:image ac:align="center" ac:alt="image-20251009-044214.png" ac:custom-width="true"
ac:layout="center" ac:original-height="908" ac:original-width="1365" ac:width="760"><ri:attachment
ri:filename="image-20251009-044214.png" ri:version-at-save="1"></ri:attachment><ac:caption><p>OAuth
Client Application 설정 수정</p></ac:caption></ac:image>
src: /administrator-manual/general/system/integrations/oauth-client-application/image-20251009-044214.png
- raw: <ac:image ac:align="center" ac:alt="image-20251009-044400.png" ac:custom-width="true"
ac:layout="center" ac:original-height="908" ac:original-width="1365" ac:width="760"><ri:attachment
ri:filename="image-20251009-044400.png" ri:version-at-save="1"></ri:attachment><ac:caption><p>OAuth
Client Application 설정 삭제</p></ac:caption></ac:image>
src: /administrator-manual/general/system/integrations/oauth-client-application/image-20251009-044400.png
mappings:
- mdx_blocks:
- 4
mdx_line_end: 8
mdx_line_start: 8
xhtml_type: heading
xhtml_xpath: h2[1]
- mdx_blocks:
- 6
mdx_line_end: 11
mdx_line_start: 10
xhtml_type: paragraph
xhtml_xpath: p[1]
- mdx_blocks:
- 8
mdx_line_end: 13
mdx_line_start: 13
xhtml_type: heading
xhtml_xpath: h2[2]
- mdx_blocks:
- 10
mdx_line_end: 20
mdx_line_start: 15
xhtml_type: html_block
xhtml_xpath: ac:image[1]
- mdx_blocks:
- 12
mdx_line_end: 27
mdx_line_start: 22
xhtml_type: list
xhtml_xpath: ul[1]
- mdx_blocks:
- 14
mdx_line_end: 34
mdx_line_start: 29
xhtml_type: html_block
xhtml_xpath: ac:image[2]
- mdx_blocks: []
xhtml_type: paragraph
xhtml_xpath: p[2]
- mdx_blocks:
- 17
mdx_line_end: 37
mdx_line_start: 37
xhtml_type: list
xhtml_xpath: ul[2]
- mdx_blocks:
- 19
mdx_line_end: 44
mdx_line_start: 39
xhtml_type: html_block
xhtml_xpath: ac:image[3]
- mdx_blocks:
- 21
mdx_line_end: 46
mdx_line_start: 46
xhtml_type: heading
xhtml_xpath: h2[3]
- mdx_blocks:
- 23
mdx_line_end: 53
mdx_line_start: 48
xhtml_type: html_block
xhtml_xpath: ac:image[4]
- mdx_blocks:
- 25
mdx_line_end: 55
mdx_line_start: 55
xhtml_type: paragraph
xhtml_xpath: p[3]
- mdx_blocks:
- 27
mdx_line_end: 58
mdx_line_start: 57
xhtml_type: list
xhtml_xpath: ul[3]
- mdx_blocks:
- 29
mdx_line_end: 60
mdx_line_start: 60
xhtml_type: heading
xhtml_xpath: h2[4]
- mdx_blocks:
- 31
mdx_line_end: 62
mdx_line_start: 62
xhtml_type: paragraph
xhtml_xpath: p[4]
- mdx_blocks:
- 33
mdx_line_end: 69
mdx_line_start: 64
xhtml_type: html_block
xhtml_xpath: ac:image[5]
- mdx_blocks: []
xhtml_type: paragraph
xhtml_xpath: p[5]
mdx_file: page.mdx
source_page_id: '1453588486'
version: 3
5 changes: 3 additions & 2 deletions confluence-mdx/tests/reverse-sync/1454342158/improved.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ LDAP 서버로부터 사용자 정보 동기화를 실행하려는 경우 **Use
* **Use an Attribute for Privilege Revoke**: 동기화 시 특정 Attribute에 따라 Privilege를 회수할지 여부를 선택합니다.
* 특정 LDAP Attribute의 변경에 의해 자동으로 DAC Privilege를 회수하고자 하는 경우 이 옵션을 활성화하세요.
* LDAP Attribute 입력 필드에 활성화 변경을 감지하려는 Attribute 이름을 입력합니다.
* **Enable Attribute Synchronization :** LDAP 사용자 속성과 QueryPie 사용자 속성을 매핑하여 동기화할지 여부를 선택합니다.
* **Enable Attribute Synchronization :** LDAP 사용자 속성과 QueryPie 사용자 속성을 매핑하여 동기화할지 여부를 선택합니다.
* LDAP에서 관리 중인 사용자 속성을 QueryPie 내 Attribute와 자동으로 연동하고자 하는 경우, 해당 옵션을 활성화하시기 바랍니다.
* 옵션 활성화 시, 하단에 LDAP Attribute Mapping UI가 표시되며 매핑 작업을 통해 연동할 LDAP Attribute와 QueryPie Attribute를 지정할 수 있습니다.
* 단, 해당 기능은 Profile Editor(Admin&gt; General &gt; User Management &gt; Profile Editor)에서 Source Priority가 Inherit from profile source로 설정된 Attribute에 한해 적용됩니다.
Expand Down Expand Up @@ -533,7 +533,7 @@ Custom Identity Provider는 인증 API 서버를 사용하는 특수한 경우
* **Name** : 식별에 용이하도록 적합한 IdP의 이름을 입력합니다.
* **Type**: Custom Identity Provider 를 선택합니다.
* **API URL** : API 서버의 End-point URL을 입력합니다.
* 사용자 정보 동기화를 실행하려는 경우 Use Synchronization with the Authentication System 옵션을 활성화합니다.
* 사용자 정보 동기화를 실행하려는 경우 Use Synchronization with the Authentication System 옵션을 활성화합니다.

<figure data-layout="center" data-align="center">
<img src="/administrator-manual/general/system/integrations/identity-providers/image-20251014-015047.png" alt="image-20251014-015047.png" width="481" />
Expand All @@ -548,3 +548,4 @@ Custom Identity Provider는 인증 API 서버를 사용하는 특수한 경우
* 0.0 ~ 1.0 사이의 값을 입력합니다. (기본값은 0.1)
* 예) 기존 유저가 100명이고, Allowed User Deletion Rate Threshold 0.1 인 경우, 다시 동기화 하였을 때, 삭제된 유저가 10명 이상이면 동기화가 실패합니다.
* 11.3.0 이전 버전에서 동기화 설정된 상태에서, 11.3.0으로 제품을 업그레이드하면 이 값이 1.0 으로 기본 설정됩니다.

Loading
Loading