Add per list member roster visibility option
[mailman.git] / src / mailman / rest / uris.py
blobe8cecdbfd3fd68adede6c30218b37ff2f6876ff3
1 # Copyright (C) 2016-2019 by the Free Software Foundation, Inc.
3 # This file is part of GNU Mailman.
5 # GNU Mailman is free software: you can redistribute it and/or modify it under
6 # the terms of the GNU General Public License as published by the Free
7 # Software Foundation, either version 3 of the License, or (at your option)
8 # any later version.
10 # GNU Mailman is distributed in the hope that it will be useful, but WITHOUT
11 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 # more details.
15 # You should have received a copy of the GNU General Public License along with
16 # GNU Mailman. If not, see <http://www.gnu.org/licenses/>.
18 """URI templates."""
20 from mailman.interfaces.template import ALL_TEMPLATES, ITemplateManager
21 from mailman.rest.helpers import (
22 CollectionMixin, bad_request, etag, no_content, not_found, okay)
23 from mailman.rest.validator import Validator
24 from operator import attrgetter
25 from public import public
26 from zope.component import getUtility
29 class _URIBase(CollectionMixin):
30 def __init__(self, context):
31 self._context = context
33 def _resource_as_dict(self, template):
34 resource = dict(
35 uri=template.uri,
36 name=template.name,
37 self_link=self.api.path_to('{}/uris/{}'.format(
38 self._prefix, template.name)),
40 if template.username is not None and template.password is not None:
41 resource['username'] = template.username
42 resource['password'] = template.password
43 return resource
45 def _get_collection(self, request):
46 manager = getUtility(ITemplateManager)
47 collection = []
48 for uri in self.URIs:
49 template = manager.raw(uri, self._raw_context)
50 if template is not None:
51 collection.append(template)
52 return sorted(collection, key=attrgetter('name'))
54 def on_get(self, request, response):
55 resource = self._make_collection(request)
56 resource['self_link'] = self.api.path_to(
57 '{}/uris'.format(self._prefix))
58 okay(response, etag(resource))
60 def _patch_put(self, request, response, is_optional):
61 kws = {uri: str for uri in self.URIs}
62 optionals = ['username', 'password']
63 if is_optional:
64 optionals.extend(self.URIs)
65 # When PATCHing or PUTing all uris, a single optional
66 # username/password applies to them all.
67 kws['username'] = str
68 kws['password'] = str
69 kws['_optional'] = optionals
70 try:
71 arguments = Validator(**kws)(request)
72 except ValueError as error:
73 bad_request(response, str(error))
74 return
75 username = arguments.pop('username', None)
76 password = arguments.pop('password', None)
77 if not username and not password:
78 # Normalize arguments.
79 set_kws = {}
80 elif username and password:
81 # It's fine if both are specified.
82 set_kws = dict(username=username, password=password)
83 else:
84 bad_request(response,
85 'Specify both username and password, or neither')
86 return
87 manager = getUtility(ITemplateManager)
88 for key, value in arguments.items():
89 if len(value) == 0:
90 # The empty string is equivalent to DELETE. Yeah, this isn't
91 # very RESTful, but practicality beats purity.
92 manager.delete(key, self._raw_context)
93 else:
94 manager.set(key, self._raw_context, value, **set_kws)
95 no_content(response)
97 def on_put(self, request, response):
98 self._patch_put(request, response, is_optional=False)
100 def on_patch(self, request, response):
101 self._patch_put(request, response, is_optional=True)
103 def on_delete(self, request, response):
104 manager = getUtility(ITemplateManager)
105 for uri in self.URIs:
106 manager.delete(uri, self._raw_context)
107 no_content(response)
110 class _ListURIBase(_URIBase):
111 def __init__(self, context):
112 super().__init__(context)
113 self._raw_context = context.list_id
114 self._prefix = 'lists/{}'.format(context.list_id)
117 @public
118 class AllListURIs(_ListURIBase):
119 URIs = [name for name in ALL_TEMPLATES if name.startswith('list:')]
121 def __init__(self, context):
122 super().__init__(context)
125 @public
126 class AListURI(_ListURIBase):
127 def __init__(self, context, template):
128 super().__init__(context)
129 self.URIs = [template]
130 self._template = template
132 def on_get(self, request, response):
133 template = getUtility(ITemplateManager).raw(
134 self._template, self._raw_context)
135 if template is None:
136 not_found(response)
137 else:
138 resource = dict(uri=template.uri)
139 resource['self_link'] = self.api.path_to(
140 '{}/uris/{}'.format(self._prefix, self._template))
141 okay(response, etag(resource))
144 class _DomainURIBase(_URIBase):
145 def __init__(self, context):
146 super().__init__(context)
147 self._raw_context = context.mail_host
148 self._prefix = 'domains/{}'.format(context.mail_host)
151 @public
152 class AllDomainURIs(_DomainURIBase):
153 URIs = [name for name in ALL_TEMPLATES
154 if name.startswith('list:') or name.startswith('domain:')]
157 @public
158 class ADomainURI(_DomainURIBase):
159 def __init__(self, context, template):
160 super().__init__(context)
161 self.URIs = [template]
162 self._template = template
164 def on_get(self, request, response):
165 template = getUtility(ITemplateManager).raw(
166 self._template, self._raw_context)
167 if template is None:
168 not_found(response)
169 else:
170 resource = dict(uri=template.uri)
171 resource['self_link'] = self.api.path_to(
172 '{}/uris/{}'.format(self._prefix, self._template))
173 okay(response, etag(resource))
176 class _SiteURIBase(_URIBase):
177 def __init__(self):
178 super().__init__(None)
179 self._raw_context = None
180 self._prefix = ''
183 @public
184 class AllSiteURIs(_SiteURIBase):
185 URIs = [name for name in ALL_TEMPLATES]
188 @public
189 class ASiteURI(_SiteURIBase):
190 def __init__(self, template):
191 super().__init__()
192 self.URIs = [template]
193 self._template = template
195 def on_get(self, request, response):
196 template = getUtility(ITemplateManager).raw(self._template, None)
197 if template is None:
198 not_found(response)
199 else:
200 resource = dict(uri=template.uri)
201 resource['self_link'] = self.api.path_to(
202 'uris/{}'.format(self._template))
203 okay(response, etag(resource))