Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
ec98f4f
Merge branch '4.x' into feature/pt-2776-create-entries-toolbar-config…
i-just May 19, 2025
d5a60cc
entry types toolbar (with raw json for now)
i-just May 19, 2025
da521c0
tweak
i-just May 20, 2025
625c953
switch to special Entry Type Select Input?
i-just May 23, 2025
a116c96
yes, switch to special Entry Type Select Input
i-just May 23, 2025
ec72494
either withText or withIcon should always be set
i-just May 23, 2025
125e671
cleanup
i-just May 23, 2025
2e00be1
v5 migration
i-just May 30, 2025
121193e
use gray color for collapsed button icon
i-just May 30, 2025
55a38b0
account for expandEntryButtons when calculating unique configs
i-just May 30, 2025
036bad2
unused files
i-just May 30, 2025
009b5d8
cleanup
i-just May 30, 2025
70ddfbb
more cleanup
i-just May 30, 2025
52ac555
we get entry types from the config now
i-just May 30, 2025
c843592
fix a bug where indicators were not added if selecting new entry type
i-just May 30, 2025
1f05c1f
and now fix it properly
i-just Jun 3, 2025
b99eec9
refactor a bit
i-just Jun 3, 2025
3efcddd
hook up to EntryTypeSelectInput applySettings
i-just Jun 3, 2025
f9ba887
build
i-just Jun 3, 2025
d8e8bed
only use colour when withColor is set
i-just Jun 3, 2025
aa377eb
accessible colours
i-just Jun 4, 2025
9346fbd
field might not have entry types to start with
i-just Jun 12, 2025
e1a634c
each entry type toolbar button is now separate and moves to the overf…
i-just Jun 12, 2025
b162a01
removed unused 'use' statement
i-just Jun 12, 2025
6adb5ab
Merge branch '5.x' into feature/pt-2776-create-entries-toolbar-config…
i-just Dec 2, 2025
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 CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
- Added the “Parse embeds” field setting. ([#409](https://github.com/craftcms/ckeditor/pull/409))
- Added the “Show toolbar buttons for entry types with icons” field settings. ([#413](https://github.com/craftcms/ckeditor/pull/413))
- Fixed an error that occurred when copy/pasting a nested entry from a top-level element’s CKEditor field into a nested element’s CKEditor field. ([#408](https://github.com/craftcms/ckeditor/issues/408))
- Fixed an error that could occur when generating a GraphQL introspection schema, if a CKEditor field’s GraphQL Mode setting was set to “Full data”. ([#414](https://github.com/craftcms/ckeditor/issues/414))
- Fixed an error that could occur when generating a GraphQL introspection schema, if a CKEditor field’s GraphQL Mode setting was set to “Full data”. ([#414](https://github.com/craftcms/ckeditor/issues/414))

## 4.8.0 - 2025-04-30

Expand Down
86 changes: 86 additions & 0 deletions src/CkeConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
use craft\base\Actionable;
use craft\base\Chippable;
use craft\base\Model;
use craft\ckeditor\helpers\CkeditorConfig;
use craft\ckeditor\models\EntryType as CkeEntryType;
use craft\helpers\Cp;
use craft\helpers\Json;
use Illuminate\Support\Collection;
use yii\base\InvalidArgumentException;
Expand Down Expand Up @@ -82,6 +85,13 @@ public static function get(int|string $id): ?static
*/
public ?string $css = null;

/**
* @var CkeEntryType[] The field’s available entry types
* @see getEntryTypes()
* @see setEntryTypes()
*/
private array $_entryTypes = [];

public function __construct($config = [])
{
if (isset($config['toolbar']) && is_array($config['toolbar'])) {
Expand Down Expand Up @@ -118,6 +128,10 @@ public function __construct($config = [])
}
}

if (isset($config['entryTypes']) && $config['entryTypes'] === '') {
$config['entryTypes'] = [];
}

unset($config['listPlugin']);

parent::__construct($config);
Expand Down Expand Up @@ -305,6 +319,78 @@ function(string $attribute, ?array $params, Validator $validator) {
},
'when' => fn() => isset($this->_json),
],
['entryTypes', function(string $attribute, ?array $params, Validator $validator) {
// ensure that each selected entry type has at least one of: withText, withIcon
foreach ($this->$attribute as $entryType) {
if (!$entryType->withIcon && !$entryType->withText) {
$validator->addError($this, $attribute, Craft::t('ckeditor', 'Each entry type must either have an icon or text.'));
}
}
}],
];
}

/**
* Returns the available entry types.
*
* @return CkeEntryType[]
* @since 5.0.0
*/
public function getEntryTypes(): array
{
return $this->_entryTypes;
}

/**
* Sets the available entry types.
*
* @param array<string> $entryTypes The entry types, or their IDs or UUIDs
* @since 5.0.0
*/
public function setEntryTypes(array $entryTypes): void
{
// normalize the $entryTypes; when saving the config, it'll be an array of json strings, like so:
foreach ($entryTypes as &$entryType) {
if (is_string($entryType)) {
try {
$entryType = Json::decode($entryType);
} catch (InvalidArgumentException) {
// do nothing?
}
}
}
unset($entryType);

foreach ($entryTypes as $entryType) {
/** @var array $entryType */
$this->_entryTypes[] = CkeditorConfig::getCkeEntryType($entryType);
}
}

/**
* Returns entry type options in form of an array with 'label' and 'value' keys for each option.
*
* @return array
* @since 5.0.0
*/
public function getEntryTypeOptions(): array
{
$entryTypeOptions = [];

foreach ($this->getEntryTypes() as $entryType) {
$entryTypeOptions[] = [
'color' => $entryType->getColor()?->value,
'expanded' => $entryType['expanded'] ?? false,
'icon' => $entryType->icon ? Cp::iconSvg($entryType->icon) : null,
'label' => Craft::t('site', $entryType->name),
'uid' => $entryType->uid,
'value' => $entryType->id,
'withColor' => $entryType['withColor'] ?? true,
'withIcon' => $entryType['withIcon'] ?? true,
'withText' => $entryType['withText'] ?? true,
];
}

return $entryTypeOptions;
}
}
25 changes: 22 additions & 3 deletions src/CkeConfigs.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
namespace craft\ckeditor;

use Craft;
use craft\models\EntryType;
use yii\base\Component;
use yii\base\InvalidArgumentException;

Expand Down Expand Up @@ -40,15 +41,15 @@ public function getAll(): array
/**
* @throws InvalidArgumentException if $uid is invalid
*/
public function getByUid(string $uid): CkeConfig
public function getByUid(string $uid, bool $forToolbarEditor = false): CkeConfig
{
$config = Craft::$app->getProjectConfig()->get($this->_pcPath($uid));

if ($config === null) {
throw new InvalidArgumentException("Invalid CKEditor config UUID: $uid");
}

$config = $this->_adjustConfig($config);
$config = $this->_adjustConfig($config, $forToolbarEditor);

return new CkeConfig($config + ['uid' => $uid]);
}
Expand All @@ -70,6 +71,10 @@ public function save(CkeConfig $ckeConfig, bool $runValidation = true): bool
'options' => $ckeConfig->options,
'js' => $ckeConfig->js,
'css' => $ckeConfig->css,
'entryTypes' => array_map(
fn(EntryType $entryType) => $entryType->getUsageConfig(),
$ckeConfig->getEntryTypes(),
),
], fn($item) => $item !== null));

return true;
Expand All @@ -91,14 +96,28 @@ private function _pcPath(string $uid): string
* @param array $config
* @return array
*/
private function _adjustConfig(array $config): array
private function _adjustConfig(array $config, bool $forToolbarEditor = false): array
{
// rewrite anchor toolbar item to bookmark
$key = array_search('anchor', $config['toolbar']);
if ($key !== false) {
$config['toolbar'][$key] = 'bookmark';
}

// rewrite createEntry into per-entry-type buttons
if (!$forToolbarEditor && in_array('createEntry', $config['toolbar'])) {
if (!empty($config['entryTypes'])) {
// ensure the dropdown option is after the individual buttons
$key = array_search('createEntry', $config['toolbar']);
unset($config['toolbar'][$key]);
// create toolbar items for individual entry types
foreach ($config['entryTypes'] as $entryType) {
$config['toolbar'][] = 'createEntry-' . $entryType['uid'];
}
$config['toolbar'][] = 'createEntry';
}
}

return $config;
}
}
92 changes: 12 additions & 80 deletions src/Field.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@
use craft\htmlfield\HtmlFieldData;
use craft\i18n\Locale;
use craft\models\CategoryGroup;
use craft\models\EntryType;
use craft\models\ImageTransform;
use craft\models\Section;
use craft\models\Volume;
Expand Down Expand Up @@ -453,26 +452,6 @@ private static function adjustFieldValues(
*/
public bool $fullGraphqlData = true;

/**
* @var string|null The “New entry” button label.
* @since 4.0.0
* @deprecated in 4.8.0
*/
public ?string $createButtonLabel = null;

/**
* @var bool Whether entry types with icons should be shown as separate buttons in the toolbar.
* @since 4.9.0
*/
public bool $expandEntryButtons = false;

/**
* @var EntryType[] The field’s available entry types
* @see getEntryTypes()
* @see setEntryTypes()
*/
private array $_entryTypes = [];

/**
* @inheritdoc
*/
Expand All @@ -483,6 +462,9 @@ public function __construct($config = [])
$config['removeInlineStyles'],
$config['removeEmptyTags'],
$config['removeNbsp'],
$config['createButtonLabel'],
$config['entryTypes'],
$config['expandEntryButtons'],
);

if (isset($config['enableSourceEditingForNonAdmins'])) {
Expand All @@ -501,10 +483,6 @@ public function __construct($config = [])
unset($config['limitUnit'], $config['fieldLimit']);
}

if (isset($config['entryTypes']) && $config['entryTypes'] === '') {
$config['entryTypes'] = [];
}

if (isset($config['graphqlMode'])) {
$config['fullGraphqlData'] = ArrayHelper::remove($config, 'graphqlMode') === 'full';
}
Expand Down Expand Up @@ -620,6 +598,7 @@ public function settingsAttributes(): array
/**
* @inheritdoc
*/

public function getUriFormatForElement(NestedElementInterface $element): ?string
{
return null;
Expand Down Expand Up @@ -756,37 +735,12 @@ public function getSettingsHtml(): ?string
]);
}

/**
* Returns the available entry types.
*
* @return EntryType[]
*/
public function getEntryTypes(): array
{
return $this->_entryTypes;
}

/**
* Sets the available entry types.
*
* @param array<int|string|EntryType> $entryTypes The entry types, or their IDs or UUIDs
*/
public function setEntryTypes(array $entryTypes): void
{
$entriesService = Craft::$app->getEntries();

$this->_entryTypes = array_values(array_filter(array_map(
fn($entryType) => $entriesService->getEntryType($entryType),
$entryTypes,
)));
}

/**
* @inheritdoc
*/
public function getFieldLayoutProviders(): array
{
return $this->getEntryTypes();
return $this->_ckeConfig()->getEntryTypes();
}

/**
Expand All @@ -801,11 +755,8 @@ public function getSettings(): array
$settings['removeInlineStyles'],
$settings['removeEmptyTags'],
$settings['removeNbsp'],
);

$settings['entryTypes'] = array_map(
fn(EntryType $entryType) => $entryType->getUsageConfig(),
$this->getEntryTypes(),
$settings['entryTypes'],
$settings['createButtonLabel'],
);

return $settings;
Expand Down Expand Up @@ -1026,7 +977,8 @@ private function _inputHtml(mixed $value, ?ElementInterface $element, bool $stat
$toolbar = array_merge($ckeConfig->toolbar);

if (!$element?->id) {
ArrayHelper::removeValue($toolbar, 'createEntry');
// remove all toolbar items that start with 'createEntry'
$toolbar = array_filter($toolbar, fn($item) => !str_starts_with($item, 'createEntry'));
}

if (!$this->isSourceEditingAllowed(Craft::$app->getUser()->getIdentity())) {
Expand All @@ -1045,8 +997,7 @@ private function _inputHtml(mixed $value, ?ElementInterface $element, bool $stat
'elementSiteId' => $element?->siteId,
'accessibleFieldName' => $this->_accessibleFieldName($element),
'describedBy' => $this->_describedBy($view),
'entryTypeOptions' => $this->_getEntryTypeOptions(),
'expandEntryButtons' => $this->expandEntryButtons,
'entryTypeOptions' => $ckeConfig->getEntryTypeOptions(),
'findAndReplace' => [
'uiType' => 'dropdown',
],
Expand Down Expand Up @@ -1478,26 +1429,6 @@ protected function searchKeywords(mixed $value, ElementInterface $element): stri
return $keywords;
}

/**
* Returns entry type options in form of an array with 'label' and 'value' keys for each option.
*
* @return array
*/
private function _getEntryTypeOptions(): array
{
$entryTypeOptions = array_map(
fn(EntryType $entryType) => [
'icon' => $entryType->icon ? Cp::iconSvg($entryType->icon) : null,
'color' => $entryType->getColor()?->value,
'label' => Craft::t('site', $entryType->name),
'value' => $entryType->id,
],
$this->getEntryTypes(),
);

return $entryTypeOptions;
}

/**
* Normalizes <figure> tags, ensuring they have an `image` or `media` class depending on their contents,
* and they contain a <div data-oembed-url> or <oembed> tag, depending on the `mediaEmbed.previewsInData`
Expand Down Expand Up @@ -1954,7 +1885,8 @@ private function _adjustPurifierConfig(HTMLPurifier_Config $purifierConfig): HTM
$def?->addAttribute('ul', 'style', 'Text');
}

if (in_array('createEntry', $ckeConfig->toolbar)) {
$createEntryToolbarItems = array_filter($ckeConfig->toolbar, fn($item) => str_starts_with($item, 'createEntry'));
if (!empty($createEntryToolbarItems)) {
$def?->addElement('craft-entry', 'Inline', 'Inline', '', [
'data-entry-id' => 'Number',
'data-site-id' => 'Number',
Expand Down
2 changes: 1 addition & 1 deletion src/Plugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public static function registerCkeditorPackage(string $name, string $entry = 'in
private static array $ckeditorPackages = [];
private static array $ckeditorImports = [];

public string $schemaVersion = '3.0.0.0';
public string $schemaVersion = '5.0.0.0';
public bool $hasCpSettings = true;
public bool $hasReadOnlyCpSettings = true;

Expand Down
Loading
Loading