diff --git a/package.json b/package.json
index be08d776b6..8cb52febdf 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "@directus/api",
"private": true,
- "version": "2.3.1",
+ "version": "2.4.0",
"description": "Directus API",
"main": "index.js",
"repository": "directus/api",
diff --git a/public/.htaccess b/public/.htaccess
index e02f624c38..939244f897 100644
--- a/public/.htaccess
+++ b/public/.htaccess
@@ -20,7 +20,8 @@ Options +SymLinksIfOwnerMatch
RewriteRule !^admin index.php?%{QUERY_STRING} [L]
-
- php_value upload_max_filesize 50M
- php_value post_max_size 100M
-
\ No newline at end of file
+# Uncomment the following lines to modify PHP settings. The lines below can be used to increase the max upload size of files in Directus
+#
+ #php_value upload_max_filesize 50M
+ #php_value post_max_size 100M
+#
diff --git a/src/core/Directus/Application/Application.php b/src/core/Directus/Application/Application.php
index cd26de8a0c..0b9d1c7149 100644
--- a/src/core/Directus/Application/Application.php
+++ b/src/core/Directus/Application/Application.php
@@ -13,7 +13,7 @@ class Application extends App
*
* @var string
*/
- const DIRECTUS_VERSION = '2.3.1';
+ const DIRECTUS_VERSION = '2.4.0';
/**
* NOT USED
diff --git a/src/core/Directus/Config/Schema/Base.php b/src/core/Directus/Config/Schema/Base.php
index 64fd0a7bdc..5fe3dc252f 100644
--- a/src/core/Directus/Config/Schema/Base.php
+++ b/src/core/Directus/Config/Schema/Base.php
@@ -113,4 +113,17 @@ public function optional($value = null)
}
return $this->_optional;
}
+
+ /**
+ * Returns the $context with normalized array keys.
+ * @param $context
+ * @return mixed
+ */
+ protected function normalize($context) {
+ foreach ($context as $context_key => $context_value) {
+ $context[strtolower(str_replace("-", "", str_replace("_", "", $context_key)))] = $context_value;
+ }
+
+ return $context;
+ }
}
diff --git a/src/core/Directus/Config/Schema/Group.php b/src/core/Directus/Config/Schema/Group.php
index 463c8b85ce..a7112c1f37 100644
--- a/src/core/Directus/Config/Schema/Group.php
+++ b/src/core/Directus/Config/Schema/Group.php
@@ -25,9 +25,7 @@ public function value($context)
$value = [];
$current = [];
- foreach ($context as $context_key => $context_value) {
- $context[strtolower(str_replace("-", "", str_replace("_", "", $context_key)))] = $context_value;
- }
+ $context = $this->normalize($context);
if (!isset($context[$this->key()])) {
if ($this->optional()) {
diff --git a/src/core/Directus/Config/Schema/Schema.php b/src/core/Directus/Config/Schema/Schema.php
index a49c977b57..f26566f75e 100644
--- a/src/core/Directus/Config/Schema/Schema.php
+++ b/src/core/Directus/Config/Schema/Schema.php
@@ -77,8 +77,8 @@ public static function get() {
]),
new Group('cors', [
new Value('enabled', Types::BOOLEAN, true),
- new Value('origin', 'array', ['*']),
- new Value('methods', 'array', [
+ new Value('origin', Types::ARRAY, ['*']),
+ new Value('methods', Types::ARRAY, [
'GET',
'POST',
'PUT',
@@ -86,8 +86,8 @@ public static function get() {
'DELETE',
'HEAD'
]),
- new Value('headers', 'array', []),
- new Value('exposed_headers', 'array', []),
+ new Value('headers', Types::ARRAY, []),
+ new Value('exposed_headers', Types::ARRAY, []),
new Value('max_age', Types::INTEGER, null),
new Value('credentials', Types::BOOLEAN, false),
]),
@@ -101,14 +101,14 @@ public static function get() {
new Value('timeout', Types::INTEGER, 10),
]),
new Group('hooks', [
- new Value('actions', 'array', []),
- new Value('filters', 'array', []),
+ new Value('actions', Types::ARRAY, []),
+ new Value('filters', Types::ARRAY, []),
]),
new Group('feedback', [
new Value('token', Types::STRING, 'a-kind-of-unique-token'),
new Value('login', Types::STRING, true),
]),
- new Value('tableBlacklist', 'array', []),
+ new Value('tableBlacklist', Types::ARRAY, []),
new Group('auth', [
new Value('secret_key', Types::STRING, ''),
new Value('public_key', Types::STRING, ''),
@@ -139,6 +139,7 @@ public static function get() {
]),
]),
]),
+ new Value('ext?', Types::ARRAY, []),
]);
}
}
diff --git a/src/core/Directus/Config/Schema/Types.php b/src/core/Directus/Config/Schema/Types.php
index 7e21ac34d1..982a6f16fc 100644
--- a/src/core/Directus/Config/Schema/Types.php
+++ b/src/core/Directus/Config/Schema/Types.php
@@ -11,4 +11,5 @@ interface Types
const FLOAT = 'float';
const STRING = 'string';
const BOOLEAN = 'boolean';
+ const ARRAY = 'array';
}
diff --git a/src/core/Directus/Config/Schema/Value.php b/src/core/Directus/Config/Schema/Value.php
index e15c09a1f8..e00eef8ca0 100644
--- a/src/core/Directus/Config/Schema/Value.php
+++ b/src/core/Directus/Config/Schema/Value.php
@@ -35,9 +35,7 @@ public function __construct($name, $type, $default = null)
*/
public function value($context)
{
- foreach ($context as $context_key => $context_value) {
- $context[strtolower(str_replace("-", "", str_replace("_", "", $context_key)))] = $context_value;
- }
+ $context = $this->normalize($context);
if (!isset($context) || !isset($context[$this->key()])) {
if ($this->optional()) {
@@ -50,19 +48,20 @@ public function value($context)
$value = $context[$this->key()];
switch ($this->_type) {
- case Types::INTEGER:
- return intval($value);
- case Types::BOOLEAN:
- $value = strtolower($value);
- return $value === "true" || $value === "1" || $value === "on" || $value === "yes" || boolval($value);
- case Types::FLOAT:
- return floatval($value);
- // TODO: add support to arrays
- case 'array':
- return $this->_default;
- case Types::STRING:
- default:
- return $value;
+ case Types::INTEGER:
+ return intval($value);
+ case Types::BOOLEAN:
+ $value = strtolower($value);
+ return $value === "true" || $value === "1" || $value === "on" || $value === "yes" || boolval($value);
+ case Types::FLOAT:
+ return floatval($value);
+ case Types::ARRAY:
+ if (!is_array($value)) {
+ return $this->_default;
+ }
+ case Types::STRING:
+ default:
+ return $value;
}
}
}
diff --git a/src/core/Directus/Filesystem/Files.php b/src/core/Directus/Filesystem/Files.php
index 5778d066da..f7a5fdc20b 100644
--- a/src/core/Directus/Filesystem/Files.php
+++ b/src/core/Directus/Filesystem/Files.php
@@ -259,6 +259,9 @@ public function saveData($fileData, $fileName, $replace = false)
// When file is uploaded via multipart form data then We will get object of Slim\Http\UploadFile
// When file is uploaded via URL (Youtube, Vimeo, or image link) then we will get base64 encode string.
$size = null;
+
+ $title = $fileName;
+
if (is_object($fileData)) {
$size = $fileData->getSize();
$checksum = hash_file('md5', $fileData->file);
@@ -281,7 +284,7 @@ public function saveData($fileData, $fileName, $replace = false)
unset($fileData);
$fileData = $this->getFileInfo($fileName);
- $fileData['title'] = Formatting::fileNameToFileTitle($fileName);
+ $fileData['title'] = Formatting::fileNameToFileTitle($title);
$fileData['filename'] = basename($filePath);
$fileData['storage'] = $this->config['adapter'];
diff --git a/src/core/Directus/Filesystem/Thumbnailer.php b/src/core/Directus/Filesystem/Thumbnailer.php
index 94ee68d7d0..e30f187429 100644
--- a/src/core/Directus/Filesystem/Thumbnailer.php
+++ b/src/core/Directus/Filesystem/Thumbnailer.php
@@ -179,9 +179,12 @@ public function contain()
// crop image
$img->resize($this->width, $this->height, function ($constraint) {
$constraint->aspectRatio();
+ if (ArrayUtils::get($options, 'preventUpsize')) {
+ $constraint->upsize();
+ }
});
- if( ArrayUtils::get($options, 'resizeCanvas')) {
+ if (ArrayUtils::get($options, 'resizeCanvas')) {
$img->resizeCanvas($this->width, $this->height, ArrayUtils::get($options, 'position', 'center'), ArrayUtils::get($options, 'resizeRelative', false), ArrayUtils::get($options, 'canvasBackground', [255, 255, 255, 0]));
}
diff --git a/src/core/Directus/Services/ItemsService.php b/src/core/Directus/Services/ItemsService.php
index fcc4396b89..d85535fa1e 100644
--- a/src/core/Directus/Services/ItemsService.php
+++ b/src/core/Directus/Services/ItemsService.php
@@ -30,7 +30,7 @@ public function createItem($collection, $payload, $params = [])
{
$this->enforceCreatePermissions($collection, $payload, $params);
$this->validatePayload($collection, null, $payload, $params);
-
+
// Validate Password if password policy settled in the system settings.
if($collection == SchemaManager::COLLECTION_USERS){
$passwordValidation = get_directus_setting('password_policy');
@@ -42,39 +42,15 @@ public function createItem($collection, $payload, $params = [])
//Validate nested payload
$tableSchema = SchemaService::getCollection($collection);
$collectionAliasColumns = $tableSchema->getAliasFields();
-
+
foreach ($collectionAliasColumns as $aliasColumnDetails) {
- $colName = $aliasColumnDetails->getName();
-
- $relationalCollectionName = "";
-
if($this->isManyToManyField($aliasColumnDetails)){
- $relationalCollectionName = $aliasColumnDetails->getRelationship()->getCollectionManyToMany();
-
- if($relationalCollectionName && isset($payload[$colName])){
- foreach($payload[$colName] as $individual){
- if(!isset($individual['$delete'])){
- $validatePayload = $individual[$aliasColumnDetails->getRelationship()->getJunctionOtherRelatedField()];
- $this->validatePayload($relationalCollectionName, null, $validatePayload,$params);
- }
- }
- }
+
+ $this->validateManyToManyCollection($payload, $params, $aliasColumnDetails);
}else{
- if($aliasColumnDetails->isOneToMany()){
- $relationalCollectionName = $aliasColumnDetails->getRelationship()->getCollectionMany();
- }else if($aliasColumnDetails->isManyToOne()){
- $relationalCollectionName = $aliasColumnDetails->getRelationship()->getCollectionOne();
- }
- if($relationalCollectionName && isset($payload[$colName])){
- foreach($payload[$colName] as $individual){
- if(!isset($individual['$delete'])){
- $this->validatePayload($relationalCollectionName, null, $individual,$params,$collection);
- }
- }
- }
- }
+ $this->validateAliasCollection($payload, $params, $aliasColumnDetails, []);
+ }
}
-
$tableGateway = $this->createTableGateway($collection);
$newRecord = $tableGateway->createRecord($payload, $this->getCRUDParams($params));
@@ -185,63 +161,59 @@ public function findOne($collection, array $params = [])
'single' => true
]));
}
-
+
/**
* Validate Parent Collection Fields
*/
public function validateParentCollectionFields($collection, $payload, $params, $recordData){
$tableColumns = SchemaService::getAllCollectionFields($collection);
$collectionFields = $payload;
-
+
foreach($tableColumns as $key => $column){
- if(!empty($recordData) && !$column->hasPrimaryKey()){
- $columnName = $column->getName();
+ if(!empty($recordData)){
+
+ $columnName = $column->getName();
+
$collectionFields[$columnName] = array_key_exists($column->getName(), $collectionFields) ? $collectionFields[$column->getName()]: (DataTypes::isJson($column->getType()) ? (array) $recordData[$columnName] : $recordData[$columnName]);
}
}
-
+
$this->validatePayload($collection, null, $collectionFields, $params);
}
-
+
/**
* Validate Many To Many Collection Fields
*/
- public function validateManyToManyCollection($payload, $params, $aliasColumnDetails, $recordData){
+ public function validateManyToManyCollection($payload, $params, $aliasColumnDetails){
$colName = $aliasColumnDetails->getName();
$relationalCollectionName = $aliasColumnDetails->getRelationship()->getCollectionManyToMany();
if($relationalCollectionName && isset($payload[$colName])){
$relationalCollectionPrimaryKey = SchemaService::getCollectionPrimaryKey($relationalCollectionName);
$relationalCollectionColumns = SchemaService::getAllCollectionFields($relationalCollectionName);
- foreach($payload[$colName] as $individual){
- if(!isset($individual['$delete'])){
+ foreach($payload[$colName] as $individual){
+ if(!isset($individual['$delete'])){
$aliasField = $aliasColumnDetails->getRelationship()->getJunctionOtherRelatedField();
$validatePayload = $individual[$aliasField];
- $storedData = (!empty($recordData) && isset($recordData[$colName])) ? $recordData[$colName] : [] ;
-
+
+
foreach($relationalCollectionColumns as $column){
- if(!empty($recordData) && !$column->isAlias() && !$column->hasPrimaryKey() && isset($recordData[$colName])){
- $search = array_search($individual[$relationalCollectionPrimaryKey], array_column($storedData, $relationalCollectionPrimaryKey));
+ if(!$column->isAlias() && !$column->hasPrimaryKey() && !empty($validatePayload[$relationalCollectionPrimaryKey])){
$columnName = $column->getName();
- if($search !== false){
- $dbObj = isset($storedData[$search][$aliasField]) ? $storedData[$search][$aliasField] : [];
- $validatePayload[$columnName] = array_key_exists($columnName, $validatePayload) ? $validatePayload[$columnName]: (isset($dbObj[$columnName]) ? ((DataTypes::isJson($column->getType()) ? (array) $dbObj[$columnName] : $dbObj[$columnName])) : null);
- }else{
- $relationalCollectionData = $this->findByIds(
- $relationalCollectionName,
- $validatePayload[$relationalCollectionPrimaryKey],
- $params
- );
- $validatePayload[$columnName] = array_key_exists($columnName, $validatePayload) ? $validatePayload[$columnName]: (isset($relationalCollectionData['data'][$columnName]) ? ((DataTypes::isJson($column->getType()) ? (array) $relationalCollectionData['data'][$columnName] : $relationalCollectionData['data'][$columnName])) : null);
- }
+ $relationalCollectionData = $this->findByIds(
+ $relationalCollectionName,
+ $validatePayload[$relationalCollectionPrimaryKey],
+ $params
+ );
+ $validatePayload[$columnName] = array_key_exists($columnName, $validatePayload) ? $validatePayload[$columnName]: (isset($relationalCollectionData['data'][$columnName]) ? ((DataTypes::isJson($column->getType()) ? (array) $relationalCollectionData['data'][$columnName] : $relationalCollectionData['data'][$columnName])) : null);
}
}
$this->validatePayload($relationalCollectionName, null, $validatePayload,$params);
}
}
- }
+ }
}
-
+
/**
* Validate Alias Collection Fields (O2M and M2O - Including Translations and Files)
*/
@@ -256,33 +228,30 @@ public function validateAliasCollection($payload, $params, $aliasColumnDetails,
$parentCollectionName = $aliasColumnDetails->getRelationship()->getCollectionMany();
}
if($relationalCollectionName && isset($payload[$colName])){
-
+
$relationalCollectionPrimaryKey = SchemaService::getCollectionPrimaryKey($relationalCollectionName);
$parentCollectionPrimaryKey = SchemaService::getCollectionPrimaryKey($parentCollectionName);
$relationalCollectionColumns = SchemaService::getAllCollectionFields($relationalCollectionName);
$foreignJoinColumn = $aliasColumnDetails->getRelationship()->getFieldMany();
-
- foreach($payload[$colName] as $individual){
- if(!isset($individual['$delete'])){
+
+ foreach($payload[$colName] as $individual){
+
+ if(!isset($individual['$delete'])){
foreach($relationalCollectionColumns as $key => $column){
- if(!empty($recordData) && !$column->isAlias() && !$column->hasPrimaryKey() && isset($recordData[$colName])){
- $search = array_search($individual[$relationalCollectionPrimaryKey], array_column($recordData[$colName], $relationalCollectionPrimaryKey));
+
+ if(!$column->isAlias() && !$column->hasPrimaryKey() && !empty($individual[$relationalCollectionPrimaryKey])){
$columnName = $column->getName();
- if($search !== false){
- $individual[$columnName] = array_key_exists($columnName, $individual) ? $individual[$columnName]: (isset($recordData[$colName][$search][$columnName]) ? ((DataTypes::isJson($column->getType()) ? (array) $recordData[$colName][$search][$columnName] : $recordData[$colName][$search][$columnName])) : null);
- }else{
- $relationalCollectionData = $this->findByIds(
- $relationalCollectionName,
- $individual[$relationalCollectionPrimaryKey],
- $params
- );
- $individual[$columnName] = array_key_exists($columnName, $individual) ? $individual[$columnName]: (isset($relationalCollectionData['data'][$columnName]) ? ((DataTypes::isJson($column->getType()) ? (array) $relationalCollectionData['data'][$columnName] : $relationalCollectionData['data'][$columnName])) : null);
- }
+ $relationalCollectionData = $this->findByIds(
+ $relationalCollectionName,
+ $individual[$relationalCollectionPrimaryKey],
+ $params
+ );
+ $individual[$columnName] = array_key_exists($columnName, $individual) ? $individual[$columnName]: (isset($relationalCollectionData['data'][$columnName]) ? ((DataTypes::isJson($column->getType()) ? (array) $relationalCollectionData['data'][$columnName] : $relationalCollectionData['data'][$columnName])) : null);
}
}
// only add parent id's to items that are lacking the parent column
- if (empty($individual[$foreignJoinColumn])) {
+ if (empty($individual[$foreignJoinColumn]) && !empty($recordData[$parentCollectionPrimaryKey])) {
$individual[$foreignJoinColumn] = $recordData[$parentCollectionPrimaryKey];
}
$this->validatePayload($relationalCollectionName, null, $individual,$params);
@@ -303,10 +272,10 @@ public function checkRelationalItemDeletable($collection, $payload, $recordData)
// Check if field is O2M and required
if($column->hasRelationship() && $column->isOneToMany() && ($column->isRequired() || (!$column->isNullable() && $column->getDefaultValue() == null))){
if(!empty($recordData)){
- $columnName = $column->getName();
-
+ $columnName = $column->getName();
+
if(isset($recordData[$columnName]) && isset($payload[$columnName]) && count($recordData[$columnName]) == count($payload[$columnName]) ){
- $fieldMany = $column->getRelationship()->getFieldMany();
+ $fieldMany = $column->getRelationship()->getFieldMany();
$collectionMany = SchemaService::getCollection($column->getRelationship()->getCollectionMany());
$primaryKeyCollectionMany = $collectionMany->getPrimaryKeyName();
$alreadyStoredEntries = array_column($recordData[$columnName],$primaryKeyCollectionMany);
@@ -325,7 +294,7 @@ public function checkRelationalItemDeletable($collection, $payload, $recordData)
}
}
}
-
+
/**
* Updates a single item in the given collection and id
*
@@ -344,22 +313,22 @@ public function update($collection, $id, $payload, array $params = [])
$dbData = $this->findByIds($collection,$id,['fields' => '*.*.*']);
$recordData = !empty($dbData['data']) ? $dbData['data'] : [];
$this->validateParentCollectionFields($collection, $payload, $params, $recordData);
-
+
//Validate alias field payload
$tableSchema = SchemaService::getCollection($collection);
$collectionAliasColumns = $tableSchema->getAliasFields();
-
+
foreach ($collectionAliasColumns as $aliasColumnDetails) {
- if($this->isManyToManyField($aliasColumnDetails)){
- $this->validateManyToManyCollection($payload, $params, $aliasColumnDetails, $recordData);
+ if($this->isManyToManyField($aliasColumnDetails)){
+ $this->validateManyToManyCollection($payload, $params, $aliasColumnDetails);
}else{
- $this->validateAliasCollection($payload, $params, $aliasColumnDetails, $recordData);
- }
+ $this->validateAliasCollection($payload, $params, $aliasColumnDetails, $recordData);
+ }
}
// There is a scenario in which user tries to delete all the relational data although it is required. This can be possible for o2M only and API have to restrict that.
- $this->checkRelationalItemDeletable($collection, $payload, $recordData);
-
+ $this->checkRelationalItemDeletable($collection, $payload, $recordData);
+
$this->checkItemExists($collection, $id);
$tableGateway = $this->createTableGateway($collection);
@@ -561,10 +530,10 @@ protected function getStatusValue($collection, $id)
return $row[$collectionObject->getStatusField()->getName()];
}
-
+
/**
* Checks whether the relationship is MANY TO MANY
- *
+ *
* @param $fieldMany
* @param $collectionMany
*
@@ -578,7 +547,7 @@ protected function isManyToManyField($field){
'collection_many' => $relationship->getCollectionMany(),
];
$tableGateway = $this->createTableGateway(SchemaManager::COLLECTION_RELATIONS);
- $junctionEntries = $tableGateway->getItems(['filter' => $junctionConditions]);
+ $junctionEntries = $tableGateway->getItems(['filter' => $junctionConditions]);
return !empty($junctionEntries['data']) ? true : false;
}
return false;
diff --git a/src/core/Directus/Services/ProjectService.php b/src/core/Directus/Services/ProjectService.php
index 6bbac8f3ba..2097804a57 100644
--- a/src/core/Directus/Services/ProjectService.php
+++ b/src/core/Directus/Services/ProjectService.php
@@ -46,6 +46,7 @@ public function create(array $data)
'timezone' => 'string',
'locale' => 'string',
+ 'logs_path' => 'string',
'project_name' => 'string',
'app_url' => 'string',
diff --git a/src/core/Directus/Services/TablesService.php b/src/core/Directus/Services/TablesService.php
index d42f792e43..c6580334c8 100644
--- a/src/core/Directus/Services/TablesService.php
+++ b/src/core/Directus/Services/TablesService.php
@@ -294,7 +294,7 @@ public function deleteField($collection, $field, array $params = [])
$hookEmitter->run('field.delete:before', [$collection, $field]);
$hookEmitter->run('field.delete.' . $collection . ':before', [$field]);
- $tableService->dropColumn($collection, $field);
+ $tableService->dropColumn($collection, $field, $params);
$hookEmitter->run('field.delete', [$collection, $field]);
$hookEmitter->run('field.delete.' . $collection, [$field]);
@@ -730,7 +730,7 @@ public function batchUpdateFieldWithIds($collectionName, array $fieldNames, arra
return $allItems;
}
- public function dropColumn($collectionName, $fieldName)
+ public function dropColumn($collectionName, $fieldName, array $params = [])
{
$tableObject = $this->getSchemaManager()->getCollection($collectionName);
if (!$tableObject) {
@@ -753,7 +753,7 @@ public function dropColumn($collectionName, $fieldName)
}
if ($columnObject->hasRelationship()) {
- $this->removeColumnRelationship($columnObject);
+ $this->removeColumnRelationship($columnObject,$params);
}
if ($columnObject->isManaged()) {
@@ -900,7 +900,7 @@ public function removeColumnInfo($collectionName, $fieldName)
*
* @return bool|int
*/
- public function removeColumnRelationship(Field $field)
+ public function removeColumnRelationship(Field $field, array $params = [])
{
if (!$field->hasRelationship()) {
return false;
@@ -909,7 +909,7 @@ public function removeColumnRelationship(Field $field)
if ($this->shouldRemoveRelationshipRecord($field)) {
$result = $this->removeRelationshipRecord($field);
} else {
- $result = $this->removeRelationshipFromRecord($field);
+ $result = $this->removeRelationshipFromRecord($field, $params);
}
return $result;
@@ -950,7 +950,7 @@ protected function removeRelationshipRecord(Field $field)
*
* @return int
*/
- protected function removeRelationshipFromRecord(Field $field)
+ protected function removeRelationshipFromRecord(Field $field, array $params = [])
{
$tableGateway = $this->getRelationsTableGateway();
@@ -975,7 +975,10 @@ protected function removeRelationshipFromRecord(Field $field)
*/
if (!empty($junctionEntries['data'])) {
$tableGateway->delete($junctionConditions);
- $this->dropTable($relationship->getCollectionMany());
+
+ if(isset($params['delete_junction'])){
+ $this->dropTable($relationship->getCollectionMany());
+ }
return $tableGateway->delete($conditions['values']);
} else {
diff --git a/src/core/Directus/Util/Installation/InstallerUtils.php b/src/core/Directus/Util/Installation/InstallerUtils.php
index 5556eb467a..d80be3e47f 100644
--- a/src/core/Directus/Util/Installation/InstallerUtils.php
+++ b/src/core/Directus/Util/Installation/InstallerUtils.php
@@ -880,7 +880,7 @@ private static function dropTables($basePath, $projectName)
private static function createConfigData(array $data)
{
$corsEnabled = ArrayUtils::get($data, 'cors_enabled', true);
- $authSecret = ArrayUtils::get($data, 'auth_secret', StringUtils::randomString(32));
+ $authSecret = ArrayUtils::get($data, 'auth_secret', StringUtils::randomString(32, false));
$authPublic = ArrayUtils::get($data, 'auth_public', generate_uuid4());
return ArrayUtils::defaults([
@@ -891,9 +891,10 @@ private static function createConfigData(array $data)
'db_password' => null,
'db_socket' => '',
'mail_from' => 'admin@example.com',
- 'feedback_token' => sha1(gmdate('U') . StringUtils::randomString(32)),
+ 'feedback_token' => sha1(gmdate('U') . StringUtils::randomString(32, false)),
'feedback_login' => true,
'timezone' => get_default_timezone(),
+ 'logs_path' => __DIR__ . '/../../../../../logs',
'cache' => [
'enabled' => false,
'response_ttl' => 3600,
diff --git a/src/core/Directus/Util/Installation/stubs/config.stub b/src/core/Directus/Util/Installation/stubs/config.stub
index 117cfc087c..6613eca69e 100644
--- a/src/core/Directus/Util/Installation/stubs/config.stub
+++ b/src/core/Directus/Util/Installation/stubs/config.stub
@@ -8,7 +8,7 @@ return [
'settings' => [
'logger' => [
- 'path' => __DIR__ . '/../logs',
+ 'path' => '{{logs_path}}',
],
],
diff --git a/src/core/Directus/Util/StringUtils.php b/src/core/Directus/Util/StringUtils.php
index bf80fe2c88..dac277322c 100644
--- a/src/core/Directus/Util/StringUtils.php
+++ b/src/core/Directus/Util/StringUtils.php
@@ -123,10 +123,14 @@ public static function random($length = 16)
*
* @return string
*/
- public static function randomString($length = 16)
+ public static function randomString($length = 16, $special_chars = true)
{
// TODO: Add options to allow symbols or user provided characters to extend the list
- $pool = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()_+}{';'?>.<,";
+ $pool = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+ if ($special_chars) {
+ $pool .= "!@#$%^&*()_+}{;?>.<,";
+ }
return substr(str_shuffle(str_repeat($pool, $length)), 0, $length);
}
@@ -234,7 +238,7 @@ public static function replacePlaceholder($string, $data = [], $placeHolderForma
}
/**
- * If any variable of the given string have null value as a replacement then the
+ * If any variable of the given string have null value as a replacement then the
* result will be 'null'(string). So we need to replace it with blank string.
*/
$string = str_replace("'null'", "''", $string);
diff --git a/src/endpoints/Settings.php b/src/endpoints/Settings.php
index 6ce625c789..b2c3bc41f9 100644
--- a/src/endpoints/Settings.php
+++ b/src/endpoints/Settings.php
@@ -7,6 +7,7 @@
use Directus\Application\Http\Response;
use Directus\Application\Route;
use Directus\Services\SettingsService;
+use Directus\Services\FilesServices;
use function Directus\regex_numeric_ids;
class Settings extends Route
@@ -149,13 +150,8 @@ public function read(Request $request, Response $response)
*
* @return Response
*/
- public function getInterfaceBasedInput($request, $setting)
+ public function getInterfaceBasedInput($request, $setting, $fieldData)
{
- $service = new SettingsService($this->container);
- $fieldData = $service->findAllFields(
- $request->getQueryParams()
- );
-
$inputData = $request->getParsedBody();
foreach ($fieldData['data'] as $key => $value) {
if ($value['field'] == $setting) {
@@ -180,6 +176,33 @@ public function getInterfaceBasedInput($request, $setting)
return $inputData;
}
+ /**
+ * @param Request $request
+ * @param Response $response
+ *
+ * @return Response
+ */
+ public function getInterfaceBasedOutput($setting, $fieldData)
+ {
+ $fileService = new FilesServices($this->container);
+ $response = $setting['value'];
+ foreach ($fieldData['data'] as $value) {
+ if ($value['field'] == $setting['key']) {
+ if ($setting['value'] != null) {
+ switch ($value['type']) {
+ case 'file':
+ $responseData = $fileService->findByIds($setting['value'],[]);
+ if( !empty($responseData['data']) ){
+ $response = $responseData['data'];
+ }
+ break;
+ }
+ }
+ }
+ }
+ return $response;
+ }
+
/**
* @param Request $request
* @param Response $response
@@ -212,7 +235,10 @@ public function update(Request $request, Response $response)
*
*/
- $inputData = $this->getInterfaceBasedInput($request, $serviceData['data']['key']);
+ $fieldData = $service->findAllFields(
+ $request->getQueryParams()
+ );
+ $inputData = $this->getInterfaceBasedInput($request, $serviceData['data']['key'], $fieldData);
$responseData = $service->update(
$request->getAttribute('id'),
$inputData,
@@ -220,6 +246,7 @@ public function update(Request $request, Response $response)
);
$responseData['data']['value'] = $payload['value'];
+ $responseData['data']['value'] = $this->getInterfaceBasedOutput($responseData['data'], $fieldData);
return $this->responseWithData($request, $response, $responseData);
}
diff --git a/src/helpers/app.php b/src/helpers/app.php
index 7c54b220b9..dea3dc3de2 100644
--- a/src/helpers/app.php
+++ b/src/helpers/app.php
@@ -100,9 +100,7 @@ function get_project_config($name = null, $basePath = null)
if (!file_exists($configFilePath)) {
throw new UnknownProjectException($name);
}
- $configData = $schema->value([
- "directus" => Context::from_file($configFilePath)
- ]);
+ $configData = $schema->value(['directus' => Context::from_file($configFilePath)]);
}
$config = new Config($configData);
diff --git a/src/helpers/file.php b/src/helpers/file.php
index d36069b559..e102056f87 100644
--- a/src/helpers/file.php
+++ b/src/helpers/file.php
@@ -191,6 +191,7 @@ function add_default_thumbnail_dimensions(array &$list)
*/
function get_thumbnails(array $row)
{
+
$filename = $row['filename'];
$type = array_get($row, 'type');
$thumbnailFilenameParts = explode('.', $filename);
@@ -218,6 +219,7 @@ function get_thumbnails(array $row)
}
$size = explode('x', $dimension);
+
if (count($size) == 2) {
$thumbnailUrl = get_thumbnail_url($filename, $size[0], $size[1]);
$thumbnailRelativeUrl = get_thumbnail_path($filename, $size[0], $size[1]);
@@ -297,7 +299,8 @@ function get_proxy_path($path)
// env/width/height/mode/quality/name
return sprintf(
'/downloads/%s/%s',
- $projectName, $path
+ $projectName,
+ $path
);
}
}
@@ -365,17 +368,16 @@ function is_a_url($value)
*
* @return bool
*/
- function validate_file($value,$constraint,$options = null)
+ function validate_file($value, $constraint, $options = null)
{
- switch ($constraint) {
+ switch ($constraint) {
case 'mimeTypes':
- validate_file_mime_type($value,$options);
- break;
+ validate_file_mime_type($value, $options);
+ break;
case 'maxSize':
- validate_file_size($value,$options);
- break;
-
- }
+ validate_file_size($value, $options);
+ break;
+ }
}
}
@@ -387,14 +389,14 @@ function validate_file($value,$constraint,$options = null)
*
* @return bool
*/
- function validate_file_mime_type($value,$options)
+ function validate_file_mime_type($value, $options)
{
$mimeTypes = $options;
$mime = $value;
-
- if($options == null) {
- $options=get_directus_setting('file_mimetype_whitelist');
- $mimeTypes = explode(",",$options);
+
+ if ($options == null) {
+ $options = get_directus_setting('file_mimetype_whitelist');
+ $mimeTypes = explode(",", $options);
}
foreach ($mimeTypes as $mimeType) {
if ($mimeType === $mime) {
@@ -406,7 +408,7 @@ function validate_file_mime_type($value,$options)
}
}
}
- $message='The mime type of the file is invalid.Allowed mime types are '.$options.'.';
+ $message = 'The mime type of the file is invalid.Allowed mime types are ' . $options . '.';
throw new InvalidRequestException($message);
}
}
@@ -418,13 +420,13 @@ function validate_file_mime_type($value,$options)
*
* @return bool
*/
- function validate_file_size($value,$options)
+ function validate_file_size($value, $options)
{
- $maxSize=$options;
- if($options == null) {
- $maxSize=get_directus_setting('file_max_size');
+ $maxSize = $options;
+ if ($options == null) {
+ $maxSize = get_directus_setting('file_max_size');
}
- $size=$maxSize;
+ $size = $maxSize;
$factors = [
'KB' => 1000,
'MB' => 1000000,
@@ -433,21 +435,20 @@ function validate_file_size($value,$options)
];
if (ctype_digit((string) $maxSize)) {
$maxSize = (int) $maxSize;
- } elseif (preg_match('/^(\d++)('.implode('|', array_keys($factors)).')$/', $maxSize, $matches)) {
+ } elseif (preg_match('/^(\d++)(' . implode('|', array_keys($factors)) . ')$/', $maxSize, $matches)) {
$maxSize = $matches[1] * $factors[$unit = $matches[2]];
} else {
throw new InvalidRequestException(sprintf('"%s" is not a valid maximum size.', $size));
}
if (0 === $value) {
- $message='An empty file is not allowed.';
+ $message = 'An empty file is not allowed.';
throw new InvalidRequestException($message);
}
- if($value > $maxSize){
- $message='The file is too large. Allowed maximum size is '.$size.'.';
+ if ($value > $maxSize) {
+ $message = 'The file is too large. Allowed maximum size is ' . $size . '.';
throw new InvalidRequestException($message);
}
}
}
-
diff --git a/tests/api/Config/ContextTest.php b/tests/api/Config/ContextTest.php
index 9c99ca2c21..28343298e8 100644
--- a/tests/api/Config/ContextTest.php
+++ b/tests/api/Config/ContextTest.php
@@ -120,6 +120,9 @@ public function testContextFile()
"b" => "2"
],
],
+ 'arrayval' => [
+ 'some' => 'array'
+ ]
];
// Should load values from php source file
diff --git a/tests/api/Config/SchemaTest.php b/tests/api/Config/SchemaTest.php
index cc5d8d40c2..97e28c8e65 100644
--- a/tests/api/Config/SchemaTest.php
+++ b/tests/api/Config/SchemaTest.php
@@ -115,6 +115,7 @@ public function testDefaults()
'social_providers' =>
[],
],
+ 'ext' => []
], $data);
}
diff --git a/tests/api/Config/sources/source.json b/tests/api/Config/sources/source.json
index 1a0c533ded..1ab116fa24 100644
--- a/tests/api/Config/sources/source.json
+++ b/tests/api/Config/sources/source.json
@@ -4,5 +4,10 @@
"a": 1,
"b": 2
}
- }
+ },
+ "arrayval": [
+ {
+ "some": "object",
+ }
+ ]
}
diff --git a/tests/api/Config/sources/source.php b/tests/api/Config/sources/source.php
index 6fcd56e723..6de17fe03b 100644
--- a/tests/api/Config/sources/source.php
+++ b/tests/api/Config/sources/source.php
@@ -7,4 +7,7 @@
"b" => 2,
],
],
+ 'arrayval' => [
+ 'some' => 'array'
+ ]
];