kci-confluence-cli 0.2.0__tar.gz → 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {kci_confluence_cli-0.2.0 → kci_confluence_cli-0.3.0}/PKG-INFO +1 -1
- {kci_confluence_cli-0.2.0 → kci_confluence_cli-0.3.0}/confluence/__version__.py +1 -1
- {kci_confluence_cli-0.2.0 → kci_confluence_cli-0.3.0}/confluence/attachment/create_attachment.py +5 -1
- {kci_confluence_cli-0.2.0 → kci_confluence_cli-0.3.0}/confluence/attachment/delete_attachment.py +1 -0
- {kci_confluence_cli-0.2.0 → kci_confluence_cli-0.3.0}/confluence/common/api.py +1 -2
- {kci_confluence_cli-0.2.0 → kci_confluence_cli-0.3.0}/confluence/common/cli.py +5 -5
- {kci_confluence_cli-0.2.0 → kci_confluence_cli-0.3.0}/confluence/local/convert_html_to_xml.py +22 -17
- {kci_confluence_cli-0.2.0 → kci_confluence_cli-0.3.0}/confluence/page/subcommand.py +3 -4
- kci_confluence_cli-0.3.0/confluence/page/update_page.py +61 -0
- {kci_confluence_cli-0.2.0 → kci_confluence_cli-0.3.0}/pyproject.toml +1 -1
- kci_confluence_cli-0.2.0/confluence/page/update_page.py +0 -49
- {kci_confluence_cli-0.2.0 → kci_confluence_cli-0.3.0}/README.md +0 -0
- {kci_confluence_cli-0.2.0 → kci_confluence_cli-0.3.0}/confluence/__init__.py +0 -0
- {kci_confluence_cli-0.2.0 → kci_confluence_cli-0.3.0}/confluence/__main__.py +0 -0
- {kci_confluence_cli-0.2.0 → kci_confluence_cli-0.3.0}/confluence/attachment/__init__.py +0 -0
- {kci_confluence_cli-0.2.0 → kci_confluence_cli-0.3.0}/confluence/attachment/get_attachment.py +0 -0
- {kci_confluence_cli-0.2.0 → kci_confluence_cli-0.3.0}/confluence/attachment/subcommand.py +0 -0
- {kci_confluence_cli-0.2.0 → kci_confluence_cli-0.3.0}/confluence/common/__init__.py +0 -0
- {kci_confluence_cli-0.2.0 → kci_confluence_cli-0.3.0}/confluence/common/utils.py +0 -0
- {kci_confluence_cli-0.2.0 → kci_confluence_cli-0.3.0}/confluence/content/__init__.py +0 -0
- {kci_confluence_cli-0.2.0 → kci_confluence_cli-0.3.0}/confluence/content/get_content_by_id.py +0 -0
- {kci_confluence_cli-0.2.0 → kci_confluence_cli-0.3.0}/confluence/content/subcommand.py +0 -0
- {kci_confluence_cli-0.2.0 → kci_confluence_cli-0.3.0}/confluence/data/logging.yaml +0 -0
- {kci_confluence_cli-0.2.0 → kci_confluence_cli-0.3.0}/confluence/local/__init__.py +0 -0
- {kci_confluence_cli-0.2.0 → kci_confluence_cli-0.3.0}/confluence/local/subcommand.py +0 -0
- {kci_confluence_cli-0.2.0 → kci_confluence_cli-0.3.0}/confluence/page/__init__.py +0 -0
- {kci_confluence_cli-0.2.0 → kci_confluence_cli-0.3.0}/confluence/page/get_page_body.py +0 -0
- {kci_confluence_cli-0.2.0 → kci_confluence_cli-0.3.0}/confluence/py.typed +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.
|
|
1
|
+
__version__ = "0.3.0" # `poetry-dynamic-versioning`を使ってGitHubのバージョンタグを取得している。変更不要
|
{kci_confluence_cli-0.2.0 → kci_confluence_cli-0.3.0}/confluence/attachment/create_attachment.py
RENAMED
|
@@ -93,7 +93,11 @@ def add_arguments_to_parser(parser: argparse.ArgumentParser): # noqa: ANN201
|
|
|
93
93
|
|
|
94
94
|
parser.add_argument("--mime_type", type=str, help="ファイル名からMIMEタイプが判別できないときに、この値を添付ファイルのMIMEタイプとします。")
|
|
95
95
|
|
|
96
|
-
parser.add_argument(
|
|
96
|
+
parser.add_argument(
|
|
97
|
+
"--allow_duplicated",
|
|
98
|
+
action="store_true",
|
|
99
|
+
help="指定した場合は、アップロード先にすでに同じファイルが存在している場合に上書きます。指定しない場合は、400 Errorが発生します。",
|
|
100
|
+
)
|
|
97
101
|
|
|
98
102
|
parser.add_argument("--filename_pattern", help="glob形式のパターンに一致するファイル名だけアップロードします。(ex) '*.png'")
|
|
99
103
|
parser.set_defaults(subcommand_func=main)
|
|
@@ -146,8 +146,7 @@ class Api:
|
|
|
146
146
|
|
|
147
147
|
https://docs.atlassian.com/ConfluenceServer/rest/6.15.7/#api/content-update
|
|
148
148
|
"""
|
|
149
|
-
|
|
150
|
-
return self._request("put", f"content/{content_id}", headers=headers, params=query_params, data=request_body).json()
|
|
149
|
+
return self._request("put", f"content/{content_id}", params=query_params, json=request_body).json()
|
|
151
150
|
|
|
152
151
|
def delete_content(self, content_id: str, *, query_params: Optional[QueryParams] = None) -> None:
|
|
153
152
|
"""
|
|
@@ -141,11 +141,11 @@ def prompt_yesno(msg: str) -> bool:
|
|
|
141
141
|
|
|
142
142
|
"""
|
|
143
143
|
while True:
|
|
144
|
-
choice = input(f"{msg} [y/
|
|
144
|
+
choice = input(f"{msg} [y/n] : ")
|
|
145
145
|
if choice == "y":
|
|
146
146
|
return True
|
|
147
147
|
|
|
148
|
-
elif choice == "
|
|
148
|
+
elif choice == "n":
|
|
149
149
|
return False
|
|
150
150
|
|
|
151
151
|
|
|
@@ -160,14 +160,14 @@ def prompt_yesnoall(msg: str) -> tuple[bool, bool]:
|
|
|
160
160
|
|
|
161
161
|
"""
|
|
162
162
|
while True:
|
|
163
|
-
choice = input(f"{msg} [y/
|
|
163
|
+
choice = input(f"{msg} [y/n/all] : ")
|
|
164
164
|
if choice == "y":
|
|
165
165
|
return True, False
|
|
166
166
|
|
|
167
|
-
elif choice == "
|
|
167
|
+
elif choice == "n":
|
|
168
168
|
return False, False
|
|
169
169
|
|
|
170
|
-
elif choice == "
|
|
170
|
+
elif choice == "all":
|
|
171
171
|
return True, True
|
|
172
172
|
|
|
173
173
|
|
{kci_confluence_cli-0.2.0 → kci_confluence_cli-0.3.0}/confluence/local/convert_html_to_xml.py
RENAMED
|
@@ -18,16 +18,11 @@ def convert_img_elm(img_elm: HtmlElement) -> None:
|
|
|
18
18
|
`<img src="foo.png">`を以下のXMLに変換する
|
|
19
19
|
|
|
20
20
|
```
|
|
21
|
-
<ac:image
|
|
22
|
-
<ri:attachment ri:filename="
|
|
21
|
+
<ac:image>
|
|
22
|
+
<ri:attachment ri:filename="foo.png"/>
|
|
23
23
|
</ac:image>
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
```
|
|
28
|
-
<ac:image><ri:url ri:value="http://confluence.atlassian.com/images/logo/confluence_48_trans.png" /></ac:image>
|
|
29
|
-
```
|
|
30
|
-
|
|
31
26
|
Args:
|
|
32
27
|
trim_dirname_for_src_value: src属性値からディレクトリ名を削除する。Confluenceにアップロードしたファイル名にスラッシュは含められないため。
|
|
33
28
|
"""
|
|
@@ -39,6 +34,10 @@ def convert_img_elm(img_elm: HtmlElement) -> None:
|
|
|
39
34
|
url_elm.tag = "ri:url"
|
|
40
35
|
url_elm.attrib["ri:value"] = src_value
|
|
41
36
|
img_elm.append(url_elm)
|
|
37
|
+
elif src_value.startswith("data:"):
|
|
38
|
+
# Data URIには対応していないので、スキップする
|
|
39
|
+
logger.warning(f"img要素のsrc属性値はData URIが含まれていました。Confluence用のXMLはData URIに対応していません。 :: {img_elm}'")
|
|
40
|
+
return
|
|
42
41
|
else:
|
|
43
42
|
attachment_elm = fromstring("<ri:attachment/>")
|
|
44
43
|
# コロン付きのタグが生成できないので、改めて置換した
|
|
@@ -49,17 +48,21 @@ def convert_img_elm(img_elm: HtmlElement) -> None:
|
|
|
49
48
|
attachment_elm.attrib["ri:filename"] = tmp[-1]
|
|
50
49
|
|
|
51
50
|
img_elm.append(attachment_elm)
|
|
52
|
-
|
|
51
|
+
del img_elm.attrib["src"]
|
|
52
|
+
|
|
53
|
+
# img要素のいくつかの属性を、`ac:image`タグの属性に変換する。
|
|
54
|
+
# https://ja.confluence.atlassian.com/doc/confluence-storage-format-790796544.html
|
|
55
|
+
# bool値を指定する以下の属性は変換しない
|
|
56
|
+
# ac:border, ac:thumbnail
|
|
57
|
+
for html_attribute_name in ("align", "class", "title", "style", "alt", "height", "width", "vspace", "hspace"):
|
|
58
|
+
attribute_value = img_elm.attrib.get(html_attribute_name)
|
|
59
|
+
if attribute_value is not None and attribute_value != "":
|
|
60
|
+
img_elm.attrib[f"ac:{html_attribute_name}"] = attribute_value
|
|
61
|
+
del img_elm.attrib[html_attribute_name]
|
|
62
|
+
|
|
63
|
+
# サムネイル画像として設定する(画像をクリックすると拡大表示される)
|
|
53
64
|
img_elm.attrib["ac:thumbnail"] = "true"
|
|
54
65
|
|
|
55
|
-
alt_value: str = img_elm.attrib.get("alt")
|
|
56
|
-
if alt_value != "":
|
|
57
|
-
img_elm.attrib["ac:alt"] = alt_value
|
|
58
|
-
|
|
59
|
-
title_value: str = img_elm.attrib.get("title")
|
|
60
|
-
if title_value != "":
|
|
61
|
-
img_elm.attrib["ac:title"] = title_value
|
|
62
|
-
|
|
63
66
|
|
|
64
67
|
def convert(input_html_file: Path, output_xml_file: Path) -> None:
|
|
65
68
|
with input_html_file.open(encoding="utf-8") as f:
|
|
@@ -73,9 +76,11 @@ def convert(input_html_file: Path, output_xml_file: Path) -> None:
|
|
|
73
76
|
|
|
74
77
|
# body要素があればその中身、なければhtmlファイルの中身をアップロードする
|
|
75
78
|
if len(pq_html("body")) > 0:
|
|
79
|
+
# body要素以下のHTMLを取得する
|
|
76
80
|
html_data = pq_html("body").html()
|
|
77
81
|
else:
|
|
78
|
-
|
|
82
|
+
# 要素自身のHTMLを取得する
|
|
83
|
+
html_data = str(pq_html)
|
|
79
84
|
|
|
80
85
|
output_xml_file.parent.mkdir(exist_ok=True, parents=True)
|
|
81
86
|
output_xml_file.write_text(html_data, encoding="utf-8")
|
|
@@ -8,18 +8,17 @@ import confluence.page.get_page_body
|
|
|
8
8
|
import confluence.page.update_page
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
def add_arguments_to_parser(parser: argparse.ArgumentParser)
|
|
11
|
+
def add_arguments_to_parser(parser: argparse.ArgumentParser) -> None:
|
|
12
12
|
subparsers = parser.add_subparsers(dest="subcommand_name")
|
|
13
13
|
|
|
14
14
|
# サブコマンドの定義
|
|
15
15
|
confluence.page.get_page_body.add_parser(subparsers)
|
|
16
|
-
|
|
17
|
-
# confluence.page.update_page.add_parser(subparsers)
|
|
16
|
+
confluence.page.update_page.add_parser(subparsers)
|
|
18
17
|
|
|
19
18
|
|
|
20
19
|
def add_parser(subparsers: Optional[argparse._SubParsersAction] = None) -> argparse.ArgumentParser:
|
|
21
20
|
subcommand_name = "page"
|
|
22
|
-
subcommand_help = "
|
|
21
|
+
subcommand_help = "ページまたはブログに関するサブコマンド"
|
|
23
22
|
|
|
24
23
|
parser = confluence.common.cli.add_parser(subparsers, subcommand_name, subcommand_help, description=subcommand_help, is_subcommand=False)
|
|
25
24
|
add_arguments_to_parser(parser)
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import logging
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import confluence
|
|
8
|
+
from confluence.common.cli import create_api_instance, prompt_yesno
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def main(args: argparse.Namespace) -> None:
|
|
14
|
+
api = create_api_instance(args)
|
|
15
|
+
content_id = args.content_id
|
|
16
|
+
xml_file: Path = args.xml_file
|
|
17
|
+
comment = args.comment if args.comment is not None else "Updated via confluence-cli"
|
|
18
|
+
xml_text = xml_file.read_text(encoding="utf-8")
|
|
19
|
+
|
|
20
|
+
old_content = api.get_content_by_id(content_id, query_params={"expand": "version,ancestors,space,body.storage"})
|
|
21
|
+
|
|
22
|
+
content_title = args.title if args.title is not None else old_content["title"]
|
|
23
|
+
space_key = old_content["space"]["key"]
|
|
24
|
+
if not args.yes:
|
|
25
|
+
if not prompt_yesno(f"次のコンテンツを更新しますか? :: content_id='{content_id}', title='{content_title}', space.key='{space_key}'"):
|
|
26
|
+
logger.info(f"コンテンツの更新をキャンセルしました。 content_id='{content_id}', title='{content_title}', space.key='{space_key}'")
|
|
27
|
+
return
|
|
28
|
+
|
|
29
|
+
request_body = {
|
|
30
|
+
"version": {"number": old_content["version"]["number"] + 1, "message": comment},
|
|
31
|
+
"title": content_title,
|
|
32
|
+
"type": old_content["type"],
|
|
33
|
+
"body": {"storage": {"value": xml_text, "representation": "storage"}},
|
|
34
|
+
}
|
|
35
|
+
_ = api.update_content(content_id, request_body=request_body)
|
|
36
|
+
logger.info(f"次のコンテンツを'{xml_file}'の内容で更新しました。 :: content_id='{content_id}', title='{content_title}', space.key='{space_key}'")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def add_arguments_to_parser(parser: argparse.ArgumentParser) -> None:
|
|
40
|
+
parser.add_argument("-c", "--content_id", required=True, help="更新対象のコンテンツのID")
|
|
41
|
+
parser.add_argument(
|
|
42
|
+
"--xml_file",
|
|
43
|
+
required=True,
|
|
44
|
+
type=Path,
|
|
45
|
+
help="storageフォーマットで記載されたXMLファイルのパス。このファイルの内容でページ(またはブログ)が更新されます。",
|
|
46
|
+
)
|
|
47
|
+
parser.add_argument("--title", help="ページまたはブログの新しいタイトル。指定しない場合は既存のタイトルが維持されます。")
|
|
48
|
+
parser.add_argument("--comment", help="ページまたはブログを更新したときに残すコメント。")
|
|
49
|
+
parser.add_argument("--yes", action="store_true", help="すべてのプロンプトに自動的に'yes'と答え、非対話的に実行します。")
|
|
50
|
+
|
|
51
|
+
parser.set_defaults(subcommand_func=main)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def add_parser(subparsers: argparse._SubParsersAction | None = None) -> argparse.ArgumentParser:
|
|
55
|
+
subcommand_name = "update"
|
|
56
|
+
subcommand_help = "ページまたはブログを更新します。"
|
|
57
|
+
|
|
58
|
+
parser = confluence.common.cli.add_parser(subparsers, subcommand_name, subcommand_help)
|
|
59
|
+
|
|
60
|
+
add_arguments_to_parser(parser)
|
|
61
|
+
return parser
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "kci-confluence-cli"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.3.0" # `poetry-dynamic-versioning`を使ってGitHubのバージョンタグを取得している。変更不要
|
|
4
4
|
description = "来栖川電算が利用しているConfluence v6.15.7を操作するためのCLIです。"
|
|
5
5
|
authors = ["Kurusugawa Computer Inc."]
|
|
6
6
|
license = "MIT"
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import argparse
|
|
4
|
-
import logging
|
|
5
|
-
from pathlib import Path
|
|
6
|
-
|
|
7
|
-
import confluence
|
|
8
|
-
from confluence.common.cli import create_api_instance
|
|
9
|
-
from confluence.common.utils import print_json
|
|
10
|
-
|
|
11
|
-
logger = logging.getLogger(__name__)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def main(args: argparse.Namespace) -> None:
|
|
15
|
-
api = create_api_instance(args)
|
|
16
|
-
content_id = args.content_id
|
|
17
|
-
xml_file: Path = args.xml_file
|
|
18
|
-
xml_text = xml_file.read_text(encoding="utf-8")
|
|
19
|
-
|
|
20
|
-
old_page = api.get_content_by_id(content_id, query_params={"expand": "version,ancestors,space,body.storage"})
|
|
21
|
-
print_json(old_page, is_pretty=True, output=None)
|
|
22
|
-
request_body = {
|
|
23
|
-
"version": {"number": old_page["version"]["number"] + 1, "message": ""},
|
|
24
|
-
"title": old_page["title"],
|
|
25
|
-
"type": old_page["type"],
|
|
26
|
-
"space": {"key": old_page["space"]["key"]},
|
|
27
|
-
"body": {"storage": {"value": xml_text, "representation": "storage"}},
|
|
28
|
-
}
|
|
29
|
-
result = api.update_content(content_id, request_body=request_body)
|
|
30
|
-
print_json(result, is_pretty=True, output=None)
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
def add_arguments_to_parser(parser: argparse.ArgumentParser): # noqa: ANN201
|
|
34
|
-
parser.add_argument("-c", "--content_id", required=True, help="取得対象のコンテンツのID")
|
|
35
|
-
parser.add_argument(
|
|
36
|
-
"--xml_file", required=True, type=Path, help="storageフォーマットで記載されたXMLファイルのパス。このファイルの内容でページが更新されます。"
|
|
37
|
-
)
|
|
38
|
-
|
|
39
|
-
parser.set_defaults(subcommand_func=main)
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
def add_parser(subparsers: argparse._SubParsersAction | None = None) -> argparse.ArgumentParser:
|
|
43
|
-
subcommand_name = "update"
|
|
44
|
-
subcommand_help = "ページを更新します。"
|
|
45
|
-
|
|
46
|
-
parser = confluence.common.cli.add_parser(subparsers, subcommand_name, subcommand_help)
|
|
47
|
-
|
|
48
|
-
add_arguments_to_parser(parser)
|
|
49
|
-
return parser
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kci_confluence_cli-0.2.0 → kci_confluence_cli-0.3.0}/confluence/attachment/get_attachment.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kci_confluence_cli-0.2.0 → kci_confluence_cli-0.3.0}/confluence/content/get_content_by_id.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|