Upgrade to Kirby 3.3.2
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "getkirby/cms",
|
"name": "getkirby/cms",
|
||||||
"description": "The Kirby 3 core",
|
"description": "The Kirby 3 core",
|
||||||
"version": "3.3.1",
|
"version": "3.3.2",
|
||||||
"license": "proprietary",
|
"license": "proprietary",
|
||||||
"keywords": ["kirby", "cms", "core"],
|
"keywords": ["kirby", "cms", "core"],
|
||||||
"homepage": "https://getkirby.com",
|
"homepage": "https://getkirby.com",
|
||||||
|
14
kirby/composer.lock
generated
14
kirby/composer.lock
generated
@@ -4,7 +4,7 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "e10295fa58c27caa37474e7fa503bb8e",
|
"content-hash": "be4d625ce32603f849e29b8d2e841006",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "claviska/simpleimage",
|
"name": "claviska/simpleimage",
|
||||||
@@ -413,16 +413,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-mbstring",
|
"name": "symfony/polyfill-mbstring",
|
||||||
"version": "v1.12.0",
|
"version": "v1.13.1",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
||||||
"reference": "b42a2f66e8f1b15ccf25652c3424265923eb4f17"
|
"reference": "7b4aab9743c30be783b73de055d24a39cf4b954f"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/b42a2f66e8f1b15ccf25652c3424265923eb4f17",
|
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/7b4aab9743c30be783b73de055d24a39cf4b954f",
|
||||||
"reference": "b42a2f66e8f1b15ccf25652c3424265923eb4f17",
|
"reference": "7b4aab9743c30be783b73de055d24a39cf4b954f",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -434,7 +434,7 @@
|
|||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-master": "1.12-dev"
|
"dev-master": "1.13-dev"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
@@ -468,7 +468,7 @@
|
|||||||
"portable",
|
"portable",
|
||||||
"shim"
|
"shim"
|
||||||
],
|
],
|
||||||
"time": "2019-08-06T08:03:45+00:00"
|
"time": "2019-11-27T14:18:11+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "true/punycode",
|
"name": "true/punycode",
|
||||||
|
@@ -438,12 +438,12 @@
|
|||||||
"upload.error.default": "El archivo no pudo ser subido",
|
"upload.error.default": "El archivo no pudo ser subido",
|
||||||
"upload.error.extension": "Subida de archivo detenida por la extensión",
|
"upload.error.extension": "Subida de archivo detenida por la extensión",
|
||||||
"upload.error.formSize": "El archivo subido excede la directiva MAX_FILE_SIZE que fue especificada en el formulario",
|
"upload.error.formSize": "El archivo subido excede la directiva MAX_FILE_SIZE que fue especificada en el formulario",
|
||||||
"upload.error.iniPostSize": "The uploaded file exceeds the post_max_size directive in php.ini",
|
"upload.error.iniPostSize": "El archivo subido excede la directiva post_max_size directive en php.ini",
|
||||||
"upload.error.iniSize": "The uploaded file exceeds the upload_max_filesize directive in php.ini",
|
"upload.error.iniSize": "El archivo subido excede la directiva upload_max_filesize en php.ini",
|
||||||
"upload.error.noFile": "No file was uploaded",
|
"upload.error.noFile": "Ningún archivo ha sido subido",
|
||||||
"upload.error.noFiles": "No files were uploaded",
|
"upload.error.noFiles": "Ningún archivo ha sido subido",
|
||||||
"upload.error.partial": "The uploaded file was only partially uploaded",
|
"upload.error.partial": "El archivo ha sido subido solo parcialmente",
|
||||||
"upload.error.tmpDir": "Missing a temporary folder",
|
"upload.error.tmpDir": "No se encuentra la carpeta temporal",
|
||||||
"upload.errors": "Error",
|
"upload.errors": "Error",
|
||||||
"upload.progress": "Subiendo...",
|
"upload.progress": "Subiendo...",
|
||||||
|
|
||||||
|
@@ -23,15 +23,15 @@
|
|||||||
|
|
||||||
"delete": "Borrar",
|
"delete": "Borrar",
|
||||||
"dimensions": "Dimensiones",
|
"dimensions": "Dimensiones",
|
||||||
"disabled": "Disabled",
|
"disabled": "Desabilitado",
|
||||||
"discard": "Descartar",
|
"discard": "Descartar",
|
||||||
"download": "Download",
|
"download": "Descargar",
|
||||||
"duplicate": "Duplicate",
|
"duplicate": "Duplicar",
|
||||||
"edit": "Editar",
|
"edit": "Editar",
|
||||||
|
|
||||||
"dialog.files.empty": "No files to select",
|
"dialog.files.empty": "No se ha seleccionado ningún archivo",
|
||||||
"dialog.pages.empty": "No pages to select",
|
"dialog.pages.empty": "No se ha seleccionado ninguna página",
|
||||||
"dialog.users.empty": "No users to select",
|
"dialog.users.empty": "No se ha seleccionado ningún usuario",
|
||||||
|
|
||||||
"email": "Correo electrónico",
|
"email": "Correo electrónico",
|
||||||
"email.placeholder": "correo@ejemplo.com",
|
"email.placeholder": "correo@ejemplo.com",
|
||||||
|
@@ -61,21 +61,21 @@
|
|||||||
"Filändelsen \"{extension}\" är inte tillåten",
|
"Filändelsen \"{extension}\" är inte tillåten",
|
||||||
"error.file.extension.missing":
|
"error.file.extension.missing":
|
||||||
"Filen \"{filename}\" saknar filändelse",
|
"Filen \"{filename}\" saknar filändelse",
|
||||||
"error.file.maxheight": "The height of the image must not exceed {height} pixels",
|
"error.file.maxheight": "Bildens höjd får inte överstiga {height} pixlar",
|
||||||
"error.file.maxsize": "The file is too large",
|
"error.file.maxsize": "Filen är för stor",
|
||||||
"error.file.maxwidth": "The width of the image must not exceed {width} pixels",
|
"error.file.maxwidth": "Bildens bredd får inte överstiga {width} pixlar",
|
||||||
"error.file.mime.differs":
|
"error.file.mime.differs":
|
||||||
"Den uppladdade filen måste vara av samma mime-typ \"{mime}\"",
|
"Den uppladdade filen måste vara av samma mime-typ \"{mime}\"",
|
||||||
"error.file.mime.forbidden": "Mediatypen \"{mime}\" är inte tillåten",
|
"error.file.mime.forbidden": "Mediatypen \"{mime}\" är inte tillåten",
|
||||||
"error.file.mime.invalid": "Invalid mime type: {mime}",
|
"error.file.mime.invalid": "Ogiltig mime-typ: {mime}",
|
||||||
"error.file.mime.missing":
|
"error.file.mime.missing":
|
||||||
"Mediatypen för \"{filename}\" kan inte detekteras",
|
"Mediatypen för \"{filename}\" kan inte detekteras",
|
||||||
"error.file.minheight": "The height of the image must be at least {height} pixels",
|
"error.file.minheight": "Bildens höjd måste vara minst {height} pixlar",
|
||||||
"error.file.minsize": "The file is too small",
|
"error.file.minsize": "Filen är för liten",
|
||||||
"error.file.minwidth": "The width of the image must be at least {width} pixels",
|
"error.file.minwidth": "Bildens bredd måste vara minst {width} pixlar",
|
||||||
"error.file.name.missing": "Filnamnet får inte vara tomt",
|
"error.file.name.missing": "Filnamnet får inte vara tomt",
|
||||||
"error.file.notFound": "Filen \"{filename}\" kan ej hittas",
|
"error.file.notFound": "Filen \"{filename}\" kan ej hittas",
|
||||||
"error.file.orientation": "The orientation of the image must be \"{orientation}\"",
|
"error.file.orientation": "Bildens orientering måste vara \"{orientation}\"",
|
||||||
"error.file.type.forbidden": "Du har inte behörighet att ladda upp filer av typen {type}",
|
"error.file.type.forbidden": "Du har inte behörighet att ladda upp filer av typen {type}",
|
||||||
"error.file.undefined": "Filen kan inte hittas",
|
"error.file.undefined": "Filen kan inte hittas",
|
||||||
|
|
||||||
@@ -304,7 +304,7 @@
|
|||||||
"loading": "Laddar",
|
"loading": "Laddar",
|
||||||
|
|
||||||
"lock.unsaved": "Osparade ändringar",
|
"lock.unsaved": "Osparade ändringar",
|
||||||
"lock.unsaved.empty": "There are no more unsaved changes",
|
"lock.unsaved.empty": "Det finns inga fler osparade ändringar",
|
||||||
"lock.isLocked": "Osparade ändringar av <strong>{email}</strong>",
|
"lock.isLocked": "Osparade ändringar av <strong>{email}</strong>",
|
||||||
"lock.file.isLocked": "Filen redigeras just nu av {email} och kan inte redigeras.",
|
"lock.file.isLocked": "Filen redigeras just nu av {email} och kan inte redigeras.",
|
||||||
"lock.page.isLocked": "Sidan redigeras just nu av {email} och kan inte redigeras.",
|
"lock.page.isLocked": "Sidan redigeras just nu av {email} och kan inte redigeras.",
|
||||||
|
2
kirby/panel/dist/css/app.css
vendored
2
kirby/panel/dist/css/app.css
vendored
File diff suppressed because one or more lines are too long
2
kirby/panel/dist/js/app.js
vendored
2
kirby/panel/dist/js/app.js
vendored
File diff suppressed because one or more lines are too long
@@ -69,6 +69,13 @@ trait AppPlugins
|
|||||||
'validators' => []
|
'validators' => []
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache for system extensions
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected static $systemExtensions = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flag when plugins have been loaded
|
* Flag when plugins have been loaded
|
||||||
* to not load them again
|
* to not load them again
|
||||||
@@ -570,6 +577,8 @@ trait AppPlugins
|
|||||||
*/
|
*/
|
||||||
protected function extensionsFromSystem()
|
protected function extensionsFromSystem()
|
||||||
{
|
{
|
||||||
|
// load static extensions only once
|
||||||
|
if (static::$systemExtensions === null) {
|
||||||
// Form Field Mixins
|
// Form Field Mixins
|
||||||
FormField::$mixins['filepicker'] = include static::$root . '/config/fields/mixins/filepicker.php';
|
FormField::$mixins['filepicker'] = include static::$root . '/config/fields/mixins/filepicker.php';
|
||||||
FormField::$mixins['min'] = include static::$root . '/config/fields/mixins/min.php';
|
FormField::$mixins['min'] = include static::$root . '/config/fields/mixins/min.php';
|
||||||
@@ -602,20 +611,6 @@ trait AppPlugins
|
|||||||
'x' => 'xml'
|
'x' => 'xml'
|
||||||
];
|
];
|
||||||
|
|
||||||
// default cache types
|
|
||||||
$this->extendCacheTypes([
|
|
||||||
'apcu' => 'Kirby\Cache\ApcuCache',
|
|
||||||
'file' => 'Kirby\Cache\FileCache',
|
|
||||||
'memcached' => 'Kirby\Cache\MemCached',
|
|
||||||
'memory' => 'Kirby\Cache\MemoryCache',
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->extendComponents(include static::$root . '/config/components.php');
|
|
||||||
$this->extendBlueprints(include static::$root . '/config/blueprints.php');
|
|
||||||
$this->extendFields(include static::$root . '/config/fields.php');
|
|
||||||
$this->extendFieldMethods((include static::$root . '/config/methods.php')($this));
|
|
||||||
$this->extendTags(include static::$root . '/config/tags.php');
|
|
||||||
|
|
||||||
// blueprint presets
|
// blueprint presets
|
||||||
PageBlueprint::$presets['pages'] = include static::$root . '/config/presets/pages.php';
|
PageBlueprint::$presets['pages'] = include static::$root . '/config/presets/pages.php';
|
||||||
PageBlueprint::$presets['page'] = include static::$root . '/config/presets/page.php';
|
PageBlueprint::$presets['page'] = include static::$root . '/config/presets/page.php';
|
||||||
@@ -636,6 +631,29 @@ trait AppPlugins
|
|||||||
Section::$types['pages'] = include static::$root . '/config/sections/pages.php';
|
Section::$types['pages'] = include static::$root . '/config/sections/pages.php';
|
||||||
Section::$types['files'] = include static::$root . '/config/sections/files.php';
|
Section::$types['files'] = include static::$root . '/config/sections/files.php';
|
||||||
Section::$types['fields'] = include static::$root . '/config/sections/fields.php';
|
Section::$types['fields'] = include static::$root . '/config/sections/fields.php';
|
||||||
|
|
||||||
|
static::$systemExtensions = [
|
||||||
|
'components' => include static::$root . '/config/components.php',
|
||||||
|
'blueprints' => include static::$root . '/config/blueprints.php',
|
||||||
|
'fields' => include static::$root . '/config/fields.php',
|
||||||
|
'fieldMethods' => include static::$root . '/config/methods.php',
|
||||||
|
'tags' => include static::$root . '/config/tags.php'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// default cache types
|
||||||
|
$this->extendCacheTypes([
|
||||||
|
'apcu' => 'Kirby\Cache\ApcuCache',
|
||||||
|
'file' => 'Kirby\Cache\FileCache',
|
||||||
|
'memcached' => 'Kirby\Cache\MemCached',
|
||||||
|
'memory' => 'Kirby\Cache\MemoryCache',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->extendComponents(static::$systemExtensions['components']);
|
||||||
|
$this->extendBlueprints(static::$systemExtensions['blueprints']);
|
||||||
|
$this->extendFields(static::$systemExtensions['fields']);
|
||||||
|
$this->extendFieldMethods((static::$systemExtensions['fieldMethods'])($this));
|
||||||
|
$this->extendTags(static::$systemExtensions['tags']);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -319,6 +319,9 @@ class Auth
|
|||||||
$log['by-ip'] = $log['by-ip'] ?? [];
|
$log['by-ip'] = $log['by-ip'] ?? [];
|
||||||
$log['by-email'] = $log['by-email'] ?? [];
|
$log['by-email'] = $log['by-email'] ?? [];
|
||||||
|
|
||||||
|
// remove all elements on the top level with different keys (old structure)
|
||||||
|
$log = array_intersect_key($log, array_flip(['by-ip', 'by-email']));
|
||||||
|
|
||||||
// remove entries that are no longer needed
|
// remove entries that are no longer needed
|
||||||
$originalLog = $log;
|
$originalLog = $log;
|
||||||
$time = time() - $this->kirby->option('auth.timeout', 3600);
|
$time = time() - $this->kirby->option('auth.timeout', 3600);
|
||||||
@@ -328,9 +331,6 @@ class Auth
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove all elements on the top level with different keys (old structure)
|
|
||||||
$log = array_intersect_key($log, array_flip(['by-ip', 'by-email']));
|
|
||||||
|
|
||||||
// write new log to the file system if it changed
|
// write new log to the file system if it changed
|
||||||
if ($read === false || $log !== $originalLog) {
|
if ($read === false || $log !== $originalLog) {
|
||||||
if (count($log['by-ip']) === 0 && count($log['by-email']) === 0) {
|
if (count($log['by-ip']) === 0 && count($log['by-email']) === 0) {
|
||||||
|
@@ -2,10 +2,11 @@
|
|||||||
|
|
||||||
namespace Kirby\Cms;
|
namespace Kirby\Cms;
|
||||||
|
|
||||||
|
use Kirby\Exception\InvalidArgumentException;
|
||||||
use Kirby\Exception\NotFoundException;
|
use Kirby\Exception\NotFoundException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper around our PHPMailer package, which
|
* Wrapper around our Email package, which
|
||||||
* handles all the magic connections between Kirby
|
* handles all the magic connections between Kirby
|
||||||
* and sending emails, like email templates, file
|
* and sending emails, like email templates, file
|
||||||
* attachments, etc.
|
* attachments, etc.
|
||||||
@@ -18,26 +19,34 @@ use Kirby\Exception\NotFoundException;
|
|||||||
*/
|
*/
|
||||||
class Email
|
class Email
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* Options configured through the `email` CMS option
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
protected $options;
|
protected $options;
|
||||||
protected $preset;
|
|
||||||
|
/**
|
||||||
|
* Props for the email object; will be passed to the
|
||||||
|
* Kirby\Email\Email class
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
protected $props;
|
protected $props;
|
||||||
|
|
||||||
protected static $transform = [
|
/**
|
||||||
'from' => 'user',
|
* Class constructor
|
||||||
'replyTo' => 'user',
|
*
|
||||||
'to' => 'user',
|
* @param string|array $preset Preset name from the config or a simple props array
|
||||||
'cc' => 'user',
|
* @param array $props Props array to override the $preset
|
||||||
'bcc' => 'user',
|
*/
|
||||||
'attachments' => 'file'
|
|
||||||
];
|
|
||||||
|
|
||||||
public function __construct($preset = [], array $props = [])
|
public function __construct($preset = [], array $props = [])
|
||||||
{
|
{
|
||||||
$this->options = App::instance()->option('email');
|
$this->options = App::instance()->option('email');
|
||||||
|
|
||||||
// load presets from options
|
// build a prop array based on preset and props
|
||||||
$this->preset = $this->preset($preset);
|
$preset = $this->preset($preset);
|
||||||
$this->props = array_merge($this->preset, $props);
|
$this->props = array_merge($preset, $props);
|
||||||
|
|
||||||
// add transport settings
|
// add transport settings
|
||||||
if (isset($this->props['transport']) === false) {
|
if (isset($this->props['transport']) === false) {
|
||||||
@@ -45,27 +54,33 @@ class Email
|
|||||||
}
|
}
|
||||||
|
|
||||||
// transform model objects to values
|
// transform model objects to values
|
||||||
foreach (static::$transform as $prop => $model) {
|
$this->transformUserSingle('from', 'fromName');
|
||||||
$this->transformProp($prop, $model);
|
$this->transformUserSingle('replyTo', 'replyToName');
|
||||||
}
|
$this->transformUserMultiple('to');
|
||||||
|
$this->transformUserMultiple('cc');
|
||||||
|
$this->transformUserMultiple('bcc');
|
||||||
|
$this->transformFile('attachments');
|
||||||
|
|
||||||
// load template for body text
|
// load template for body text
|
||||||
$this->template();
|
$this->template();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string|array $preset
|
* Grabs a preset from the options; supports fixed
|
||||||
|
* prop arrays in case a preset is not needed
|
||||||
|
*
|
||||||
|
* @param string|array $preset Preset name or simple prop array
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
protected function preset($preset): array
|
protected function preset($preset): array
|
||||||
{
|
{
|
||||||
// only passed props, not preset name
|
// only passed props, not preset name
|
||||||
if (is_string($preset) !== true) {
|
if (is_array($preset) === true) {
|
||||||
return $preset;
|
return $preset;
|
||||||
}
|
}
|
||||||
|
|
||||||
// preset does not exist
|
// preset does not exist
|
||||||
if (isset($this->options['presets'][$preset]) === false) {
|
if (isset($this->options['presets'][$preset]) !== true) {
|
||||||
throw new NotFoundException([
|
throw new NotFoundException([
|
||||||
'key' => 'email.preset.notFound',
|
'key' => 'email.preset.notFound',
|
||||||
'data' => ['name' => $preset]
|
'data' => ['name' => $preset]
|
||||||
@@ -75,6 +90,12 @@ class Email
|
|||||||
return $this->options['presets'][$preset];
|
return $this->options['presets'][$preset];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the email template(s) and sets the body props
|
||||||
|
* to the result
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
protected function template(): void
|
protected function template(): void
|
||||||
{
|
{
|
||||||
if (isset($this->props['template']) === true) {
|
if (isset($this->props['template']) === true) {
|
||||||
@@ -105,10 +126,10 @@ class Email
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Undocumented function
|
* Returns an email template by name and type
|
||||||
*
|
*
|
||||||
* @param string $name
|
* @param string $name Template name
|
||||||
* @param string|null $type
|
* @param string|null $type `html` or `text`
|
||||||
* @return \Kirby\Cms\Template
|
* @return \Kirby\Cms\Template
|
||||||
*/
|
*/
|
||||||
protected function getTemplate(string $name, string $type = null)
|
protected function getTemplate(string $name, string $type = null)
|
||||||
@@ -116,47 +137,112 @@ class Email
|
|||||||
return App::instance()->template('emails/' . $name, $type, 'text');
|
return App::instance()->template('emails/' . $name, $type, 'text');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the prop array
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
public function toArray(): array
|
public function toArray(): array
|
||||||
{
|
{
|
||||||
return $this->props;
|
return $this->props;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function transformFile($file)
|
/**
|
||||||
|
* Transforms file object(s) to an array of file roots;
|
||||||
|
* supports simple strings, file objects or collections/arrays of either
|
||||||
|
*
|
||||||
|
* @param string $prop Prop to transform
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function transformFile(string $prop): void
|
||||||
{
|
{
|
||||||
return $this->transformModel($file, 'Kirby\Cms\File', 'root');
|
$this->props[$prop] = $this->transformModel($prop, 'Kirby\Cms\File', 'root');
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function transformModel($value, $class, $content)
|
/**
|
||||||
|
* Transforms Kirby models to a simplified collection
|
||||||
|
*
|
||||||
|
* @param string $prop Prop to transform
|
||||||
|
* @param string $class Fully qualified class name of the supported model
|
||||||
|
* @param string $contentValue Model method that returns the array value
|
||||||
|
* @param string|null $contentKey Optional model method that returns the array key;
|
||||||
|
* returns a simple value-only array if not given
|
||||||
|
* @return array Simple key-value or just value array with the transformed prop data
|
||||||
|
*/
|
||||||
|
protected function transformModel(string $prop, string $class, string $contentValue, string $contentKey = null): array
|
||||||
{
|
{
|
||||||
|
$value = $this->props[$prop] ?? [];
|
||||||
|
|
||||||
|
// ensure consistent input by making everything an iterable value
|
||||||
|
if (is_iterable($value) !== true) {
|
||||||
|
$value = [$value];
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = [];
|
||||||
|
foreach ($value as $key => $item) {
|
||||||
|
if (is_string($item) === true) {
|
||||||
// value is already a string
|
// value is already a string
|
||||||
if (is_string($value) === true) {
|
if ($contentKey !== null && is_string($key) === true) {
|
||||||
return $value;
|
$result[$key] = $item;
|
||||||
|
} else {
|
||||||
|
$result[] = $item;
|
||||||
}
|
}
|
||||||
|
} elseif (is_a($item, $class) === true) {
|
||||||
// value is a model object, get value through content method
|
// value is a model object, get value through content method(s)
|
||||||
if (is_a($value, $class) === true) {
|
if ($contentKey !== null) {
|
||||||
return $value->$content();
|
$result[(string)$item->$contentKey()] = (string)$item->$contentValue();
|
||||||
|
} else {
|
||||||
|
$result[] = (string)$item->$contentValue();
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
// value is an array or collection, call transform on each item
|
// invalid input
|
||||||
if (is_array($value) === true || is_a($value, 'Kirby\Cms\Collection') === true) {
|
throw new InvalidArgumentException('Invalid input for prop "' . $prop . '", expected string or "' . $class . '" object or collection');
|
||||||
$models = [];
|
|
||||||
foreach ($value as $model) {
|
|
||||||
$models[] = $this->transformModel($model, $class, $content);
|
|
||||||
}
|
|
||||||
return $models;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function transformProp(string $prop, string $model): void
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms an user object to the email address and name;
|
||||||
|
* supports simple strings, user objects or collections/arrays of either
|
||||||
|
* (note: only the first item in a collection/array will be used)
|
||||||
|
*
|
||||||
|
* @param string $addressProp Prop with the email address
|
||||||
|
* @param string $nameProp Prop with the name corresponding to the $addressProp
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function transformUserSingle(string $addressProp, string $nameProp): void
|
||||||
{
|
{
|
||||||
if (isset($this->props[$prop]) === true) {
|
$result = $this->transformModel($addressProp, 'Kirby\Cms\User', 'name', 'email');
|
||||||
$this->props[$prop] = $this->{'transform' . ucfirst($model)}($this->props[$prop]);
|
|
||||||
|
$address = array_keys($result)[0] ?? null;
|
||||||
|
$name = $result[$address] ?? null;
|
||||||
|
|
||||||
|
// if the array is non-associative, the value is the address
|
||||||
|
if (is_int($address) === true) {
|
||||||
|
$address = $name;
|
||||||
|
$name = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// always use the address as we have transformed that prop above
|
||||||
|
$this->props[$addressProp] = $address;
|
||||||
|
|
||||||
|
// only use the name from the user if no custom name was set
|
||||||
|
if (isset($this->props[$nameProp]) === false || $this->props[$nameProp] === null) {
|
||||||
|
$this->props[$nameProp] = $name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function transformUser($user)
|
/**
|
||||||
|
* Transforms user object(s) to the email address(es) and name(s);
|
||||||
|
* supports simple strings, user objects or collections/arrays of either
|
||||||
|
*
|
||||||
|
* @param string $prop Prop to transform
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function transformUserMultiple(string $prop): void
|
||||||
{
|
{
|
||||||
return $this->transformModel($user, 'Kirby\Cms\User', 'email');
|
$this->props[$prop] = $this->transformModel($prop, 'Kirby\Cms\User', 'name', 'email');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -15,7 +15,9 @@ use Kirby\Toolkit\Properties;
|
|||||||
*/
|
*/
|
||||||
class FileVersion
|
class FileVersion
|
||||||
{
|
{
|
||||||
use FileFoundation;
|
use FileFoundation {
|
||||||
|
toArray as parentToArray;
|
||||||
|
}
|
||||||
use Properties;
|
use Properties;
|
||||||
|
|
||||||
protected $modifications;
|
protected $modifications;
|
||||||
@@ -89,7 +91,7 @@ class FileVersion
|
|||||||
*/
|
*/
|
||||||
public function toArray(): array
|
public function toArray(): array
|
||||||
{
|
{
|
||||||
$array = array_merge(parent::toArray(), [
|
$array = array_merge($this->parentToArray(), [
|
||||||
'modifications' => $this->modifications(),
|
'modifications' => $this->modifications(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@@ -51,7 +51,13 @@ trait PageActions
|
|||||||
|
|
||||||
// actually move the page on disk
|
// actually move the page on disk
|
||||||
if ($oldPage->exists() === true) {
|
if ($oldPage->exists() === true) {
|
||||||
Dir::move($oldPage->root(), $newPage->root());
|
if (Dir::move($oldPage->root(), $newPage->root()) === true) {
|
||||||
|
// Updates the root path of the old page with the root path
|
||||||
|
// of the moved new page to use fly actions on old page in loop
|
||||||
|
$oldPage->setRoot($newPage->root());
|
||||||
|
} else {
|
||||||
|
throw new LogicException('The page directory cannot be moved');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// overwrite the child in the parent page
|
// overwrite the child in the parent page
|
||||||
@@ -506,10 +512,14 @@ trait PageActions
|
|||||||
|
|
||||||
return $num;
|
return $num;
|
||||||
default:
|
default:
|
||||||
|
// get instance with default language
|
||||||
|
$app = $this->kirby()->clone();
|
||||||
|
$app->setCurrentLanguage();
|
||||||
|
|
||||||
$template = Str::template($mode, [
|
$template = Str::template($mode, [
|
||||||
'kirby' => $this->kirby(),
|
'kirby' => $app,
|
||||||
'page' => $this,
|
'page' => $app->page($this->id()),
|
||||||
'site' => $this->site(),
|
'site' => $app->site(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return (int)$template;
|
return (int)$template;
|
||||||
@@ -706,7 +716,7 @@ trait PageActions
|
|||||||
$sibling->changeNum($index);
|
$sibling->changeNum($index);
|
||||||
}
|
}
|
||||||
|
|
||||||
$parent->children = $siblings->sortBy('num', 'desc');
|
$parent->children = $siblings->sortBy('num', 'asc');
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@@ -272,7 +272,8 @@ class User extends ModelWithContent
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hashes user password
|
* Hashes the user's password unless it is `null`,
|
||||||
|
* which will leave it as `null`
|
||||||
*
|
*
|
||||||
* @internal
|
* @internal
|
||||||
* @param string|null $password
|
* @param string|null $password
|
||||||
@@ -281,12 +282,8 @@ class User extends ModelWithContent
|
|||||||
public static function hashPassword($password): ?string
|
public static function hashPassword($password): ?string
|
||||||
{
|
{
|
||||||
if ($password !== null) {
|
if ($password !== null) {
|
||||||
$info = password_get_info($password);
|
|
||||||
|
|
||||||
if ($info['algo'] === 0) {
|
|
||||||
$password = password_hash($password, PASSWORD_DEFAULT);
|
$password = password_hash($password, PASSWORD_DEFAULT);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return $password;
|
return $password;
|
||||||
}
|
}
|
||||||
@@ -773,7 +770,7 @@ class User extends ModelWithContent
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets and hashes a new user password
|
* Sets the user's password hash
|
||||||
*
|
*
|
||||||
* @param string $password
|
* @param string $password
|
||||||
* @return self
|
* @return self
|
||||||
|
@@ -94,7 +94,7 @@ trait UserActions
|
|||||||
{
|
{
|
||||||
return $this->commit('changePassword', [$this, $password], function ($user, $password) {
|
return $this->commit('changePassword', [$this, $password], function ($user, $password) {
|
||||||
$user = $user->clone([
|
$user = $user->clone([
|
||||||
'password' => $password = $user->hashPassword($password)
|
'password' => $password = User::hashPassword($password)
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$user->writePassword($password);
|
$user->writePassword($password);
|
||||||
@@ -165,7 +165,7 @@ trait UserActions
|
|||||||
$data = $props;
|
$data = $props;
|
||||||
|
|
||||||
if (isset($props['password']) === true) {
|
if (isset($props['password']) === true) {
|
||||||
$data['password'] = static::hashPassword($props['password']);
|
$data['password'] = User::hashPassword($props['password']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$props['role'] = $props['model'] = strtolower($props['role'] ?? 'default');
|
$props['role'] = $props['model'] = strtolower($props['role'] ?? 'default');
|
||||||
|
@@ -25,7 +25,9 @@ class Email
|
|||||||
protected $bcc;
|
protected $bcc;
|
||||||
protected $cc;
|
protected $cc;
|
||||||
protected $from;
|
protected $from;
|
||||||
|
protected $fromName;
|
||||||
protected $replyTo;
|
protected $replyTo;
|
||||||
|
protected $replyToName;
|
||||||
protected $isSent = false;
|
protected $isSent = false;
|
||||||
protected $subject;
|
protected $subject;
|
||||||
protected $to;
|
protected $to;
|
||||||
@@ -75,6 +77,11 @@ class Email
|
|||||||
return $this->from;
|
return $this->from;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function fromName(): ?string
|
||||||
|
{
|
||||||
|
return $this->fromName;
|
||||||
|
}
|
||||||
|
|
||||||
public function isHtml()
|
public function isHtml()
|
||||||
{
|
{
|
||||||
return $this->body()->html() !== null;
|
return $this->body()->html() !== null;
|
||||||
@@ -90,6 +97,11 @@ class Email
|
|||||||
return $this->replyTo;
|
return $this->replyTo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function replyToName(): ?string
|
||||||
|
{
|
||||||
|
return $this->replyToName;
|
||||||
|
}
|
||||||
|
|
||||||
protected function resolveEmail($email = null, bool $multiple = true)
|
protected function resolveEmail($email = null, bool $multiple = true)
|
||||||
{
|
{
|
||||||
if ($email === null) {
|
if ($email === null) {
|
||||||
@@ -97,16 +109,27 @@ class Email
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (is_array($email) === false) {
|
if (is_array($email) === false) {
|
||||||
$email = [$email];
|
$email = [$email => null];
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($email as $address) {
|
$result = [];
|
||||||
|
foreach ($email as $address => $name) {
|
||||||
|
// convert simple email arrays to associative arrays
|
||||||
|
if (is_int($address) === true) {
|
||||||
|
// the value is the address, there is no name
|
||||||
|
$address = $name;
|
||||||
|
$result[$address] = null;
|
||||||
|
} else {
|
||||||
|
$result[$address] = $name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure that the address is valid
|
||||||
if (V::email($address) === false) {
|
if (V::email($address) === false) {
|
||||||
throw new Exception(sprintf('"%s" is not a valid email address', $address));
|
throw new Exception(sprintf('"%s" is not a valid email address', $address));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $multiple === true ? $email : $email[0];
|
return $multiple === true ? $result : array_keys($result)[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function send(): bool
|
public function send(): bool
|
||||||
@@ -148,12 +171,24 @@ class Email
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function setFromName(string $fromName = null)
|
||||||
|
{
|
||||||
|
$this->fromName = $fromName;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
protected function setReplyTo(string $replyTo = null)
|
protected function setReplyTo(string $replyTo = null)
|
||||||
{
|
{
|
||||||
$this->replyTo = $this->resolveEmail($replyTo, false);
|
$this->replyTo = $this->resolveEmail($replyTo, false);
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function setReplyToName(string $replyToName = null)
|
||||||
|
{
|
||||||
|
$this->replyToName = $replyToName;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
protected function setSubject(string $subject)
|
protected function setSubject(string $subject)
|
||||||
{
|
{
|
||||||
$this->subject = $subject;
|
$this->subject = $subject;
|
||||||
|
@@ -21,22 +21,22 @@ class PHPMailer extends Email
|
|||||||
$mailer = new Mailer(true);
|
$mailer = new Mailer(true);
|
||||||
|
|
||||||
// set sender's address
|
// set sender's address
|
||||||
$mailer->setFrom($this->from());
|
$mailer->setFrom($this->from(), $this->fromName() ?? '');
|
||||||
|
|
||||||
// optional reply-to address
|
// optional reply-to address
|
||||||
if ($replyTo = $this->replyTo()) {
|
if ($replyTo = $this->replyTo()) {
|
||||||
$mailer->addReplyTo($replyTo);
|
$mailer->addReplyTo($replyTo, $this->replyToName() ?? '');
|
||||||
}
|
}
|
||||||
|
|
||||||
// add (multiple) recepient, CC & BCC addresses
|
// add (multiple) recepient, CC & BCC addresses
|
||||||
foreach ($this->to() as $to) {
|
foreach ($this->to() as $email => $name) {
|
||||||
$mailer->addAddress($to);
|
$mailer->addAddress($email, $name ?? '');
|
||||||
}
|
}
|
||||||
foreach ($this->cc() as $cc) {
|
foreach ($this->cc() as $email => $name) {
|
||||||
$mailer->addCC($cc);
|
$mailer->addCC($email, $name ?? '');
|
||||||
}
|
}
|
||||||
foreach ($this->bcc() as $bcc) {
|
foreach ($this->bcc() as $email => $name) {
|
||||||
$mailer->addBCC($bcc);
|
$mailer->addBCC($email, $name ?? '');
|
||||||
}
|
}
|
||||||
|
|
||||||
$mailer->Subject = $this->subject();
|
$mailer->Subject = $this->subject();
|
||||||
|
@@ -23,6 +23,7 @@ class Remote
|
|||||||
*/
|
*/
|
||||||
public static $defaults = [
|
public static $defaults = [
|
||||||
'agent' => null,
|
'agent' => null,
|
||||||
|
'basicAuth' => null,
|
||||||
'body' => true,
|
'body' => true,
|
||||||
'data' => [],
|
'data' => [],
|
||||||
'encoding' => 'utf-8',
|
'encoding' => 'utf-8',
|
||||||
@@ -170,7 +171,22 @@ class Remote
|
|||||||
|
|
||||||
// add all headers
|
// add all headers
|
||||||
if (empty($this->options['headers']) === false) {
|
if (empty($this->options['headers']) === false) {
|
||||||
$this->curlopt[CURLOPT_HTTPHEADER] = $this->options['headers'];
|
// convert associative arrays to strings
|
||||||
|
$headers = [];
|
||||||
|
foreach ($this->options['headers'] as $key => $value) {
|
||||||
|
if (is_string($key) === true) {
|
||||||
|
$headers[] = $key . ': ' . $value;
|
||||||
|
} else {
|
||||||
|
$headers[] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->curlopt[CURLOPT_HTTPHEADER] = $headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add HTTP Basic authentication
|
||||||
|
if (empty($this->options['basicAuth']) === false) {
|
||||||
|
$this->curlopt[CURLOPT_USERPWD] = $this->options['basicAuth'];
|
||||||
}
|
}
|
||||||
|
|
||||||
// add the user agent
|
// add the user agent
|
||||||
|
@@ -72,13 +72,18 @@ class File
|
|||||||
/**
|
/**
|
||||||
* Returns the file as data uri
|
* Returns the file as data uri
|
||||||
*
|
*
|
||||||
|
* @param bool $base64 Whether the data should be base64 encoded or not
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function dataUri(): string
|
public function dataUri(bool $base64 = true): string
|
||||||
{
|
{
|
||||||
|
if ($base64 === true) {
|
||||||
return 'data:' . $this->mime() . ';base64,' . $this->base64();
|
return 'data:' . $this->mime() . ';base64,' . $this->base64();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 'data:' . $this->mime() . ',' . Escape::url($this->read());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes the file
|
* Deletes the file
|
||||||
*
|
*
|
||||||
|
@@ -31,7 +31,7 @@ class Mime
|
|||||||
'avi' => 'video/x-msvideo',
|
'avi' => 'video/x-msvideo',
|
||||||
'bmp' => 'image/bmp',
|
'bmp' => 'image/bmp',
|
||||||
'css' => 'text/css',
|
'css' => 'text/css',
|
||||||
'csv' => ['text/x-comma-separated-values', 'text/comma-separated-values', 'application/octet-stream'],
|
'csv' => ['text/csv', 'text/x-comma-separated-values', 'text/comma-separated-values', 'application/octet-stream'],
|
||||||
'doc' => 'application/msword',
|
'doc' => 'application/msword',
|
||||||
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||||
'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
|
'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
|
||||||
|
@@ -512,7 +512,9 @@ final class Mbstring
|
|||||||
$offset = 0;
|
$offset = 0;
|
||||||
} elseif ($offset = (int) $offset) {
|
} elseif ($offset = (int) $offset) {
|
||||||
if ($offset < 0) {
|
if ($offset < 0) {
|
||||||
|
if (0 > $offset += self::mb_strlen($needle)) {
|
||||||
$haystack = self::mb_substr($haystack, 0, $offset, $encoding);
|
$haystack = self::mb_substr($haystack, 0, $offset, $encoding);
|
||||||
|
}
|
||||||
$offset = 0;
|
$offset = 0;
|
||||||
} else {
|
} else {
|
||||||
$haystack = self::mb_substr($haystack, $offset, 2147483647, $encoding);
|
$haystack = self::mb_substr($haystack, $offset, 2147483647, $encoding);
|
||||||
@@ -532,7 +534,7 @@ final class Mbstring
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($split_length < 1) {
|
if (1 > $split_length = (int) $split_length) {
|
||||||
trigger_error('The length of each segment must be greater than zero', E_USER_WARNING);
|
trigger_error('The length of each segment must be greater than zero', E_USER_WARNING);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -542,6 +544,10 @@ final class Mbstring
|
|||||||
$encoding = mb_internal_encoding();
|
$encoding = mb_internal_encoding();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ('UTF-8' === $encoding = self::getEncoding($encoding)) {
|
||||||
|
return preg_split("/(.{{$split_length}})/u", $string, null, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
|
||||||
|
}
|
||||||
|
|
||||||
$result = array();
|
$result = array();
|
||||||
$length = mb_strlen($string, $encoding);
|
$length = mb_strlen($string, $encoding);
|
||||||
|
|
||||||
@@ -815,11 +821,16 @@ final class Mbstring
|
|||||||
return self::$internalEncoding;
|
return self::$internalEncoding;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ('UTF-8' === $encoding) {
|
||||||
|
return 'UTF-8';
|
||||||
|
}
|
||||||
|
|
||||||
$encoding = strtoupper($encoding);
|
$encoding = strtoupper($encoding);
|
||||||
|
|
||||||
if ('8BIT' === $encoding || 'BINARY' === $encoding) {
|
if ('8BIT' === $encoding || 'BINARY' === $encoding) {
|
||||||
return 'CP850';
|
return 'CP850';
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('UTF8' === $encoding) {
|
if ('UTF8' === $encoding) {
|
||||||
return 'UTF-8';
|
return 'UTF-8';
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user