# HG changeset patch # User Goffi # Date 1679582628 -3600 # Node ID 4941cd102f93f75a801408d7bd6c69bc8381bf02 # Parent 78b5f356900c16058980e048211158ba6fa18421 jp (blog): new `--attachment` argument to attach files or external data to a blog post diff -r 78b5f356900c -r 4941cd102f93 sat_frontends/jp/cmd_blog.py --- a/sat_frontends/jp/cmd_blog.py Thu Mar 23 15:42:21 2023 +0100 +++ b/sat_frontends/jp/cmd_blog.py Thu Mar 23 15:43:48 2023 +0100 @@ -18,24 +18,28 @@ # along with this program. If not, see . -import json -import sys -import os.path -import os -import tempfile -import subprocess import asyncio from asyncio.subprocess import DEVNULL +from configparser import NoOptionError, NoSectionError +import json +import os +import os.path from pathlib import Path -from . import base, cmd_pubsub +import re +import subprocess +import sys +import tempfile +from urllib.parse import urlparse + from sat.core.i18n import _ -from sat_frontends.jp.constants import Const as C +from sat.tools import config +from sat.tools.common import uri +from sat.tools.common import data_format +from sat.tools.common.ansi import ANSI as A from sat_frontends.jp import common -from sat.tools.common.ansi import ANSI as A -from sat.tools.common import uri -from sat.tools import config -from configparser import NoSectionError, NoOptionError -from sat.tools.common import data_format +from sat_frontends.jp.constants import Const as C + +from . import base, cmd_pubsub __commands__ = ["Blog"] @@ -84,6 +88,8 @@ "extra" ) OUTPUT_OPT_NO_HEADER = "no-header" +RE_ATTACHMENT_METADATA = re.compile(r"^(?P[a-z_]+)=(?P.*)") +ALLOWER_ATTACH_MD_KEY = ("desc", "media_type", "external") async def guessSyntaxFromPath(host, sat_conf, path): @@ -104,7 +110,7 @@ return await host.bridge.getParamA("Syntax", "Composition", "value", host.profile) -class BlogPublishCommon(object): +class BlogPublishCommon: """handle common option for publising commands (Set and Edit)""" async def get_current_syntax(self): @@ -148,6 +154,16 @@ help=_("language of the item (ISO 639 code)"), ) + self.parser.add_argument( + "-a", + "--attachment", + dest="attachments", + nargs="+", + help=_( + "attachment in the form URL [metadata_name=value]" + ) + ) + comments_group = self.parser.add_mutually_exclusive_group() comments_group.add_argument( "-C", @@ -192,7 +208,7 @@ help=_("cryptographically sign the blog post") ) - async def setMbDataContent(self, content, mb_data): + async def set_mb_data_content(self, content, mb_data): if self.default_syntax_used: # default syntax has been used mb_data["content_rich"] = content @@ -203,7 +219,51 @@ content, self.current_syntax, SYNTAX_XHTML, False, self.profile ) - def setMbDataFromArgs(self, mb_data): + def handle_attachments(self, mb_data: dict) -> None: + """Check, validate and add attachments to mb_data""" + if self.args.attachments: + attachments = [] + attachment = {} + for arg in self.args.attachments: + m = RE_ATTACHMENT_METADATA.match(arg) + if m is None: + # we should have an URL + url_parsed = urlparse(arg) + if url_parsed.scheme not in ("http", "https"): + self.parser.error( + "invalid URL in --attachment (only http(s) scheme is " + f" accepted): {arg}" + ) + if attachment: + # if we hae a new URL, we have a new attachment + attachments.append(attachment) + attachment = {} + attachment["url"] = arg + else: + # we should have a metadata + if "url" not in attachment: + self.parser.error( + "you must to specify an URL before any metadata in " + "--attachment" + ) + key = m.group("key") + if key not in ALLOWER_ATTACH_MD_KEY: + self.parser.error( + f"invalid metadata key in --attachment: {key!r}" + ) + value = m.group("value").strip() + if key == "external": + if not value: + value=True + else: + value = C.bool(value) + attachment[key] = value + if attachment: + attachments.append(attachment) + if attachments: + mb_data.setdefault("extra", {})["attachments"] = attachments + + def set_mb_data_from_args(self, mb_data): """set microblog metadata according to command line options if metadata already exist, it will be overwritten @@ -222,6 +282,7 @@ mb_data["signed"] = True if self.args.encrypt_for: mb_data["encrypted_for"] = {"targets": self.args.encrypt_for} + self.handle_attachments(mb_data) class Set(base.CommandBase, BlogPublishCommon): @@ -243,11 +304,11 @@ self.current_syntax = await self.get_current_syntax() self.pubsub_item = self.args.item mb_data = {} - self.setMbDataFromArgs(mb_data) + self.set_mb_data_from_args(mb_data) if self.pubsub_item: mb_data["id"] = self.pubsub_item content = sys.stdin.read() - await self.setMbDataContent(content, mb_data) + await self.set_mb_data_content(content, mb_data) try: item_id = await self.host.bridge.mbSend( @@ -533,7 +594,7 @@ except KeyError: pass # and override metadata with command-line arguments - self.setMbDataFromArgs(mb_data) + self.set_mb_data_from_args(mb_data) if self.args.no_publish: mb_data["publish"] = False @@ -597,7 +658,7 @@ await asyncio.gather(*coroutines) async def publish(self, content, mb_data): - await self.setMbDataContent(content, mb_data) + await self.set_mb_data_content(content, mb_data) if self.pubsub_item: mb_data["id"] = self.pubsub_item