plone.api 2.2.4__tar.gz → 2.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.
- {plone_api-2.2.4 → plone_api-2.3.0}/CHANGES.rst +42 -0
- {plone_api-2.2.4 → plone_api-2.3.0}/MANIFEST.in +2 -0
- {plone_api-2.2.4 → plone_api-2.3.0}/PKG-INFO +44 -2
- {plone_api-2.2.4 → plone_api-2.3.0}/docs/content.md +56 -3
- {plone_api-2.2.4 → plone_api-2.3.0}/docs/contribute.md +27 -44
- {plone_api-2.2.4 → plone_api-2.3.0}/docs/portal.md +44 -1
- {plone_api-2.2.4 → plone_api-2.3.0}/pyproject.toml +2 -2
- {plone_api-2.2.4 → plone_api-2.3.0}/setup.py +2 -2
- {plone_api-2.2.4 → plone_api-2.3.0}/src/plone/api/content.py +36 -1
- {plone_api-2.2.4 → plone_api-2.3.0}/src/plone/api/portal.py +43 -2
- {plone_api-2.2.4 → plone_api-2.3.0}/src/plone/api/relation.py +1 -1
- {plone_api-2.2.4 → plone_api-2.3.0}/src/plone/api/testing.zcml +1 -1
- {plone_api-2.2.4 → plone_api-2.3.0}/src/plone/api/tests/doctests/content.md +56 -3
- plone_api-2.3.0/src/plone/api/tests/doctests/contribute.md +217 -0
- {plone_api-2.2.4 → plone_api-2.3.0}/src/plone/api/tests/doctests/portal.md +44 -1
- {plone_api-2.2.4 → plone_api-2.3.0}/src/plone/api/tests/test_content.py +75 -2
- {plone_api-2.2.4 → plone_api-2.3.0}/src/plone/api/tests/test_doctests.py +1 -1
- {plone_api-2.2.4 → plone_api-2.3.0}/src/plone/api/tests/test_portal.py +70 -1
- {plone_api-2.2.4 → plone_api-2.3.0}/src/plone.api.egg-info/PKG-INFO +44 -2
- {plone_api-2.2.4 → plone_api-2.3.0}/src/plone.api.egg-info/SOURCES.txt +1 -0
- {plone_api-2.2.4 → plone_api-2.3.0}/src/plone.api.egg-info/requires.txt +1 -1
- {plone_api-2.2.4 → plone_api-2.3.0}/tox.ini +13 -1
- {plone_api-2.2.4 → plone_api-2.3.0}/CONTRIBUTING.rst +0 -0
- {plone_api-2.2.4 → plone_api-2.3.0}/LICENSE +0 -0
- {plone_api-2.2.4 → plone_api-2.3.0}/README.md +0 -0
- {plone_api-2.2.4 → plone_api-2.3.0}/docs/about.md +0 -0
- {plone_api-2.2.4 → plone_api-2.3.0}/docs/env.md +0 -0
- {plone_api-2.2.4 → plone_api-2.3.0}/docs/group.md +0 -0
- {plone_api-2.2.4 → plone_api-2.3.0}/docs/index.md +0 -0
- {plone_api-2.2.4 → plone_api-2.3.0}/docs/relation.md +0 -0
- {plone_api-2.2.4 → plone_api-2.3.0}/docs/user.md +0 -0
- {plone_api-2.2.4 → plone_api-2.3.0}/setup.cfg +0 -0
- {plone_api-2.2.4 → plone_api-2.3.0}/src/plone/__init__.py +0 -0
- {plone_api-2.2.4 → plone_api-2.3.0}/src/plone/api/__init__.py +0 -0
- {plone_api-2.2.4 → plone_api-2.3.0}/src/plone/api/configure.zcml +0 -0
- {plone_api-2.2.4 → plone_api-2.3.0}/src/plone/api/env.py +0 -0
- {plone_api-2.2.4 → plone_api-2.3.0}/src/plone/api/exc.py +0 -0
- {plone_api-2.2.4 → plone_api-2.3.0}/src/plone/api/group.py +0 -0
- {plone_api-2.2.4 → plone_api-2.3.0}/src/plone/api/profiles/testfixture/metadata.xml +0 -0
- {plone_api-2.2.4 → plone_api-2.3.0}/src/plone/api/profiles/testfixture/types/Dexterity_Folder.xml +0 -0
- {plone_api-2.2.4 → plone_api-2.3.0}/src/plone/api/profiles/testfixture/types/Dexterity_Item.xml +0 -0
- {plone_api-2.2.4 → plone_api-2.3.0}/src/plone/api/profiles/testfixture/types.xml +0 -0
- {plone_api-2.2.4 → plone_api-2.3.0}/src/plone/api/tests/Dexterity_Folder.xml +0 -0
- {plone_api-2.2.4 → plone_api-2.3.0}/src/plone/api/tests/Dexterity_Item.xml +0 -0
- {plone_api-2.2.4 → plone_api-2.3.0}/src/plone/api/tests/__init__.py +0 -0
- {plone_api-2.2.4 → plone_api-2.3.0}/src/plone/api/tests/base.py +0 -0
- {plone_api-2.2.4 → plone_api-2.3.0}/src/plone/api/tests/doctests/about.md +0 -0
- {plone_api-2.2.4 → plone_api-2.3.0}/src/plone/api/tests/doctests/env.md +0 -0
- {plone_api-2.2.4 → plone_api-2.3.0}/src/plone/api/tests/doctests/group.md +0 -0
- {plone_api-2.2.4 → plone_api-2.3.0}/src/plone/api/tests/doctests/relation.md +0 -0
- {plone_api-2.2.4 → plone_api-2.3.0}/src/plone/api/tests/doctests/user.md +0 -0
- {plone_api-2.2.4 → plone_api-2.3.0}/src/plone/api/tests/test_env.py +0 -0
- {plone_api-2.2.4 → plone_api-2.3.0}/src/plone/api/tests/test_group.py +0 -0
- {plone_api-2.2.4 → plone_api-2.3.0}/src/plone/api/tests/test_relation.py +0 -0
- {plone_api-2.2.4 → plone_api-2.3.0}/src/plone/api/tests/test_user.py +0 -0
- {plone_api-2.2.4 → plone_api-2.3.0}/src/plone/api/tests/test_validation.py +0 -0
- {plone_api-2.2.4 → plone_api-2.3.0}/src/plone/api/user.py +0 -0
- {plone_api-2.2.4 → plone_api-2.3.0}/src/plone/api/validation.py +0 -0
- {plone_api-2.2.4 → plone_api-2.3.0}/src/plone.api.egg-info/dependency_links.txt +0 -0
- {plone_api-2.2.4 → plone_api-2.3.0}/src/plone.api.egg-info/namespace_packages.txt +0 -0
- {plone_api-2.2.4 → plone_api-2.3.0}/src/plone.api.egg-info/not-zip-safe +0 -0
- {plone_api-2.2.4 → plone_api-2.3.0}/src/plone.api.egg-info/top_level.txt +0 -0
|
@@ -8,6 +8,48 @@ Changelog
|
|
|
8
8
|
|
|
9
9
|
.. towncrier release notes start
|
|
10
10
|
|
|
11
|
+
2.3.0 (2025-02-21)
|
|
12
|
+
------------------
|
|
13
|
+
|
|
14
|
+
New features:
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
- Added the content API helper function ``api.content.get_path``, which gets either the relative or absolute path of an object. @ujsquared (#532)
|
|
18
|
+
- Added two new portal API functions:
|
|
19
|
+
- ``api.portal.get_vocabulary``: Get a vocabulary by name
|
|
20
|
+
- ``api.portal.get_vocabulary_names``: Get a list of all available vocabulary names
|
|
21
|
+
@ujsquared (#533)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
Internal:
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
- Making it easier for new contributors to get started with a simple Makefile and a tidy 'contributing' chapter. @ksuess (#558)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
2.2.5 (2025-01-24)
|
|
31
|
+
------------------
|
|
32
|
+
|
|
33
|
+
Bug fixes:
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
- Fix api.content.get(path=path) when a item in the path is not accessible to the user.
|
|
37
|
+
[pbauer] (#549)
|
|
38
|
+
- Fix DeprecationWarnings. [maurits] (#4090)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
Documentation:
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
- Preview docs on Read the Docs instead of Netlify. @stevepiercy (#545)
|
|
45
|
+
- Remove Netlify stuff, follow up to #545. @stevepiercy
|
|
46
|
+
- Sort and remove duplicate entries in `pyproject.toml`
|
|
47
|
+
- Remove unused docs requirements.
|
|
48
|
+
- Fix comments and remove unnecessary steps from `tox.ini`.
|
|
49
|
+
- Enable copy button for code blocks.
|
|
50
|
+
- Add linkcheck to documentation of documentation. (#546)
|
|
51
|
+
|
|
52
|
+
|
|
11
53
|
2.2.4 (2024-12-16)
|
|
12
54
|
------------------
|
|
13
55
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: plone.api
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.0
|
|
4
4
|
Summary: A Plone API.
|
|
5
5
|
Home-page: https://github.com/plone/plone.api
|
|
6
6
|
Author: Plone Foundation
|
|
@@ -34,8 +34,8 @@ Requires-Dist: decorator
|
|
|
34
34
|
Requires-Dist: plone.app.uuid
|
|
35
35
|
Requires-Dist: plone.app.dexterity
|
|
36
36
|
Requires-Dist: plone.app.intid
|
|
37
|
-
Requires-Dist: plone.app.layout
|
|
38
37
|
Requires-Dist: plone.app.linkintegrity
|
|
38
|
+
Requires-Dist: plone.base
|
|
39
39
|
Requires-Dist: plone.dexterity
|
|
40
40
|
Requires-Dist: plone.i18n
|
|
41
41
|
Requires-Dist: plone.registry
|
|
@@ -110,6 +110,48 @@ Changelog
|
|
|
110
110
|
|
|
111
111
|
.. towncrier release notes start
|
|
112
112
|
|
|
113
|
+
2.3.0 (2025-02-21)
|
|
114
|
+
------------------
|
|
115
|
+
|
|
116
|
+
New features:
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
- Added the content API helper function ``api.content.get_path``, which gets either the relative or absolute path of an object. @ujsquared (#532)
|
|
120
|
+
- Added two new portal API functions:
|
|
121
|
+
- ``api.portal.get_vocabulary``: Get a vocabulary by name
|
|
122
|
+
- ``api.portal.get_vocabulary_names``: Get a list of all available vocabulary names
|
|
123
|
+
@ujsquared (#533)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
Internal:
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
- Making it easier for new contributors to get started with a simple Makefile and a tidy 'contributing' chapter. @ksuess (#558)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
2.2.5 (2025-01-24)
|
|
133
|
+
------------------
|
|
134
|
+
|
|
135
|
+
Bug fixes:
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
- Fix api.content.get(path=path) when a item in the path is not accessible to the user.
|
|
139
|
+
[pbauer] (#549)
|
|
140
|
+
- Fix DeprecationWarnings. [maurits] (#4090)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
Documentation:
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
- Preview docs on Read the Docs instead of Netlify. @stevepiercy (#545)
|
|
147
|
+
- Remove Netlify stuff, follow up to #545. @stevepiercy
|
|
148
|
+
- Sort and remove duplicate entries in `pyproject.toml`
|
|
149
|
+
- Remove unused docs requirements.
|
|
150
|
+
- Fix comments and remove unnecessary steps from `tox.ini`.
|
|
151
|
+
- Enable copy button for code blocks.
|
|
152
|
+
- Add linkcheck to documentation of documentation. (#546)
|
|
153
|
+
|
|
154
|
+
|
|
113
155
|
2.2.4 (2024-12-16)
|
|
114
156
|
------------------
|
|
115
157
|
|
|
@@ -78,7 +78,7 @@ plone (portal root)
|
|
|
78
78
|
% api.content.create(container=events, type='Event', id='conference')
|
|
79
79
|
% api.content.create(container=events, type='Event', id='sprint')
|
|
80
80
|
|
|
81
|
-
The following operations will get objects from the structure above,
|
|
81
|
+
The following operations will get objects from the structure above, using {meth}`api.content.get`.
|
|
82
82
|
|
|
83
83
|
```python
|
|
84
84
|
# let's first get the portal object
|
|
@@ -118,7 +118,7 @@ not_found = api.content.get(UID='notfound')
|
|
|
118
118
|
|
|
119
119
|
## Find content objects
|
|
120
120
|
|
|
121
|
-
You can use the find function to search for content.
|
|
121
|
+
You can use the {func}`api.content.find` function to search for content.
|
|
122
122
|
|
|
123
123
|
Finding all Documents:
|
|
124
124
|
|
|
@@ -460,7 +460,6 @@ portal = api.portal.get()
|
|
|
460
460
|
api.content.transition(obj=portal['about'], transition='reject', comment='You had a typo on your page.')
|
|
461
461
|
```
|
|
462
462
|
|
|
463
|
-
|
|
464
463
|
(content-disable-roles-acquisition-example)=
|
|
465
464
|
|
|
466
465
|
## Disable local roles acquisition
|
|
@@ -536,6 +535,60 @@ view = api.content.get_view(
|
|
|
536
535
|
%
|
|
537
536
|
% self.assertEqual(view.__name__, u'plone')
|
|
538
537
|
|
|
538
|
+
(content-get-path-example)=
|
|
539
|
+
|
|
540
|
+
## Get content path
|
|
541
|
+
|
|
542
|
+
To get the path of a content object, use {func}`api.content.get_path`.
|
|
543
|
+
This function accepts an object for which you want to get its path as the required parameter `obj`, and an optional boolean parameter `relative` whose default is `False`.
|
|
544
|
+
|
|
545
|
+
It returns either an absolute path from the Zope root by default or when `relative` is set to `False`, or a relative path from the portal root when `relative` is set to `True`.
|
|
546
|
+
|
|
547
|
+
The following example shows how to get the absolute path from the Zope root.
|
|
548
|
+
|
|
549
|
+
```python
|
|
550
|
+
from plone import api
|
|
551
|
+
portal = api.portal.get()
|
|
552
|
+
|
|
553
|
+
folder = portal['events']['training']
|
|
554
|
+
path = api.content.get_path(obj=folder)
|
|
555
|
+
assert path == '/plone/events/training'
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
The following example shows how to get the portal-relative path.
|
|
559
|
+
|
|
560
|
+
```python
|
|
561
|
+
rel_path = api.content.get_path(obj=folder, relative=True)
|
|
562
|
+
assert rel_path == 'events/training'
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
If the API is used to fetch an object with the `relative` parameter set as `True`, and the object is outside the portal, it throws an `InvalidParameterError` error.
|
|
566
|
+
|
|
567
|
+
% invisible-code-block: python
|
|
568
|
+
%
|
|
569
|
+
% # Setup an object outside portal for testing error case
|
|
570
|
+
% app = portal.aq_parent
|
|
571
|
+
% app.manage_addFolder('outside_folder')
|
|
572
|
+
%
|
|
573
|
+
% # Test that getting relative path for object outside portal raises error
|
|
574
|
+
% from plone.api.exc import InvalidParameterError
|
|
575
|
+
% with self.assertRaises(InvalidParameterError):
|
|
576
|
+
% api.content.get_path(obj=app.outside_folder, relative=True)
|
|
577
|
+
|
|
578
|
+
```python
|
|
579
|
+
from plone.api.exc import InvalidParameterError
|
|
580
|
+
|
|
581
|
+
# Getting path of an object outside portal raises InvalidParameterError
|
|
582
|
+
try:
|
|
583
|
+
outside_path = api.content.get_path(
|
|
584
|
+
obj=app.outside_folder,
|
|
585
|
+
relative=True
|
|
586
|
+
)
|
|
587
|
+
assert False, "Should raise InvalidParameterError and not reach this code"
|
|
588
|
+
except InvalidParameterError as e:
|
|
589
|
+
assert "Object not in portal path" in str(e)
|
|
590
|
+
```
|
|
591
|
+
|
|
539
592
|
## Further reading
|
|
540
593
|
|
|
541
594
|
For more information on possible flags and usage options please see the full {ref}`plone-api-content` specification.
|
|
@@ -18,65 +18,42 @@ Prepare your system by installing prerequisites.
|
|
|
18
18
|
|
|
19
19
|
### System libraries
|
|
20
20
|
|
|
21
|
-
You need to install system libraries, as described in {ref}`plone:plone-prerequisites-label
|
|
21
|
+
You need to install system libraries, as described in {ref}`plone:plone-prerequisites-label`.
|
|
22
22
|
|
|
23
|
-
### tox
|
|
24
|
-
|
|
25
|
-
[tox](https://tox.wiki/en/stable/index.html) automates and standardizes testing in Python.
|
|
26
|
-
Install tox into your Python user space with the following command.
|
|
27
|
-
|
|
28
|
-
```shell
|
|
29
|
-
python -m pip install --user tox
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
### pre-commit
|
|
33
|
-
|
|
34
|
-
`plone.api` uses [pre-commit](https://pre-commit.com/) to automate code quality checks before every commit.
|
|
35
|
-
|
|
36
|
-
Install pre-commit either with your system package manager.
|
|
37
|
-
Alternatively you can install pre-commit into your Python user.
|
|
38
|
-
|
|
39
|
-
```shell
|
|
40
|
-
python -m pip install --user pre-commit
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
Once installed, set up the git hook scripts to run on every commit.
|
|
44
|
-
|
|
45
|
-
```shell
|
|
46
|
-
pre-commit install
|
|
47
|
-
```
|
|
48
23
|
|
|
49
24
|
## Create development environment
|
|
50
25
|
|
|
51
26
|
After satisfying the prerequisites, you are ready to create your development environment.
|
|
52
27
|
`plone.api` uses `tox` as a wrapper around `coredev.buildout` to simplify development, whereas Plone core uses `coredev.buildout` directly.
|
|
28
|
+
Additionally `plone.api` uses `make` commands that invoke the `tox` commands as a convenience and for consistency across Plone packages.
|
|
53
29
|
|
|
54
|
-
Start by changing your working directory to your project folder, and
|
|
30
|
+
Start by changing your working directory to your project folder, and check out the latest `plone.api` source code.
|
|
55
31
|
|
|
56
32
|
```shell
|
|
57
33
|
cd <your_project_folder>
|
|
58
34
|
git clone https://github.com/plone/plone.api.git
|
|
59
35
|
```
|
|
60
36
|
|
|
61
|
-
|
|
37
|
+
Run `make help` to see the available `make` commands.
|
|
62
38
|
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
|
|
39
|
+
```console
|
|
40
|
+
check Check code base according to Plone standards
|
|
41
|
+
clean Clean environment
|
|
42
|
+
help This help message
|
|
43
|
+
linkcheck Check links in documentation
|
|
44
|
+
livehtml Build docs and watch for changes
|
|
45
|
+
test Run tests
|
|
66
46
|
```
|
|
67
47
|
|
|
68
|
-
|
|
48
|
+
The `make` commands `check`, `livehtml`, and `test` will create a development environment, if it does not already exist.
|
|
69
49
|
|
|
70
|
-
|
|
71
|
-
Some helpful `tox` commands are shown below.
|
|
50
|
+
Test your code changes with the following command.
|
|
72
51
|
|
|
73
52
|
```shell
|
|
74
|
-
|
|
75
|
-
tox -e plone6docs # build documentation
|
|
76
|
-
tox -e livehtml # build, serve, and reload changes to documentation
|
|
77
|
-
tox -l # list all tox environments
|
|
53
|
+
make test
|
|
78
54
|
```
|
|
79
55
|
|
|
56
|
+
|
|
80
57
|
(git-workflow)=
|
|
81
58
|
|
|
82
59
|
## git
|
|
@@ -109,16 +86,17 @@ For every feature change or addition to `plone.api`, you must add documentation
|
|
|
109
86
|
{doc}`plone:contributing/documentation/index`
|
|
110
87
|
```
|
|
111
88
|
|
|
112
|
-
|
|
89
|
+
When adding or modifying documentation, you can build the documentation with the following command.
|
|
90
|
+
As you edit the documentation, the started process automatically reloads your changes in the web browser.
|
|
113
91
|
|
|
114
92
|
```shell
|
|
115
|
-
|
|
93
|
+
make livehtml
|
|
116
94
|
```
|
|
117
95
|
|
|
118
|
-
|
|
96
|
+
You can run a link checker on documentation.
|
|
119
97
|
|
|
120
98
|
```shell
|
|
121
|
-
|
|
99
|
+
make linkcheck
|
|
122
100
|
```
|
|
123
101
|
|
|
124
102
|
The [`plone.api` documentation](https://6.docs.plone.org/plone.api) is automatically generated from the documentation source files when its submodule is updated in the [main Plone `documentation` repository](https://github.com/plone/documentation/).
|
|
@@ -166,6 +144,10 @@ def foo(path=None, UID=None):
|
|
|
166
144
|
% InvalidParameterError,
|
|
167
145
|
% lambda: foo("/plone/blog", "abcd001")
|
|
168
146
|
% )
|
|
147
|
+
%
|
|
148
|
+
% # Make it available for testing below
|
|
149
|
+
% from plone import api
|
|
150
|
+
% api.content.foo = foo
|
|
169
151
|
|
|
170
152
|
Add documentation in {file}`docs/api/content.md`.
|
|
171
153
|
Narrative documentation should describe what your function does.
|
|
@@ -192,7 +174,8 @@ blog_foo = api.content.foo(path="/plone/blog")
|
|
|
192
174
|
|
|
193
175
|
% invisible-code-block: python
|
|
194
176
|
%
|
|
195
|
-
% self.assertEqual(blog_foo,"foo")
|
|
177
|
+
% self.assertEqual(blog_foo, "foo")
|
|
178
|
+
|
|
196
179
|
````
|
|
197
180
|
|
|
198
181
|
Code blocks are rendered in documentation.
|
|
@@ -209,7 +192,7 @@ Invisible code blocks are not rendered in documentation and can be used for test
|
|
|
209
192
|
```markdown
|
|
210
193
|
% invisible-code-block: python
|
|
211
194
|
%
|
|
212
|
-
% self.assertEqual(blog_foo,"foo")
|
|
195
|
+
% self.assertEqual(blog_foo, "foo")
|
|
213
196
|
```
|
|
214
197
|
|
|
215
198
|
Invisible code blocks are also handy for enriching the namespace without cluttering the narrative documentation.
|
|
@@ -45,7 +45,7 @@ Assuming there is a document `english_page` in a folder `en`, which is the navig
|
|
|
45
45
|
% invisible-code-block: python
|
|
46
46
|
%
|
|
47
47
|
% from plone import api
|
|
48
|
-
% from plone.
|
|
48
|
+
% from plone.base.interfaces import INavigationRoot
|
|
49
49
|
% from zope.interface import alsoProvides
|
|
50
50
|
%
|
|
51
51
|
% portal = api.portal.get()
|
|
@@ -416,6 +416,49 @@ api.portal.set_registry_record('field_one', 'new value', interface=IMyRegistrySe
|
|
|
416
416
|
% 'new value'
|
|
417
417
|
% )
|
|
418
418
|
|
|
419
|
+
(portal-get-vocabulary-example)=
|
|
420
|
+
|
|
421
|
+
## Get vocabulary
|
|
422
|
+
|
|
423
|
+
To get a vocabulary by name, use {func}`api.portal.get_vocabulary`.
|
|
424
|
+
|
|
425
|
+
```python
|
|
426
|
+
from plone import api
|
|
427
|
+
|
|
428
|
+
# Get vocabulary using default portal context
|
|
429
|
+
vocabulary = api.portal.get_vocabulary(name='plone.app.vocabularies.PortalTypes')
|
|
430
|
+
|
|
431
|
+
# Get vocabulary with specific context
|
|
432
|
+
context = api.portal.get()
|
|
433
|
+
states_vocabulary = api.portal.get_vocabulary(
|
|
434
|
+
name='plone.app.vocabularies.WorkflowStates',
|
|
435
|
+
context=context
|
|
436
|
+
)
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
(portal-get-all-vocabulary-names-example)=
|
|
440
|
+
|
|
441
|
+
## Get all vocabulary names
|
|
442
|
+
|
|
443
|
+
To get a list of all available vocabulary names in your Plone site, use {meth}`api.portal.get_vocabulary_names`.
|
|
444
|
+
|
|
445
|
+
```python
|
|
446
|
+
from plone import api
|
|
447
|
+
|
|
448
|
+
# Get all vocabulary names
|
|
449
|
+
vocabulary_names = api.portal.get_vocabulary_names()
|
|
450
|
+
|
|
451
|
+
# Common vocabularies that should be available
|
|
452
|
+
common_vocabularies = [
|
|
453
|
+
'plone.app.vocabularies.PortalTypes',
|
|
454
|
+
'plone.app.vocabularies.WorkflowStates',
|
|
455
|
+
'plone.app.vocabularies.WorkflowTransitions'
|
|
456
|
+
]
|
|
457
|
+
|
|
458
|
+
for vocabulary_name in common_vocabularies:
|
|
459
|
+
assert vocabulary_name in vocabulary_names
|
|
460
|
+
```
|
|
461
|
+
|
|
419
462
|
## Further reading
|
|
420
463
|
|
|
421
464
|
For more information on possible flags and usage options please see the full {ref}`plone-api-portal` specification.
|
|
@@ -140,14 +140,14 @@ ignore = [
|
|
|
140
140
|
"dependabot.yml",
|
|
141
141
|
"mx.ini",
|
|
142
142
|
"tox.ini",
|
|
143
|
-
".editorconfig",
|
|
144
143
|
"*.cfg",
|
|
144
|
+
".editorconfig",
|
|
145
|
+
".readthedocs.yaml",
|
|
145
146
|
"constraints_plone52.txt",
|
|
146
147
|
"constraints_plone60.txt",
|
|
147
148
|
"constraints.txt",
|
|
148
149
|
"fix-converted-myst.py",
|
|
149
150
|
"Makefile",
|
|
150
|
-
"netlify.toml",
|
|
151
151
|
"requirements-docs.txt",
|
|
152
152
|
"requirements.txt",
|
|
153
153
|
|
|
@@ -3,7 +3,7 @@ from setuptools import find_packages
|
|
|
3
3
|
from setuptools import setup
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
version = "2.
|
|
6
|
+
version = "2.3.0"
|
|
7
7
|
|
|
8
8
|
long_description = (
|
|
9
9
|
f"{Path('README.md').read_text()}\n"
|
|
@@ -36,8 +36,8 @@ setup(
|
|
|
36
36
|
"plone.app.uuid",
|
|
37
37
|
"plone.app.dexterity",
|
|
38
38
|
"plone.app.intid",
|
|
39
|
-
"plone.app.layout",
|
|
40
39
|
"plone.app.linkintegrity",
|
|
40
|
+
"plone.base",
|
|
41
41
|
"plone.dexterity",
|
|
42
42
|
"plone.i18n",
|
|
43
43
|
"plone.registry",
|
|
@@ -133,7 +133,12 @@ def get(path=None, UID=None):
|
|
|
133
133
|
relative_path=path,
|
|
134
134
|
)
|
|
135
135
|
try:
|
|
136
|
-
|
|
136
|
+
path = path.split("/")
|
|
137
|
+
if len(path) > 1:
|
|
138
|
+
parent = site.unrestrictedTraverse(path[:-1])
|
|
139
|
+
content = parent.restrictedTraverse(path[-1])
|
|
140
|
+
else:
|
|
141
|
+
content = site.restrictedTraverse(path[-1])
|
|
137
142
|
except (KeyError, AttributeError):
|
|
138
143
|
return None # When no object is found don't raise an error
|
|
139
144
|
else:
|
|
@@ -564,6 +569,36 @@ def get_uuid(obj=None):
|
|
|
564
569
|
return IUUID(obj)
|
|
565
570
|
|
|
566
571
|
|
|
572
|
+
@required_parameters("obj")
|
|
573
|
+
def get_path(obj=None, relative=False):
|
|
574
|
+
"""Get the path of an object.
|
|
575
|
+
|
|
576
|
+
:param obj: [required] Object for which to get its path
|
|
577
|
+
:type obj: Content object
|
|
578
|
+
:param relative: Return a relative path from the portal root
|
|
579
|
+
:type relative: boolean
|
|
580
|
+
:returns: Path to the object
|
|
581
|
+
:rtype: string
|
|
582
|
+
:raises:
|
|
583
|
+
InvalidParameterError
|
|
584
|
+
:Example: :ref:`content-get-path-example`
|
|
585
|
+
"""
|
|
586
|
+
if not hasattr(obj, "getPhysicalPath"):
|
|
587
|
+
raise InvalidParameterError(f"Cannot get path of object {obj!r}")
|
|
588
|
+
|
|
589
|
+
if not relative:
|
|
590
|
+
return "/".join(obj.getPhysicalPath())
|
|
591
|
+
site = portal.get()
|
|
592
|
+
site_path = site.getPhysicalPath()
|
|
593
|
+
obj_path = obj.getPhysicalPath()
|
|
594
|
+
if obj_path[: len(site_path)] != site_path:
|
|
595
|
+
raise InvalidParameterError(
|
|
596
|
+
"Object not in portal path. Object path: {}".format("/".join(obj_path))
|
|
597
|
+
)
|
|
598
|
+
rel_path = obj_path[len(site_path) :]
|
|
599
|
+
return "/".join(rel_path) if rel_path else ""
|
|
600
|
+
|
|
601
|
+
|
|
567
602
|
def _parse_object_provides_query(query):
|
|
568
603
|
"""Create a query for the object_provides index.
|
|
569
604
|
|
|
@@ -7,16 +7,19 @@ from logging import getLogger
|
|
|
7
7
|
from plone.api.exc import CannotGetPortalError
|
|
8
8
|
from plone.api.exc import InvalidParameterError
|
|
9
9
|
from plone.api.validation import required_parameters
|
|
10
|
-
from plone.
|
|
10
|
+
from plone.base.navigationroot import get_navigation_root_object
|
|
11
11
|
from plone.registry.interfaces import IRegistry
|
|
12
12
|
from Products.CMFCore.interfaces import ISiteRoot
|
|
13
13
|
from Products.CMFCore.utils import getToolByName
|
|
14
14
|
from Products.statusmessages.interfaces import IStatusMessage
|
|
15
|
+
from zope.component import ComponentLookupError
|
|
16
|
+
from zope.component import getUtilitiesFor
|
|
15
17
|
from zope.component import getUtility
|
|
16
18
|
from zope.component import providedBy
|
|
17
19
|
from zope.component.hooks import getSite
|
|
18
20
|
from zope.globalrequest import getRequest
|
|
19
21
|
from zope.interface.interfaces import IInterface
|
|
22
|
+
from zope.schema.interfaces import IVocabularyFactory
|
|
20
23
|
|
|
21
24
|
import datetime as dtime
|
|
22
25
|
import re
|
|
@@ -86,7 +89,7 @@ def get_navigation_root(context=None):
|
|
|
86
89
|
:Example: :ref:`portal-get-navigation-root-example`
|
|
87
90
|
"""
|
|
88
91
|
context = aq_inner(context)
|
|
89
|
-
return
|
|
92
|
+
return get_navigation_root_object(context, get())
|
|
90
93
|
|
|
91
94
|
|
|
92
95
|
@required_parameters("name")
|
|
@@ -431,3 +434,41 @@ def translate(msgid, domain="plone", lang=None):
|
|
|
431
434
|
# Pass the request, so zope.i18n.translate can negotiate the language.
|
|
432
435
|
query["context"] = getRequest()
|
|
433
436
|
return translation_service.utranslate(**query)
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
@required_parameters("name")
|
|
440
|
+
def get_vocabulary(name=None, context=None):
|
|
441
|
+
"""Return a vocabulary object with the given name.
|
|
442
|
+
|
|
443
|
+
:param name: Name of the vocabulary.
|
|
444
|
+
:type name: str
|
|
445
|
+
:param context: Context to be applied to the vocabulary. Default: portal root.
|
|
446
|
+
:type context: object
|
|
447
|
+
:returns: A SimpleVocabulary instance that implements IVocabularyTokenized.
|
|
448
|
+
:rtype: zope.schema.vocabulary.SimpleVocabulary
|
|
449
|
+
:Example: :ref:`portal-get-vocabulary-example`
|
|
450
|
+
"""
|
|
451
|
+
if context is None:
|
|
452
|
+
context = get()
|
|
453
|
+
try:
|
|
454
|
+
vocabulary = getUtility(IVocabularyFactory, name)
|
|
455
|
+
except ComponentLookupError:
|
|
456
|
+
raise InvalidParameterError(
|
|
457
|
+
"Cannot find a vocabulary with name '{name}'.\n"
|
|
458
|
+
"Available vocabularies are:\n"
|
|
459
|
+
"{vocabularies}".format(
|
|
460
|
+
name=name,
|
|
461
|
+
vocabularies="\n".join(get_vocabulary_names()),
|
|
462
|
+
),
|
|
463
|
+
)
|
|
464
|
+
return vocabulary(context)
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
def get_vocabulary_names():
|
|
468
|
+
"""Return a list of vocabulary names.
|
|
469
|
+
|
|
470
|
+
:returns: A sorted list of vocabulary names.
|
|
471
|
+
:rtype: list[str]
|
|
472
|
+
:Example: :ref:`portal-get-all-vocabulary-names-example`
|
|
473
|
+
"""
|
|
474
|
+
return sorted([name for name, vocabulary in getUtilitiesFor(IVocabularyFactory)])
|
|
@@ -10,8 +10,8 @@ from plone.api.validation import at_least_one_of
|
|
|
10
10
|
from plone.api.validation import required_parameters
|
|
11
11
|
from plone.app.linkintegrity.handlers import modifiedContent
|
|
12
12
|
from plone.app.linkintegrity.utils import referencedRelationship
|
|
13
|
+
from plone.base.utils import base_hasattr
|
|
13
14
|
from plone.dexterity.utils import iterSchemataForType
|
|
14
|
-
from Products.CMFPlone.utils import base_hasattr
|
|
15
15
|
from z3c.relationfield import event
|
|
16
16
|
from z3c.relationfield import RelationValue
|
|
17
17
|
from z3c.relationfield.schema import Relation
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
title="plone.api: Test fixture"
|
|
15
15
|
description="Extension profile to configure a test fixture"
|
|
16
16
|
provides="Products.GenericSetup.interfaces.EXTENSION"
|
|
17
|
-
for="
|
|
17
|
+
for="plone.base.interfaces.ITestCasePloneSiteRoot"
|
|
18
18
|
directory="profiles/testfixture"
|
|
19
19
|
/>
|
|
20
20
|
|
|
@@ -78,7 +78,7 @@ plone (portal root)
|
|
|
78
78
|
% api.content.create(container=events, type='Event', id='conference')
|
|
79
79
|
% api.content.create(container=events, type='Event', id='sprint')
|
|
80
80
|
|
|
81
|
-
The following operations will get objects from the structure above,
|
|
81
|
+
The following operations will get objects from the structure above, using {meth}`api.content.get`.
|
|
82
82
|
|
|
83
83
|
```python
|
|
84
84
|
# let's first get the portal object
|
|
@@ -118,7 +118,7 @@ not_found = api.content.get(UID='notfound')
|
|
|
118
118
|
|
|
119
119
|
## Find content objects
|
|
120
120
|
|
|
121
|
-
You can use the find function to search for content.
|
|
121
|
+
You can use the {func}`api.content.find` function to search for content.
|
|
122
122
|
|
|
123
123
|
Finding all Documents:
|
|
124
124
|
|
|
@@ -460,7 +460,6 @@ portal = api.portal.get()
|
|
|
460
460
|
api.content.transition(obj=portal['about'], transition='reject', comment='You had a typo on your page.')
|
|
461
461
|
```
|
|
462
462
|
|
|
463
|
-
|
|
464
463
|
(content-disable-roles-acquisition-example)=
|
|
465
464
|
|
|
466
465
|
## Disable local roles acquisition
|
|
@@ -536,6 +535,60 @@ view = api.content.get_view(
|
|
|
536
535
|
%
|
|
537
536
|
% self.assertEqual(view.__name__, u'plone')
|
|
538
537
|
|
|
538
|
+
(content-get-path-example)=
|
|
539
|
+
|
|
540
|
+
## Get content path
|
|
541
|
+
|
|
542
|
+
To get the path of a content object, use {func}`api.content.get_path`.
|
|
543
|
+
This function accepts an object for which you want to get its path as the required parameter `obj`, and an optional boolean parameter `relative` whose default is `False`.
|
|
544
|
+
|
|
545
|
+
It returns either an absolute path from the Zope root by default or when `relative` is set to `False`, or a relative path from the portal root when `relative` is set to `True`.
|
|
546
|
+
|
|
547
|
+
The following example shows how to get the absolute path from the Zope root.
|
|
548
|
+
|
|
549
|
+
```python
|
|
550
|
+
from plone import api
|
|
551
|
+
portal = api.portal.get()
|
|
552
|
+
|
|
553
|
+
folder = portal['events']['training']
|
|
554
|
+
path = api.content.get_path(obj=folder)
|
|
555
|
+
assert path == '/plone/events/training'
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
The following example shows how to get the portal-relative path.
|
|
559
|
+
|
|
560
|
+
```python
|
|
561
|
+
rel_path = api.content.get_path(obj=folder, relative=True)
|
|
562
|
+
assert rel_path == 'events/training'
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
If the API is used to fetch an object with the `relative` parameter set as `True`, and the object is outside the portal, it throws an `InvalidParameterError` error.
|
|
566
|
+
|
|
567
|
+
% invisible-code-block: python
|
|
568
|
+
%
|
|
569
|
+
% # Setup an object outside portal for testing error case
|
|
570
|
+
% app = portal.aq_parent
|
|
571
|
+
% app.manage_addFolder('outside_folder')
|
|
572
|
+
%
|
|
573
|
+
% # Test that getting relative path for object outside portal raises error
|
|
574
|
+
% from plone.api.exc import InvalidParameterError
|
|
575
|
+
% with self.assertRaises(InvalidParameterError):
|
|
576
|
+
% api.content.get_path(obj=app.outside_folder, relative=True)
|
|
577
|
+
|
|
578
|
+
```python
|
|
579
|
+
from plone.api.exc import InvalidParameterError
|
|
580
|
+
|
|
581
|
+
# Getting path of an object outside portal raises InvalidParameterError
|
|
582
|
+
try:
|
|
583
|
+
outside_path = api.content.get_path(
|
|
584
|
+
obj=app.outside_folder,
|
|
585
|
+
relative=True
|
|
586
|
+
)
|
|
587
|
+
assert False, "Should raise InvalidParameterError and not reach this code"
|
|
588
|
+
except InvalidParameterError as e:
|
|
589
|
+
assert "Object not in portal path" in str(e)
|
|
590
|
+
```
|
|
591
|
+
|
|
539
592
|
## Further reading
|
|
540
593
|
|
|
541
594
|
For more information on possible flags and usage options please see the full {ref}`plone-api-content` specification.
|