Skip to content

Commit

Permalink
Fix #2513 for all element types
Browse files Browse the repository at this point in the history
  • Loading branch information
brandonkelly committed Mar 8, 2018
1 parent 6668af4 commit 2e05ca4
Show file tree
Hide file tree
Showing 6 changed files with 130 additions and 11 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG-v3.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@

## Unreleased

### Added
- Added `craft\db\Connection::getSupportsMb4()` and `setSupportsMb4()`.
- Added `craft\helpers\StringHelper::containsMb4()`.
- Added `craft\validators\StringValidator`.

### Changed
- Element titles now get a validation error if they contain any 4+ byte characters (like emoji), on servers running MySQL. ([#2513](https://github.com/craftcms/cms/issues/2513))

### Fixed
- Fixed an error that occurred when creating a new entry draft. ([#2544](https://github.com/craftcms/cms/issues/2544))
- Fixed a bug where the primary action button on element index pages was getting positioned off-screen on IE11. ([#2545](https://github.com/craftcms/cms/issues/2545))
Expand Down
4 changes: 2 additions & 2 deletions src/base/Element.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
use craft\validators\ElementUriValidator;
use craft\validators\SiteIdValidator;
use craft\validators\SlugValidator;
use craft\validators\StringValidator;
use craft\web\UploadedFile;
use DateTime;
use yii\base\Event;
Expand All @@ -45,7 +46,6 @@
use yii\base\InvalidConfigException;
use yii\base\UnknownPropertyException;
use yii\validators\NumberValidator;
use yii\validators\StringValidator;
use yii\validators\Validator;

/**
Expand Down Expand Up @@ -901,7 +901,7 @@ public function rules()
];

if (static::hasTitles()) {
$rules[] = [['title'], 'string', 'max' => 255, 'on' => [self::SCENARIO_DEFAULT, self::SCENARIO_LIVE]];
$rules[] = [['title'], StringValidator::class, 'max' => 255, 'disallowMb4' => true, 'on' => [self::SCENARIO_DEFAULT, self::SCENARIO_LIVE]];
$rules[] = [['title'], 'required', 'on' => [self::SCENARIO_DEFAULT, self::SCENARIO_LIVE]];
}

Expand Down
7 changes: 0 additions & 7 deletions src/controllers/EntriesController.php
Original file line number Diff line number Diff line change
Expand Up @@ -532,13 +532,6 @@ public function actionSaveEntry()
$revisionsService->saveVersion($currentEntry);
}

// Validate that the title does not have an emoji
if (StringHelper::hasMb4($entry->title)) {
Craft::$app->getSession()->setError(Craft::t('app', 'Couldn’t save entry with emoji in title.'));
return null;
}


// Save the entry (finally!)
if ($entry->enabled && $entry->enabledForSite) {
$entry->setScenario(Element::SCENARIO_LIVE);
Expand Down
34 changes: 34 additions & 0 deletions src/db/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
* @inheritdoc
* @property MysqlQueryBuilder|PgsqlQueryBuilder $queryBuilder The query builder for the current DB connection.
* @property MysqlSchema|PgsqlSchema $schema The schema information for the database opened by this connection.
* @property bool $supportsMb4 Whether the database supports 4+ byte characters.
* @method MysqlQueryBuilder|PgsqlQueryBuilder getQueryBuilder() Returns the query builder for the current DB connection.
* @method MysqlSchema|PgsqlSchema getSchema() Returns the schema information for the database opened by this connection.
* @method TableSchema getTableSchema($name, $refresh = false) Obtains the schema information for the named table.
Expand Down Expand Up @@ -103,6 +104,16 @@ public static function createFromConfig(DbConfig $config): Connection
]);
}

// Properties
// =========================================================================

/**
* @var bool|null whether the database supports 4+ byte characters
* @see getSupportsMb4()
* @see setSupportsMb4()
*/
private $_supportsMb4;

// Public Methods
// =========================================================================

Expand Down Expand Up @@ -137,6 +148,29 @@ public function getVersion(): string
return App::normalizeVersion($version);
}

/**
* Returns whether the database supports 4+ byte characters.
*
* @return bool
*/
public function getSupportsMb4(): bool
{
if ($this->_supportsMb4 !== null) {
return $this->_supportsMb4;
}
return $this->_supportsMb4 = $this->getIsPgsql();
}

/**
* Sets whether the database supports 4+ byte characters.
*
* @param bool $supportsMb4
*/
public function setSupportsMb4(bool $supportsMb4)
{
$this->_supportsMb4 = $supportsMb4;
}

/**
* @inheritdoc
* @throws DbConnectException if there are any issues
Expand Down
4 changes: 2 additions & 2 deletions src/helpers/StringHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -971,7 +971,7 @@ public static function encoding(string $string): string
* @param string $string
* @return bool
*/
public static function hasMb4(string $string): bool
public static function containsMb4(string $string): bool
{
return max(array_map('ord', str_split($string))) >= 240;
}
Expand All @@ -986,7 +986,7 @@ public static function hasMb4(string $string): bool
public static function encodeMb4(string $string): string
{
// Does this string have any 4+ byte Unicode chars?
if (max(array_map('ord', str_split($string))) >= 240) {
if (static::containsMb4($string)) {
$string = preg_replace_callback('/./u', function(array $match) {
if (strlen($match[0]) >= 4) {
// (Logic pulled from WP's wp_encode_emoji() function)
Expand Down
84 changes: 84 additions & 0 deletions src/validators/StringValidator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<?php
/**
* @link https://craftcms.com/
* @copyright Copyright (c) Pixel & Tonic, Inc.
* @license https://craftcms.github.io/license/
*/

namespace craft\validators;

use Craft;
use craft\helpers\ElementHelper;
use craft\helpers\StringHelper;
use yii\validators\Validator;

/**
* Class StringValidator.
*
* @author Pixel & Tonic, Inc. <support@pixelandtonic.com>
* @since 3.0
*/
class StringValidator extends \yii\validators\StringValidator
{
// Properties
// =========================================================================

/**
* @var bool whether the string should be checked for 4+ byte characters (like emoji)
*/
public $disallowMb4 = false;

/**
* @var string user-defined error message used when the value contains 4+ byte characters
* (like emoji) and the database doesn’t support it.
*/
public $containsMb4;

// Public Methods
// =========================================================================

/**
* @inheritdoc
*/
public function init()
{
parent::init();

if ($this->containsMb4 === null) {
$this->containsMb4 = Craft::t('app', '{attribute} cannot contain emoji.');
}
}

/**
* @inheritdoc
*/
public function validateAttribute($model, $attribute)
{
parent::validateAttribute($model, $attribute);

$value = $model->$attribute;
if (!is_string($value)) {
return;
}

if ($this->disallowMb4 && !Craft::$app->getDb()->getSupportsMb4() && StringHelper::containsMb4($value)) {
$this->addError($model, $attribute, $this->containsMb4);
}
}

/**
* @inheritdoc
*/
public function validateValue($value)
{
if (!empty($result = parent::validateValue($value))) {
return $result;
}

if ($this->disallowMb4 && !Craft::$app->getDb()->getSupportsMb4() && StringHelper::containsMb4($value)) {
return [$this->containsMb4, []];
}

return null;
}
}

0 comments on commit 2e05ca4

Please # to comment.