wp_python 0.1.0__py3-none-any.whl
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.
- src/wordpress_api/__init__.py +31 -0
- src/wordpress_api/auth.py +174 -0
- src/wordpress_api/client.py +293 -0
- src/wordpress_api/endpoints/__init__.py +43 -0
- src/wordpress_api/endpoints/application_passwords.py +235 -0
- src/wordpress_api/endpoints/autosaves.py +106 -0
- src/wordpress_api/endpoints/base.py +117 -0
- src/wordpress_api/endpoints/blocks.py +107 -0
- src/wordpress_api/endpoints/categories.py +91 -0
- src/wordpress_api/endpoints/comments.py +127 -0
- src/wordpress_api/endpoints/media.py +164 -0
- src/wordpress_api/endpoints/menus.py +120 -0
- src/wordpress_api/endpoints/pages.py +109 -0
- src/wordpress_api/endpoints/plugins.py +89 -0
- src/wordpress_api/endpoints/post_types.py +61 -0
- src/wordpress_api/endpoints/posts.py +131 -0
- src/wordpress_api/endpoints/revisions.py +121 -0
- src/wordpress_api/endpoints/search.py +81 -0
- src/wordpress_api/endpoints/settings.py +55 -0
- src/wordpress_api/endpoints/statuses.py +56 -0
- src/wordpress_api/endpoints/tags.py +79 -0
- src/wordpress_api/endpoints/taxonomies.py +41 -0
- src/wordpress_api/endpoints/themes.py +51 -0
- src/wordpress_api/endpoints/users.py +129 -0
- src/wordpress_api/exceptions.py +79 -0
- src/wordpress_api/models/__init__.py +49 -0
- src/wordpress_api/models/base.py +65 -0
- src/wordpress_api/models/category.py +41 -0
- src/wordpress_api/models/comment.py +75 -0
- src/wordpress_api/models/media.py +108 -0
- src/wordpress_api/models/menu.py +83 -0
- src/wordpress_api/models/page.py +80 -0
- src/wordpress_api/models/plugin.py +36 -0
- src/wordpress_api/models/post.py +112 -0
- src/wordpress_api/models/settings.py +32 -0
- src/wordpress_api/models/tag.py +38 -0
- src/wordpress_api/models/taxonomy.py +49 -0
- src/wordpress_api/models/theme.py +50 -0
- src/wordpress_api/models/user.py +82 -0
- wp_python-0.1.0.dist-info/METADATA +12 -0
- wp_python-0.1.0.dist-info/RECORD +42 -0
- wp_python-0.1.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"""Posts endpoint."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from ..models.post import Post, PostCreate, PostUpdate, PostStatus
|
|
8
|
+
from .base import CRUDEndpoint
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class PostsEndpoint(CRUDEndpoint[Post]):
|
|
12
|
+
"""Endpoint for managing WordPress posts."""
|
|
13
|
+
|
|
14
|
+
_path = "/wp/v2/posts"
|
|
15
|
+
_model_class = Post
|
|
16
|
+
|
|
17
|
+
def list(
|
|
18
|
+
self,
|
|
19
|
+
page: int = 1,
|
|
20
|
+
per_page: int = 10,
|
|
21
|
+
search: str | None = None,
|
|
22
|
+
order: str = "desc",
|
|
23
|
+
orderby: str = "date",
|
|
24
|
+
status: str | PostStatus | list[str] | None = None,
|
|
25
|
+
categories: list[int] | None = None,
|
|
26
|
+
categories_exclude: list[int] | None = None,
|
|
27
|
+
tags: list[int] | None = None,
|
|
28
|
+
tags_exclude: list[int] | None = None,
|
|
29
|
+
author: int | list[int] | None = None,
|
|
30
|
+
author_exclude: list[int] | None = None,
|
|
31
|
+
before: str | None = None,
|
|
32
|
+
after: str | None = None,
|
|
33
|
+
modified_before: str | None = None,
|
|
34
|
+
modified_after: str | None = None,
|
|
35
|
+
slug: str | list[str] | None = None,
|
|
36
|
+
sticky: bool | None = None,
|
|
37
|
+
**kwargs: Any,
|
|
38
|
+
) -> list[Post]:
|
|
39
|
+
"""List posts with filtering options.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
page: Current page of the collection.
|
|
43
|
+
per_page: Maximum number of items to return.
|
|
44
|
+
search: Limit results to those matching a string.
|
|
45
|
+
order: Order sort attribute ascending or descending.
|
|
46
|
+
orderby: Sort collection by attribute.
|
|
47
|
+
status: Limit result set to posts with specific status(es).
|
|
48
|
+
categories: Limit result set to posts with specific categories.
|
|
49
|
+
categories_exclude: Exclude posts with specific categories.
|
|
50
|
+
tags: Limit result set to posts with specific tags.
|
|
51
|
+
tags_exclude: Exclude posts with specific tags.
|
|
52
|
+
author: Limit result set to posts by specific author(s).
|
|
53
|
+
author_exclude: Exclude posts by specific authors.
|
|
54
|
+
before: Limit to posts published before a given date.
|
|
55
|
+
after: Limit to posts published after a given date.
|
|
56
|
+
modified_before: Limit to posts modified before a given date.
|
|
57
|
+
modified_after: Limit to posts modified after a given date.
|
|
58
|
+
slug: Limit result set to posts with specific slug(s).
|
|
59
|
+
sticky: Limit result set to sticky or non-sticky posts.
|
|
60
|
+
"""
|
|
61
|
+
params: dict[str, Any] = {
|
|
62
|
+
"page": page,
|
|
63
|
+
"per_page": per_page,
|
|
64
|
+
"order": order,
|
|
65
|
+
"orderby": orderby,
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if search:
|
|
69
|
+
params["search"] = search
|
|
70
|
+
if status:
|
|
71
|
+
if isinstance(status, PostStatus):
|
|
72
|
+
params["status"] = status.value
|
|
73
|
+
elif isinstance(status, list):
|
|
74
|
+
params["status"] = ",".join(status)
|
|
75
|
+
else:
|
|
76
|
+
params["status"] = status
|
|
77
|
+
if categories:
|
|
78
|
+
params["categories"] = ",".join(map(str, categories))
|
|
79
|
+
if categories_exclude:
|
|
80
|
+
params["categories_exclude"] = ",".join(map(str, categories_exclude))
|
|
81
|
+
if tags:
|
|
82
|
+
params["tags"] = ",".join(map(str, tags))
|
|
83
|
+
if tags_exclude:
|
|
84
|
+
params["tags_exclude"] = ",".join(map(str, tags_exclude))
|
|
85
|
+
if author is not None:
|
|
86
|
+
if isinstance(author, list):
|
|
87
|
+
params["author"] = ",".join(map(str, author))
|
|
88
|
+
else:
|
|
89
|
+
params["author"] = author
|
|
90
|
+
if author_exclude:
|
|
91
|
+
params["author_exclude"] = ",".join(map(str, author_exclude))
|
|
92
|
+
if before:
|
|
93
|
+
params["before"] = before
|
|
94
|
+
if after:
|
|
95
|
+
params["after"] = after
|
|
96
|
+
if modified_before:
|
|
97
|
+
params["modified_before"] = modified_before
|
|
98
|
+
if modified_after:
|
|
99
|
+
params["modified_after"] = modified_after
|
|
100
|
+
if slug:
|
|
101
|
+
if isinstance(slug, list):
|
|
102
|
+
params["slug"] = ",".join(slug)
|
|
103
|
+
else:
|
|
104
|
+
params["slug"] = slug
|
|
105
|
+
if sticky is not None:
|
|
106
|
+
params["sticky"] = sticky
|
|
107
|
+
|
|
108
|
+
params.update(kwargs)
|
|
109
|
+
response = self._get(self._path, params=params)
|
|
110
|
+
return [Post.model_validate(item) for item in response]
|
|
111
|
+
|
|
112
|
+
def create(self, data: PostCreate | dict[str, Any]) -> Post:
|
|
113
|
+
"""Create a new post."""
|
|
114
|
+
return super().create(data)
|
|
115
|
+
|
|
116
|
+
def update(self, id: int, data: PostUpdate | dict[str, Any]) -> Post:
|
|
117
|
+
"""Update an existing post."""
|
|
118
|
+
return super().update(id, data)
|
|
119
|
+
|
|
120
|
+
def trash(self, id: int) -> Post:
|
|
121
|
+
"""Move a post to trash."""
|
|
122
|
+
return self.update(id, PostUpdate(status=PostStatus.TRASH))
|
|
123
|
+
|
|
124
|
+
def restore(self, id: int) -> Post:
|
|
125
|
+
"""Restore a post from trash."""
|
|
126
|
+
return self.update(id, PostUpdate(status=PostStatus.DRAFT))
|
|
127
|
+
|
|
128
|
+
def get_by_slug(self, slug: str) -> Post | None:
|
|
129
|
+
"""Get a post by its slug."""
|
|
130
|
+
posts = self.list(slug=slug)
|
|
131
|
+
return posts[0] if posts else None
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"""Revisions endpoint."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from pydantic import BaseModel, Field, field_validator
|
|
9
|
+
|
|
10
|
+
from ..models.base import RenderedContent, parse_datetime
|
|
11
|
+
from .base import BaseEndpoint
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Revision(BaseModel):
|
|
15
|
+
"""WordPress Revision object."""
|
|
16
|
+
|
|
17
|
+
id: int
|
|
18
|
+
author: int = 0
|
|
19
|
+
date: datetime | None = None
|
|
20
|
+
date_gmt: datetime | None = None
|
|
21
|
+
guid: RenderedContent | None = None
|
|
22
|
+
modified: datetime | None = None
|
|
23
|
+
modified_gmt: datetime | None = None
|
|
24
|
+
parent: int = 0
|
|
25
|
+
slug: str = ""
|
|
26
|
+
title: RenderedContent | None = None
|
|
27
|
+
content: RenderedContent | None = None
|
|
28
|
+
excerpt: RenderedContent | None = None
|
|
29
|
+
|
|
30
|
+
@field_validator("date", "date_gmt", "modified", "modified_gmt", mode="before")
|
|
31
|
+
@classmethod
|
|
32
|
+
def parse_dates(cls, v: Any) -> datetime | None:
|
|
33
|
+
return parse_datetime(v)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class RevisionsEndpoint(BaseEndpoint):
|
|
37
|
+
"""Endpoint for managing post and page revisions."""
|
|
38
|
+
|
|
39
|
+
def list_post_revisions(
|
|
40
|
+
self,
|
|
41
|
+
post_id: int,
|
|
42
|
+
page: int = 1,
|
|
43
|
+
per_page: int = 10,
|
|
44
|
+
**kwargs: Any,
|
|
45
|
+
) -> list[Revision]:
|
|
46
|
+
"""List revisions for a post."""
|
|
47
|
+
params: dict[str, Any] = {
|
|
48
|
+
"page": page,
|
|
49
|
+
"per_page": per_page,
|
|
50
|
+
}
|
|
51
|
+
params.update(kwargs)
|
|
52
|
+
|
|
53
|
+
response = self._get(f"/wp/v2/posts/{post_id}/revisions", params=params)
|
|
54
|
+
return [Revision.model_validate(item) for item in response]
|
|
55
|
+
|
|
56
|
+
def get_post_revision(self, post_id: int, revision_id: int) -> Revision:
|
|
57
|
+
"""Get a specific post revision."""
|
|
58
|
+
response = self._get(f"/wp/v2/posts/{post_id}/revisions/{revision_id}")
|
|
59
|
+
return Revision.model_validate(response)
|
|
60
|
+
|
|
61
|
+
def delete_post_revision(
|
|
62
|
+
self,
|
|
63
|
+
post_id: int,
|
|
64
|
+
revision_id: int,
|
|
65
|
+
force: bool = True,
|
|
66
|
+
) -> dict[str, Any]:
|
|
67
|
+
"""Delete a post revision."""
|
|
68
|
+
return self._delete(
|
|
69
|
+
f"/wp/v2/posts/{post_id}/revisions/{revision_id}",
|
|
70
|
+
params={"force": force},
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
def list_page_revisions(
|
|
74
|
+
self,
|
|
75
|
+
page_id: int,
|
|
76
|
+
page: int = 1,
|
|
77
|
+
per_page: int = 10,
|
|
78
|
+
**kwargs: Any,
|
|
79
|
+
) -> list[Revision]:
|
|
80
|
+
"""List revisions for a page."""
|
|
81
|
+
params: dict[str, Any] = {
|
|
82
|
+
"page": page,
|
|
83
|
+
"per_page": per_page,
|
|
84
|
+
}
|
|
85
|
+
params.update(kwargs)
|
|
86
|
+
|
|
87
|
+
response = self._get(f"/wp/v2/pages/{page_id}/revisions", params=params)
|
|
88
|
+
return [Revision.model_validate(item) for item in response]
|
|
89
|
+
|
|
90
|
+
def get_page_revision(self, page_id: int, revision_id: int) -> Revision:
|
|
91
|
+
"""Get a specific page revision."""
|
|
92
|
+
response = self._get(f"/wp/v2/pages/{page_id}/revisions/{revision_id}")
|
|
93
|
+
return Revision.model_validate(response)
|
|
94
|
+
|
|
95
|
+
def delete_page_revision(
|
|
96
|
+
self,
|
|
97
|
+
page_id: int,
|
|
98
|
+
revision_id: int,
|
|
99
|
+
force: bool = True,
|
|
100
|
+
) -> dict[str, Any]:
|
|
101
|
+
"""Delete a page revision."""
|
|
102
|
+
return self._delete(
|
|
103
|
+
f"/wp/v2/pages/{page_id}/revisions/{revision_id}",
|
|
104
|
+
params={"force": force},
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
def get_latest_revision(self, post_id: int, post_type: str = "posts") -> Revision | None:
|
|
108
|
+
"""Get the most recent revision."""
|
|
109
|
+
if post_type == "pages":
|
|
110
|
+
revisions = self.list_page_revisions(post_id, per_page=1)
|
|
111
|
+
else:
|
|
112
|
+
revisions = self.list_post_revisions(post_id, per_page=1)
|
|
113
|
+
return revisions[0] if revisions else None
|
|
114
|
+
|
|
115
|
+
def count_revisions(self, post_id: int, post_type: str = "posts") -> int:
|
|
116
|
+
"""Count the number of revisions for a post or page."""
|
|
117
|
+
if post_type == "pages":
|
|
118
|
+
all_revisions = self.list_page_revisions(post_id, per_page=100)
|
|
119
|
+
else:
|
|
120
|
+
all_revisions = self.list_post_revisions(post_id, per_page=100)
|
|
121
|
+
return len(all_revisions)
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""Search endpoint."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel
|
|
8
|
+
|
|
9
|
+
from .base import BaseEndpoint
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class SearchResult(BaseModel):
|
|
13
|
+
"""WordPress search result."""
|
|
14
|
+
|
|
15
|
+
id: int
|
|
16
|
+
title: str = ""
|
|
17
|
+
url: str = ""
|
|
18
|
+
type: str = ""
|
|
19
|
+
subtype: str = ""
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class SearchEndpoint(BaseEndpoint):
|
|
23
|
+
"""Endpoint for WordPress site-wide search."""
|
|
24
|
+
|
|
25
|
+
_path = "/wp/v2/search"
|
|
26
|
+
|
|
27
|
+
def search(
|
|
28
|
+
self,
|
|
29
|
+
search: str,
|
|
30
|
+
page: int = 1,
|
|
31
|
+
per_page: int = 10,
|
|
32
|
+
type: str | None = None,
|
|
33
|
+
subtype: str | list[str] | None = None,
|
|
34
|
+
**kwargs: Any,
|
|
35
|
+
) -> list[SearchResult]:
|
|
36
|
+
"""Search the site.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
search: Search query string.
|
|
40
|
+
page: Current page of the collection.
|
|
41
|
+
per_page: Maximum number of items to return.
|
|
42
|
+
type: Limit results to a specific type (post, term, post-format).
|
|
43
|
+
subtype: Limit results to specific subtypes (post, page, category, etc.).
|
|
44
|
+
"""
|
|
45
|
+
params: dict[str, Any] = {
|
|
46
|
+
"search": search,
|
|
47
|
+
"page": page,
|
|
48
|
+
"per_page": per_page,
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if type:
|
|
52
|
+
params["type"] = type
|
|
53
|
+
if subtype:
|
|
54
|
+
if isinstance(subtype, list):
|
|
55
|
+
params["subtype"] = ",".join(subtype)
|
|
56
|
+
else:
|
|
57
|
+
params["subtype"] = subtype
|
|
58
|
+
params.update(kwargs)
|
|
59
|
+
|
|
60
|
+
response = self._get(self._path, params=params)
|
|
61
|
+
return [SearchResult.model_validate(item) for item in response]
|
|
62
|
+
|
|
63
|
+
def search_posts(self, query: str, **kwargs: Any) -> list[SearchResult]:
|
|
64
|
+
"""Search only posts."""
|
|
65
|
+
return self.search(query, type="post", subtype="post", **kwargs)
|
|
66
|
+
|
|
67
|
+
def search_pages(self, query: str, **kwargs: Any) -> list[SearchResult]:
|
|
68
|
+
"""Search only pages."""
|
|
69
|
+
return self.search(query, type="post", subtype="page", **kwargs)
|
|
70
|
+
|
|
71
|
+
def search_categories(self, query: str, **kwargs: Any) -> list[SearchResult]:
|
|
72
|
+
"""Search only categories."""
|
|
73
|
+
return self.search(query, type="term", subtype="category", **kwargs)
|
|
74
|
+
|
|
75
|
+
def search_tags(self, query: str, **kwargs: Any) -> list[SearchResult]:
|
|
76
|
+
"""Search only tags."""
|
|
77
|
+
return self.search(query, type="term", subtype="post_tag", **kwargs)
|
|
78
|
+
|
|
79
|
+
def search_all_content(self, query: str, **kwargs: Any) -> list[SearchResult]:
|
|
80
|
+
"""Search all post types (posts, pages, custom post types)."""
|
|
81
|
+
return self.search(query, type="post", **kwargs)
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""Settings endpoint."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from ..models.settings import Settings
|
|
8
|
+
from .base import BaseEndpoint
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class SettingsEndpoint(BaseEndpoint):
|
|
12
|
+
"""Endpoint for managing WordPress site settings."""
|
|
13
|
+
|
|
14
|
+
_path = "/wp/v2/settings"
|
|
15
|
+
|
|
16
|
+
def get(self) -> Settings:
|
|
17
|
+
"""Get all site settings."""
|
|
18
|
+
response = self._get(self._path)
|
|
19
|
+
return Settings.model_validate(response)
|
|
20
|
+
|
|
21
|
+
def update(self, **settings: Any) -> Settings:
|
|
22
|
+
"""Update site settings.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
**settings: Key-value pairs of settings to update.
|
|
26
|
+
"""
|
|
27
|
+
response = self._post(self._path, data=settings)
|
|
28
|
+
return Settings.model_validate(response)
|
|
29
|
+
|
|
30
|
+
def get_title(self) -> str:
|
|
31
|
+
"""Get the site title."""
|
|
32
|
+
settings = self.get()
|
|
33
|
+
return settings.title
|
|
34
|
+
|
|
35
|
+
def set_title(self, title: str) -> Settings:
|
|
36
|
+
"""Set the site title."""
|
|
37
|
+
return self.update(title=title)
|
|
38
|
+
|
|
39
|
+
def get_description(self) -> str:
|
|
40
|
+
"""Get the site description (tagline)."""
|
|
41
|
+
settings = self.get()
|
|
42
|
+
return settings.description
|
|
43
|
+
|
|
44
|
+
def set_description(self, description: str) -> Settings:
|
|
45
|
+
"""Set the site description (tagline)."""
|
|
46
|
+
return self.update(description=description)
|
|
47
|
+
|
|
48
|
+
def get_timezone(self) -> str:
|
|
49
|
+
"""Get the site timezone."""
|
|
50
|
+
settings = self.get()
|
|
51
|
+
return settings.timezone
|
|
52
|
+
|
|
53
|
+
def set_timezone(self, timezone: str) -> Settings:
|
|
54
|
+
"""Set the site timezone."""
|
|
55
|
+
return self.update(timezone=timezone)
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"""Post Statuses endpoint."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel
|
|
8
|
+
|
|
9
|
+
from .base import BaseEndpoint
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class PostStatusInfo(BaseModel):
|
|
13
|
+
"""WordPress Post Status object."""
|
|
14
|
+
|
|
15
|
+
name: str = ""
|
|
16
|
+
slug: str = ""
|
|
17
|
+
public: bool = True
|
|
18
|
+
queryable: bool = True
|
|
19
|
+
date_floating: bool = False
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class StatusesEndpoint(BaseEndpoint):
|
|
23
|
+
"""Endpoint for viewing WordPress post statuses."""
|
|
24
|
+
|
|
25
|
+
_path = "/wp/v2/statuses"
|
|
26
|
+
|
|
27
|
+
def list(self, **kwargs: Any) -> dict[str, PostStatusInfo]:
|
|
28
|
+
"""List all registered post statuses."""
|
|
29
|
+
response = self._get(self._path, params=kwargs)
|
|
30
|
+
return {k: PostStatusInfo.model_validate(v) for k, v in response.items()}
|
|
31
|
+
|
|
32
|
+
def get(self, status: str, **kwargs: Any) -> PostStatusInfo:
|
|
33
|
+
"""Get a specific post status by slug."""
|
|
34
|
+
response = self._get(f"{self._path}/{status}", params=kwargs)
|
|
35
|
+
return PostStatusInfo.model_validate(response)
|
|
36
|
+
|
|
37
|
+
def get_publish(self) -> PostStatusInfo:
|
|
38
|
+
"""Get the 'publish' status."""
|
|
39
|
+
return self.get("publish")
|
|
40
|
+
|
|
41
|
+
def get_draft(self) -> PostStatusInfo:
|
|
42
|
+
"""Get the 'draft' status."""
|
|
43
|
+
return self.get("draft")
|
|
44
|
+
|
|
45
|
+
def get_pending(self) -> PostStatusInfo:
|
|
46
|
+
"""Get the 'pending' status."""
|
|
47
|
+
return self.get("pending")
|
|
48
|
+
|
|
49
|
+
def get_private(self) -> PostStatusInfo:
|
|
50
|
+
"""Get the 'private' status."""
|
|
51
|
+
return self.get("private")
|
|
52
|
+
|
|
53
|
+
def list_public(self) -> dict[str, PostStatusInfo]:
|
|
54
|
+
"""List only public post statuses."""
|
|
55
|
+
all_statuses = self.list()
|
|
56
|
+
return {k: v for k, v in all_statuses.items() if v.public}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""Tags endpoint."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from ..models.tag import Tag, TagCreate, TagUpdate
|
|
8
|
+
from .base import CRUDEndpoint
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class TagsEndpoint(CRUDEndpoint[Tag]):
|
|
12
|
+
"""Endpoint for managing WordPress tags."""
|
|
13
|
+
|
|
14
|
+
_path = "/wp/v2/tags"
|
|
15
|
+
_model_class = Tag
|
|
16
|
+
|
|
17
|
+
def list(
|
|
18
|
+
self,
|
|
19
|
+
page: int = 1,
|
|
20
|
+
per_page: int = 10,
|
|
21
|
+
search: str | None = None,
|
|
22
|
+
order: str = "asc",
|
|
23
|
+
orderby: str = "name",
|
|
24
|
+
hide_empty: bool = False,
|
|
25
|
+
post: int | None = None,
|
|
26
|
+
slug: str | list[str] | None = None,
|
|
27
|
+
include: list[int] | None = None,
|
|
28
|
+
exclude: list[int] | None = None,
|
|
29
|
+
**kwargs: Any,
|
|
30
|
+
) -> list[Tag]:
|
|
31
|
+
"""List tags with filtering options."""
|
|
32
|
+
params: dict[str, Any] = {
|
|
33
|
+
"page": page,
|
|
34
|
+
"per_page": per_page,
|
|
35
|
+
"order": order,
|
|
36
|
+
"orderby": orderby,
|
|
37
|
+
"hide_empty": hide_empty,
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if search:
|
|
41
|
+
params["search"] = search
|
|
42
|
+
if post is not None:
|
|
43
|
+
params["post"] = post
|
|
44
|
+
if slug:
|
|
45
|
+
if isinstance(slug, list):
|
|
46
|
+
params["slug"] = ",".join(slug)
|
|
47
|
+
else:
|
|
48
|
+
params["slug"] = slug
|
|
49
|
+
if include:
|
|
50
|
+
params["include"] = ",".join(map(str, include))
|
|
51
|
+
if exclude:
|
|
52
|
+
params["exclude"] = ",".join(map(str, exclude))
|
|
53
|
+
|
|
54
|
+
params.update(kwargs)
|
|
55
|
+
response = self._get(self._path, params=params)
|
|
56
|
+
return [Tag.model_validate(item) for item in response]
|
|
57
|
+
|
|
58
|
+
def create(self, data: TagCreate | dict[str, Any]) -> Tag:
|
|
59
|
+
"""Create a new tag."""
|
|
60
|
+
return super().create(data)
|
|
61
|
+
|
|
62
|
+
def update(self, id: int, data: TagUpdate | dict[str, Any]) -> Tag:
|
|
63
|
+
"""Update an existing tag."""
|
|
64
|
+
return super().update(id, data)
|
|
65
|
+
|
|
66
|
+
def get_by_slug(self, slug: str) -> Tag | None:
|
|
67
|
+
"""Get a tag by its slug."""
|
|
68
|
+
tags = self.list(slug=slug)
|
|
69
|
+
return tags[0] if tags else None
|
|
70
|
+
|
|
71
|
+
def get_for_post(self, post_id: int) -> list[Tag]:
|
|
72
|
+
"""Get tags assigned to a specific post."""
|
|
73
|
+
return self.list(post=post_id)
|
|
74
|
+
|
|
75
|
+
def get_popular(self, limit: int = 10) -> list[Tag]:
|
|
76
|
+
"""Get the most popular tags by post count."""
|
|
77
|
+
all_tags = list(self.iterate_all(hide_empty=True))
|
|
78
|
+
sorted_tags = sorted(all_tags, key=lambda t: t.count, reverse=True)
|
|
79
|
+
return sorted_tags[:limit]
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""Taxonomies endpoint."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from ..models.taxonomy import Taxonomy
|
|
8
|
+
from .base import BaseEndpoint
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class TaxonomiesEndpoint(BaseEndpoint):
|
|
12
|
+
"""Endpoint for viewing WordPress taxonomies."""
|
|
13
|
+
|
|
14
|
+
_path = "/wp/v2/taxonomies"
|
|
15
|
+
|
|
16
|
+
def list(self, type: str | None = None, **kwargs: Any) -> dict[str, Taxonomy]:
|
|
17
|
+
"""List all taxonomies.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
type: Limit results to taxonomies associated with a post type.
|
|
21
|
+
"""
|
|
22
|
+
params: dict[str, Any] = {}
|
|
23
|
+
if type:
|
|
24
|
+
params["type"] = type
|
|
25
|
+
params.update(kwargs)
|
|
26
|
+
|
|
27
|
+
response = self._get(self._path, params=params)
|
|
28
|
+
return {k: Taxonomy.model_validate(v) for k, v in response.items()}
|
|
29
|
+
|
|
30
|
+
def get(self, taxonomy: str, **kwargs: Any) -> Taxonomy:
|
|
31
|
+
"""Get a specific taxonomy by slug."""
|
|
32
|
+
response = self._get(f"{self._path}/{taxonomy}", params=kwargs)
|
|
33
|
+
return Taxonomy.model_validate(response)
|
|
34
|
+
|
|
35
|
+
def get_category(self) -> Taxonomy:
|
|
36
|
+
"""Get the category taxonomy."""
|
|
37
|
+
return self.get("category")
|
|
38
|
+
|
|
39
|
+
def get_post_tag(self) -> Taxonomy:
|
|
40
|
+
"""Get the post_tag taxonomy."""
|
|
41
|
+
return self.get("post_tag")
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""Themes endpoint."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from ..models.theme import Theme
|
|
8
|
+
from .base import BaseEndpoint
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ThemesEndpoint(BaseEndpoint):
|
|
12
|
+
"""Endpoint for managing WordPress themes."""
|
|
13
|
+
|
|
14
|
+
_path = "/wp/v2/themes"
|
|
15
|
+
|
|
16
|
+
def list(self, status: str | list[str] | None = None, **kwargs: Any) -> list[Theme]:
|
|
17
|
+
"""List installed themes.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
status: Filter by theme status (active, inactive).
|
|
21
|
+
"""
|
|
22
|
+
params: dict[str, Any] = {}
|
|
23
|
+
if status:
|
|
24
|
+
if isinstance(status, list):
|
|
25
|
+
params["status"] = ",".join(status)
|
|
26
|
+
else:
|
|
27
|
+
params["status"] = status
|
|
28
|
+
params.update(kwargs)
|
|
29
|
+
|
|
30
|
+
response = self._get(self._path, params=params)
|
|
31
|
+
return [Theme.model_validate(item) for item in response]
|
|
32
|
+
|
|
33
|
+
def get(self, stylesheet: str, **kwargs: Any) -> Theme:
|
|
34
|
+
"""Get a specific theme.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
stylesheet: Theme stylesheet name.
|
|
38
|
+
"""
|
|
39
|
+
response = self._get(f"{self._path}/{stylesheet}", params=kwargs)
|
|
40
|
+
return Theme.model_validate(response)
|
|
41
|
+
|
|
42
|
+
def get_active(self) -> Theme:
|
|
43
|
+
"""Get the currently active theme."""
|
|
44
|
+
themes = self.list(status="active")
|
|
45
|
+
if themes:
|
|
46
|
+
return themes[0]
|
|
47
|
+
raise ValueError("No active theme found")
|
|
48
|
+
|
|
49
|
+
def list_inactive(self) -> list[Theme]:
|
|
50
|
+
"""List all inactive themes."""
|
|
51
|
+
return self.list(status="inactive")
|