Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
9656d63
tests: cover pro_search_encode helper
TomJaeger Jun 29, 2026
89db3bb
tests(coverage): pro_search_helper.php coverage tests for GlobalScope…
TomJaeger Jun 29, 2026
981338c
tests(coverage): pro_search_helper.php coverage tests for GlobalScope…
TomJaeger Jun 29, 2026
de43c75
tests(coverage): pro_search_helper.php coverage tests for GlobalScope…
TomJaeger Jun 29, 2026
eb31ecb
tests(coverage): pro_search_helper.php coverage tests for GlobalScope…
TomJaeger Jun 29, 2026
ebe2b5c
tests(coverage): pro_search_helper.php coverage tests for GlobalScope…
TomJaeger Jun 29, 2026
4c2e462
tests(coverage): pro_search_helper.php coverage tests for GlobalScope…
TomJaeger Jun 29, 2026
497fd46
tests(coverage): pro_search_helper.php coverage tests for GlobalScope…
TomJaeger Jun 29, 2026
ee47701
tests(coverage): pro_search_helper.php coverage tests for GlobalScope…
TomJaeger Jun 29, 2026
87d0991
tests(coverage): pro_search_helper.php coverage tests for GlobalScope…
TomJaeger Jun 29, 2026
3bbdfe5
tests(coverage): pro_search_helper.php coverage tests for GlobalScope…
TomJaeger Jun 29, 2026
0ee409f
tests(coverage): pro_search_helper.php coverage tests for GlobalScope…
TomJaeger Jun 29, 2026
d282e25
tests(coverage): pro_search_helper.php coverage tests for GlobalScope…
TomJaeger Jun 29, 2026
d08005a
tests(coverage): pro_search_helper.php coverage tests for GlobalScope…
TomJaeger Jun 29, 2026
d70199e
tests(coverage): pro_search_helper.php to 100%
TomJaeger Jun 29, 2026
b3a2e1b
tests(coverage): pro_search_helper.php coverage tests for GlobalScope…
TomJaeger Jun 29, 2026
18565f1
tests(coverage): pro_search_helper.php coverage tests for GlobalScope…
TomJaeger Jun 29, 2026
ec2d8b6
tests(coverage): pro_search_helper.php to 100%
TomJaeger Jun 29, 2026
c67b38d
tests(coverage): pro_search_helper.php coverage tests for GlobalScope…
TomJaeger Jun 29, 2026
944be02
tests(coverage): pro_search_helper.php coverage tests for GlobalScope…
TomJaeger Jun 29, 2026
394a865
tests(coverage): pro_search_helper.php coverage tests for GlobalScope…
TomJaeger Jun 29, 2026
bf0fe2b
tests(coverage): pro_search_helper.php coverage tests for GlobalScope…
TomJaeger Jun 29, 2026
a6bc9ec
tests(coverage): pro_search_helper.php coverage tests for GlobalScope…
TomJaeger Jun 29, 2026
3594089
tests(coverage): pro_search_helper.php coverage tests for GlobalScope…
TomJaeger Jun 29, 2026
dee1772
tests(coverage): pro_search_helper.php coverage tests for GlobalScope…
TomJaeger Jun 29, 2026
0df6b76
Resolved #5335 where Channel Form {field_data} was not always parsed
bryannielsen Jun 29, 2026
8535332
Resolved #4600 where creating a manipulation with a reserved name cau…
bryannielsen Jul 2, 2026
9ca1dc4
Encode member field instructions in CP
TomJaeger Jul 6, 2026
0399dca
Fix empty file dimension accessors
TomJaeger Jul 8, 2026
f58e2a2
Fix database backup table shortcuts
TomJaeger Jul 8, 2026
c25a77c
Fix database function argument forwarding
TomJaeger Jul 8, 2026
2147695
Fix native database error messages
TomJaeger Jul 8, 2026
b91fd40
Fix CP form group toggle serialization
TomJaeger Jul 17, 2026
e82f047
Add IDs to Channel Form file inputs
TomJaeger Jul 17, 2026
35a600d
Guard invalid live preview route hook results
TomJaeger Jul 17, 2026
dfda3ad
Remove outdated link from README
kerstenremco Jul 19, 2026
ff6c9e9
version bump
TomJaeger Jul 21, 2026
1b5a19c
Document reserved short name validation callback
TomJaeger Jul 21, 2026
1132ece
Update empty or malformed dimensions to return null
bryannielsen Jul 22, 2026
126300e
Fix cypress tests, pin maildev version to 2.x
bryannielsen Jul 22, 2026
c3a75f1
Merge pull request #5351 from ExpressionEngine/version-7.5.26
TomJaeger Jul 22, 2026
3607693
Merge pull request #5347 from ExpressionEngine/fix/file-dimensions-em…
TomJaeger Jul 22, 2026
62e24ce
Merge pull request #5352 from ExpressionEngine/fix/live-preview-route…
TomJaeger Jul 22, 2026
c65236a
Merge pull request #5354 from ExpressionEngine/fix/channel-form-file-…
TomJaeger Jul 22, 2026
5e15880
Merge pull request #5353 from ExpressionEngine/fix/group-toggle-js-error
TomJaeger Jul 22, 2026
3c45c8f
Merge pull request #5348 from ExpressionEngine/fix/db-fixes
TomJaeger Jul 22, 2026
98eb265
Merge pull request #5339 from ExpressionEngine/tests/pro-search-helpe…
TomJaeger Jul 22, 2026
121cb2a
Merge pull request #5338 from ExpressionEngine/bug/7.x/5335-channel-f…
TomJaeger Jul 22, 2026
1a056ee
Merge pull request #5343 from ExpressionEngine/bug/7.x/4600-image-man…
TomJaeger Jul 22, 2026
ff0ae7b
Merge pull request #5355 from ExpressionEngine/member-field-instructi…
TomJaeger Jul 22, 2026
13488a8
Improve Pro Search query decoding
TomJaeger Jun 30, 2026
01277d5
Merge pull request #5356 from ExpressionEngine/pro-search-query-decoding
TomJaeger Jul 22, 2026
2e9a178
Align template PHP settings on save
TomJaeger Jul 21, 2026
7f60bc2
Merge pull request #5357 from ExpressionEngine/fix/template-allow-php
TomJaeger Jul 22, 2026
eadaf5f
Validate template group removal requests
TomJaeger Jul 21, 2026
bb31192
Merge pull request #5358 from ExpressionEngine/fix/template-group-rem…
TomJaeger Jul 22, 2026
f9da9f6
Merge pull request #5349 from kerstenremco/7.dev-remove-link
TomJaeger Jul 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/tests-minimal.yml
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ jobs:

- name: Start SMTP Server
run: |
npm install -g maildev
npm install -g maildev@2.x
maildev &

# This will get a Stable Chrome version that is 2 releases back from latest, and install it
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ jobs:

- name: Start SMTP Server
run: |
npm install -g maildev
npm install -g maildev@2.x
maildev &
# This will get a Stable Chrome version that is 2 releases back from latest, and install it
Expand Down
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ ExpressionEngine separates your content from your design, enabling you to make s
If you're new to ExpressionEngine, check out:

- [The Big Picture](https://docs.expressionengine.com/latest/getting-started/the-big-picture.html)
- [Building a Simple News Site from Start to Finish](https://docs.expressionengine.com/latest/how_to/building_a_simple_news_site.html)
- [10-minute ExpressionEngine Primer](https://www.youtube.com/watch?v=qKaOirMRz2s) on ExpressionEngineTV

## How to Contribute
Expand Down
2 changes: 1 addition & 1 deletion build-tools/build.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"tag": "7.5.25",
"tag": "7.5.26",
"repositories": {
"app": "git@github.com:ExpressionEngine/ExpressionEngine",
"docs": "git@github.com:ExpressionEngine/ExpressionEngine-User-Guide"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1113,10 +1113,12 @@ private function _build_custom_field_variables()
*/
private function _swap_custom_field_variables($custom_field_variables_row, $tagdata)
{
$integer_variables = array('field_id', 'field_data', 'rows', 'maxlength');

foreach ($custom_field_variables_row as $key => $value) {
if (is_array($value)) {
$tagdata = $this->swap_var_pair($key, $value, $tagdata);
} elseif ($key === 'field_id') {
} elseif (in_array($key, $integer_variables, true)) {
$tagdata = ee()->TMPL->swap_var_single($key, (string) $value, $tagdata);
} elseif (! is_int($value)) {
// don't use our conditionals as vars
Expand Down
8 changes: 5 additions & 3 deletions system/ee/ExpressionEngine/Addons/file/ft.file.php
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,10 @@ public function save($data)
}

/**
* Show the publish field
* Render the publish field for the current request.
*
* @access public
* @param mixed $data Stored file field data
* @return string Rendered file field
*/
public function display_field($data)
{
Expand Down Expand Up @@ -188,7 +189,8 @@ public function display_field($data)
$allowed_file_dirs,
$content_type,
$filebrowser,
($show_existing == 'y') ? $existing_limit : null
($show_existing == 'y') ? $existing_limit : null,
($this->content_type() === 'channel') ? $this->field_name : null
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,11 @@ function pro_search_encode($array = array(), $url = true)
}

/**
* Decode a query back to the array
* Decode a Pro Search query payload back to an array.
*
* @param string $str Encoded query string.
* @param bool $url Whether the payload is URL-safe base64 encoded.
* @return array
*/
if (! function_exists('pro_search_decode')) {
function pro_search_decode($str = '', $url = true)
Expand All @@ -71,21 +75,71 @@ function pro_search_decode($str = '', $url = true)
$str = str_replace(' ', '+', $str);

// Decode back
$str = base64_decode($str);
$str = base64_decode($str, true);

if ($str === false) {
return array();
}
}

// Decoding method
$array = (substr($str, 0, 2) == 'a:') ? @unserialize($str) : @json_decode($str, true);
$array = (substr($str, 0, 2) == 'a:')
? @unserialize($str, array('allowed_classes' => false))
: @json_decode($str, true);

// Force array output
if (! is_array($array)) {
if (! is_array($array) || contains_non_scalar_or_recursive_values($array)) {
$array = array();
}

return $array;
}
}

/**
* Determine if an array contains non-scalar or recursive values.
*
* @param array $array Decoded query array.
* @return bool
*/
if (! function_exists('contains_non_scalar_or_recursive_values')) {
function contains_non_scalar_or_recursive_values($array)
{
$nonScalar = false;
$recursive = false;

set_error_handler(function () use (&$recursive) {
$recursive = true;

return true;
});

try {
array_walk_recursive($array, function ($value) use (&$nonScalar) {
if (is_object($value) || is_resource($value)) {
$nonScalar = true;
}
});
} catch (Throwable $exception) {
$recursive = true;
} finally {
restore_error_handler();
}

if ($recursive) {
return true;
}

foreach ($array as $value) {
if (is_object($value) || is_resource($value)) {
return true;
}
}

return $nonScalar;
}
}

// --------------------------------------------------------------------

/**
Expand Down
26 changes: 22 additions & 4 deletions system/ee/ExpressionEngine/Controller/Design/Group.php
Original file line number Diff line number Diff line change
Expand Up @@ -476,17 +476,35 @@ public function edit($group_name, $group_id = null)
ee()->cp->render('settings/form', $vars);
}

/**
* Remove a Template Group.
*
* @return void
*/
public function remove()
{
if (! ee('Permission')->can('delete_template_groups')) {
if (
! ee('Permission')->can('delete_template_groups') ||
ee('Request')->method() !== 'POST'
) {
show_error(lang('unauthorized_access'), 403);
}

$group_id = ee()->input->post('group_id');
$group_name = ee()->input->post('group_name');

if (
! is_numeric($group_id) &&
(! is_string($group_name) || $group_name === '')
) {
show_error(lang('group_not_found'));
}

$groups = ee('Model')->get('TemplateGroup');
if (is_numeric(ee()->input->post('group_id'))) {
$groups = $groups->filter('group_id', ee()->input->post('group_id'));
if (is_numeric($group_id)) {
$groups = $groups->filter('group_id', $group_id);
} else {
$groups = $groups->filter('group_name', ee()->input->post('group_name'));
$groups = $groups->filter('group_name', $group_name);
}
$groups = $groups->filter('site_id', ee()->config->item('site_id'))
->all();
Expand Down
5 changes: 5 additions & 0 deletions system/ee/ExpressionEngine/Controller/Design/Template.php
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,11 @@ private function validateTemplate(TemplateModel $template)
$_POST['template_engine'] = null;
}

if (! ee('Permission')->isSuperAdmin()) {
$_POST['allow_php'] = $template->isNew() ? 'n' : $template->allow_php;
$_POST['php_parse_location'] = $template->isNew() ? 'o' : $template->php_parse_location;
}

$template->set($_POST);
$template->edit_date = ee()->localize->now;
$template->last_author_id = ee()->session->userdata('member_id');
Expand Down
2 changes: 1 addition & 1 deletion system/ee/ExpressionEngine/Controller/Members/Members.php
Original file line number Diff line number Diff line change
Expand Up @@ -929,7 +929,7 @@ private function renderMemberTab($errors)
foreach (ee('Model')->make('Member')->getDisplay()->getFields() as $field) {
$sections['custom_fields'][] = [
'title' => $field->getLabel(),
'desc' => $field->getInstructions(),
'desc' => ee('Format')->make('Text', (string) $field->getInstructions())->convertToEntities()->compile(),
'fields' => [
$field->getName() => [
'type' => 'html',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ public function index()
foreach ($this->member->getDisplay()->getFields() as $field) {
$vars['sections']['custom_fields'][] = array(
'title' => $field->getLabel(),
'desc' => $field->getInstructions(),
'desc' => ee('Format')->make('Text', (string) $field->getInstructions())->convertToEntities()->compile(),
'fields' => array(
$field->getName() => array(
'type' => 'html',
Expand Down
6 changes: 6 additions & 0 deletions system/ee/ExpressionEngine/Library/CP/Form/Field.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ public function get(string $key)
}

/**
* Convert the field into a shared form definition.
*
* @return array
*/
public function toArray(): array
Expand All @@ -92,6 +94,10 @@ public function toArray(): array
}
}

if (isset($return['group_toggle']) && is_string($return['group_toggle'])) {
$return['group_toggle'] = ['y' => $return['group_toggle']];
}

return $return;
}

Expand Down
51 changes: 47 additions & 4 deletions system/ee/ExpressionEngine/Model/File/File.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,61 @@
*/
class File extends FileSystemEntity
{
/**
* Get the stored original image width.
*
* @return string|null Original image width as a numeric string, or null when unavailable.
*/
public function get__width()
{
$dimensions = explode(" ", $this->getProperty('file_hw_original'));
$dimensions = $this->getOriginalDimensions();

return $dimensions[1];
return $dimensions === null ? null : $dimensions['width'];
}

/**
* Get the stored original image height.
*
* @return string|null Original image height as a numeric string, or null when unavailable.
*/
public function get__height()
{
$dimensions = explode(" ", $this->getProperty('file_hw_original'));
$dimensions = $this->getOriginalDimensions();

return $dimensions[0];
return $dimensions === null ? null : $dimensions['height'];
}

/**
* Parse the stored original image dimensions as an atomic pair.
*
* @return array|null Height and width as positive numeric strings, or null when invalid.
*/
private function getOriginalDimensions()
{
$value = $this->getProperty('file_hw_original');

if (! is_string($value)) {
return null;
}

$dimensions = explode(' ', $value);

if (count($dimensions) !== 2) {
return null;
}

list($height, $width) = $dimensions;

if (
! ctype_digit($height)
|| ! ctype_digit($width)
|| (int) $height <= 0
|| (int) $width <= 0
) {
return null;
}

return compact('height', 'width');
}

public function get__title()
Expand Down
29 changes: 28 additions & 1 deletion system/ee/ExpressionEngine/Model/File/FileDimension.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class FileDimension extends Model
);

protected static $_validation_rules = array(
'short_name' => 'required|xss|alphaDash|notNumeric|uniqueWithinSiblings[UploadDestination,FileDimensions]',
'short_name' => 'required|xss|alphaDash|notNumeric|validateShortNameIsNotReserved|uniqueWithinSiblings[UploadDestination,FileDimensions]',
'resize_type' => 'enum[crop,constrain]',
'width' => 'isNatural|validateDimension',
'height' => 'isNatural|validateDimension',
Expand All @@ -67,6 +67,15 @@ class FileDimension extends Model
protected $watermark_id;
protected $quality;

private $reserved_short_names = array(
'thumbs',
'resize',
'crop',
'rotate',
'webp',
'avif',
);

public function onAfterDelete()
{
//delete the root manipulation folder
Expand All @@ -85,6 +94,24 @@ public function onAfterDelete()
}
}

/**
* Prevent custom manipulations from colliding with built-in manipulation folders.
*
* @param string $key Field key being validated
* @param mixed $value Field value being validated
* @param array $params Validation rule parameters
* @param \ExpressionEngine\Service\Validation\Rule\Callback $rule Validation callback rule
* @return bool|string True when valid, otherwise a localized error message
*/
public function validateShortNameIsNotReserved($key, $value, $params, $rule)
{
if (in_array(strtolower((string) $value), $this->reserved_short_names, true)) {
return lang('invalid_short_name');
}

return true;
}

/**
* At least a height OR a width must be specified if there is no watermark selected
*/
Expand Down
18 changes: 15 additions & 3 deletions system/ee/ExpressionEngine/Service/LivePreview/LivePreview.php
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,13 @@ public function forEntryId($id)
}

/**
* generate and display the live preview
* Generate and display the live preview.
*
* @param int $channel_id Channel ID being previewed
* @param int|null $entry_id Entry ID being previewed
* @param string|null $preview_url Explicit preview URL
* @param bool $prefer_system_preview Whether to prefer the system preview route
* @return void
*/
public function preview($channel_id, $entry_id = null, $preview_url = null, $prefer_system_preview = false)
{
Expand Down Expand Up @@ -216,8 +222,14 @@ public function preview($channel_id, $entry_id = null, $preview_url = null, $pre
// - Added 4.2.0
if (ee()->extensions->active_hook('publish_live_preview_route') === true) {
$route = ee()->extensions->call('publish_live_preview_route', array_merge($_POST, $data), $uri, $template_id);
$uri = $route['uri'];
$template_id = $route['template_id'];
if (
is_array($route) &&
array_key_exists('uri', $route) &&
array_key_exists('template_id', $route)
) {
$uri = $route['uri'];
$template_id = $route['template_id'];
}
}
//
// -------------------------------------------
Expand Down
Loading
Loading