Clean up some templates; given by raj-abhilash1
[mailman.git] / src / mailman / rest / moderation.py
blob984e2d34a11431b9ed3cda916558c94fb0072481
1 # Copyright (C) 2012-2015 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 """REST API for Message moderation."""
20 __all__ = [
21 'HeldMessage',
22 'HeldMessages',
23 'MembershipChangeRequest',
24 'SubscriptionRequests',
28 from mailman.app.moderator import (
29 handle_message, handle_subscription, handle_unsubscription)
30 from mailman.interfaces.action import Action
31 from mailman.interfaces.messages import IMessageStore
32 from mailman.interfaces.requests import IListRequests, RequestType
33 from mailman.rest.helpers import (
34 CollectionMixin, bad_request, child, etag, no_content, not_found, okay)
35 from mailman.rest.validator import Validator, enum_validator
36 from zope.component import getUtility
39 HELD_MESSAGE_REQUESTS = (RequestType.held_message,)
40 MEMBERSHIP_CHANGE_REQUESTS = (RequestType.subscription,
41 RequestType.unsubscription)
45 class _ModerationBase:
46 """Common base class."""
48 def _make_resource(self, request_id, expected_request_types):
49 requests = IListRequests(self._mlist)
50 results = requests.get_request(request_id)
51 if results is None:
52 return None
53 key, data = results
54 resource = dict(key=key, request_id=request_id)
55 # Flatten the IRequest payload into the JSON representation.
56 resource.update(data)
57 # Check for a matching request type, and insert the type name into the
58 # resource.
59 request_type = RequestType[resource.pop('_request_type')]
60 if request_type not in expected_request_types:
61 return None
62 resource['type'] = request_type.name
63 # This key isn't what you think it is. Usually, it's the Pendable
64 # record's row id, which isn't helpful at all. If it's not there,
65 # that's fine too.
66 resource.pop('id', None)
67 return resource
71 class _HeldMessageBase(_ModerationBase):
72 """Held messages are a little different."""
74 def _make_resource(self, request_id):
75 resource = super(_HeldMessageBase, self)._make_resource(
76 request_id, HELD_MESSAGE_REQUESTS)
77 if resource is None:
78 return None
79 # Grab the message and insert its text representation into the
80 # resource. XXX See LP: #967954
81 key = resource.pop('key')
82 msg = getUtility(IMessageStore).get_message_by_id(key)
83 resource['msg'] = msg.as_string()
84 # Some of the _mod_* keys we want to rename and place into the JSON
85 # resource. Others we can drop. Since we're mutating the dictionary,
86 # we need to make a copy of the keys. When you port this to Python 3,
87 # you'll need to list()-ify the .keys() dictionary view.
88 for key in list(resource):
89 if key in ('_mod_subject', '_mod_hold_date', '_mod_reason',
90 '_mod_sender', '_mod_message_id'):
91 resource[key[5:]] = resource.pop(key)
92 elif key.startswith('_mod_'):
93 del resource[key]
94 # Also, held message resources will always be this type, so ignore
95 # this key value.
96 del resource['type']
97 return resource
100 class HeldMessage(_HeldMessageBase):
101 """Resource for moderating a held message."""
103 def __init__(self, mlist, request_id):
104 self._mlist = mlist
105 self._request_id = request_id
107 def on_get(self, request, response):
108 try:
109 request_id = int(self._request_id)
110 except ValueError:
111 bad_request(response)
112 return
113 resource = self._make_resource(request_id)
114 if resource is None:
115 not_found(response)
116 else:
117 okay(response, etag(resource))
119 def on_post(self, request, response):
120 try:
121 validator = Validator(action=enum_validator(Action))
122 arguments = validator(request)
123 except ValueError as error:
124 bad_request(response, str(error))
125 return
126 requests = IListRequests(self._mlist)
127 try:
128 request_id = int(self._request_id)
129 except ValueError:
130 bad_request(response)
131 return
132 results = requests.get_request(request_id, RequestType.held_message)
133 if results is None:
134 not_found(response)
135 else:
136 handle_message(self._mlist, request_id, **arguments)
137 no_content(response)
141 class HeldMessages(_HeldMessageBase, CollectionMixin):
142 """Resource for messages held for moderation."""
144 def __init__(self, mlist):
145 self._mlist = mlist
146 self._requests = None
148 def _resource_as_dict(self, request):
149 """See `CollectionMixin`."""
150 return self._make_resource(request.id)
152 def _get_collection(self, request):
153 requests = IListRequests(self._mlist)
154 self._requests = requests
155 return list(requests.of_type(RequestType.held_message))
157 def on_get(self, request, response):
158 """/lists/listname/held"""
159 resource = self._make_collection(request)
160 okay(response, etag(resource))
162 @child(r'^(?P<id>[^/]+)')
163 def message(self, request, segments, **kw):
164 return HeldMessage(self._mlist, kw['id'])
168 class MembershipChangeRequest(_ModerationBase):
169 """Resource for moderating a membership change."""
171 def __init__(self, mlist, request_id):
172 self._mlist = mlist
173 self._request_id = request_id
175 def on_get(self, request, response):
176 try:
177 request_id = int(self._request_id)
178 except ValueError:
179 bad_request(response)
180 return
181 resource = self._make_resource(request_id, MEMBERSHIP_CHANGE_REQUESTS)
182 if resource is None:
183 not_found(response)
184 else:
185 # Remove unnecessary keys.
186 del resource['key']
187 okay(response, etag(resource))
189 def on_post(self, request, response):
190 try:
191 validator = Validator(action=enum_validator(Action))
192 arguments = validator(request)
193 except ValueError as error:
194 bad_request(response, str(error))
195 return
196 requests = IListRequests(self._mlist)
197 try:
198 request_id = int(self._request_id)
199 except ValueError:
200 bad_request(response)
201 return
202 results = requests.get_request(request_id)
203 if results is None:
204 not_found(response)
205 return
206 key, data = results
207 try:
208 request_type = RequestType[data['_request_type']]
209 except ValueError:
210 bad_request(response)
211 return
212 if request_type is RequestType.subscription:
213 handle_subscription(self._mlist, request_id, **arguments)
214 elif request_type is RequestType.unsubscription:
215 handle_unsubscription(self._mlist, request_id, **arguments)
216 else:
217 bad_request(response)
218 return
219 no_content(response)
222 class SubscriptionRequests(_ModerationBase, CollectionMixin):
223 """Resource for membership change requests."""
225 def __init__(self, mlist):
226 self._mlist = mlist
227 self._requests = None
229 def _resource_as_dict(self, request):
230 """See `CollectionMixin`."""
231 resource = self._make_resource(request.id, MEMBERSHIP_CHANGE_REQUESTS)
232 # Remove unnecessary keys.
233 del resource['key']
234 return resource
236 def _get_collection(self, request):
237 requests = IListRequests(self._mlist)
238 self._requests = requests
239 items = []
240 for request_type in MEMBERSHIP_CHANGE_REQUESTS:
241 for request in requests.of_type(request_type):
242 items.append(request)
243 return items
245 def on_get(self, request, response):
246 """/lists/listname/requests"""
247 resource = self._make_collection(request)
248 okay(response, etag(resource))
250 @child(r'^(?P<id>[^/]+)')
251 def subscription(self, request, segments, **kw):
252 return MembershipChangeRequest(self._mlist, kw['id'])