Add an image field, required for WordPress posts

Posts get a nullable image_url. WordPressPublisher now downloads it
and uploads it to WordPress's media endpoint before creating the
post, setting it as featured_media — and rejects publishing (without
touching status) if a blog post has no image set, mirroring the
existing title requirement. Every other network treats it as optional.

Not enforced at save time, same as title — you can still draft an
incomplete WordPress post, it just can't be published until an image
is set. Exposed through the UI form (with a thumbnail/""missing""
indicator in the table), the JSON API, and the Python CLI wrapper.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
m105
2026-07-21 20:03:25 +03:00
co-authored by Claude Sonnet 5
parent fd185ab813
commit 42abc658f2
14 changed files with 259 additions and 28 deletions
+6 -2
View File
@@ -6,10 +6,10 @@ Usage:
python3 posts.py get POST_ID
python3 posts.py create --site-id ID --type TYPE --body BODY
[--social-account-id ID] [--title TITLE] [--target TARGET]
[--status STATUS] [--scheduled-at DATETIME]
[--image-url URL] [--status STATUS] [--scheduled-at DATETIME]
python3 posts.py update POST_ID [--site-id ID] [--social-account-id ID]
[--type TYPE] [--title TITLE] [--body BODY] [--target TARGET]
[--status STATUS] [--scheduled-at DATETIME]
[--image-url URL] [--status STATUS] [--scheduled-at DATETIME]
[--published-url URL]
python3 posts.py delete POST_ID
@@ -47,6 +47,7 @@ def main() -> None:
create_parser.add_argument("--social-account-id", type=int)
create_parser.add_argument("--title")
create_parser.add_argument("--target", help="Network-specific destination, e.g. a subreddit for Reddit posts")
create_parser.add_argument("--image-url", help="Image URL — required for WordPress (type=blog), optional otherwise")
create_parser.add_argument("--status", choices=STATUSES)
create_parser.add_argument("--scheduled-at", help="ISO 8601 datetime, e.g. 2026-07-20T09:00:00")
@@ -58,6 +59,7 @@ def main() -> None:
update_parser.add_argument("--title")
update_parser.add_argument("--body")
update_parser.add_argument("--target", help="Network-specific destination, e.g. a subreddit for Reddit posts")
update_parser.add_argument("--image-url", help="Image URL — required for WordPress (type=blog), optional otherwise")
update_parser.add_argument("--status", choices=STATUSES)
update_parser.add_argument("--scheduled-at", help="ISO 8601 datetime")
update_parser.add_argument("--published-url")
@@ -88,6 +90,7 @@ def main() -> None:
"title": args.title,
"body": args.body,
"target": args.target,
"image_url": args.image_url,
"status": args.status,
"scheduled_at": args.scheduled_at,
}
@@ -102,6 +105,7 @@ def main() -> None:
"title": args.title,
"body": args.body,
"target": args.target,
"image_url": args.image_url,
"status": args.status,
"scheduled_at": args.scheduled_at,
"published_url": args.published_url,