Upgrade to Kirby 3.3.2

This commit is contained in:
Bastian Allgeier
2019-12-17 10:31:21 +01:00
parent 3a82406bed
commit 91919964db
20 changed files with 361 additions and 181 deletions

View File

@@ -1,7 +1,7 @@
{
"name": "getkirby/cms",
"description": "The Kirby 3 core",
"version": "3.3.1",
"version": "3.3.2",
"license": "proprietary",
"keywords": ["kirby", "cms", "core"],
"homepage": "https://getkirby.com",

14
kirby/composer.lock generated
View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "e10295fa58c27caa37474e7fa503bb8e",
"content-hash": "be4d625ce32603f849e29b8d2e841006",
"packages": [
{
"name": "claviska/simpleimage",
@@ -413,16 +413,16 @@
},
{
"name": "symfony/polyfill-mbstring",
"version": "v1.12.0",
"version": "v1.13.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "b42a2f66e8f1b15ccf25652c3424265923eb4f17"
"reference": "7b4aab9743c30be783b73de055d24a39cf4b954f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/b42a2f66e8f1b15ccf25652c3424265923eb4f17",
"reference": "b42a2f66e8f1b15ccf25652c3424265923eb4f17",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/7b4aab9743c30be783b73de055d24a39cf4b954f",
"reference": "7b4aab9743c30be783b73de055d24a39cf4b954f",
"shasum": ""
},
"require": {
@@ -434,7 +434,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.12-dev"
"dev-master": "1.13-dev"
}
},
"autoload": {
@@ -468,7 +468,7 @@
"portable",
"shim"
],
"time": "2019-08-06T08:03:45+00:00"
"time": "2019-11-27T14:18:11+00:00"
},
{
"name": "true/punycode",

View File

@@ -438,12 +438,12 @@
"upload.error.default": "El archivo no pudo ser subido",
"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.iniPostSize": "The uploaded file exceeds the post_max_size directive in php.ini",
"upload.error.iniSize": "The uploaded file exceeds the upload_max_filesize directive in php.ini",
"upload.error.noFile": "No file was uploaded",
"upload.error.noFiles": "No files were uploaded",
"upload.error.partial": "The uploaded file was only partially uploaded",
"upload.error.tmpDir": "Missing a temporary folder",
"upload.error.iniPostSize": "El archivo subido excede la directiva post_max_size directive en php.ini",
"upload.error.iniSize": "El archivo subido excede la directiva upload_max_filesize en php.ini",
"upload.error.noFile": "Ningún archivo ha sido subido",
"upload.error.noFiles": "Ningún archivo ha sido subido",
"upload.error.partial": "El archivo ha sido subido solo parcialmente",
"upload.error.tmpDir": "No se encuentra la carpeta temporal",
"upload.errors": "Error",
"upload.progress": "Subiendo...",

View File

@@ -23,15 +23,15 @@
"delete": "Borrar",
"dimensions": "Dimensiones",
"disabled": "Disabled",
"disabled": "Desabilitado",
"discard": "Descartar",
"download": "Download",
"duplicate": "Duplicate",
"download": "Descargar",
"duplicate": "Duplicar",
"edit": "Editar",
"dialog.files.empty": "No files to select",
"dialog.pages.empty": "No pages to select",
"dialog.users.empty": "No users to select",
"dialog.files.empty": "No se ha seleccionado ningún archivo",
"dialog.pages.empty": "No se ha seleccionado ninguna página",
"dialog.users.empty": "No se ha seleccionado ningún usuario",
"email": "Correo electrónico",
"email.placeholder": "correo@ejemplo.com",

View File

@@ -61,21 +61,21 @@
"Filändelsen \"{extension}\" är inte tillåten",
"error.file.extension.missing":
"Filen \"{filename}\" saknar filändelse",
"error.file.maxheight": "The height of the image must not exceed {height} pixels",
"error.file.maxsize": "The file is too large",
"error.file.maxwidth": "The width of the image must not exceed {width} pixels",
"error.file.maxheight": "Bildens höjd får inte överstiga {height} pixlar",
"error.file.maxsize": "Filen är för stor",
"error.file.maxwidth": "Bildens bredd får inte överstiga {width} pixlar",
"error.file.mime.differs":
"Den uppladdade filen måste vara av samma mime-typ \"{mime}\"",
"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":
"Mediatypen för \"{filename}\" kan inte detekteras",
"error.file.minheight": "The height of the image must be at least {height} pixels",
"error.file.minsize": "The file is too small",
"error.file.minwidth": "The width of the image must be at least {width} pixels",
"error.file.minheight": "Bildens höjd måste vara minst {height} pixlar",
"error.file.minsize": "Filen är för liten",
"error.file.minwidth": "Bildens bredd måste vara minst {width} pixlar",
"error.file.name.missing": "Filnamnet får inte vara tomt",
"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.undefined": "Filen kan inte hittas",
@@ -304,7 +304,7 @@
"loading": "Laddar",
"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.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.",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -69,6 +69,13 @@ trait AppPlugins
'validators' => []
];
/**
* Cache for system extensions
*
* @var array
*/
protected static $systemExtensions = null;
/**
* Flag when plugins have been loaded
* to not load them again
@@ -570,37 +577,69 @@ trait AppPlugins
*/
protected function extensionsFromSystem()
{
// Form Field Mixins
FormField::$mixins['filepicker'] = include static::$root . '/config/fields/mixins/filepicker.php';
FormField::$mixins['min'] = include static::$root . '/config/fields/mixins/min.php';
FormField::$mixins['options'] = include static::$root . '/config/fields/mixins/options.php';
FormField::$mixins['pagepicker'] = include static::$root . '/config/fields/mixins/pagepicker.php';
FormField::$mixins['picker'] = include static::$root . '/config/fields/mixins/picker.php';
FormField::$mixins['upload'] = include static::$root . '/config/fields/mixins/upload.php';
FormField::$mixins['userpicker'] = include static::$root . '/config/fields/mixins/userpicker.php';
// load static extensions only once
if (static::$systemExtensions === null) {
// Form Field Mixins
FormField::$mixins['filepicker'] = include static::$root . '/config/fields/mixins/filepicker.php';
FormField::$mixins['min'] = include static::$root . '/config/fields/mixins/min.php';
FormField::$mixins['options'] = include static::$root . '/config/fields/mixins/options.php';
FormField::$mixins['pagepicker'] = include static::$root . '/config/fields/mixins/pagepicker.php';
FormField::$mixins['picker'] = include static::$root . '/config/fields/mixins/picker.php';
FormField::$mixins['upload'] = include static::$root . '/config/fields/mixins/upload.php';
FormField::$mixins['userpicker'] = include static::$root . '/config/fields/mixins/userpicker.php';
// Tag Aliases
KirbyTag::$aliases = [
'youtube' => 'video',
'vimeo' => 'video'
];
// Tag Aliases
KirbyTag::$aliases = [
'youtube' => 'video',
'vimeo' => 'video'
];
// Field method aliases
Field::$aliases = [
'bool' => 'toBool',
'esc' => 'escape',
'excerpt' => 'toExcerpt',
'float' => 'toFloat',
'h' => 'html',
'int' => 'toInt',
'kt' => 'kirbytext',
'kti' => 'kirbytextinline',
'link' => 'toLink',
'md' => 'markdown',
'sp' => 'smartypants',
'v' => 'isValid',
'x' => 'xml'
];
// Field method aliases
Field::$aliases = [
'bool' => 'toBool',
'esc' => 'escape',
'excerpt' => 'toExcerpt',
'float' => 'toFloat',
'h' => 'html',
'int' => 'toInt',
'kt' => 'kirbytext',
'kti' => 'kirbytextinline',
'link' => 'toLink',
'md' => 'markdown',
'sp' => 'smartypants',
'v' => 'isValid',
'x' => 'xml'
];
// blueprint presets
PageBlueprint::$presets['pages'] = include static::$root . '/config/presets/pages.php';
PageBlueprint::$presets['page'] = include static::$root . '/config/presets/page.php';
PageBlueprint::$presets['files'] = include static::$root . '/config/presets/files.php';
// section mixins
Section::$mixins['empty'] = include static::$root . '/config/sections/mixins/empty.php';
Section::$mixins['headline'] = include static::$root . '/config/sections/mixins/headline.php';
Section::$mixins['help'] = include static::$root . '/config/sections/mixins/help.php';
Section::$mixins['layout'] = include static::$root . '/config/sections/mixins/layout.php';
Section::$mixins['max'] = include static::$root . '/config/sections/mixins/max.php';
Section::$mixins['min'] = include static::$root . '/config/sections/mixins/min.php';
Section::$mixins['pagination'] = include static::$root . '/config/sections/mixins/pagination.php';
Section::$mixins['parent'] = include static::$root . '/config/sections/mixins/parent.php';
// section types
Section::$types['info'] = include static::$root . '/config/sections/info.php';
Section::$types['pages'] = include static::$root . '/config/sections/pages.php';
Section::$types['files'] = include static::$root . '/config/sections/files.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([
@@ -610,32 +649,11 @@ trait AppPlugins
'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
PageBlueprint::$presets['pages'] = include static::$root . '/config/presets/pages.php';
PageBlueprint::$presets['page'] = include static::$root . '/config/presets/page.php';
PageBlueprint::$presets['files'] = include static::$root . '/config/presets/files.php';
// section mixins
Section::$mixins['empty'] = include static::$root . '/config/sections/mixins/empty.php';
Section::$mixins['headline'] = include static::$root . '/config/sections/mixins/headline.php';
Section::$mixins['help'] = include static::$root . '/config/sections/mixins/help.php';
Section::$mixins['layout'] = include static::$root . '/config/sections/mixins/layout.php';
Section::$mixins['max'] = include static::$root . '/config/sections/mixins/max.php';
Section::$mixins['min'] = include static::$root . '/config/sections/mixins/min.php';
Section::$mixins['pagination'] = include static::$root . '/config/sections/mixins/pagination.php';
Section::$mixins['parent'] = include static::$root . '/config/sections/mixins/parent.php';
// section types
Section::$types['info'] = include static::$root . '/config/sections/info.php';
Section::$types['pages'] = include static::$root . '/config/sections/pages.php';
Section::$types['files'] = include static::$root . '/config/sections/files.php';
Section::$types['fields'] = include static::$root . '/config/sections/fields.php';
$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']);
}
/**

View File

@@ -319,6 +319,9 @@ class Auth
$log['by-ip'] = $log['by-ip'] ?? [];
$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
$originalLog = $log;
$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
if ($read === false || $log !== $originalLog) {
if (count($log['by-ip']) === 0 && count($log['by-email']) === 0) {

View File

@@ -2,10 +2,11 @@
namespace Kirby\Cms;
use Kirby\Exception\InvalidArgumentException;
use Kirby\Exception\NotFoundException;
/**
* Wrapper around our PHPMailer package, which
* Wrapper around our Email package, which
* handles all the magic connections between Kirby
* and sending emails, like email templates, file
* attachments, etc.
@@ -18,26 +19,34 @@ use Kirby\Exception\NotFoundException;
*/
class Email
{
/**
* Options configured through the `email` CMS option
*
* @var array
*/
protected $options;
protected $preset;
/**
* Props for the email object; will be passed to the
* Kirby\Email\Email class
*
* @var array
*/
protected $props;
protected static $transform = [
'from' => 'user',
'replyTo' => 'user',
'to' => 'user',
'cc' => 'user',
'bcc' => 'user',
'attachments' => 'file'
];
/**
* Class constructor
*
* @param string|array $preset Preset name from the config or a simple props array
* @param array $props Props array to override the $preset
*/
public function __construct($preset = [], array $props = [])
{
$this->options = App::instance()->option('email');
// load presets from options
$this->preset = $this->preset($preset);
$this->props = array_merge($this->preset, $props);
// build a prop array based on preset and props
$preset = $this->preset($preset);
$this->props = array_merge($preset, $props);
// add transport settings
if (isset($this->props['transport']) === false) {
@@ -45,27 +54,33 @@ class Email
}
// transform model objects to values
foreach (static::$transform as $prop => $model) {
$this->transformProp($prop, $model);
}
$this->transformUserSingle('from', 'fromName');
$this->transformUserSingle('replyTo', 'replyToName');
$this->transformUserMultiple('to');
$this->transformUserMultiple('cc');
$this->transformUserMultiple('bcc');
$this->transformFile('attachments');
// load template for body text
$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
*/
protected function preset($preset): array
{
// only passed props, not preset name
if (is_string($preset) !== true) {
if (is_array($preset) === true) {
return $preset;
}
// preset does not exist
if (isset($this->options['presets'][$preset]) === false) {
if (isset($this->options['presets'][$preset]) !== true) {
throw new NotFoundException([
'key' => 'email.preset.notFound',
'data' => ['name' => $preset]
@@ -75,6 +90,12 @@ class Email
return $this->options['presets'][$preset];
}
/**
* Renders the email template(s) and sets the body props
* to the result
*
* @return void
*/
protected function template(): void
{
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|null $type
* @param string $name Template name
* @param string|null $type `html` or `text`
* @return \Kirby\Cms\Template
*/
protected function getTemplate(string $name, string $type = null)
@@ -116,47 +137,112 @@ class Email
return App::instance()->template('emails/' . $name, $type, 'text');
}
/**
* Returns the prop array
*
* @return array
*/
public function toArray(): array
{
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 is already a string
if (is_string($value) === true) {
return $value;
$value = $this->props[$prop] ?? [];
// ensure consistent input by making everything an iterable value
if (is_iterable($value) !== true) {
$value = [$value];
}
// value is a model object, get value through content method
if (is_a($value, $class) === true) {
return $value->$content();
}
// value is an array or collection, call transform on each item
if (is_array($value) === true || is_a($value, 'Kirby\Cms\Collection') === true) {
$models = [];
foreach ($value as $model) {
$models[] = $this->transformModel($model, $class, $content);
$result = [];
foreach ($value as $key => $item) {
if (is_string($item) === true) {
// value is already a string
if ($contentKey !== null && is_string($key) === true) {
$result[$key] = $item;
} else {
$result[] = $item;
}
} elseif (is_a($item, $class) === true) {
// value is a model object, get value through content method(s)
if ($contentKey !== null) {
$result[(string)$item->$contentKey()] = (string)$item->$contentValue();
} else {
$result[] = (string)$item->$contentValue();
}
} else {
// invalid input
throw new InvalidArgumentException('Invalid input for prop "' . $prop . '", expected string or "' . $class . '" object or collection');
}
return $models;
}
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
{
$result = $this->transformModel($addressProp, 'Kirby\Cms\User', 'name', 'email');
$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 transformProp(string $prop, string $model): void
/**
* 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
{
if (isset($this->props[$prop]) === true) {
$this->props[$prop] = $this->{'transform' . ucfirst($model)}($this->props[$prop]);
}
}
protected function transformUser($user)
{
return $this->transformModel($user, 'Kirby\Cms\User', 'email');
$this->props[$prop] = $this->transformModel($prop, 'Kirby\Cms\User', 'name', 'email');
}
}

View File

@@ -15,7 +15,9 @@ use Kirby\Toolkit\Properties;
*/
class FileVersion
{
use FileFoundation;
use FileFoundation {
toArray as parentToArray;
}
use Properties;
protected $modifications;
@@ -89,7 +91,7 @@ class FileVersion
*/
public function toArray(): array
{
$array = array_merge(parent::toArray(), [
$array = array_merge($this->parentToArray(), [
'modifications' => $this->modifications(),
]);

View File

@@ -51,7 +51,13 @@ trait PageActions
// actually move the page on disk
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
@@ -506,10 +512,14 @@ trait PageActions
return $num;
default:
// get instance with default language
$app = $this->kirby()->clone();
$app->setCurrentLanguage();
$template = Str::template($mode, [
'kirby' => $this->kirby(),
'page' => $this,
'site' => $this->site(),
'kirby' => $app,
'page' => $app->page($this->id()),
'site' => $app->site(),
]);
return (int)$template;
@@ -706,7 +716,7 @@ trait PageActions
$sibling->changeNum($index);
}
$parent->children = $siblings->sortBy('num', 'desc');
$parent->children = $siblings->sortBy('num', 'asc');
}
return true;

View File

@@ -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
* @param string|null $password
@@ -281,11 +282,7 @@ class User extends ModelWithContent
public static function hashPassword($password): ?string
{
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;
@@ -773,7 +770,7 @@ class User extends ModelWithContent
}
/**
* Sets and hashes a new user password
* Sets the user's password hash
*
* @param string $password
* @return self

View File

@@ -94,7 +94,7 @@ trait UserActions
{
return $this->commit('changePassword', [$this, $password], function ($user, $password) {
$user = $user->clone([
'password' => $password = $user->hashPassword($password)
'password' => $password = User::hashPassword($password)
]);
$user->writePassword($password);
@@ -165,7 +165,7 @@ trait UserActions
$data = $props;
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');

View File

@@ -25,7 +25,9 @@ class Email
protected $bcc;
protected $cc;
protected $from;
protected $fromName;
protected $replyTo;
protected $replyToName;
protected $isSent = false;
protected $subject;
protected $to;
@@ -75,6 +77,11 @@ class Email
return $this->from;
}
public function fromName(): ?string
{
return $this->fromName;
}
public function isHtml()
{
return $this->body()->html() !== null;
@@ -90,6 +97,11 @@ class Email
return $this->replyTo;
}
public function replyToName(): ?string
{
return $this->replyToName;
}
protected function resolveEmail($email = null, bool $multiple = true)
{
if ($email === null) {
@@ -97,16 +109,27 @@ class Email
}
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) {
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
@@ -148,12 +171,24 @@ class Email
return $this;
}
protected function setFromName(string $fromName = null)
{
$this->fromName = $fromName;
return $this;
}
protected function setReplyTo(string $replyTo = null)
{
$this->replyTo = $this->resolveEmail($replyTo, false);
return $this;
}
protected function setReplyToName(string $replyToName = null)
{
$this->replyToName = $replyToName;
return $this;
}
protected function setSubject(string $subject)
{
$this->subject = $subject;

View File

@@ -21,22 +21,22 @@ class PHPMailer extends Email
$mailer = new Mailer(true);
// set sender's address
$mailer->setFrom($this->from());
$mailer->setFrom($this->from(), $this->fromName() ?? '');
// optional reply-to address
if ($replyTo = $this->replyTo()) {
$mailer->addReplyTo($replyTo);
$mailer->addReplyTo($replyTo, $this->replyToName() ?? '');
}
// add (multiple) recepient, CC & BCC addresses
foreach ($this->to() as $to) {
$mailer->addAddress($to);
foreach ($this->to() as $email => $name) {
$mailer->addAddress($email, $name ?? '');
}
foreach ($this->cc() as $cc) {
$mailer->addCC($cc);
foreach ($this->cc() as $email => $name) {
$mailer->addCC($email, $name ?? '');
}
foreach ($this->bcc() as $bcc) {
$mailer->addBCC($bcc);
foreach ($this->bcc() as $email => $name) {
$mailer->addBCC($email, $name ?? '');
}
$mailer->Subject = $this->subject();

View File

@@ -22,16 +22,17 @@ class Remote
* @var array
*/
public static $defaults = [
'agent' => null,
'body' => true,
'data' => [],
'encoding' => 'utf-8',
'file' => null,
'headers' => [],
'method' => 'GET',
'progress' => null,
'test' => false,
'timeout' => 10,
'agent' => null,
'basicAuth' => null,
'body' => true,
'data' => [],
'encoding' => 'utf-8',
'file' => null,
'headers' => [],
'method' => 'GET',
'progress' => null,
'test' => false,
'timeout' => 10,
];
/**
@@ -170,7 +171,22 @@ class Remote
// add all headers
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

View File

@@ -72,11 +72,16 @@ class File
/**
* Returns the file as data uri
*
* @param bool $base64 Whether the data should be base64 encoded or not
* @return string
*/
public function dataUri(): string
public function dataUri(bool $base64 = true): string
{
return 'data:' . $this->mime() . ';base64,' . $this->base64();
if ($base64 === true) {
return 'data:' . $this->mime() . ';base64,' . $this->base64();
}
return 'data:' . $this->mime() . ',' . Escape::url($this->read());
}
/**

View File

@@ -31,7 +31,7 @@ class Mime
'avi' => 'video/x-msvideo',
'bmp' => 'image/bmp',
'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',
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',

View File

@@ -512,7 +512,9 @@ final class Mbstring
$offset = 0;
} elseif ($offset = (int) $offset) {
if ($offset < 0) {
$haystack = self::mb_substr($haystack, 0, $offset, $encoding);
if (0 > $offset += self::mb_strlen($needle)) {
$haystack = self::mb_substr($haystack, 0, $offset, $encoding);
}
$offset = 0;
} else {
$haystack = self::mb_substr($haystack, $offset, 2147483647, $encoding);
@@ -532,7 +534,7 @@ final class Mbstring
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);
return false;
@@ -542,6 +544,10 @@ final class Mbstring
$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();
$length = mb_strlen($string, $encoding);
@@ -815,11 +821,16 @@ final class Mbstring
return self::$internalEncoding;
}
if ('UTF-8' === $encoding) {
return 'UTF-8';
}
$encoding = strtoupper($encoding);
if ('8BIT' === $encoding || 'BINARY' === $encoding) {
return 'CP850';
}
if ('UTF8' === $encoding) {
return 'UTF-8';
}