/** * Renders data (usually the result of a controller action) and generates a string * representation of it, based on the type of expected output. * * @param object $response A reference to a Response object into which the operation will be * rendered. The content of the render operation will be assigned to the `$body` * property of the object, and the `'Content-type'` header will be set accordingly. * @param mixed $data * @param array $options * @return void * @filter */ public static function render(&$response, $data = null, array $options = array()) { $params = array('response' => &$response) + compact('data', 'options'); $types = static::_types(); $handlers = static::_handlers(); static::_filter(__FUNCTION__, $params, function($self, $params) use ($types, $handlers) { $defaults = array('encode' => null, 'template' => null, 'layout' => '', 'view' => null); $response =& $params['response']; $data = $params['data']; $options = $params['options'] + array('type' => $response->type()); $result = null; $type = $options['type']; if (!isset($handlers[$type])) { throw new MediaException("Unhandled media type `{$type}`."); } $handler = $options + $handlers[$type] + $defaults; $filter = function($v) { return $v !== null; }; $handler = array_filter($handler, $filter) + $handlers['default'] + $defaults; if (isset($types[$type])) { $header = current((array) $types[$type]); $header .= $response->encoding ? "; charset={$response->encoding}" : ''; $response->headers('Content-type', $header); } $response->body($self::invokeMethod('_handle', array($handler, $data, $response))); }); }
/** * Translate the message, configuration and given options into * supported parameters for the Mailgun API. * * Returns an array including the following: * - the API URL: can be configured two ways * - with option keys `'api'` and `'domain'` the URL will be * `$api/$domain/messages.mime` * (`'api'` defaults to `'https://api.mailgun.net/v2'`) * - with the option key `'url'` it will be final URL * - the API key: can be configured with the option key `'key'` * - parameters: * - `'to'` address * - generated message (with headers, in MIME format) * - extra parameters, see `$_extraParameters` * - variables with the option key `'variables'` * * @see li3_mailer\net\mail\transport\adapter\Mailgun::$_extraParameters * @see http://documentation.mailgun.net/api-sending.html * @param object $message The message to deliver. * @param array $options Given options. * @return array An array including the API URL, authentication credentials and parameters. */ protected function _parameters($message, array $options = array()) { $defaults = array('api' => 'https://api.mailgun.net/v2'); $config = $this->_config + $options + $defaults; if (isset($config['url'])) { $url = $config['url']; } else { if (!isset($config['domain'])) { $error = "No `domain` (nor `url`) configured "; $error .= "for `Mailgun` transport adapter."; throw new RuntimeException($error); } if (substr($config['api'], -1) === '/') { $error = "API endpoint should not end with '/'."; throw new RuntimeException($error); } if (substr($config['domain'], 0, 1) === '/') { $error = "Domain should not start with '/'."; throw new RuntimeException($error); } if (substr($config['domain'], -1) === '/') { $error = "Domain should not end with '/'."; throw new RuntimeException($error); } $url = array($config['api'], $config['domain'], 'messages'); $url = join($url, "/"); } foreach (array('to', 'from', 'cc', 'bcc') as $field) { if (!$message->{$field}) { continue; } $parameters[$field] = $this->_address($message->{$field}); } if ($subject = $message->subject) { $parameters += compact('subject'); } if ($text = $message->body('text')) { $parameters += compact('text'); } if ($html = $message->body('html')) { $parameters += compact('html'); } foreach ($this->_extraParameters as $name => $type) { if (is_int($name)) { $name = $type; $type = null; } if (isset($config[$name])) { if ($type === "array") { $list = array_values($config[$name]); foreach ($list as $idx => $val) { $key = 'o:' . $name . '[' . ($idx + 1) . ']'; $parameters[$key] = $val; } } else { $parameters['o:' . $name] = $config[$name]; } } } if (isset($config['variables'])) { foreach ((array) $config['variables'] as $name => $val) { $parameters['v:' . $name] = $val; } } $auth = array('username' => 'api', 'password' => $config['key']); return array($url, $auth, $parameters); }
/** * Translate the message, configuration and given options into * supported parameters for the Mailgun API. * * Returns an array including the following: * - the API URL: can be configured two ways * - with option keys `'api'` and `'domain'` the URL will be * `$api/$domain/messages.mime` * (`'api'` defaults to `'https://api.mailgun.net/v2'`) * - with the option key `'url'` it will be final URL * - the API key: can be configured with the option key `'key'` * - parameters: * - `'to'` address * - generated message (with headers, in MIME format) * - extra parameters, see `$_extraParameters` * - variables with the option key `'variables'` * * @see li3_mailer\net\mail\transport\adapter\Mailgun::$_extraParameters * @see http://documentation.mailgun.net/api-sending.html * @param object $message The message to deliver. * @param array $options Given options. * @return array An array including the API URL, authentication credentials and parameters. */ protected function _parameters($message, array $options = array()) { $defaults = array('api' => 'https://api.mailgun.net/v2'); $config = $this->_config + $options + $defaults; if (isset($config['url'])) { $url = $config['url']; } else { if (!isset($config['domain'])) { $error = "No `domain` (nor `url`) configured "; $error .= "for `Mailgun` transport adapter."; throw new RuntimeException($error); } if (substr($config['api'], -1) === '/') { $error = "API endpoint should not end with '/'."; throw new RuntimeException($error); } if (substr($config['domain'], 0, 1) === '/') { $error = "Domain should not start with '/'."; throw new RuntimeException($error); } if (substr($config['domain'], -1) === '/') { $error = "Domain should not end with '/'."; throw new RuntimeException($error); } $url = array($config['api'], $config['domain'], 'messages'); $url = join($url, "/"); } foreach (array('to', 'from', 'cc', 'bcc') as $field) { if (!$message->{$field}) { continue; } $parameters[$field] = $this->_address($message->{$field}); } if ($message->replyTo) { $parameters['h:Reply-To'] = $this->_address($message->replyTo); } if ($subject = $message->subject) { $parameters += compact('subject'); } if ($text = $message->body('text')) { $parameters += compact('text'); } if ($html = $message->body('html')) { $parameters += compact('html'); } foreach ($message->attachments() as $key => $item) { if (isset($item['data'])) { if (is_resource($item['data'])) { rewind($item['data']); } $file = tempnam(realpath(sys_get_temp_dir()) . '/', 'li3_mailer_attachment'); $this->_temporaryFiles[] = $file; file_put_contents($file, $item['data']); $item['path'] = $file; } $parameters["attachment[{$key}]"] = new CURLFile($item['path'], $item['content-type'], $item['filename']); } foreach ($this->_extraParameters as $name => $type) { if (is_int($name)) { $name = $type; $type = null; } if (isset($config[$name])) { if ($type === "array") { $list = array_values($config[$name]); foreach ($list as $idx => $val) { $key = 'o:' . $name . '[' . ($idx + 1) . ']'; $parameters[$key] = $val; } } else { $parameters['o:' . $name] = $config[$name]; } } } if (isset($config['variables'])) { foreach ((array) $config['variables'] as $name => $val) { $parameters['v:' . $name] = $val; } } $auth = array('username' => 'api', 'password' => $config['key']); return array($url, $auth, $parameters); }
/** * Create a `Swift_Message` from `li3_mailer\net\mail\Message`. * * @see li3_mailer\net\mail\transport\adapter\Swift::$_messageProperties * @see li3_mailer\net\mail\transport\adapter\Swift::$_attachmentProperties * @see li3_mailer\net\mail\Message * @see Swift_Message * @param object $message The `Message` object to translate. * @return object The translated `Swift_Message` object. */ protected function _message($message) { $swiftMessage = Swift_Message::newInstance(); foreach ($this->_messageProperties as $prop => $translated) { if (is_int($prop)) { $prop = $translated; } if (!is_null($message->{$prop})) { $value = $message->{$prop}; if ($translated === 'address') { $translated = $prop; if (is_array($value)) { $newvalue = array(); foreach ($value as $name => $address) { if (is_int($name)) { $newvalue[] = $address; } else { $newvalue[$address] = $name; } } $value = $newvalue; } } $method = "set" . Inflector::camelize($translated); $swiftMessage->{$method}($value); } } $first = true; foreach ($message->types() as $type => $contentType) { if ($first) { $first = false; $swiftMessage->setBody($message->body($type), $contentType); } else { $swiftMessage->addPart($message->body($type), $contentType); } } $headers = $swiftMessage->getHeaders(); foreach ($message->headers as $header => $value) { $headers->addTextHeader($header, $value); } foreach ($message->attachments() as $attachment) { if (isset($attachment['path'])) { $path = $attachment['path']; $swiftAttachment = Swift_Attachment::fromPath($path); } else { $data = $attachment['data']; $swiftAttachment = Swift_Attachment::newInstance($data); } foreach ($this->_attachmentProperties as $prop => $translated) { if (is_int($prop)) { $prop = $translated; } if (isset($attachment[$prop])) { $method = "set" . Inflector::camelize($translated); $swiftAttachment->{$method}($attachment[$prop]); } } $swiftMessage->attach($swiftAttachment); } return $swiftMessage; }
/** * Generate headers and body of a message in MIME format. * * @param object $message The message. * @return array Message headers and body in MIME format. */ protected function _generate($message) { $headers = $message->headers; foreach ($this->_messageAddresses as $property => $header) { if (is_int($property)) { $property = $header; $header = ucfirst($property); } $headers[$header] = $this->_address($message->{$property}); } $headers['Date'] = date('r', $message->date); $headers['MIME-Version'] = "1.0"; $types = $message->types(); $attachments = $message->attachments(); $charset = $message->charset; if (count($types) === 1 && count($attachments) === 0) { $type = key($types); $contentType = current($types); $headers['Content-Type'] = "{$contentType};charset=\"{$charset}\""; $body = wordwrap($message->body($type), 70); } else { $boundary = uniqid('LI3_MAILER_SIMPLE_'); $contentType = "multipart/alternative;boundary=\"{$boundary}\""; $headers['Content-Type'] = $contentType; $body = "This is a multi-part message in MIME format.\n\n"; foreach ($types as $type => $contentType) { $body .= "--{$boundary}\n"; $contentType .= ";charset=\"{$charset}\""; $body .= "Content-Type: {$contentType}\n\n"; $body .= wordwrap($message->body($type), 70) . "\n"; } foreach ($attachments as $attachment) { if (isset($attachment['path'])) { $local = $attachment['path'][0] === '/'; if ($local && !is_readable($attachment['path'])) { $content = false; } else { $content = file_get_contents($attachment['path']); } if ($content === false) { $error = "Can not attach path `{$attachment['path']}`."; throw new RuntimeException($error); } } else { $content = $attachment['data']; } $body .= "--{$boundary}\n"; if (isset($attachment['filename'])) { $filename = $attachment['filename']; } else { $filename = null; } if (isset($attachment['content-type'])) { $contentType = $attachment['content-type']; if ($filename && !preg_match('/;\\s+name=/', $contentType)) { $contentType .= "; name=\"{$filename}\""; } $body .= "Content-Type: {$contentType}\n"; } if (isset($attachment['disposition'])) { $disposition = $attachment['disposition']; $pattern = '/;\\s+filename=/'; if ($filename && !preg_match($pattern, $disposition)) { $disposition .= "; filename=\"{$filename}\""; } $body .= "Content-Disposition: {$disposition}\n"; } if (isset($attachment['id'])) { $body .= "Content-ID: <{$attachment['id']}>\n"; } $body .= "\n" . wordwrap($content, 70) . "\n"; } $body .= "--{$boundary}--"; } $headers = join("\r\n", array_map(function ($name, $value) { return "{$name}: {$value}"; }, array_keys($headers), $headers)); return array($headers, $body); }
/** * Handler for generating arguments list for POST methods. * * @see Lead\Resource\Controller::args() * * @param object $request The request instance. * @param array $options An options array. * @return array */ protected function _post($request, $options) { $payload = Payload::parse($request->body()); if (!($collection = $payload->export())) { throw new ResourceException("No data provided for `{$this->name()}` resource(s), nothing to process.", 422); } $list = []; foreach ($collection as $data) { $list[] = [$options['binding']::create($data), $payload]; } return $list; }
/** * Renders data (usually the result of a mailer delivery action) and generates a string * representation of it, based on the types of expected output. Also ensures the message's * fields are valid according to the RFC 2822. * * @param object $message A reference to a Message object into which the operation will be * rendered. The content of the render operation will be assigned to the `$body` * property of the object. * @param mixed $data * @param array $options * @return void * @filter */ public static function render(&$message, $data = null, array $options = array()) { $params = array('message' => &$message) + compact('data', 'options'); $handlers = static::_handlers(); static::_filter(__FUNCTION__, $params, function ($self, $params) use($handlers) { $defaults = array('template' => null, 'layout' => 'default', 'view' => null); $message =& $params['message']; $data = $params['data']; $options = $params['options']; foreach ((array) $message->types as $type) { if (!isset($handlers[$type])) { throw new MediaException("Unhandled media type `{$type}`."); } $handler = $options + $handlers[$type] + $defaults + array('type' => $type); $filter = function ($v) { return $v !== null; }; $handler = array_filter($handler, $filter) + $handlers['default'] + $defaults; $handler['paths'] = $self::invokeMethod('_finalize_paths', array($handler['paths'], $options)); $params = array($handler, $data, $message); $message->body($type, $self::invokeMethod('_handle', $params)); } $message->ensureStandardCompliance(); }); }
/** * Helper method to get message property data for `String::insert()` * style formatters. Additional data may be added with the * configuration key `'message_data'`, which should be an array of: * * - strings: property names (with integer keys) or the special * `'address'` value with the property name as the key (in which * case the property will be transformed with `address()`). * - closures: the keys should be property names, the closure * receives the message's property as the first argument and * should return altered data. If the key is `''` the closure will * receive the message object as the first argument and should * return an array which will be merged into the data array. * * @see li3_mailer\net\mail\transport\adapter\Debug::_formatters() * @see li3_mailer\net\mail\transport\adapter\Debug::format() * @see li3_mailer\net\mail\Message * @param object $message Message. * @return array Message data. */ protected function _message_data($message) { $config = $this->_config + array('message_data' => array()); $map = (array) $config['message_data'] + array('subject', 'charset', 'return_path', 'sender' => 'address', 'from' => 'address', 'reply_to' => 'address', 'to' => 'address', 'cc' => 'address', 'bcc' => 'address', 'date' => function ($time) { return date('Y-m-d H:i:s', $time); }, 'types' => function ($types) { return join(', ', $types); }, 'headers' => function ($headers) { return join(PHP_EOL, $headers); }, 'body' => function ($bodies) { return join(PHP_EOL, array_map(function ($body) { return join(PHP_EOL, $body); }, $bodies)); }, '' => function ($message) { return array_combine(array_map(function ($type) { return "body_{$type}"; }, $message->types), array_map(function ($type) use($message) { return $message->body($type); }, $message->types)); }); $data = array(); foreach ($map as $prop => $config) { if ($prop === '') { continue; } if (is_int($prop)) { $prop = $config; $config = null; } $value = $message->{$prop}; if ($config instanceof Closure) { $value = $config($value); } else { if ($config == 'address') { $value = $this->address($value); } } $data[$prop] = $value; } if (isset($map['']) && $map[''] instanceof Closure) { $data = $map['']($message) + $data; } return $data; }
/** * Renders data (usually the result of a controller action) and generates a string * representation of it, based on the type of expected output. * * @param object $response A reference to a Response object into which the operation will be * rendered. The content of the render operation will be assigned to the `$body` * property of the object, and the `'Content-type'` header will be set accordingly. * @param mixed $data * @param array $options * @return void * @filter * @todo Implement proper exception handling */ public static function render(&$response, $data = null, $options = array()) { $params = array('response' => &$response) + compact('data', 'options'); $types = static::$_types; $handlers = static::$_handlers; static::_filter(__FUNCTION__, $params, function ($self, $params) use($types, $handlers) { $defaults = array('encode' => null, 'template' => null, 'layout' => null, 'view' => null); $response =& $params['response']; $data = $params['data']; $options = $params['options'] + array('type' => $response->type()); $result = null; $type = $options['type']; $hasHandler = isset($handlers[$type]); $handler = $options + ($hasHandler ? $handlers[$type] : array()) + $defaults; if (!$handler['encode'] && !$handler['view'] && !$hasHandler) { throw new Exception("Unhandled media type '{$type}'"); } $filter = function ($v) { return $v !== null; }; $handler = array_filter($handler, $filter) + $handlers['default'] + $defaults; $response->body($self::invokeMethod('_handle', array($handler, $data, $options))); $response->headers('Content-type', current((array) $types[$type])); }); }