Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/2.5' into 3.0
Browse files Browse the repository at this point in the history
Conflicts:
	CONTRIBUTING.md
	VERSION.txt
	lib/Cake/Cache/Cache.php
	lib/Cake/Console/Command/AclShell.php
	lib/Cake/Controller/Controller.php
	lib/Cake/Model/Datasource/DboSource.php
	lib/Cake/Model/Model.php
	lib/Cake/Test/Case/Model/Datasource/Database/MysqlTest.php
	lib/Cake/Test/Case/Routing/DispatcherTest.php
	lib/Cake/Test/Case/View/Helper/FormHelperTest.php
	lib/Cake/TestSuite/Fixture/CakeTestFixture.php
	src/Controller/Component/Auth/BaseAuthorize.php
	src/Network/Email/SmtpTransport.php
	src/Routing/Dispatcher.php
	src/basics.php
  • Loading branch information
markstory committed Mar 31, 2014
2 parents f3ca5e4 + 7419ac3 commit 40bf521
Show file tree
Hide file tree
Showing 16 changed files with 465 additions and 136 deletions.
13 changes: 7 additions & 6 deletions CONTRIBUTING.md
@@ -1,9 +1,10 @@
# How to contribute

CakePHP loves to welcome your contributions. There are several ways to help out:

* Create an [issue](https://github.com/cakephp/cakephp/issues) on GitHub, if you have found a bug
* Write testcases for open bug issues
* Write patches for open bug/feature issues, preferably with testcases included
* Write test cases for open bug issues
* Write patches for open bug/feature issues, preferably with test cases included
* Contribute to the [documentation](https://github.com/cakephp/docs)

There are a few guidelines that we need contributors to follow so that we have a
Expand All @@ -30,7 +31,7 @@ chance of keeping on top of things.
* Make commits of logical units.
* Check for unnecessary whitespace with `git diff --check` before committing.
* Use descriptive commit messages and reference the #issue number.
* Core testcases should continue to pass. You can run tests locally or enable
* Core test cases should continue to pass. You can run tests locally or enable
[travis-ci](https://travis-ci.org/) for your fork, so all tests and codesniffs
will be executed.
* Your work should apply the CakePHP coding standards.
Expand All @@ -48,10 +49,10 @@ chance of keeping on top of things.
* Submit a pull request to the repository in the cakephp organization, with the
correct target branch.

## Testcases and codesniffer
## Test cases and codesniffer

CakePHP tests requires [PHPUnit](http://phpunit.de/manual/current/en/installation.html)
3.5 or higher. To run the testcases locally use the following command:
CakePHP tests requires [PHPUnit](http://www.phpunit.de/manual/current/en/installation.html)
3.5 or higher. To run the test cases locally use the following command:

phpunit --stderr tests/TestCase/

Expand Down
17 changes: 11 additions & 6 deletions src/Controller/Component/Auth/BaseAuthorize.php
Expand Up @@ -139,16 +139,23 @@ public function action(Request $request, $path = '/:plugin/:controller/:action')
* $this->Auth->mapActions(array('create' => array('add', 'register'));
* }}}
*
* Or equivalently:
*
* {{{
* $this->Auth->mapActions(array('register' => 'create', 'add' => 'create'));
* }}}
*
* Create mappings for custom CRUD operations:
*
* {{{
* $this->Auth->mapActions(array('my_action' => 'admin'));
* $this->Auth->mapActions(array('range' => 'search'));
* }}}
*
* You can use the custom CRUD operations to create additional generic permissions
* that behave like CRUD operations. Doing this will require additional columns on the
* permissions lookup. When using with DbAcl, you'll have to add additional _admin type columns
* to the `aros_acos` table.
* permissions lookup. For example if one wanted an additional search CRUD operation
* one would create and additional column '_search' in the aros_acos table. One could
* create a custom admin CRUD operation for administration functions similarly if needed.
*
* @param array $map Either an array of mappings, or undefined to get current values.
* @return mixed Either the current mappings or null when setting.
Expand All @@ -158,10 +165,8 @@ public function mapActions($map = array()) {
if (empty($map)) {
return $this->_config['actionMap'];
}

$crud = array('create', 'read', 'update', 'delete');
foreach ($map as $action => $type) {
if (in_array($action, $crud) && is_array($type)) {
if (is_array($type)) {
foreach ($type as $typedAction) {
$this->_config['actionMap'][$typedAction] = $action;
}
Expand Down
64 changes: 63 additions & 1 deletion src/I18n/I18n.php
Expand Up @@ -92,6 +92,68 @@ class I18n {
'LC_ALL', 'LC_COLLATE', 'LC_CTYPE', 'LC_MONETARY', 'LC_NUMERIC', 'LC_TIME', 'LC_MESSAGES'
);

/**
* Constants for the translation categories.
*
* The constants may be used in translation fetching
* instead of hardcoded integers.
* Example:
* {{{
* I18n::translate('CakePHP is awesome.', null, null, I18n::LC_MESSAGES)
* }}}
*
* To keep the code more readable, I18n constants are preferred over
* hardcoded integers.
*/
/**
* Constant for LC_ALL.
*
* @var int
*/
const LC_ALL = 0;

/**
* Constant for LC_COLLATE.
*
* @var int
*/
const LC_COLLATE = 1;

/**
* Constant for LC_CTYPE.
*
* @var int
*/
const LC_CTYPE = 2;

/**
* Constant for LC_MONETARY.
*
* @var int
*/
const LC_MONETARY = 3;

/**
* Constant for LC_NUMERIC.
*
* @var int
*/
const LC_NUMERIC = 4;

/**
* Constant for LC_TIME.
*
* @var int
*/
const LC_TIME = 5;

/**
* Constant for LC_MESSAGES.
*
* @var int
*/
const LC_MESSAGES = 6;

/**
* Escape string
*
Expand Down Expand Up @@ -145,7 +207,7 @@ public static function getInstance() {
* @return string translated string.
* @throws \Cake\Error\Exception When '' is provided as a domain.
*/
public static function translate($singular, $plural = null, $domain = null, $category = 6, $count = null, $language = null) {
public static function translate($singular, $plural = null, $domain = null, $category = self::LC_MESSAGES, $count = null, $language = null) {
$_this = I18n::getInstance();

if (strpos($singular, "\r\n") !== false) {
Expand Down
150 changes: 133 additions & 17 deletions src/Network/Email/SmtpTransport.php
Expand Up @@ -60,6 +60,42 @@ class SmtpTransport extends AbstractTransport {
*/
protected $_content;

/**
* The response of the last sent SMTP command.
*
* @var array
*/
protected $_lastResponse = array();

/**
* Returns the response of the last sent SMTP command.
*
* A response consists of one or more lines containing a response
* code and an optional response message text:
* {{{
* array(
* array(
* 'code' => '250',
* 'message' => 'mail.example.com'
* ),
* array(
* 'code' => '250',
* 'message' => 'PIPELINING'
* ),
* array(
* 'code' => '250',
* 'message' => '8BITMIME'
* ),
* // etc...
* )
* }}}
*
* @return array
*/
public function getLastResponse() {
return $this->_lastResponse;
}

/**
* Send mail
*
Expand All @@ -79,6 +115,25 @@ public function send(Email $email) {
return $this->_content;
}

/**
* Parses and stores the reponse lines in `'code' => 'message'` format.
*
* @param array $responseLines
* @return void
*/
protected function _bufferResponseLines(array $responseLines) {
$response = array();
foreach ($responseLines as $responseLine) {
if (preg_match('/^(\d{3})(?:[ -]+(.*))?$/', $responseLine, $match)) {
$response[] = array(
'code' => $match[1],
'message' => isset($match[2]) ? $match[2] : null
);
}
}
$this->_lastResponse = array_merge($this->_lastResponse, $response);
}

/**
* Connect to SMTP Server
*
Expand Down Expand Up @@ -147,38 +202,65 @@ protected function _auth() {
}

/**
* Send emails
* Prepares the `MAIL FROM` SMTP command.
*
* @return void
* @throws \Cake\Error\SocketException
* @param string $email The email address to send with the command.
* @return string
*/
protected function _sendRcpt() {
protected function _prepareFromCmd($email) {
return 'MAIL FROM:<' . $email . '>';
}

/**
* Prepares the `RCPT TO` SMTP command.
*
* @param string $email The email address to send with the command.
* @return string
*/
protected function _prepareRcptCmd($email) {
return 'RCPT TO:<' . $email . '>';
}

/**
* Prepares the `from` email address.
*
* @return array
*/
protected function _prepareFromAddress() {
$from = $this->_cakeEmail->returnPath();
if (empty($from)) {
$from = $this->_cakeEmail->from();
}
$this->_smtpSend('MAIL FROM:<' . key($from) . '>');
return $from;
}

/**
* Prepares the recipient email addresses.
*
* @return array
*/
protected function _prepareRecipientAddresses() {
$to = $this->_cakeEmail->to();
$cc = $this->_cakeEmail->cc();
$bcc = $this->_cakeEmail->bcc();
$emails = array_merge(array_keys($to), array_keys($cc), array_keys($bcc));
foreach ($emails as $email) {
$this->_smtpSend('RCPT TO:<' . $email . '>');
}
return array_merge(array_keys($to), array_keys($cc), array_keys($bcc));
}

/**
* Send Data
* Prepares the message headers.
*
* @return void
* @throws \Cake\Error\SocketException
* @return array
*/
protected function _sendData() {
$this->_smtpSend('DATA', '354');
protected function _prepareMessageHeaders() {
return $this->_cakeEmail->getHeaders(array('from', 'sender', 'replyTo', 'readReceipt', 'to', 'cc', 'subject'));
}

$headers = $this->_cakeEmail->getHeaders(array('from', 'sender', 'replyTo', 'readReceipt', 'to', 'cc', 'subject'));
$headers = $this->_headersToString($headers);
/**
* Prepares the message body.
*
* @return string
*/
protected function _prepareMessage() {
$lines = $this->_cakeEmail->message();
$messages = array();
foreach ($lines as $line) {
Expand All @@ -188,7 +270,37 @@ protected function _sendData() {
$messages[] = $line;
}
}
$message = implode("\r\n", $messages);
return implode("\r\n", $messages);
}

/**
* Send emails
*
* @return void
* @throws \Cake\Error\SocketException
*/
protected function _sendRcpt() {
$from = $this->_prepareFromAddress();
$this->_smtpSend($this->_prepareFromCmd(key($from)));

$emails = $this->_prepareRecipientAddresses();
foreach ($emails as $email) {
$this->_smtpSend($this->_prepareRcptCmd($email));
}
}

/**
* Send Data
*
* @return void
* @throws \Cake\Error\SocketException
*/
protected function _sendData() {
$this->_smtpSend('DATA', '354');

$headers = $this->_headersToString($this->_prepareMessageHeaders());
$message = $this->_prepareMessage();

$this->_smtpSend($headers . "\r\n\r\n" . $message . "\r\n\r\n\r\n.");
$this->_content = array('headers' => $headers, 'message' => $message);
}
Expand Down Expand Up @@ -223,6 +335,8 @@ protected function _generateSocket() {
* @throws \Cake\Error\SocketException
*/
protected function _smtpSend($data, $checkCode = '250') {
$this->_lastResponse = array();

if ($data !== null) {
$this->_socket->write($data . "\r\n");
}
Expand All @@ -241,6 +355,8 @@ protected function _smtpSend($data, $checkCode = '250') {
$responseLines = explode("\r\n", rtrim($response, "\r\n"));
$response = end($responseLines);

$this->_bufferResponseLines($responseLines);

if (preg_match('/^(' . $checkCode . ')(.)/', $response, $code)) {
if ($code[2] === '-') {
continue;
Expand Down
2 changes: 1 addition & 1 deletion src/TestSuite/ControllerTestCase.php
Expand Up @@ -296,7 +296,7 @@ protected function _testAction($url = '', $options = array()) {
* ### Mocks:
*
* - `methods` Methods to mock on the controller. `_stop()` is mocked by default
* - `models` Models to mock. Models are added to the ClassRegistry so they any
* - `models` Models to mock. Models are added to the ClassRegistry so any
* time they are instantiated the mock will be created. Pass as key value pairs
* with the value being specific methods on the model to mock. If `true` or
* no value is passed, the entire model will be mocked.
Expand Down

0 comments on commit 40bf521

Please sign in to comment.