Skip to content

Commit

Permalink
Merge 5.7 and build
Browse files Browse the repository at this point in the history
  • Loading branch information
Lupe Camacho authored and Lupe Camacho committed Jan 23, 2025
2 parents e39e840 + 0418dcf commit 5491273
Show file tree
Hide file tree
Showing 101 changed files with 4,377 additions and 3,161 deletions.
1 change: 0 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ on:
push:
branches:
- 5.x
- '5.6'
pull_request:
permissions:
contents: read
Expand Down
189 changes: 2 additions & 187 deletions CHANGELOG-WIP.md

Large diffs are not rendered by default.

222 changes: 222 additions & 0 deletions CHANGELOG.md

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions src/base/Field.php
Original file line number Diff line number Diff line change
Expand Up @@ -544,8 +544,8 @@ public function getActionMenuItems(): array
$editId = sprintf('action-edit-%s', mt_rand());
$items[] = [
'id' => $editId,
'icon' => 'edit',
'label' => Craft::t('app', 'Edit field'),
'icon' => 'gear',
'label' => Craft::t('app', 'Field settings'),
];
$view->registerJsWithVars(fn($id, $params) => <<<JS
(() => {
Expand Down
2 changes: 1 addition & 1 deletion src/base/FieldInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ interface FieldInterface extends SavableComponentInterface, Chippable, Grippable
* The returned icon can be a system icon’s name (e.g. `'whiskey-glass-ice'`),
* the path to an SVG file, or raw SVG markup.
*
* System icons can be found in `src/icons/solid/.`
* System icons can be found in `src/icons/solid/`.
*
* @return string
* @since 5.0.0
Expand Down
2 changes: 1 addition & 1 deletion src/base/Iconic.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ interface Iconic
* The returned icon can be a system icon’s name (e.g. `'whiskey-glass-ice'`),
* the path to an SVG file, or raw SVG markup.
*
* System icons can be found in `src/icons/solid/.`
* System icons can be found in `src/icons/solid/`.
*
* @return string|null
*/
Expand Down
33 changes: 33 additions & 0 deletions src/base/Indicative.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php
/**
* @link https://craftcms.com/
* @copyright Copyright (c) Pixel & Tonic, Inc.
* @license https://craftcms.github.io/license/
*/

namespace craft\base;

use craft\enums\Color;

/**
* Indicative defines the common interface to be implemented by components that
* have indicator icons within their chips.
*
* @author Pixel & Tonic, Inc. <support@pixelandtonic.com>
* @since 5.6.0
*/
interface Indicative
{
/**
* Returns the component’s indicators.
*
* Each indicator should be a nested array with the following keys:
*
* - `label` – The indicator label.
* - `icon` – The indicator icon name. System icons can be found in `src/icons/solid/`.
* - `iconColor` – The color of the icon.
*
* @return array{label:string,icon:string,iconColor?:Color|string}[]
*/
public function getIndicators(): array;
}
18 changes: 2 additions & 16 deletions src/base/Model.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,10 @@
use craft\events\DefineFieldsEvent;
use craft\events\DefineRulesEvent;
use craft\helpers\App;
use craft\helpers\Component;
use craft\helpers\DateTimeHelper;
use craft\helpers\StringHelper;
use craft\helpers\Typecast;
use DateTime;
use ReflectionClass;
use ReflectionNamedType;
use ReflectionProperty;
use yii\validators\Validator;

/**
Expand Down Expand Up @@ -242,18 +239,7 @@ public function fields(): array
{
$fields = parent::fields();

$datetimeAttributes = [];
foreach ((new ReflectionClass($this))->getProperties(ReflectionProperty::IS_PUBLIC) as $property) {
if (!$property->isStatic()) {
$type = $property->getType();
if ($type instanceof ReflectionNamedType && $type->getName() === DateTime::class) {
$datetimeAttributes[] = $property->getName();
}
}
}

// Include datetimeAttributes() for now
$datetimeAttributes = array_unique(array_merge($datetimeAttributes, $this->datetimeAttributes()));
$datetimeAttributes = Component::datetimeAttributes($this);

// Have all DateTime attributes converted to ISO-8601 strings
foreach ($datetimeAttributes as $attribute) {
Expand Down
2 changes: 1 addition & 1 deletion src/base/UtilityInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public static function id(): string;
* The returned icon can be a system icon’s name (e.g. `'whiskey-glass-ice'`),
* the path to an SVG file, or raw SVG markup.
*
* System icons can be found in `src/icons/solid/.`
* System icons can be found in `src/icons/solid/`.
*
* @return string|null
* @since 5.0.0
Expand Down
2 changes: 1 addition & 1 deletion src/base/WidgetInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ interface WidgetInterface extends SavableComponentInterface
* The returned icon can be a system icon’s name (e.g. `'whiskey-glass-ice'`),
* the path to an SVG file, or raw SVG markup.
*
* System icons can be found in `src/icons/solid/.`
* System icons can be found in `src/icons/solid/`.
*
* @return string|null
* @since 3.2.0
Expand Down
4 changes: 2 additions & 2 deletions src/config/app.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
return [
'id' => 'CraftCMS',
'name' => 'Craft CMS',
'version' => '5.5.10',
'schemaVersion' => '5.6.0.1',
'version' => '5.6.0.2',
'schemaVersion' => '5.6.0.2',
'minVersionRequired' => '4.5.0',
'basePath' => dirname(__DIR__), // Defines the @app alias
'runtimePath' => '@storage/runtime', // Defines the @runtime alias
Expand Down
1 change: 1 addition & 0 deletions src/config/cproutes/common.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,4 +88,5 @@
'plugin-store' => 'plugin-store',
'plugin-store/callback' => 'plugin-store/callback',
'plugin-store/<url:(.*)>' => 'plugin-store',
'preview/<elementId:\d+><slug:(?:-[^\/]*)?>' => 'elements/preview',
];
8 changes: 7 additions & 1 deletion src/console/controllers/EntryTypesController.php
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,13 @@ private function modifyEntryTypes(array $entryTypes, EntryType $persistingEntryT
foreach ($entryTypes as $entryType) {
if ($entryType->uid === $outgoingEntryType->uid) {
if (!$hasPersistingEntryType) {
$modified[] = $persistingEntryType;
// Clone the persisting entry type with the original name & handle,
// in case the usage supports name & handle overrides
$clone = $modified[] = clone $persistingEntryType;
$clone->original = $persistingEntryType;
$clone->name = $entryType->name;
$clone->handle = $entryType->handle;

$hasPersistingEntryType = true;
}
} else {
Expand Down
2 changes: 1 addition & 1 deletion src/controllers/EditUserTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ protected function asEditUserScreen(User $user, string $screen): Response
$screens = $event->screens;
}

if ($user->getIsCurrent()) {
if ($user->getIsCurrent() && $user->getHasPassword()) {
$screens[self::SCREEN_PASSWORD] = ['label' => Craft::t('app', 'Password & Verification')];
$screens[self::SCREEN_PASSKEYS] = ['label' => Craft::t('app', 'Passkeys')];
}
Expand Down
57 changes: 57 additions & 0 deletions src/controllers/ElementsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,63 @@ public function actionEdit(?ElementInterface $element, ?int $elementId = null):
return $response;
}

/**
* Displays a standalone Live Preview editor for an element.
*
* @param int $elementId
* @since 5.6.0
*/
public function actionPreview(int $elementId): Response
{
$this->requireCpRequest();

/** @var Element|DraftBehavior|RevisionBehavior|Response|null $element */
$element = $this->_element($elementId, checkForProvisionalDraft: true);

if ($element instanceof Response) {
return $element;
}

if (!$element) {
throw new BadRequestHttpException('No element was identified by the request.');
}

$this->element = $element;

$this->getView()->registerJsWithVars(fn(
$elementType,
$elementId,
$draftId,
$revisionId,
$siteId,
) => <<<JS
(() => {
const preview = new Craft.Preview({
elementType: $elementType,
elementId: $elementId,
draftId: $draftId,
revisionId: $revisionId,
siteId: $siteId,
standaloneMode: true,
});
preview.open();
})();
JS, [
$element::class,
$element->isProvisionalDraft ? $element->getCanonicalId() : $element->id,
!$element->isProvisionalDraft ? $element->draftId : null,
$element->revisionId,
$element->siteId,
], View::POS_END);

[$docTitle, $title] = $this->_editElementTitles($element);

return $this->renderTemplate('_layouts/base.twig', [
'docTitle' => $docTitle,
'title' => $title,
]);
}

/**
* Copies field/attribute values on an element from one site to another.
*
Expand Down
81 changes: 81 additions & 0 deletions src/controllers/EntryTypesController.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@
use craft\elements\Entry;
use craft\enums\Color;
use craft\fieldlayoutelements\entries\EntryTitleField;
use craft\helpers\ArrayHelper;
use craft\helpers\Cp;
use craft\helpers\Html;
use craft\helpers\StringHelper;
use craft\models\EntryType;
use craft\models\Section;
use craft\web\Controller;
Expand Down Expand Up @@ -284,4 +286,83 @@ public function actionTableData(): Response
'data' => $tableData,
]);
}

/**
* Renders an entry type’s override settings for an entry type select input.
*
* @return Response
* @since 5.6.0
*/
public function actionRenderOverrideSettings(): Response
{
$entryType = $this->_entryTypeForSelectInput();
$entryType->name = $this->request->getBodyParam('name') ?? $entryType->name;
$entryType->handle = $this->request->getBodyParam('handle') ?? $entryType->handle;

$namespace = StringHelper::randomString(10);
$view = Craft::$app->getView();

$html = $view->namespaceInputs(
fn() => $view->renderTemplate('_includes/forms/entry-type-select/selection-settings.twig', [
'entryType' => $entryType,
]),
$namespace,
);

return $this->asJson([
'settingsHtml' => $html,
'namespace' => $namespace,
'headHtml' => $view->getHeadHtml(),
'bodyHtml' => $view->getBodyHtml(),
]);
}

/**
* Validates and returns an entry type’s override settings for an entry type select input.
*
* @return Response
* @since 5.6.0
*/
public function actionApplyOverrideSettings(): Response
{
$entryType = $this->_entryTypeForSelectInput();

$settingsStr = $this->request->getBodyParam('settings');
parse_str($settingsStr, $postedSettings);
$settingsNamespace = $this->request->getRequiredBodyParam('settingsNamespace');
$settings = array_filter(ArrayHelper::getValue($postedSettings, $settingsNamespace, []));

if (!empty($settings)) {
Craft::configure($entryType, $settings);
$entryType->validateHandleUniqueness = false;

if (!$entryType->validate(array_keys($settings))) {
return $this->asModelFailure($entryType, Craft::t('app', 'Couldn’t apply changes.'), 'entryType');
}
}

$chipHtml = Cp::chipHtml($entryType, [
'showHandle' => true,
'showIndicators' => true,
]);

return $this->asJson([
'config' => $entryType->toArray(['id', 'name', 'handle']),
'chipHtml' => $chipHtml,
]);
}

private function _entryTypeForSelectInput(): EntryType
{
$id = $this->request->getRequiredBodyParam('id');
$original = Craft::$app->getEntries()->getEntryTypeById($id);

if (!$original) {
throw new BadRequestHttpException("Invalid entry type ID: $id");
}

$entryType = clone $original;
$entryType->original = $original;
return $entryType;
}
}
7 changes: 5 additions & 2 deletions src/controllers/FieldsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -523,7 +523,7 @@ public function actionApplyLayoutElementSettings(): Response
$field->addErrors(['label' => $field->getErrors('name')]);
$field->clearErrors('name');
}
return $this->asModelFailure($field, Craft::t('app', 'Couldn’t save field.'), 'field');
return $this->asModelFailure($field, Craft::t('app', 'Couldn’t apply changes.'), 'field');
}
}

Expand Down Expand Up @@ -585,7 +585,10 @@ public function actionRenderCardPreview()
$showThumb = $this->request->getBodyParam('showThumb', false);

if (!isset($fieldLayoutConfig['id'])) {
$fieldLayout = Craft::createObject(FieldLayout::class, $fieldLayoutConfig);
$fieldLayout = Craft::createObject([
'class' => FieldLayout::class,
...$fieldLayoutConfig,
]);
$fieldLayout->type = $fieldLayoutConfig['type'];
} else {
$fieldLayout = Craft::$app->getFields()->getLayoutById($fieldLayoutConfig['id']);
Expand Down
3 changes: 1 addition & 2 deletions src/controllers/SectionsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -165,8 +165,7 @@ public function actionSaveSection(): ?Response
$section->defaultPlacement = $this->request->getBodyParam('defaultPlacement') ?? $section->defaultPlacement;
}

$entryTypeIds = $this->request->getBodyParam('entryTypes') ?: [];
$section->setEntryTypes(array_map(fn($id) => $sectionsService->getEntryTypeById((int)$id), array_filter($entryTypeIds)));
$section->setEntryTypes(array_filter($this->request->getBodyParam('entryTypes') ?: []));

// Site-specific settings
$allSiteSettings = [];
Expand Down
13 changes: 11 additions & 2 deletions src/controllers/UsersController.php
Original file line number Diff line number Diff line change
Expand Up @@ -1059,9 +1059,13 @@ public function actionCreate(): Response

$user->setScenario(Element::SCENARIO_ESSENTIALS);
if (!Craft::$app->getDrafts()->saveElementAsDraft($user, Craft::$app->getUser()->getId(), null, null, false)) {
return $this->asModelFailure($user, StringHelper::upperCaseFirst(Craft::t('app', 'Couldn’t create {type}.', [
$response = $this->asModelFailure($user, StringHelper::upperCaseFirst(Craft::t('app', 'Couldn’t create {type}.', [
'type' => User::lowerDisplayName(),
])), 'user');
if ($response === null) {
throw new InvalidElementException($user, sprintf('Couldn’t create user: %s', implode(', ', $user->getFirstErrors())));
}
return $response;
}

$editUrl = $user->getCpEditUrl();
Expand Down Expand Up @@ -1365,13 +1369,18 @@ public function actionSavePassword(): ?Response
throw new BadRequestHttpException('An elevated session is required to change your password.');
}

$user = static::currentUser();

if (!$user->getHasPassword()) {
throw new BadRequestHttpException('Only users with current passwords can set new ones.');
}

$newPassword = $this->request->getRequiredBodyParam('newPassword');

if ($newPassword === '') {
return null;
}

$user = static::currentUser();
$user->newPassword = $newPassword;
$user->setScenario(User::SCENARIO_PASSWORD);

Expand Down
Loading

0 comments on commit 5491273

Please sign in to comment.