1 <?php
2 /* --------------------------------------------------------------
3 EmailsApiV2Controller.inc.php 2016-03-07
4 Gambio GmbH
5 http://www.gambio.de
6 Copyright (c) 2016 Gambio GmbH
7 Released under the GNU General Public License (Version 2)
8 [http://www.gnu.org/licenses/gpl-2.0.html]
9 --------------------------------------------------------------
10 */
11
12 MainFactory::load_class('HttpApiV2Controller');
13
14 /**
15 * Class EmailsApiV2Controller
16 *
17 * This controller provides a gateway to the EmailService of the system. The user is able to get, send, queue
18 * and delete email records through his shop. Email forwarding is not directly supported but can be easily
19 * implemented by getting an existing email and then sending it with the updated contact addresses.
20 *
21 * API consumers can also upload attachments through the AttachmentsApiV2Controller and later use them within
22 * their emails. The upload operation must be executed before sending/queuing the email because it is not possible
23 * to send the email JSON data and upload a file at the same time. For more info see the AttachmentsV2Controller.
24 *
25 * @category System
26 * @package ApiV2Controllers
27 */
28 class EmailsApiV2Controller extends HttpApiV2Controller
29 {
30 /**
31 * E-Mail service.
32 *
33 * @var EmailService
34 */
35 protected $emailService;
36
37 /**
38 * E-Mail serializer.
39 *
40 * @var EmailJsonSerializer
41 */
42 protected $emailSerializer;
43
44
45 /**
46 * Initialize controller components.
47 */
48 protected function __initialize()
49 {
50 $this->emailService = StaticGXCoreLoader::getService('Email');
51 $this->emailSerializer = MainFactory::create('EmailJsonSerializer');
52 }
53
54
55 /**
56 * @api {post} /emails/:id Send Email
57 * @apiVersion 2.1.0
58 * @apiName SendEmail
59 * @apiGroup Emails
60 *
61 * @apiDescription
62 * This method will send and save a new or an existing email to the system. If you include mail attachments
63 * then they must already exist in the server. You will need to provide the full path to the file. To see an
64 * example usage take a look at
65 * `docs/REST/samples/email-service/send_email.php`
66 *
67 * @apiParamExample {json} Request-Body
68 * {
69 * "subject": "Test Subject",
70 * "sender": {
71 * "emailAddress": "sender@email.de",
72 * "contactName": "John Doe"
73 * },
74 * "recipient": {
75 * "emailAddress": "recipient@email.de",
76 * "contactName": "Jane Doe"
77 * },
78 * "replyTo": {
79 * "emailAddress": "reply_to@email.de",
80 * "contactName": "John Doe (Reply To)"
81 * },
82 * "contentHtml": "<strong>HTML Content</content>",
83 * "contentPlain": "Plain Content",
84 * "bcc": [
85 * {
86 * "emailAddress": "bcc@email.de",
87 * "contactName": "Chris Doe"
88 * }
89 * ],
90 * "cc": [
91 * {
92 * "emailAddress": "cc@email.de",
93 * "contactName": "Chloe Doe"
94 * }
95 * ],
96 * "attachments": [
97 * {
98 * "path": "/var/www/html/shop/uploads/attachments/1434614398/myfile.txt",
99 * "name": "Display For MyFile.txt"
100 * }
101 * ]
102 * }
103 *
104 * @apiParam {Number} [id] If provided then an existing email will be resend (only applies to URL).
105 * @apiParam {String} [subject] Email subject to be sent.
106 * @apiParam {Object} sender Contains the sender contact data.
107 * @apiParam {String} sender.emailAddress Sender's email address.
108 * @apiParam {String} [sender.contactName] Sender display name.
109 * @apiParam {Object} recipient Contains the recipient contact data.
110 * @apiParam {String} recipient.emailAddress Recipient's email address.
111 * @apiParam {String} [recipient.contactName] Recipient's display name.
112 * @apiParam {Object} replyTo Contains the reply to contact data.
113 * @apiParam {String} replyTo.emailAddress Email address of the 'Reply-To' contact.
114 * @apiParam {String} replyTo.contactName Name of the 'Reply-To' contact.
115 * @apiParam {String} [contentHtml] Email plain content.
116 * @apiParam {String} [contentPlain] Email HTML content.
117 * @apiParam {Array} [bcc] Contains the BCC contacts of the email.
118 * @apiParam {Array} [cc] Contains the CC contacts of the email.
119 * @apiParam {Array} [attachments] Contains the attachment data.
120 * @apiParam {String} attachments[].path The path to the attachments (the file must already exist in the server).
121 * @apiParam {String} [attachments[].name] Set a display name for the attachment file (must also contain
122 * the file extension).
123 *
124 * @apiSuccess (Success 201) Response-Body If successful, this method returns a complete email resource in the
125 * response body.
126 *
127 * @apiError 400-BadRequest Email data were not provided.
128 * @apiErrorExample Error-Response (400)
129 * HTTP/1.1 400 Bad Request
130 * {
131 * "code": 400,
132 * "status": "error",
133 * "message": "Email data were not provided."
134 * }
135 *
136 * @apiError 404-NotFound Email record was not found.
137 * @apiErrorExample Error-Response (404)
138 * HTTP/1.1 404 Not Found
139 * {
140 * "code": 404,
141 * "status": "error",
142 * "message": "Email record was not found."
143 * }
144 */
145 public function post()
146 {
147 $emailJsonString = $this->api->request->getBody();
148
149 // Resend existing email without any changes.
150 if(isset($this->uri[1]) && is_numeric($this->uri[1]) && empty($emailJsonString))
151 {
152 $email = $this->emailService->findById(new IdType((int)$this->uri[1]));
153
154 if($email === null)
155 {
156 throw new HttpApiV2Exception('Email record was not found.', 404);
157 }
158 }
159 // Email json data were provided and they will be used to update the $baseObject.
160 else
161 {
162 if(empty($emailJsonString))
163 {
164 throw new HttpApiV2Exception('Email data were not provided.', 400);
165 }
166
167 $baseObject = null;
168
169 if(isset($this->uri[1]) && is_numeric($this->uri[1]))
170 {
171 $baseObject = $this->emailService->findById(new IdType((int)$this->uri[1]));
172
173 if($baseObject === null) // base object could not be found
174 {
175 throw new HttpApiV2Exception('Email record was not found.', 404);
176 }
177
178 // Ensure that the email has the correct email id of the request url
179 $emailJsonString = $this->_setJsonValue($emailJsonString, 'id', (int)$this->uri[1]);
180 }
181
182 $email = $this->emailSerializer->deserialize($emailJsonString, $baseObject);
183 }
184
185 // Send the email through the service.
186 $this->emailService->send($email);
187
188 // Return response to the client.
189 $this->_locateResource('emails', (string)$email->getId());
190 $this->_writeResponse($this->emailSerializer->serialize($email, false), 201);
191 }
192
193
194 /**
195 * @api {put} /emails Queue Email
196 * @apiVersion 2.1.0
197 * @apiName QueueEmail
198 * @apiGroup Emails
199 *
200 * @apiDescription
201 * This method will queue a new email so that it can be send later (with the POST method). See
202 * the "post" method for parameter description. To see an example usage take a look at
203 * `docs/REST/samples/email-service/queue_email.php`
204 *
205 * @apiSuccess (Success 200 - Email Was Queued) {String} Response-Body If successful, this
206 * method returns a complete email resource in the response body.
207 *
208 * @apiError 400-BadRequest Email data were not provided.
209 * @apiErrorExample Error-Response
210 * HTTP/1.1 400 Bad Request
211 * {
212 * "code": 400,
213 * "status": "error",
214 * "message": "Email data were not provided."
215 * }
216 */
217 public function put()
218 {
219 $emailJsonString = $this->api->request->getBody();
220
221 if(empty($emailJsonString))
222 {
223 throw new HttpApiV2Exception('Email data were not provided.', 400);
224 }
225
226 // Parse the email.
227 $email = $this->emailSerializer->deserialize($emailJsonString);
228
229 // Queue the email to the database.
230 $this->emailService->queue($email);
231
232 // Return response to the client.
233 $this->_writeResponse($this->emailSerializer->serialize($email, false));
234 }
235
236
237 /**
238 * @todo Do not throw an error if a record does not exist. Because this is what other controllers do.
239 *
240 * @api {delete} /emails/:id Delete Email
241 * @apiVersion 2.1.0
242 * @apiName DeleteEmail
243 * @apiGroup Emails
244 *
245 * @apiDescription
246 * Delete an email record from database. To see an example usage take a look at
247 * `docs/REST/samples/email-service/remove_email.php`.
248 *
249 * @apiSuccess Response-Body If successful this method returns information about the deleted record.
250 *
251 * @apiExample {curl} Delete Email with ID = 572
252 * curl -X DELETE --user admin@shop.de:12345 http://shop.de/api.php/v2/emails/572
253 *
254 * @apiSuccessExample {json} Success-Response
255 * {
256 * "code": 200,
257 * "status": "success",
258 * "action": "delete",
259 * "emailId": 73
260 * }
261 *
262 * @apiError 400-BadRequest The email ID parameter is missing or is not valid.
263 */
264 public function delete()
265 {
266 if(!isset($this->uri[1]) || !is_numeric($this->uri[1]))
267 {
268 throw new HttpApiV2Exception('Email record ID was not provided or is not valid in the requested URI.', 400);
269 }
270
271 $id = (int)$this->uri[1];
272 $email = $this->emailService->getById(new IdType($id));
273 $this->emailService->delete($email);
274
275 // Return response JSON.
276 $response = array(
277 'code' => 200,
278 'status' => 'success',
279 'action' => 'delete',
280 'emailId' => $id
281 );
282
283 $this->_writeResponse($response);
284 }
285
286
287 /**
288 * @api {get} /emails/:id Get Emails
289 * @apiVersion 2.1.0
290 * @apiName GetEmails
291 * @apiGroup Emails
292 *
293 * @apiDescription
294 * Get multiple or a single email record through the GET method. This resource supports
295 * the following GET parameters as described in the first section of documentation: sorting
296 * minimization, search, pagination. Additionally you can filter emails by providing the
297 * GET parameter "state=pending" or "state=sent". These filter parameters do not apply when
298 * a single emails record is selected (e.g. api.php/v2/emails/84) or when the emails are searched
299 * by the "q" parameter. To see an example usage take a look at
300 * `docs/REST/samples/email-service/fetch_email.php`
301 *
302 * @apiExample {curl} Get All Emails
303 * curl -i --user admin@shop.de:12345 http://shop.de/api.php/v2/emails
304 *
305 * @apiExample {curl} Get Email With ID = 527
306 * curl -i --user admin@shop.de:12345 http://shop.de/api.php/v2/emails/527
307 *
308 * @apiExample {curl} Get Pending Emails
309 * curl -i --user admin@shop.de:12345 http://shop.de/api.php/v2/emails?state=pending
310 *
311 * @apiExample {curl} Search Emails
312 * curl -i --user admin@shop.de:12345 http://shop.de/api.php/v2/emails?q=admin@shop.de
313 *
314 * @apiError 404-NotFound Email record not found.
315 *
316 * @apiErrorExample Error-Response
317 * HTTP/1.1 404 Not Found
318 * {
319 * "code": 404,
320 * "status": "error",
321 * "message": "Email record could not be found."
322 * }
323 */
324 public function get()
325 {
326 $emails = MainFactory::create('EmailCollection');
327
328 // Get specific email record (email id was provided in the URL).
329 if(isset($this->uri[1]) && is_numeric($this->uri[1]))
330 {
331 $email = $this->emailService->findById(new IdType((int)$this->uri[1]));
332
333 if($email === null)
334 {
335 throw new HttpApiV2Exception('Email record could not be found.', 404);
336 }
337
338 $emails->add($email);
339 }
340 // Search email records (state filter cannot be applied).
341 else
342 {
343 if($this->api->request->get('q') !== null)
344 {
345 $emails = $this->emailService->filter($this->api->request->get('q'));
346 }
347 // Filter results by state ("pending" or "sent").
348 else
349 {
350 if($this->api->request->get('state') !== null)
351 {
352 $emails = ($this->api->request->get('state')
353 === 'pending') ? $this->emailService->getPending() : $this->emailService->getSent();
354 }
355 // Get all the records without applying filters.
356 else
357 {
358 $emails = $this->emailService->getAll();
359 }
360 }
361 }
362
363 // Serialize email records to be returned with response.
364 $response = array();
365
366 foreach($emails->getArray() as $email)
367 {
368 $response[] = $this->emailSerializer->serialize($email, false);
369 }
370
371 // Apply common response filters.
372 $this->_sortResponse($response);
373 $this->_paginateResponse($response);
374 $this->_minimizeResponse($response);
375
376 // Check if a single resource was requested.
377 if(isset($this->uri[1]) && is_numeric($this->uri[1]) && count($response) > 0)
378 {
379 $response = $response[0];
380 }
381
382 $this->_writeResponse($response);
383 }
384 }
385