diff --git a/packages/craftcms-asset-bundles/bundles/cp/src/js/CategoryIndex.js b/packages/craftcms-asset-bundles/bundles/cp/src/js/CategoryIndex.js
deleted file mode 100644
index d07f18c6c74..00000000000
--- a/packages/craftcms-asset-bundles/bundles/cp/src/js/CategoryIndex.js
+++ /dev/null
@@ -1,267 +0,0 @@
-/** global: Craft */
-/** global: Garnish */
-/**
- * Category index class
- */
-Craft.CategoryIndex = Craft.BaseElementIndex.extend({
- editableGroups: null,
- $newCategoryBtnGroup: null,
- $newCategoryBtn: null,
-
- init: function (elementType, $container, settings) {
- this.editableGroups = [];
- this.on('selectSource', this.updateButton.bind(this));
- this.on('selectSite', this.updateButton.bind(this));
- this.base(elementType, $container, settings);
- },
-
- afterInit: function () {
- // Find which of the visible groups the user has permission to create new categories in
- this.editableGroups = Craft.editableCategoryGroups.filter(
- (g) => !!this.getSourceByKey(`group:${g.uid}`)
- );
-
- this.base();
- },
-
- getDefaultSourceKey: function () {
- // Did they request a specific category group in the URL?
- if (
- this.settings.context === 'index' &&
- typeof defaultGroupHandle !== 'undefined'
- ) {
- for (let i = 0; i < this.$sources.length; i++) {
- const $source = $(this.$sources[i]);
- if ($source.data('handle') === defaultGroupHandle) {
- return $source.data('key');
- }
- }
- }
-
- return this.base();
- },
-
- updateButton: function () {
- if (!this.$source) {
- return;
- }
-
- // Get the handle of the selected source
- const selectedSourceHandle = this.$source.data('handle');
-
- // Update the New Category button
- // ---------------------------------------------------------------------
-
- if (this.editableGroups.length) {
- // Remove the old button, if there is one
- if (this.$newCategoryBtnGroup) {
- this.$newCategoryBtnGroup.remove();
- }
-
- // Determine if they are viewing a group that they have permission to create categories in
- const selectedGroup = this.editableGroups.find(
- (g) => g.handle === selectedSourceHandle
- );
-
- this.$newCategoryBtnGroup = $(
- '
'
- );
- let $menuBtn;
- const menuId = 'new-category-menu-' + Craft.randomString(10);
-
- // If they are, show a primary "New category" button, and a dropdown of the other groups (if any).
- // Otherwise only show a menu button
- if (selectedGroup) {
- const visibleLabel =
- this.settings.context === 'index'
- ? Craft.uppercaseFirst(
- Craft.t('app', 'New {type}', {
- type: Craft.elementTypeNames['craft\\elements\\Category'][2],
- })
- )
- : Craft.t('app', 'New {group} category', {
- group: selectedGroup.name,
- });
- const ariaLabel =
- this.settings.context === 'index'
- ? Craft.t('app', 'New category in the {group} category group', {
- group: selectedGroup.name,
- })
- : visibleLabel;
-
- const role = this.settings.context === 'index' ? 'link' : null;
-
- this.$newCategoryBtn = Craft.ui
- .createButton({
- label: visibleLabel,
- ariaLabel: ariaLabel,
- spinner: true,
- role: role,
- })
- .addClass('submit add icon')
- .appendTo(this.$newCategoryBtnGroup);
-
- this.addListener(this.$newCategoryBtn, 'click mousedown', (ev) => {
- // If this is the element index, check for Ctrl+clicks and middle button clicks
- if (
- this.settings.context === 'index' &&
- ((ev.type === 'click' && Garnish.isCtrlKeyPressed(ev)) ||
- (ev.type === 'mousedown' && ev.originalEvent.button === 1))
- ) {
- window.open(Craft.getUrl(`categories/${selectedGroup.handle}/new`));
- } else if (ev.type === 'click') {
- this._createCategory(selectedGroup.id);
- }
- });
-
- if (this.editableGroups.length > 1) {
- $menuBtn = $(' ', {
- type: 'button',
- class: 'btn submit menubtn btngroup-btn-last',
- 'aria-controls': menuId,
- 'data-disclosure-trigger': '',
- 'aria-label': Craft.t(
- 'app',
- 'New category, choose a category group'
- ),
- }).appendTo(this.$newCategoryBtnGroup);
- }
- } else {
- this.$newCategoryBtn = $menuBtn = Craft.ui
- .createButton({
- label: Craft.uppercaseFirst(
- Craft.t('app', 'New {type}', {
- type: Craft.elementTypeNames['craft\\elements\\Category'][2],
- })
- ),
- ariaLabel: Craft.t('app', 'New category, choose a category group'),
- spinner: true,
- })
- .addClass('submit add icon menubtn btngroup-btn-last')
- .attr('aria-controls', menuId)
- .attr('data-disclosure-trigger', '')
- .appendTo(this.$newCategoryBtnGroup);
- }
-
- this.addButton(this.$newCategoryBtnGroup);
-
- if ($menuBtn) {
- const $menuContainer = $('
', {
- id: menuId,
- class: 'menu menu--disclosure',
- }).appendTo(this.$newCategoryBtnGroup);
- const $ul = $('').appendTo($menuContainer);
-
- for (const group of this.editableGroups) {
- const anchorRole =
- this.settings.context === 'index' ? 'link' : 'button';
- if (this.settings.context === 'index' || group !== selectedGroup) {
- const $li = $(' ').appendTo($ul);
- const $a = $(' ', {
- role: anchorRole === 'button' ? 'button' : null,
- href: '#', // Allows for click listener and tab order
- type: anchorRole === 'button' ? 'button' : null,
- text: Craft.t('app', 'New {group} category', {
- group: group.name,
- }),
- }).appendTo($li);
- this.addListener($a, 'click', () => {
- $menuBtn.data('trigger').hide();
- this._createCategory(group.id);
- });
-
- if (anchorRole === 'button') {
- this.addListener($a, 'keydown', (event) => {
- if (event.keyCode === Garnish.SPACE_KEY) {
- event.preventDefault();
- $menuBtn.data('trigger').hide();
- this._createCategory(group.id);
- }
- });
- }
- }
- }
-
- new Garnish.DisclosureMenu($menuBtn);
- }
- }
-
- // Update the URL if we're on the Categories index
- // ---------------------------------------------------------------------
-
- if (this.settings.context === 'index') {
- let uri = 'categories';
-
- if (selectedSourceHandle) {
- uri += '/' + selectedSourceHandle;
- }
-
- Craft.setPath(uri);
- }
- },
-
- _createCategory: function (groupId) {
- if (this.$newCategoryBtn.hasClass('loading')) {
- console.warn('New category creation already in progress.');
- return;
- }
-
- // Find the group
- const group = this.editableGroups.find((s) => s.id === groupId);
-
- if (!group) {
- throw `Invalid category group ID: ${groupId}`;
- }
-
- this.$newCategoryBtn.addClass('loading');
- Craft.cp.announce(Craft.t('app', 'Loading'));
-
- Craft.sendActionRequest('POST', 'elements/create', {
- data: {
- elementType: this.elementType,
- siteId: this.siteId,
- groupId: groupId,
- },
- })
- .then(({data}) => {
- if (this.settings.context === 'index') {
- document.location.href = Craft.getUrl(data.cpEditUrl, {fresh: 1});
- } else {
- const slideout = Craft.createElementEditor(this.elementType, {
- siteId: this.siteId,
- elementId: data.element.id,
- draftId: data.element.draftId,
- params: {
- fresh: 1,
- updateSearchIndexImmediately: 1,
- },
- });
- slideout.on('submit', async (ev) => {
- // Make sure the right group is selected
- const groupSourceKey = `group:${group.uid}`;
-
- if (this.sourceKey !== groupSourceKey) {
- await this.asyncSelectSourceByKey(groupSourceKey);
- }
-
- this.clearSearch(false);
- this.startSearching();
- this.$search.val(ev.data.title);
- this.searchText = ev.data.title;
-
- this.selectElementAfterUpdate(data.element.id);
- this.updateElements();
- });
- }
- })
- .finally(() => {
- this.$newCategoryBtn.removeClass('loading');
- });
- },
-});
-
-// Register it!
-Craft.registerElementIndexClass(
- 'craft\\elements\\Category',
- Craft.CategoryIndex
-);
diff --git a/packages/craftcms-asset-bundles/bundles/cp/src/js/CategorySelectInput.js b/packages/craftcms-asset-bundles/bundles/cp/src/js/CategorySelectInput.js
deleted file mode 100644
index a2f16730fc1..00000000000
--- a/packages/craftcms-asset-bundles/bundles/cp/src/js/CategorySelectInput.js
+++ /dev/null
@@ -1,125 +0,0 @@
-/** global: Craft */
-/** global: Garnish */
-/**
- * Category select input
- * @deprecated in 4.3.0. Use Craft.BaseElementSelectInput instead.
- */
-Craft.CategorySelectInput = Craft.BaseElementSelectInput.extend({
- setSettings: function () {
- this.base.apply(this, arguments);
- this.settings.sortable = false;
- },
-
- getModalSettings: function () {
- var settings = this.base();
- settings.hideOnSelect = false;
- return settings;
- },
-
- getElements: function () {
- return this.$elementsContainer.find('.element');
- },
-
- onModalSelect: function (elements) {
- // Disable the modal
- this.modal.disable();
- this.modal.disableCancelBtn();
- this.modal.disableSelectBtn();
- this.modal.showFooterSpinner();
-
- // Get the new category HTML
- var selectedCategoryIds = this.getSelectedElementIds();
-
- for (var i = 0; i < elements.length; i++) {
- selectedCategoryIds.push(elements[i].id);
- }
-
- var data = {
- categoryIds: selectedCategoryIds,
- siteId: elements[0].siteId,
- id: this.settings.id,
- name: this.settings.name,
- branchLimit: this.settings.branchLimit,
- selectionLabel: this.settings.selectionLabel,
- };
-
- const onResponse = () => {
- this.modal.enable();
- this.modal.enableCancelBtn();
- this.modal.enableSelectBtn();
- this.modal.hideFooterSpinner();
- };
- Craft.sendActionRequest('POST', 'categories/input-html', {data})
- .then((response) => {
- onResponse();
- var $newInput = $(response.data.html),
- $newElementsContainer = $newInput.children('.elements');
-
- this.$elementsContainer.replaceWith($newElementsContainer);
- this.$elementsContainer = $newElementsContainer;
- this.resetElements();
-
- var filteredElements = [];
-
- for (var i = 0; i < elements.length; i++) {
- var element = elements[i],
- $element = this.getElementById(element.id);
-
- if ($element) {
- this.animateElementIntoPlace(element.$element, $element);
- filteredElements.push(element);
- }
- }
-
- this.updateDisabledElementsInModal();
- this.modal.hide();
- this.onSelectElements(filteredElements);
- })
- .catch(({response}) => {
- onResponse();
- });
- },
-
- removeElement: function ($element) {
- // Find any descendants this category might have
- const $allCategories = $element.add(
- $element.parent().siblings('ul').find('.element')
- );
-
- // Remove our record of them all at once
- this.removeElements($allCategories);
-
- // Animate them away one at a time
- for (let i = 0; i < $allCategories.length; i++) {
- this._animateCategoryAway($allCategories, i);
- }
- },
-
- _animateCategoryAway: function ($allCategories, i) {
- let callback;
-
- // Is this the last one?
- if (i === $allCategories.length - 1) {
- callback = () => {
- const $li = $allCategories.first().parent().parent();
- const $ul = $li.parent();
-
- if ($ul[0] === this.$elementsContainer[0] || $li.siblings().length) {
- $li.remove();
- } else {
- $ul.remove();
- }
- };
- }
-
- const func = () => {
- this.animateElementAway($allCategories.eq(i), callback);
- };
-
- if (i === 0) {
- func();
- } else {
- setTimeout(func, 100 * i);
- }
- },
-});
diff --git a/packages/craftcms-asset-bundles/bundles/cp/src/js/TagSelectInput.js b/packages/craftcms-asset-bundles/bundles/cp/src/js/TagSelectInput.js
deleted file mode 100644
index d489b9974b0..00000000000
--- a/packages/craftcms-asset-bundles/bundles/cp/src/js/TagSelectInput.js
+++ /dev/null
@@ -1,396 +0,0 @@
-/** global: Craft */
-/** global: Garnish */
-/**
- * Tag select input
- */
-Craft.TagSelectInput = Craft.BaseElementSelectInput.extend(
- {
- searchTimeout: null,
- searchMenu: null,
-
- $container: null,
- $elementsContainer: null,
- $elements: null,
- $addTagInput: null,
- $spinner: null,
-
- _ignoreBlur: false,
-
- init: function (settings) {
- // Normalize the settings
- // ---------------------------------------------------------------------
-
- // Are they still passing in a bunch of arguments?
- if (!$.isPlainObject(settings)) {
- // Loop through all of the old arguments and apply them to the settings
- var normalizedSettings = {},
- args = ['id', 'name', 'tagGroupId', 'sourceElementId'];
-
- for (var i = 0; i < args.length; i++) {
- if (typeof arguments[i] !== 'undefined') {
- normalizedSettings[args[i]] = arguments[i];
- } else {
- break;
- }
- }
-
- settings = normalizedSettings;
- }
-
- this.base($.extend({}, Craft.TagSelectInput.defaults, settings));
-
- this.$addTagInput = this.$container.children('.add').children('.text');
- this.$spinner = this.$addTagInput.next();
-
- this.addListener(this.$addTagInput, 'input', () => {
- if (this.searchTimeout) {
- clearTimeout(this.searchTimeout);
- }
-
- this.searchTimeout = setTimeout(this.searchForTags.bind(this), 500);
- });
-
- this.addListener(this.$addTagInput, 'keydown', function (ev) {
- if (ev.keyCode === Garnish.RETURN_KEY) {
- ev.preventDefault();
- }
-
- let $option;
-
- switch (ev.keyCode) {
- case Garnish.RETURN_KEY: {
- ev.preventDefault();
- if (this.searchMenu) {
- this.selectTag(this.searchMenu.$options.filter('.hover'));
- }
- return;
- }
-
- case Garnish.DOWN_KEY: {
- ev.preventDefault();
- if (this.searchMenu) {
- let $hoverOption = this.searchMenu.$options.filter('.hover');
- if ($hoverOption.length) {
- let $nextOption = $hoverOption
- .parent()
- .nextAll()
- .find('.menu-item:not(.disabled)')
- .first();
- if ($nextOption.length) {
- this.focusOption($nextOption);
- }
- } else {
- this.focusOption(this.searchMenu.$options.eq(0));
- }
- }
- return;
- }
-
- case Garnish.UP_KEY: {
- ev.preventDefault();
- if (this.searchMenu) {
- let $hoverOption = this.searchMenu.$options.filter('.hover');
- if ($hoverOption.length) {
- let $prevOption = $hoverOption
- .parent()
- .prevAll()
- .find('.menu-item:not(.disabled)')
- .last();
- if ($prevOption.length) {
- this.focusOption($prevOption);
- }
- } else {
- this.focusOption(
- this.searchMenu.$options.eq(
- this.searchMenu.$options.length - 1
- )
- );
- }
- }
- return;
- }
- }
- });
-
- this.addListener(this.$addTagInput, 'focus', function () {
- if (this.searchMenu) {
- this.searchMenu.show();
- }
- });
-
- this.addListener(this.$addTagInput, 'blur', function () {
- if (this._ignoreBlur) {
- this._ignoreBlur = false;
- return;
- }
-
- setTimeout(() => {
- if (this.searchMenu) {
- this.searchMenu.hide();
- }
- }, 1);
- });
- },
-
- get fieldName() {
- const $legend = this.$container.closest('fieldset').find('legend');
- if ($legend.length == 0) {
- return null;
- }
-
- return $legend[0].innerText;
- },
-
- focusOption: function ($option) {
- this.searchMenu.$options.removeClass('hover');
- this.searchMenu.$ariaOptions.attr('aria-selected', 'false');
-
- const activeDescendant = $option.parent('li').attr('id');
-
- $option.addClass('hover');
- this.$addTagInput.attr(
- 'aria-activedescendant',
- $option.parent('li').attr('id')
- );
- },
-
- // No "add" button
- getAddElementsBtn: function () {
- return [];
- },
-
- getElementSortAxis: function () {
- if (this.$container.parents('.inline-editing').length == 1) {
- return 'y';
- }
- return 'x';
- },
-
- searchForTags: function () {
- if (this.searchMenu) {
- this.killSearchMenu();
- }
-
- var val = this.$addTagInput.val();
-
- if (val) {
- this.$spinner.removeClass('hidden');
- Craft.cp.announce(Craft.t('app', 'Loading'));
-
- var excludeIds = [];
-
- for (var i = 0; i < this.$elements.length; i++) {
- var id = $(this.$elements[i]).data('id');
-
- if (id) {
- excludeIds.push(id);
- }
- }
-
- // take allowSelfRelations into consideration too
- if (
- this.settings.sourceElementId &&
- !this.settings.allowSelfRelations
- ) {
- excludeIds.push(this.settings.sourceElementId);
- }
-
- var data = {
- search: this.$addTagInput.val(),
- tagGroupId: this.settings.tagGroupId,
- excludeIds: excludeIds,
- };
-
- Craft.sendActionRequest('POST', 'tags/search-for-tags', {data})
- .then((response) => {
- if (this.searchMenu) {
- this.killSearchMenu();
- }
- this.$spinner.addClass('hidden');
- Craft.cp.announce(Craft.t('app', 'Loading complete'));
-
- let fieldName = this.fieldName;
- let $menu = $('');
- if (fieldName !== null) {
- $menu.attr('aria-label', fieldName);
- }
- $menu.appendTo(Garnish.$bod);
- let $ul = $('').appendTo($menu);
-
- let $li;
- let optionLabel;
-
- for (var i = 0; i < response.data.tags.length; i++) {
- $li = $(' ').appendTo($ul);
- optionLabel = `${Craft.t('app', 'Existing {type}', {
- type: Craft.t('app', 'Tag'),
- })}: ${response.data.tags[i].title}`;
- $li.attr('aria-label', optionLabel);
-
- $('')
- .appendTo($li)
- .text(response.data.tags[i].title)
- .data('id', response.data.tags[i].id)
- .addClass(response.data.tags[i].exclude ? 'disabled' : '');
- }
-
- if (!response.data.exactMatch) {
- $li = $(' ').appendTo($ul);
- optionLabel = `${Craft.t('app', 'Create {type}', {
- type: Craft.t('app', 'Tag'),
- })}: ${data.search}`;
- $li.attr('aria-label', optionLabel);
-
- $('')
- .appendTo($li)
- .text(data.search);
- }
-
- $ul.find('.menu-item:not(.disabled):first').addClass('hover');
-
- this.searchMenu = new Garnish.Menu($menu, {
- anchor: this.$addTagInput,
- onOptionSelect: this.selectTag.bind(this),
- });
-
- // Add required ARIA attributes
- this.$addTagInput.attr('aria-controls', this.searchMenu.menuId);
-
- this.searchMenu.on('show', () => {
- this.$addTagInput.attr('aria-expanded', 'true');
- this.focusSelectedOption();
- });
-
- this.searchMenu.on('hide', () => {
- this.$addTagInput.attr('aria-expanded', 'false');
- this.$addTagInput.removeAttr('aria-activedescendant');
- });
-
- this.addListener($menu, 'mousedown', () => {
- this._ignoreBlur = true;
- });
-
- this.searchMenu.show();
- })
- .catch(({response}) => {
- // Just in case
- if (this.searchMenu) {
- this.killSearchMenu();
- }
-
- this.$spinner.addClass('hidden');
- Craft.cp.announce(Craft.t('app', 'Loading complete'));
- });
- } else {
- // No need to update the live region here
- this.$spinner.addClass('hidden');
- }
- },
-
- focusSelectedOption: function () {
- let $option = this.searchMenu.$options.filter('.hover:first');
-
- if ($option.length) {
- this.focusOption($option);
- } else {
- this.focusFirstOption();
- }
- },
-
- focusFirstOption: function () {
- const $option = this.searchMenu.$options.first();
- this.focusOption($option);
- },
-
- selectTag: function (option) {
- const $option = $(option);
-
- if ($option.hasClass('disabled')) {
- return;
- }
-
- const $li = $(' ');
-
- if (this.settings.defaultPlacement === 'beginning') {
- $li.prependTo(this.$elementsContainer);
- } else {
- $li.appendTo(this.$elementsContainer);
- }
-
- const id = $option.data('id');
- const title = $option.text();
-
- const $element = $('
', {
- class: 'chip element small removable',
- 'data-id': id,
- 'data-site-id': this.settings.targetSiteId,
- 'data-label': title,
- 'data-editable': '1',
- }).appendTo($li);
-
- const $chipContent = $('
', {
- class: 'chip-content',
- }).appendTo($element);
-
- const $titleContainer = $(' ', {
- class: 'label',
- }).appendTo($chipContent);
-
- $(' ', {
- class: 'label-link',
- text: title,
- }).appendTo($titleContainer);
-
- $('
', {
- class: 'chip-actions',
- }).appendTo($chipContent);
-
- const $input = $(' ', {
- type: 'hidden',
- name: this.settings.name + '[]',
- value: id,
- }).appendTo($chipContent);
-
- this.$elements = this.$elements.add($element);
-
- this.addElements($element);
-
- this.killSearchMenu();
- this.$addTagInput.val('');
- this.$addTagInput.focus();
-
- if (!id) {
- // We need to create the tag first
- $element.addClass('loading disabled');
-
- var data = {
- groupId: this.settings.tagGroupId,
- title: title,
- };
-
- Craft.sendActionRequest('POST', 'tags/create-tag', {data})
- .then((response) => {
- $element.attr('data-id', response.data.id);
- $input.val(response.data.id);
-
- $element.removeClass('loading disabled');
- })
- .catch((e) => {
- this.removeElement($element);
- Craft.cp.displayError(e?.response?.data?.message);
- });
- }
- },
-
- killSearchMenu: function () {
- this.searchMenu.hide();
- this.searchMenu.destroy();
- this.searchMenu = null;
- },
- },
- {
- defaults: {
- tagGroupId: null,
- },
- }
-);
diff --git a/resources/templates/_components/fieldtypes/Categories/input.twig b/resources/templates/_components/fieldtypes/Categories/input.twig
deleted file mode 100644
index 291653c47e9..00000000000
--- a/resources/templates/_components/fieldtypes/Categories/input.twig
+++ /dev/null
@@ -1,60 +0,0 @@
-{{ hiddenInput(name, '') }}
-
-
- {% nav category in elements %}
-
- {% set indent = (category.level - 1) * 35 %}
-
- {{- elementChip(element, {
- element: category,
- context: 'field',
- inputName: (name ?? false) ? "#{name}[]" : null,
- }) -}}
-
-
- {% ifchildren %}
-
- {% endifchildren %}
-
- {% endnav %}
-
-
-
- {{ tag('button', {
- type: 'button',
- text: selectionLabel,
- class: [
- 'btn',
- 'add',
- 'icon',
- 'dashed',
- ],
- aria: {
- label: selectionLabel,
- describedby: describedBy ?? false,
- },
- }) }}
-
-
-
-
-{% if jsClass is defined %}
- {% js %}
- new {{ jsClass }}({
- id: "{{ id|namespaceInputId|e('js') }}",
- name: "{{ name|namespaceInputName|e('js') }}",
- elementType: "{{ elementType|e('js') }}",
- sources: {{ sources|json_encode|raw }},
- criteria: {{ criteria|json_encode|raw }},
- sourceElementId: {{ sourceElementId ?: 'null' }},
- prevalidate: {{ (prevalidate ?? false) ? 'true' : 'false' }},
- branchLimit: {{ branchLimit ?: 'null' }},
- showSiteMenu: {{ (showSiteMenu ?? false)|json_encode|raw }},
- modalStorageKey: "{{ storageKey|e('js') }}",
- selectionLabel: "{{ selectionLabel|e('js') }}",
- allowSelfRelations: {{ (allowSelfRelations ?? false)|json_encode|raw }},
- });
- {% endjs %}
-{% endif %}
diff --git a/resources/templates/_components/fieldtypes/Categories/settings.twig b/resources/templates/_components/fieldtypes/Categories/settings.twig
deleted file mode 100644
index 4481628ea6d..00000000000
--- a/resources/templates/_components/fieldtypes/Categories/settings.twig
+++ /dev/null
@@ -1,27 +0,0 @@
-{% extends "_components/fieldtypes/elementfieldsettings" %}
-
-{% import "_includes/forms" as forms %}
-
-
-{% block fieldSettings %}
- {{ block('sourcesField') }}
-
- {% block branchLimitField %}
- {{ forms.textField({
- label: "Branch Limit"|t('app'),
- instructions: "Limit the number of selectable category branches."|t('app'),
- id: 'branchLimit',
- name: 'branchLimit',
- value: field.branchLimit,
- size: 2,
- errors: field.getErrors('branchLimit')
- }) }}
- {% endblock %}
-
- {{ block('defaultPlacementField') }}
- {{ block('viewModeField') }}
- {{ block('selectionLabelField') }}
- {{ block('showSearchInputField') }}
- {{ block('validateRelatedElementsField') }}
- {{ block('advancedSettings') }}
-{% endblock %}
diff --git a/resources/templates/_components/fieldtypes/Tags/input.twig b/resources/templates/_components/fieldtypes/Tags/input.twig
deleted file mode 100644
index 1baf4494af6..00000000000
--- a/resources/templates/_components/fieldtypes/Tags/input.twig
+++ /dev/null
@@ -1,53 +0,0 @@
-{% if name is defined and name %}
- {{ hiddenInput(name, '') }}
-{% endif -%}
-
-{% set elements = (elements is defined ? elements : []) -%}
-{% set criteria = (criteria is defined and criteria ? criteria : null) -%}
-{% set sourceElementId = (sourceElementId is defined and sourceElementId ? sourceElementId : null) -%}
-{% set defaultPlacement = defaultPlacement ?? 'end' %}
-
-{% from "_includes/forms" import text %}
-
-
-
-{% set jsSettings = {
- id: id|namespaceInputId,
- name: name|namespaceInputName,
- tagGroupId: tagGroupId,
- sourceElementId: sourceElementId ?: null,
- targetSiteId: targetSiteId,
- allowSelfRelations: allowSelfRelations ?? false,
- defaultPlacement,
-} %}
-
-{% js %}
- new Craft.TagSelectInput({{ jsSettings|json_encode|raw }});
-{% endjs %}
diff --git a/resources/templates/categories/_index.twig b/resources/templates/categories/_index.twig
deleted file mode 100644
index 4a8b08a34d1..00000000000
--- a/resources/templates/categories/_index.twig
+++ /dev/null
@@ -1,10 +0,0 @@
-{% extends "_layouts/elementindex" %}
-{% set title = "Categories"|t('app') %}
-{% set elementType = 'craft\\elements\\Category' %}
-
-
-{% if groupHandle is defined %}
- {% js %}
- window.defaultGroupHandle = '{{ groupHandle }}';
- {% endjs %}
-{% endif %}
diff --git a/resources/templates/globals/_edit.twig b/resources/templates/globals/_edit.twig
deleted file mode 100644
index 7a372f912e0..00000000000
--- a/resources/templates/globals/_edit.twig
+++ /dev/null
@@ -1,49 +0,0 @@
-{% extends "_layouts/cp" %}
-{% set title = globalSet.name|t('site') %}
-{% set fullPageForm = true %}
-{% set retainScrollOnSaveShortcut = true %}
-
-{% if craft.sites.isMultiSite() %}
- {% set siteMenuItems = siteMenuItems(null, requestedSite) %}
- {% set crumbs = [{
- id: 'site-crumb',
- icon: 'world',
- label: requestedSite.name|t('site'),
- menu: {
- items: siteMenuItems|length > 1 ? siteMenuItems : null,
- label: 'Select site'|t('app')
- },
- }] %}
-{% endif %}
-
-{% hook "cp.globals.edit" %}
-
-
-{% block sidebar %}
-
-
-
-{% endblock %}
-
-
-{% block content %}
- {{ actionInput('globals/save-content') }}
- {{ hiddenInput('setId', globalSet.id) }}
- {{ hiddenInput('siteId', globalSet.siteId) }}
- {{ csrfInput() }}
-
- {% if globalSet.getFieldLayout().getTabs()|length %}
-
- {{ fieldsHtml|raw }}
-
- {% else %}
- {{ "This global set doesn’t have any fields assigned to it in its field layout."|t('app') }}
- {% endif %}
-
- {# Give plugins a chance to add other things here #}
- {% hook "cp.globals.edit.content" %}
-{% endblock %}
diff --git a/resources/templates/settings/categories/_edit.twig b/resources/templates/settings/categories/_edit.twig
deleted file mode 100644
index 1b3adc84aaf..00000000000
--- a/resources/templates/settings/categories/_edit.twig
+++ /dev/null
@@ -1,170 +0,0 @@
-{% extends '_layouts/cp.twig' %}
-
-{% set readOnly = readOnly ?? false %}
-{% set fullPageForm = not readOnly %}
-
-{% set formActions = [
- {
- label: 'Save and continue editing'|t('app'),
- redirect: 'settings/categories/{id}'|hash,
- shortcut: true,
- retainScroll: true,
- },
-] %}
-
-{% import '_includes/forms.twig' as forms %}
-
-{% set headlessMode = app.config.craft.general.headlessMode %}
-
-{% if readOnly %}
- {% set contentNotice = readOnlyNotice() %}
-{% endif %}
-
-{% block content %}
- {% if not readOnly %}
- {{ actionInput('categories/save-group') }}
- {{ redirectInput('settings/categories') }}
-
- {% if categoryGroup.id %}{{ hiddenInput('groupId', categoryGroup.id) }}{% endif %}
- {% endif %}
-
- {{ forms.textField({
- first: true,
- label: "Name"|t('app'),
- instructions: "What this category group will be called in the control panel."|t('app'),
- id: 'name',
- name: 'name',
- value: categoryGroup.name,
- errors: categoryGroup.getErrors('name'),
- autofocus: true,
- required: true,
- disabled: readOnly,
- }) }}
-
- {{ forms.textField({
- label: "Handle"|t('app'),
- instructions: "How you’ll refer to this category group in the templates."|t('app'),
- id: 'handle',
- name: 'handle',
- class: 'code',
- autocorrect: false,
- autocapitalize: false,
- value: categoryGroup.handle,
- errors: categoryGroup.getErrors('handle'),
- required: true,
- disabled: readOnly,
- }) }}
-
- {{ forms.textField({
- label: "Max Levels"|t('app'),
- instructions: 'The maximum number of levels this category group can have.'|t('app'),
- id: 'maxLevels',
- name: 'maxLevels',
- value: categoryGroup.maxLevels,
- size: 5,
- errors: categoryGroup.getErrors('maxLevels'),
- disabled: readOnly,
- }) }}
-
- {{ forms.selectField({
- label: 'Default {type} Placement'|t('app', {type: 'Category'|t('app')}),
- instructions: 'Where new {type} should be placed by default in the structure.'|t('app', {type: 'categories'|t('app')}),
- id: 'default-placement',
- name: 'defaultPlacement',
- options: [
- {label: 'Before other {type}'|t('app', {type: 'categories'|t('app')}), value: 'beginning'},
- {label: 'After other {type}'|t('app', {type: 'categories'|t('app')}), value: 'end'},
- ],
- value: categoryGroup.defaultPlacement,
- disabled: readOnly,
- }) }}
-
-
-
- {% set siteRows = [] %}
- {% set siteErrors = categoryGroup.getErrors('siteSettings') %}
-
- {% for site in craft.sites.getAllSites() %}
- {% set siteSettings = categoryGroup.siteSettings[site.id] ?? null %}
- {% if siteSettings %}
- {% for attribute, errors in siteSettings.getErrors() %}
- {% set siteErrors = siteErrors|merge(errors) %}
- {% endfor %}
- {% endif %}
- {% set siteRows = siteRows|merge({
- (site.handle): {
- heading: site.name|t('site')|e,
- uriFormat: {
- value: siteSettings.uriFormat ?? null,
- hasErrors: siteSettings.hasErrors('uriFormat') ?? false
- },
- template: not headlessMode ? {
- value: siteSettings.template ?? null,
- hasErrors: siteSettings.hasErrors('template') ?? false,
- }
- }
- }) %}
- {% endfor %}
-
- {{ forms.editableTableField({
- label: "Site Settings"|t('app'),
- instructions: "Configure the category group’s site-specific settings."|t('app'),
- id: 'sites',
- name: 'sites',
- cols: {
- heading: {
- type: 'heading',
- heading: "Site"|t('app'),
- class: 'thin'
- },
- uriFormat: {
- type: 'singleline',
- heading: "Category URI Format"|t('app'),
- info: "What category URIs should look like for the site."|t('app'),
- placeholder: "Leave blank if categories don’t have URLs"|t('app'),
- code: true
- },
- template: not headlessMode ? {
- type: 'template',
- heading: "Template"|t('app'),
- info: "Which template should be loaded when a category’s URL is requested."|t('app'),
- code: true
- },
- }|filter,
- rows: siteRows,
- allowAdd: false,
- allowDelete: false,
- allowReorder: false,
- errors: siteErrors|unique,
- static: readOnly,
- }) }}
-
-
-
- {{ forms.fieldLayoutDesignerField({
- fieldLayout: categoryGroup.getFieldLayout(),
- withGeneratedFields: true,
- withCardViewDesigner: true,
- disabled: readOnly,
- }) }}
-{% endblock %}
-
-
-{% if brandNewGroup %}
- {% js on ready %}
- new Craft.HandleGenerator('#name', '#handle');
-
- {% for site in craft.sites.getAllSites() %}
- new Craft.UriFormatGenerator(
- '#name',
- '#sites tr[data-id="{{ site.handle }}"] textarea[name$="[uriFormat]"]',
- { suffix: '/{slug}' }
- );
- new Craft.UriFormatGenerator(
- '#name',
- '#sites tr[data-id="{{ site.handle }}"] input[name$="[template]"]',
- { suffix: '/_category.twig' }
- );
- {% endfor %}
- {% endjs %}
-{% endif %}
diff --git a/resources/templates/settings/categories/index.twig b/resources/templates/settings/categories/index.twig
deleted file mode 100644
index 665a7b8999c..00000000000
--- a/resources/templates/settings/categories/index.twig
+++ /dev/null
@@ -1,76 +0,0 @@
-{% extends "_layouts/cp" %}
-{% set title = "Category Groups"|t('app') %}
-{% set readOnly = not app.config.craft.general.allowAdminChanges %}
-
-{% do view.registerAssetBundle('craft\\web\\assets\\admintable\\AdminTableAsset') -%}
-
-{% do view.registerTranslations('app', [
- "Name",
- "Handle",
- "Manage categories",
- "No category groups exist yet.",
-]) %}
-
-{% block actionButton %}
- {% if not readOnly %}
- {% set buttonLabel = "New category group"|t('app') %}
- {{ buttonLabel }}
- {% endif %}
-{% endblock %}
-
-{% set crumbs = [
- { label: "Settings"|t('app'), url: url('settings') }
-] %}
-
-{% if readOnly %}
- {% set contentNotice = readOnlyNotice() %}
-{% endif %}
-
-{% block content %}
-
-{% endblock %}
-
-{% set tableData = [] %}
-{% for group in categoryGroups %}
- {% set tableData = tableData|merge([{
- id: group.id,
- title: group.name|t('site'),
- url: url('settings/categories/' ~ group.id),
- name: group.name|t('site')|e,
- handle: group.handle,
- manageCategories: url('categories/'~group.handle),
- }]) %}
-{% endfor %}
-
-{% js %}
- var columns = [
- {
- name: '__slot:title',
- title: Craft.t('app', 'Name'),
- },
- {
- name: '__slot:handle',
- title: Craft.t('app', 'Handle'),
- },
- {
- name: 'manageCategories',
- title: "",
- callback: function(value) {
- return '' + Craft.escapeHtml(Craft.t('app', "Manage categories")) + ' ';
- }
- },
- ];
-
- let config = {
- columns: columns,
- container: '#categorygroups-vue-admin-table',
- emptyMessage: Craft.t('app', 'No category groups exist yet.'),
- tableData: {{ tableData|json_encode|raw }}
- };
-
- {% if not readOnly %}
- config['deleteAction'] = 'categories/delete-category-group';
- {% endif %}
-
- new Craft.VueAdminTable(config);
-{% endjs %}
diff --git a/resources/templates/settings/globals/_edit.twig b/resources/templates/settings/globals/_edit.twig
deleted file mode 100644
index da3ea1224ab..00000000000
--- a/resources/templates/settings/globals/_edit.twig
+++ /dev/null
@@ -1,72 +0,0 @@
-{% extends '_layouts/cp.twig' %}
-
-{% set readOnly = readOnly ?? false %}
-
-{% set fullPageForm = not readOnly %}
-
-{% set formActions = [
- {
- label: 'Save and continue editing'|t('app'),
- redirect: 'settings/globals/{id}'|hash,
- shortcut: true,
- retainScroll: true,
- },
-] %}
-
-{% import '_includes/forms.twig' as forms %}
-
-
-{% if readOnly %}
- {% set contentNotice = readOnlyNotice() %}
-{% endif %}
-
-{% block content %}
- {% if not readOnly %}
- {{ actionInput('globals/save-set') }}
- {{ redirectInput('settings/globals') }}
-
- {% if globalSet.id %}{{ hiddenInput('setId', globalSet.id) }}{% endif %}
- {% endif %}
-
- {{ forms.textField({
- first: true,
- label: "Name"|t('app'),
- instructions: "What this global set will be called in the control panel."|t('app'),
- id: 'name',
- name: 'name',
- value: globalSet.name,
- errors: globalSet.getErrors('name'),
- autofocus: true,
- required: true,
- disabled: readOnly,
- }) }}
-
- {{ forms.textField({
- label: "Handle"|t('app'),
- instructions: "How you’ll refer to this global set in the templates."|t('app'),
- id: 'handle',
- name: 'handle',
- class: 'code',
- autocorrect: false,
- autocapitalize: false,
- value: globalSet.handle,
- errors: globalSet.getErrors('handle'),
- required: true,
- disabled: readOnly,
- }) }}
-
-
-
- {{ forms.fieldLayoutDesignerField({
- fieldLayout: globalSet.getFieldLayout(),
- withGeneratedFields: true,
- disabled: readOnly,
- }) }}
-{% endblock %}
-
-
-{% if not globalSet.handle %}
- {% js %}
- new Craft.HandleGenerator('#name', '#handle');
- {% endjs %}
-{% endif %}
diff --git a/resources/templates/settings/globals/_index.twig b/resources/templates/settings/globals/_index.twig
deleted file mode 100644
index 1db5ba38000..00000000000
--- a/resources/templates/settings/globals/_index.twig
+++ /dev/null
@@ -1,58 +0,0 @@
-{% requireAdmin false %}
-
-{% extends "_layouts/cp" %}
-
-{% set readOnly = readOnly ?? false %}
-
-{% block actionButton %}
- {% if not readOnly %}
-
- {{ buttonLabel }}
-
- {% endif %}
-{% endblock %}
-
-{% if readOnly %}
- {% set contentNotice = readOnlyNotice() %}
-{% endif %}
-
-{% block content %}
-
-{% endblock %}
-
-{% set tableData = [] %}
-{% for globalSet in globalSets %}
- {% set tableData = tableData|merge([{
- id: globalSet.id,
- title: globalSet.name|t('site'),
- url: url('settings/globals/' ~ globalSet.id),
- handle: globalSet.handle,
- }]) %}
-{% endfor %}
-
-{% js %}
-var columns = [
- {
- name: '__slot:title',
- title: Craft.t('app', 'Global Set Name'),
- },
- {
- name: '__slot:handle',
- title: Craft.t('app', 'Handle'),
- },
-];
-
-let config = {
- columns: columns,
- container: '#sets-vue-admin-table',
- emptyMessage: Craft.t('app', 'No global sets exist yet.'),
- tableData: {{ tableData|json_encode|raw }}
-}
-
-{% if not readOnly %}
- config['deleteAction'] = 'globals/delete-set';
- config['reorderAction'] = '{{ globalSets|length > 1 ? 'globals/reorder-sets' : ''}}';
-{% endif %}
-
-new Craft.VueAdminTable(config);
-{% endjs %}
diff --git a/resources/templates/settings/tags/_edit.twig b/resources/templates/settings/tags/_edit.twig
deleted file mode 100644
index 256bd79f9a9..00000000000
--- a/resources/templates/settings/tags/_edit.twig
+++ /dev/null
@@ -1,69 +0,0 @@
-{% extends '_layouts/cp.twig' %}
-
-{% set readOnly = readOnly ?? false %}
-{% set fullPageForm = not readOnly %}
-
-{% set formActions = [
- {
- label: 'Save and continue editing'|t('app'),
- redirect: 'settings/tags/{id}'|hash,
- shortcut: true,
- retainScroll: true,
- },
-] %}
-
-{% import '_includes/forms.twig' as forms %}
-
-{% if readOnly %}
- {% set contentNotice = readOnlyNotice() %}
-{% endif %}
-
-{% block content %}
- {% if not readOnly %}
- {{ actionInput('tags/save-tag-group') }}
- {{ redirectInput('settings/tags') }}
-
- {% if tagGroup.id %}{{ hiddenInput('tagGroupId', tagGroup.id) }}{% endif %}
- {% endif %}
-
- {{ forms.textField({
- first: true,
- label: "Name"|t('app'),
- instructions: "What this tag group will be called in the control panel."|t('app'),
- id: 'name',
- name: 'name',
- value: tagGroup.name,
- errors: tagGroup.getErrors('name'),
- autofocus: true,
- required: true,
- disabled: readOnly,
- }) }}
-
- {{ forms.textField({
- label: "Handle"|t('app'),
- instructions: "How you’ll refer to this tag group in the templates."|t('app'),
- id: 'handle',
- name: 'handle',
- class: 'code',
- autocorrect: false,
- autocapitalize: false,
- value: tagGroup.handle,
- errors: tagGroup.getErrors('handle'),
- required: true,
- disabled: readOnly,
- }) }}
-
-
-
- {{ forms.fieldLayoutDesignerField({
- fieldLayout: tagGroup.getFieldLayout(),
- disabled: readOnly,
- }) }}
-{% endblock %}
-
-
-{% if not tagGroup.handle %}
- {% js %}
- new Craft.HandleGenerator('#name', '#handle');
- {% endjs %}
-{% endif %}
diff --git a/resources/templates/settings/tags/index.twig b/resources/templates/settings/tags/index.twig
deleted file mode 100644
index ad365934226..00000000000
--- a/resources/templates/settings/tags/index.twig
+++ /dev/null
@@ -1,60 +0,0 @@
-{% extends "_layouts/cp" %}
-{% set title = "Tag Groups"|t('app') %}
-{% set readOnly = not app.config.craft.general.allowAdminChanges %}
-
-{% do view.registerAssetBundle('craft\\web\\assets\\admintable\\AdminTableAsset') -%}
-
-{% do view.registerTranslations('app', [
- "Name",
- "Handle",
- "No tag groups exist yet.",
-]) %}
-
-{% block actionButton %}
- {% if not readOnly %}
- {{ "New tag group"|t('app') }}
- {% endif %}
-{% endblock %}
-
-{% set crumbs = [
- { label: "Settings"|t('app'), url: url('settings') }
-] %}
-
-{% if readOnly %}
- {% set contentNotice = readOnlyNotice() %}
-{% endif %}
-
-{% block content %}
-
-{% endblock %}
-
-{% set tableData = [] %}
-{% for tagGroup in tagGroups %}
- {% set tableData = tableData|merge([{
- id: tagGroup.id,
- title: tagGroup.name|t('site'),
- url: url('settings/tags/' ~ tagGroup.id),
- name: tagGroup.name|t('site')|e,
- handle: tagGroup.handle,
- }]) %}
-{% endfor %}
-
-{% js %}
- var columns = [
- { name: '__slot:title', title: Craft.t('app', 'Name') },
- { name: '__slot:handle', title: Craft.t('app', 'Handle') },
- ];
-
- let config = {
- columns: columns,
- container: '#taggroups-vue-admin-table',
- emptyMessage: Craft.t('app', 'No tag groups exist yet.'),
- tableData: {{ tableData|json_encode|raw }}
- };
-
- {% if not readOnly %}
- config['deleteAction'] = 'tags/delete-tag-group';
- {% endif %}
-
- new Craft.VueAdminTable(config);
-{% endjs %}
diff --git a/resources/translations/en/app.php b/resources/translations/en/app.php
index 0c7a13b1f0e..56ea6e60d33 100644
--- a/resources/translations/en/app.php
+++ b/resources/translations/en/app.php
@@ -1,5 +1,7 @@
' and ',
'"{attribute}" does not support operator "{operator}".' => '"{attribute}" does not support operator "{operator}".',
@@ -51,14 +53,12 @@
'Activity' => 'Activity',
'Add Group' => 'Add Group',
'Add Row Label' => 'Add Row Label',
- 'Add a category' => 'Add a category',
'Add a color' => 'Add a color',
'Add a column' => 'Add a column',
'Add a filter' => 'Add a filter',
'Add a passkey' => 'Add a passkey',
'Add a row' => 'Add a row',
'Add a rule' => 'Add a rule',
- 'Add a tag' => 'Add a tag',
'Add a token' => 'Add a token',
'Add a user' => 'Add a user',
'Add all to cart' => 'Add all to cart',
@@ -127,7 +127,6 @@
'Alternative Text' => 'Alternative Text',
'Amber' => 'Amber',
'An elevated session is required to change a user’s email.' => 'An elevated session is required to change a user’s email.',
- 'An error occurred when duplicating the category.' => 'An error occurred when duplicating the category.',
'An error occurred when duplicating the entry.' => 'An error occurred when duplicating the entry.',
'An error occurred when installing {name}.' => 'An error occurred when installing {name}.',
'An error occurred while processing your request.' => 'An error occurred while processing your request.',
@@ -265,13 +264,6 @@
'Card grid' => 'Card grid',
'Cards' => 'Cards',
'Cart' => 'Cart',
- 'Categories' => 'Categories',
- 'Category Group - {name}' => 'Category Group - {name}',
- 'Category Group' => 'Category Group',
- 'Category Groups' => 'Category Groups',
- 'Category URI Format' => 'Category URI Format',
- 'Category group saved.' => 'Category group saved.',
- 'Category' => 'Category',
'Center-Center' => 'Center-Center',
'Center-Left' => 'Center-Left',
'Center-Right' => 'Center-Right',
@@ -357,7 +349,6 @@
'Compressed' => 'Compressed',
'Condition for "{attribute}" should be either a value or valid operator specification.' => 'Condition for "{attribute}" should be either a value or valid operator specification.',
'Condition {num, number}' => 'Condition {num, number}',
- 'Configure the category group’s site-specific settings.' => 'Configure the category group’s site-specific settings.',
'Confirm your identity.' => 'Confirm your identity.',
'Congrats! You’re up to date.' => 'Congrats! You’re up to date.',
'Connect the database' => 'Connect the database',
@@ -447,9 +438,7 @@
'Couldn’t save route.' => 'Couldn’t save route.',
'Couldn’t save schema.' => 'Couldn’t save schema.',
'Couldn’t save section.' => 'Couldn’t save section.',
- 'Couldn’t save the category group.' => 'Couldn’t save the category group.',
'Couldn’t save the site.' => 'Couldn’t save the site.',
- 'Couldn’t save the tag group.' => 'Couldn’t save the tag group.',
'Couldn’t save token.' => 'Couldn’t save token.',
'Couldn’t save user fields.' => 'Couldn’t save user fields.',
'Couldn’t save volume.' => 'Couldn’t save volume.',
@@ -477,7 +466,6 @@
'Create a new GraphQL Schema' => 'Create a new GraphQL Schema',
'Create a new GraphQL token' => 'Create a new GraphQL token',
'Create a new asset volume' => 'Create a new asset volume',
- 'Create a new category group' => 'Create a new category group',
'Create a new child {type}' => 'Create a new child {type}',
'Create a new entry type' => 'Create a new entry type',
'Create a new field' => 'Create a new field',
@@ -487,7 +475,6 @@
'Create a new route' => 'Create a new route',
'Create a new section' => 'Create a new section',
'Create a new site' => 'Create a new site',
- 'Create a new tag group' => 'Create a new tag group',
'Create a new user group' => 'Create a new user group',
'Create a new volume…' => 'Create a new volume…',
'Create a new {section} entry' => 'Create a new {section} entry',
@@ -559,7 +546,6 @@
'Delayed' => 'Delayed',
'Delete (with descendants)' => 'Delete (with descendants)',
'Delete assets from the “{volume}” volume' => 'Delete assets from the “{volume}” volume',
- 'Delete categories from the “{categoryGroup}” category group' => 'Delete categories from the “{categoryGroup}” category group',
'Delete custom source' => 'Delete custom source',
'Delete entries in the “{section}” section' => 'Delete entries in the “{section}” section',
'Delete entries in the “{section}” {type} field' => 'Delete entries in the “{section}” {type} field',
@@ -574,7 +560,6 @@
'Delete photo' => 'Delete photo',
'Delete row {index}' => 'Delete row {index}',
'Delete selected group' => 'Delete selected group',
- 'Delete tags from the “{tagGroup}” tag group' => 'Delete tags from the “{tagGroup}” tag group',
'Delete their content' => 'Delete their content',
'Delete them' => 'Delete them',
'Delete users' => 'Delete users',
@@ -636,7 +621,6 @@
'Dropdown' => 'Dropdown',
'Duplicate (with descendants)' => 'Duplicate (with descendants)',
'Duplicate' => 'Duplicate',
- 'Edit Category Group' => 'Edit Category Group',
'Edit Entry Type' => 'Edit Entry Type',
'Edit Field' => 'Edit Field',
'Edit Filesystem' => 'Edit Filesystem',
@@ -648,11 +632,9 @@
'Edit Route' => 'Edit Route',
'Edit Section' => 'Edit Section',
'Edit Site' => 'Edit Site',
- 'Edit Tag Group' => 'Edit Tag Group',
'Edit User Group' => 'Edit User Group',
'Edit Volume' => 'Edit Volume',
'Edit assets in the “{volume}” volume' => 'Edit assets in the “{volume}” volume',
- 'Edit categories in the “{categoryGroup}” category group' => 'Edit categories in the “{categoryGroup}” category group',
'Edit draft settings' => 'Edit draft settings',
'Edit entries in the “{name}” section' => 'Edit entries in the “{name}” section',
'Edit entries in the “{name}” {type} field' => 'Edit entries in the “{name}” {type} field',
@@ -661,9 +643,7 @@
'Edit entry types' => 'Edit entry types',
'Edit images uploaded by other users' => 'Edit images uploaded by other users',
'Edit images' => 'Edit images',
- 'Edit tags in the “{tagGroup}” tag group' => 'Edit tags in the “{tagGroup}” tag group',
'Edit the public GraphQL schema' => 'Edit the public GraphQL schema',
- 'Edit the “{globalSet}” global set.' => 'Edit the “{globalSet}” global set.',
'Edit {type}' => 'Edit {type}',
'Edit “{title}”' => 'Edit “{title}”',
'Edit' => 'Edit',
@@ -799,12 +779,6 @@
'Get help' => 'Get help',
'Give feedback' => 'Give feedback',
'Give your tab a name.' => 'Give your tab a name.',
- 'Global Set Name' => 'Global Set Name',
- 'Global Sets' => 'Global Sets',
- 'Global set' => 'Global set',
- 'Global sets' => 'Global sets',
- 'Global' => 'Global',
- 'Globals' => 'Globals',
'Go to Craft CMS' => 'Go to Craft CMS',
'Go to Updates' => 'Go to Updates',
'Go' => 'Go',
@@ -847,13 +821,10 @@
'How should {name} values be translated?' => 'How should {name} values be translated?',
'How the field should be presented in the control panel.' => 'How the field should be presented in the control panel.',
'How the related {type} should be displayed within element indexes.' => 'How the related {type} should be displayed within element indexes.',
- 'How you’ll refer to this category group in the templates.' => 'How you’ll refer to this category group in the templates.',
'How you’ll refer to this entry type in the templates.' => 'How you’ll refer to this entry type in the templates.',
'How you’ll refer to this field in the templates.' => 'How you’ll refer to this field in the templates.',
- 'How you’ll refer to this global set in the templates.' => 'How you’ll refer to this global set in the templates.',
'How you’ll refer to this section in the templates.' => 'How you’ll refer to this section in the templates.',
'How you’ll refer to this site in the templates.' => 'How you’ll refer to this site in the templates.',
- 'How you’ll refer to this tag group in the templates.' => 'How you’ll refer to this tag group in the templates.',
'How-to’s and other questions' => 'How-to’s and other questions',
'ID' => 'ID',
'Icon' => 'Icon',
@@ -956,7 +927,6 @@
'Learn how' => 'Learn how',
'Learn more' => 'Learn more',
'Leave a review' => 'Leave a review',
- 'Leave blank if categories don’t have URLs' => 'Leave blank if categories don’t have URLs',
'Leave blank if entries don’t have URLs' => 'Leave blank if entries don’t have URLs',
'Leave blank if the entry doesn’t have a URL' => 'Leave blank if the entry doesn’t have a URL',
'Leave it uninstalled' => 'Leave it uninstalled',
@@ -972,7 +942,6 @@
'Licensed' => 'Licensed',
'Lightswitch' => 'Lightswitch',
'Lime' => 'Lime',
- 'Limit the number of selectable category branches.' => 'Limit the number of selectable category branches.',
'Limit the number of selectable {type} branches.' => 'Limit the number of selectable {type} branches.',
'Limit' => 'Limit',
'Line Break' => 'Line Break',
@@ -1008,7 +977,6 @@
'Make sure you’re not overwriting changes in the YAML files that were made on another environment.' => 'Make sure you’re not overwriting changes in the YAML files that were made on another environment.',
'Make sure you’ve followed the Environment Setup instructions before applying project config YAML changes.' => 'Make sure you’ve followed the Environment Setup instructions before applying project config YAML changes.',
'Make this the primary site' => 'Make this the primary site',
- 'Manage categories' => 'Manage categories',
'Manage element thumbnails' => 'Manage element thumbnails',
'Manage your Craft Console account' => 'Manage your Craft Console account',
'Manipulated SVG image rasterizing is unreliable. See \\craft\\services\\Images::loadImage()' => 'Manipulated SVG image rasterizing is unreliable. See \\craft\\services\\Images::loadImage()',
@@ -1088,9 +1056,6 @@
'New Issues' => 'New Issues',
'New Password' => 'New Password',
'New Tab' => 'New Tab',
- 'New category group' => 'New category group',
- 'New category in the {group} category group' => 'New category in the {group} category group',
- 'New category, choose a category group' => 'New category, choose a category group',
'New child' => 'New child',
'New custom source' => 'New custom source',
'New email addresses must be verified before taking effect.' => 'New email addresses must be verified before taking effect.',
@@ -1112,12 +1077,10 @@
'New section' => 'New section',
'New site' => 'New site',
'New subfolder' => 'New subfolder',
- 'New tag group' => 'New tag group',
'New token' => 'New token',
'New user group' => 'New user group',
'New volume' => 'New volume',
'New widget' => 'New widget',
- 'New {group} category' => 'New {group} category',
'New {section} entry' => 'New {section} entry',
'New {total, plural, =1{position} other{positions}} saved.' => 'New {total, plural, =1{position} other{positions}} saved.',
'New {type}' => 'New {type}',
@@ -1125,7 +1088,6 @@
'Next Page' => 'Next Page',
'Next' => 'Next',
'No GraphQL tokens exist yet.' => 'No GraphQL tokens exist yet.',
- 'No category groups exist yet.' => 'No category groups exist yet.',
'No color' => 'No color',
'No deprecation warnings to report!' => 'No deprecation warnings to report!',
'No entries exist yet.' => 'No entries exist yet.',
@@ -1138,7 +1100,6 @@
'No folder exists with the ID “{id}”' => 'No folder exists with the ID “{id}”',
'No font properties have been set. Call ImageHelper::setFontProperties() first.' => 'No font properties have been set. Call ImageHelper::setFontProperties() first.',
'No font properties have been set. Call Raster::setFontProperties() first.' => 'No font properties have been set. Call Raster::setFontProperties() first.',
- 'No global sets exist yet.' => 'No global sets exist yet.',
'No groups exist yet.' => 'No groups exist yet.',
'No image transforms exist yet.' => 'No image transforms exist yet.',
'No indexing session specified.' => 'No indexing session specified.',
@@ -1159,7 +1120,6 @@
'No sections exist yet.' => 'No sections exist yet.',
'No sites exist for this group yet.' => 'No sites exist for this group yet.',
'No sources exist yet.' => 'No sources exist yet.',
- 'No tag groups exist yet.' => 'No tag groups exist yet.',
'No template path has been chosen yet.' => 'No template path has been chosen yet.',
'No usages' => 'No usages',
'No user groups exist yet.' => 'No user groups exist yet.',
@@ -1341,16 +1301,13 @@
'Quality must be a number between 1 and 100 (included).' => 'Quality must be a number between 1 and 100 (included).',
'Quality' => 'Quality',
'Query for assets in the “{name}” volume' => 'Query for assets in the “{name}” volume',
- 'Query for categories in the “{name}” category group' => 'Query for categories in the “{name}” category group',
'Query for element drafts' => 'Query for element drafts',
'Query for element revisions' => 'Query for element revisions',
'Query for elements in the “{site}” site' => 'Query for elements in the “{site}” site',
'Query for entries in the “{name}” section' => 'Query for entries in the “{name}” section',
'Query for entries in the “{name}” {type} field' => 'Query for entries in the “{name}” {type} field',
'Query for non-enabled elements' => 'Query for non-enabled elements',
- 'Query for tags in the “{name}” tag group' => 'Query for tags in the “{name}” tag group',
'Query for the “{name}” entry' => 'Query for the “{name}” entry',
- 'Query for the “{name}” global set' => 'Query for the “{name}” global set',
'Query for users in the “{name}” user group' => 'Query for users in the “{name}” user group',
'Query for users' => 'Query for users',
'Query params (e.g. {ex1}) or a URI fragment (e.g. {ex2}) that should be appended to the URL.' => 'Query params (e.g. {ex1}) or a URI fragment (e.g. {ex2}) that should be appended to the URL.',
@@ -1479,7 +1436,6 @@
'Save as a new entry type' => 'Save as a new entry type',
'Save as a new {type}' => 'Save as a new {type}',
'Save assets uploaded by other users' => 'Save assets uploaded by other users',
- 'Save categories in the “{categoryGroup}” category group' => 'Save categories in the “{categoryGroup}” category group',
'Save entries in the “{section}” section' => 'Save entries in the “{section}” section',
'Save entries in the “{section}” {type} field' => 'Save entries in the “{section}” {type} field',
'Save entries to all sites enabled for this section' => 'Save entries to all sites enabled for this section',
@@ -1487,7 +1443,6 @@
'Save entries to other sites in the same site group' => 'Save entries to other sites in the same site group',
'Save entries to other sites with the same language' => 'Save entries to other sites with the same language',
'Save other users’ {type}' => 'Save other users’ {type}',
- 'Save tags in the “{tagGroup}” tag group' => 'Save tags in the “{tagGroup}” tag group',
'Save the loaded project config data to YAML files in your {folder} folder.' => 'Save the loaded project config data to YAML files in your {folder} folder.',
'Save the “{section}” entry' => 'Save the “{section}” entry',
'Save {type}' => 'Save {type}',
@@ -1679,11 +1634,6 @@
'Table Columns' => 'Table Columns',
'Table' => 'Table',
'Tablet' => 'Tablet',
- 'Tag Group' => 'Tag Group',
- 'Tag Groups' => 'Tag Groups',
- 'Tag group saved.' => 'Tag group saved.',
- 'Tag' => 'Tag',
- 'Tags' => 'Tags',
'Target' => 'Target',
'Teal' => 'Teal',
'Team permissions can be managed from {link}.' => 'Team permissions can be managed from {link}.',
@@ -1751,7 +1701,6 @@
'The maximum length (in bytes) the field can hold.' => 'The maximum length (in bytes) the field can hold.',
'The maximum number of authors that entries in this section can have.' => 'The maximum number of authors that entries in this section can have.',
'The maximum number of characters or bytes the field is allowed to have.' => 'The maximum number of characters or bytes the field is allowed to have.',
- 'The maximum number of levels this category group can have.' => 'The maximum number of levels this category group can have.',
'The maximum number of levels this section can have.' => 'The maximum number of levels this section can have.',
'The maximum number of rows the field is allowed to have.' => 'The maximum number of rows the field is allowed to have.',
'The maximum number of {type} that may be selected.' => 'The maximum number of {type} that may be selected.',
@@ -1826,7 +1775,6 @@
'This field has been modified.' => 'This field has been modified.',
'This field is conditional' => 'This field is conditional',
'This field is included in element cards' => 'This field is included in element cards',
- 'This field is not set to a valid category group.' => 'This field is not set to a valid category group.',
'This field is not set to a valid source.' => 'This field is not set to a valid source.',
'This field is required' => 'This field is required',
'This field is translatable.' => 'This field is translatable.',
@@ -1839,7 +1787,6 @@
'This field’s values are used as search keywords.' => 'This field’s values are used as search keywords.',
'This field’s {attributes} {totalAttributes, plural, =1{has} other{have}} been overridden.' => 'This field’s {attributes} {totalAttributes, plural, =1{has} other{have}} been overridden.',
'This filesystem has been reserved for temporary asset uploads. Please choose a different one for your volume.' => 'This filesystem has been reserved for temporary asset uploads. Please choose a different one for your volume.',
- 'This global set doesn’t have any fields assigned to it in its field layout.' => 'This global set doesn’t have any fields assigned to it in its field layout.',
'This is a new {type}.' => 'This is a new {type}.',
'This is being overridden by the {setting} config setting.' => 'This is being overridden by the {setting} config setting.',
'This license is for the {name} edition.' => 'This license is for the {name} edition.',
@@ -2070,22 +2017,18 @@
'Website' => 'Website',
'Week Start Day' => 'Week Start Day',
'What auto-generated entry titles should look like. You can include tags that output entry properties, such as {ex}.' => 'What auto-generated entry titles should look like. You can include tags that output entry properties, such as {ex}.',
- 'What category URIs should look like for the site.' => 'What category URIs should look like for the site.',
'What do you want to do with any content that is only available in {language}?' => 'What do you want to do with any content that is only available in {language}?',
'What do you want to do with their content?' => 'What do you want to do with their content?',
'What do you want to do?' => 'What do you want to do?',
'What do you want to name the group?' => 'What do you want to name the group?',
'What entry URIs should look like for the site. Leave blank if entries don’t have URLs.' => 'What entry URIs should look like for the site. Leave blank if entries don’t have URLs.',
'What the entry URI should be for the site. Leave blank if the entry doesn’t have a URL.' => 'What the entry URI should be for the site. Leave blank if the entry doesn’t have a URL.',
- 'What this category group will be called in the control panel.' => 'What this category group will be called in the control panel.',
'What this entry type will be called in the control panel.' => 'What this entry type will be called in the control panel.',
'What this field will be called in the control panel.' => 'What this field will be called in the control panel.',
- 'What this global set will be called in the control panel.' => 'What this global set will be called in the control panel.',
'What this group will be called in the control panel.' => 'What this group will be called in the control panel.',
'What this schema will be called in the control panel.' => 'What this schema will be called in the control panel.',
'What this section will be called in the control panel.' => 'What this section will be called in the control panel.',
'What this site will be called in the control panel.' => 'What this site will be called in the control panel.',
- 'What this tag group will be called in the control panel.' => 'What this tag group will be called in the control panel.',
'What this token will be called in the control panel.' => 'What this token will be called in the control panel.',
'What type of field is this?' => 'What type of field is this?',
'What type of filesystem is this?' => 'What type of filesystem is this?',
@@ -2125,7 +2068,6 @@
'Which sites should entries be saved to?' => 'Which sites should entries be saved to?',
'Which source do you want to select {type} from?' => 'Which source do you want to select {type} from?',
'Which sources do you want to select {type} from?' => 'Which sources do you want to select {type} from?',
- 'Which template should be loaded when a category’s URL is requested.' => 'Which template should be loaded when a category’s URL is requested.',
'Which template should be loaded when an entry’s URL is requested.' => 'Which template should be loaded when an entry’s URL is requested.',
'Which type of entries do you want to create?' => 'Which type of entries do you want to create?',
'Widget Title' => 'Widget Title',
@@ -2194,8 +2136,6 @@
'begins with' => 'begins with',
'by {author}' => 'by {author}',
'by {creator}' => 'by {creator}',
- 'categories' => 'categories',
- 'category' => 'category',
'contains' => 'contains',
'content block' => 'content block',
'content blocks' => 'content blocks',
@@ -2220,8 +2160,6 @@
'forgot_password_heading' => 'When someone forgets their password:',
'forgot_password_subject' => 'Reset your password',
'four' => 'four',
- 'global set' => 'global set',
- 'global sets' => 'global sets',
'has a value' => 'has a value',
'hour' => 'hour',
'hours ago' => 'hours ago',
@@ -2267,8 +2205,6 @@
'seconds' => 'seconds',
'seven' => 'seven',
'six' => 'six',
- 'tag' => 'tag',
- 'tags' => 'tags',
'test_email_body' => "Hey {{user.friendlyName|e}},\n\nCongratulations! Craft was successfully able to send an email.\n\nHere are the settings you used:\n\n{{ settings }}",
'test_email_heading' => 'When you are testing your email settings:',
'test_email_subject' => 'This is a test email from Craft',
diff --git a/src/Database/Migrations/0000_00_00_000004_migrate_field_types.php b/src/Database/Migrations/0000_00_00_000004_migrate_field_types.php
index f9488e17258..08b36240a53 100644
--- a/src/Database/Migrations/0000_00_00_000004_migrate_field_types.php
+++ b/src/Database/Migrations/0000_00_00_000004_migrate_field_types.php
@@ -13,7 +13,6 @@
\craft\fields\Addresses::class => \CraftCms\Cms\Field\Addresses::class,
\craft\fields\Assets::class => \CraftCms\Cms\Field\Assets::class,
\craft\fields\ButtonGroup::class => \CraftCms\Cms\Field\ButtonGroup::class,
- \craft\fields\Categories::class => \CraftCms\Cms\Field\Categories::class,
\craft\fields\Checkboxes::class => \CraftCms\Cms\Field\Checkboxes::class,
\craft\fields\Color::class => \CraftCms\Cms\Field\Color::class,
\craft\fields\ContentBlock::class => \CraftCms\Cms\Field\ContentBlock::class,
@@ -35,7 +34,6 @@
\craft\fields\RadioButtons::class => \CraftCms\Cms\Field\RadioButtons::class,
\craft\fields\Range::class => \CraftCms\Cms\Field\Range::class,
\craft\fields\Table::class => \CraftCms\Cms\Field\Table::class,
- \craft\fields\Tags::class => \CraftCms\Cms\Field\Tags::class,
\craft\fields\Time::class => \CraftCms\Cms\Field\Time::class,
\craft\fields\Users::class => \CraftCms\Cms\Field\Users::class,
];
diff --git a/src/Database/Migrations/0000_00_00_000005_drop_old_tables.php b/src/Database/Migrations/0000_00_00_000005_drop_old_tables.php
new file mode 100644
index 00000000000..9dabcd5f0b0
--- /dev/null
+++ b/src/Database/Migrations/0000_00_00_000005_drop_old_tables.php
@@ -0,0 +1,30 @@
+tables as $table) {
+ Schema::dropIfExists($table);
+ }
+ }
+
+ public function down(): void
+ {
+ $this->output->writeln('0000_00_00_000005_drop_old_tables migration cannot be reverted.');
+ }
+};
diff --git a/src/Database/Migrations/Install.php b/src/Database/Migrations/Install.php
index c46f7bf11ae..e4b11300300 100644
--- a/src/Database/Migrations/Install.php
+++ b/src/Database/Migrations/Install.php
@@ -12,7 +12,6 @@
use craft\elements\User;
use craft\helpers\DateTimeHelper;
use craft\mail\transportadapters\Sendmail;
-use craft\models\CategoryGroup;
use craft\web\Response;
use CraftCms\Cms\Cms;
use CraftCms\Cms\Database\Migration;
@@ -221,41 +220,6 @@ public function createTables(): void
$table->primary(['key', 'senderClass', 'eventName']);
});
- Schema::create(Table::CATEGORIES, function (Blueprint $table) {
- $table->integer('id');
- $table->integer('groupId');
- $table->integer('parentId')->nullable();
- $table->boolean('deletedWithGroup')->nullable();
- $table->dateTime('dateCreated');
- $table->dateTime('dateUpdated');
- $table->primary('id');
- });
-
- Schema::create(Table::CATEGORYGROUPS, function (Blueprint $table) {
- $table->integer('id', true);
- $table->integer('structureId');
- $table->integer('fieldLayoutId')->nullable();
- $table->string('name');
- $table->string('handle');
- $table->enum('defaultPlacement', [CategoryGroup::DEFAULT_PLACEMENT_BEGINNING, CategoryGroup::DEFAULT_PLACEMENT_END])->default('end');
- $table->dateTime('dateCreated');
- $table->dateTime('dateUpdated');
- $table->dateTime('dateDeleted')->nullable()->default(null);
- $table->char('uid', 36)->default('0');
- });
-
- Schema::create(Table::CATEGORYGROUPS_SITES, function (Blueprint $table) {
- $table->integer('id', true);
- $table->integer('groupId');
- $table->integer('siteId');
- $table->boolean('hasUrls')->default(true);
- $table->text('uriFormat')->nullable();
- $table->string('template', 500)->nullable();
- $table->dateTime('dateCreated');
- $table->dateTime('dateUpdated');
- $table->char('uid', 36)->default('0');
- });
-
Schema::create(Table::CHANGEDATTRIBUTES, function (Blueprint $table) {
$table->integer('elementId');
$table->integer('siteId');
@@ -475,17 +439,6 @@ public function createTables(): void
$table->char('uid', 36)->default('0');
});
- Schema::create(Table::GLOBALSETS, function (Blueprint $table) {
- $table->integer('id', true);
- $table->string('name');
- $table->string('handle');
- $table->integer('fieldLayoutId')->nullable();
- $table->unsignedSmallInteger('sortOrder')->nullable();
- $table->dateTime('dateCreated');
- $table->dateTime('dateUpdated');
- $table->char('uid', 36)->default('0');
- });
-
Schema::create(Table::GQLTOKENS, function (Blueprint $table) {
$table->integer('id', true);
$table->string('name');
@@ -720,25 +673,6 @@ public function createTables(): void
$table->char('uid', 36)->default('0');
});
- Schema::create(Table::TAGGROUPS, function (Blueprint $table) {
- $table->integer('id', true);
- $table->string('name');
- $table->string('handle');
- $table->integer('fieldLayoutId')->nullable();
- $table->dateTime('dateCreated');
- $table->dateTime('dateUpdated');
- $table->dateTime('dateDeleted')->nullable()->default(null);
- $table->char('uid', 36)->default('0');
- });
-
- Schema::create(Table::TAGS, function (Blueprint $table) {
- $table->integer('id');
- $table->integer('groupId');
- $table->boolean('deletedWithGroup')->nullable();
- $table->dateTime('dateCreated');
- $table->dateTime('dateUpdated');
- });
-
Schema::create(Table::TOKENS, function (Blueprint $table) {
$table->integer('id', true);
$table->char('token', 32);
@@ -902,14 +836,6 @@ public function createIndexes(): void
Schema::createIndex(Table::ASSETS, ['folderId']);
Schema::createIndex(Table::ASSETS, ['volumeId']);
Schema::createIndex(Table::BULKOPEVENTS, ['timestamp']);
- Schema::createIndex(Table::CATEGORIES, ['groupId']);
- Schema::createIndex(Table::CATEGORYGROUPS, ['name']);
- Schema::createIndex(Table::CATEGORYGROUPS, ['handle']);
- Schema::createIndex(Table::CATEGORYGROUPS, ['structureId']);
- Schema::createIndex(Table::CATEGORYGROUPS, ['fieldLayoutId']);
- Schema::createIndex(Table::CATEGORYGROUPS, ['dateDeleted']);
- Schema::createIndex(Table::CATEGORYGROUPS_SITES, ['groupId', 'siteId'], unique: true);
- Schema::createIndex(Table::CATEGORYGROUPS_SITES, ['siteId']);
Schema::createIndex(Table::CHANGEDATTRIBUTES, ['elementId', 'siteId', 'dateUpdated']);
Schema::createIndex(Table::CHANGEDFIELDS, ['elementId', 'siteId', 'dateUpdated']);
Schema::createIndex(Table::CONTENTBLOCKS, ['primaryOwnerId']);
@@ -951,10 +877,6 @@ public function createIndexes(): void
Schema::createIndex(Table::FIELDS, ['handle', 'context']);
Schema::createIndex(Table::FIELDS, ['context']);
Schema::createIndex(Table::FIELDS, ['dateDeleted']);
- Schema::createIndex(Table::GLOBALSETS, ['name']);
- Schema::createIndex(Table::GLOBALSETS, ['handle']);
- Schema::createIndex(Table::GLOBALSETS, ['fieldLayoutId']);
- Schema::createIndex(Table::GLOBALSETS, ['sortOrder']);
Schema::createIndex(Table::GQLTOKENS, ['accessToken'], unique: true);
Schema::createIndex(Table::GQLTOKENS, ['name'], unique: true);
Schema::createIndex(Table::IMAGETRANSFORMINDEX, ['assetId', 'transformString']);
@@ -992,10 +914,6 @@ public function createIndexes(): void
Schema::createIndex(Table::STRUCTUREELEMENTS, ['level']);
Schema::createIndex(Table::STRUCTUREELEMENTS, ['elementId']);
Schema::createIndex(Table::STRUCTURES, ['dateDeleted']);
- Schema::createIndex(Table::TAGGROUPS, ['name']);
- Schema::createIndex(Table::TAGGROUPS, ['handle']);
- Schema::createIndex(Table::TAGGROUPS, ['dateDeleted']);
- Schema::createIndex(Table::TAGS, ['groupId']);
Schema::createIndex(Table::TOKENS, ['token'], unique: true);
Schema::createIndex(Table::TOKENS, ['expiryDate']);
Schema::createIndex(Table::USERGROUPS, ['handle']);
@@ -1069,13 +987,6 @@ public function addForeignKeys(): void
Schema::table(Table::ASSETS_SITES, fn (Blueprint $table) => $table->foreign('assetId')->references('id')->on(Table::ASSETS)->cascadeOnDelete());
Schema::table(Table::ASSETS_SITES, fn (Blueprint $table) => $table->foreign('siteId')->references('id')->on(Table::SITES)->cascadeOnDelete()->cascadeOnUpdate());
Schema::table(Table::AUTHENTICATOR, fn (Blueprint $table) => $table->foreign('userId')->references('id')->on(Table::USERS)->cascadeOnDelete());
- Schema::table(Table::CATEGORIES, fn (Blueprint $table) => $table->foreign('groupId')->references('id')->on(Table::CATEGORYGROUPS)->cascadeOnDelete());
- Schema::table(Table::CATEGORIES, fn (Blueprint $table) => $table->foreign('id')->references('id')->on(Table::ELEMENTS)->cascadeOnDelete());
- Schema::table(Table::CATEGORIES, fn (Blueprint $table) => $table->foreign('parentId')->references('id')->on(Table::CATEGORIES)->nullOnDelete());
- Schema::table(Table::CATEGORYGROUPS, fn (Blueprint $table) => $table->foreign('fieldLayoutId')->references('id')->on(Table::FIELDLAYOUTS)->nullOnDelete());
- Schema::table(Table::CATEGORYGROUPS, fn (Blueprint $table) => $table->foreign('structureId')->references('id')->on(Table::STRUCTURES)->cascadeOnDelete());
- Schema::table(Table::CATEGORYGROUPS_SITES, fn (Blueprint $table) => $table->foreign('groupId')->references('id')->on(Table::CATEGORYGROUPS)->cascadeOnDelete());
- Schema::table(Table::CATEGORYGROUPS_SITES, fn (Blueprint $table) => $table->foreign('siteId')->references('id')->on(Table::SITES)->cascadeOnDelete()->cascadeOnUpdate());
Schema::table(Table::CHANGEDATTRIBUTES, fn (Blueprint $table) => $table->foreign('elementId')->references('id')->on(Table::ELEMENTS)->cascadeOnDelete()->cascadeOnUpdate());
Schema::table(Table::CHANGEDATTRIBUTES, fn (Blueprint $table) => $table->foreign('siteId')->references('id')->on(Table::SITES)->cascadeOnDelete()->cascadeOnUpdate());
Schema::table(Table::CHANGEDATTRIBUTES, fn (Blueprint $table) => $table->foreign('userId')->references('id')->on(Table::USERS)->nullOnDelete()->cascadeOnUpdate());
@@ -1109,8 +1020,6 @@ public function addForeignKeys(): void
Schema::table(Table::ENTRIES, fn (Blueprint $table) => $table->foreign('fieldId')->references('id')->on(Table::FIELDS)->cascadeOnDelete());
Schema::table(Table::ENTRIES, fn (Blueprint $table) => $table->foreign('primaryOwnerId')->references('id')->on(Table::ELEMENTS)->cascadeOnDelete());
Schema::table(Table::ENTRYTYPES, fn (Blueprint $table) => $table->foreign('fieldLayoutId')->references('id')->on(Table::FIELDLAYOUTS)->nullOnDelete());
- Schema::table(Table::GLOBALSETS, fn (Blueprint $table) => $table->foreign('fieldLayoutId')->references('id')->on(Table::FIELDLAYOUTS)->nullOnDelete());
- Schema::table(Table::GLOBALSETS, fn (Blueprint $table) => $table->foreign('id')->references('id')->on(Table::ELEMENTS)->cascadeOnDelete());
Schema::table(Table::GQLTOKENS, fn (Blueprint $table) => $table->foreign('schemaId')->references('id')->on(Table::GQLSCHEMAS)->nullOnDelete());
Schema::table(Table::RELATIONS, fn (Blueprint $table) => $table->foreign('fieldId')->references('id')->on(Table::FIELDS)->cascadeOnDelete());
Schema::table(Table::RELATIONS, fn (Blueprint $table) => $table->foreign('sourceId')->references('id')->on(Table::ELEMENTS)->cascadeOnDelete());
@@ -1129,9 +1038,6 @@ public function addForeignKeys(): void
Schema::table(Table::SITES, fn (Blueprint $table) => $table->foreign('groupId')->references('id')->on(Table::SITEGROUPS)->cascadeOnDelete());
Schema::table(Table::SSO_IDENTITIES, fn (Blueprint $table) => $table->foreign('userId')->references('id')->on(Table::USERS)->cascadeOnDelete());
Schema::table(Table::STRUCTUREELEMENTS, fn (Blueprint $table) => $table->foreign('structureId')->references('id')->on(Table::STRUCTURES)->cascadeOnDelete());
- Schema::table(Table::TAGGROUPS, fn (Blueprint $table) => $table->foreign('fieldLayoutId')->references('id')->on(Table::FIELDLAYOUTS)->nullOnDelete());
- Schema::table(Table::TAGS, fn (Blueprint $table) => $table->foreign('groupId')->references('id')->on(Table::TAGGROUPS)->cascadeOnDelete());
- Schema::table(Table::TAGS, fn (Blueprint $table) => $table->foreign('id')->references('id')->on(Table::ELEMENTS)->cascadeOnDelete());
Schema::table(Table::USERGROUPS_USERS, fn (Blueprint $table) => $table->foreign('groupId')->references('id')->on(Table::USERGROUPS)->cascadeOnDelete());
Schema::table(Table::USERGROUPS_USERS, fn (Blueprint $table) => $table->foreign('userId')->references('id')->on(Table::USERS)->cascadeOnDelete());
Schema::table(Table::USERPERMISSIONS_USERGROUPS, fn (Blueprint $table) => $table->foreign('groupId')->references('id')->on(Table::USERGROUPS)->cascadeOnDelete());
diff --git a/src/Database/Table.php b/src/Database/Table.php
index 7c1af4f49e4..4db27fee9c9 100644
--- a/src/Database/Table.php
+++ b/src/Database/Table.php
@@ -27,12 +27,6 @@
public const string CACHE = 'cache';
- public const string CATEGORIES = 'categories';
-
- public const string CATEGORYGROUPS = 'categorygroups';
-
- public const string CATEGORYGROUPS_SITES = 'categorygroups_sites';
-
public const string CHANGEDATTRIBUTES = 'changedattributes';
public const string CHANGEDFIELDS = 'changedfields';
@@ -63,8 +57,6 @@
public const string FIELDS = 'fields';
- public const string GLOBALSETS = 'globalsets';
-
public const string GQLSCHEMAS = 'gqlschemas';
public const string GQLTOKENS = 'gqltokens';
@@ -123,10 +115,6 @@
public const string SYSTEMMESSAGES = 'systemmessages';
- public const string TAGGROUPS = 'taggroups';
-
- public const string TAGS = 'tags';
-
public const string TOKENS = 'tokens';
public const string USERGROUPS = 'usergroups';
diff --git a/src/Field/Categories.php b/src/Field/Categories.php
deleted file mode 100644
index 0b8564db16f..00000000000
--- a/src/Field/Categories.php
+++ /dev/null
@@ -1,199 +0,0 @@
-', CategoryQuery::class, ElementCollection::class, Category::class);
- }
-
- /**
- * {@inheritdoc}
- */
- public bool $allowMultipleSources = false;
-
- /**
- * {@inheritdoc}
- */
- public function __construct(array $config = [])
- {
- // allow categories to limit selection if `maintainHierarchy` isn't checked
- $config['allowLimit'] = true;
-
- // Default maintainHierarchy to true for existing Assets fields
- if (isset($config['id']) && ! isset($config['maintainHierarchy'])) {
- $config['maintainHierarchy'] = true;
- }
-
- parent::__construct($config);
- }
-
- /**
- * {@inheritdoc}
- */
- #[\Override]
- public function normalizeValue(mixed $value, ?ElementInterface $element): mixed
- {
- if (is_array($value) && $this->maintainHierarchy) {
- /** @var Category[] $categories */
- $categories = Category::find()
- ->siteId($this->targetSiteId($element))
- ->id(array_values(array_filter($value)))
- ->status(null)
- ->all();
-
- // Fill in any gaps
- Structures::fillGapsInElements($categories);
-
- // Enforce the branch limit
- if ($this->branchLimit) {
- Structures::applyBranchLimitToElements($categories, $this->branchLimit);
- }
-
- $value = array_map(fn (Category $category) => $category->id, $categories);
- }
-
- return parent::normalizeValue($value, $element);
- }
-
- /**
- * {@inheritdoc}
- */
- #[\Override]
- protected function inputHtml(mixed $value, ?ElementInterface $element, bool $inline): string
- {
- // Make sure the field is set to a valid category group
- if ($this->source) {
- $source = ElementHelper::findSource(self::elementType(), $this->source, ElementSources::CONTEXT_FIELD);
- }
-
- if (empty($source)) {
- return ''.t('This field is not set to a valid category group.').'
';
- }
-
- return parent::inputHtml($value, $element, $inline);
- }
-
- /**
- * {@inheritdoc}
- */
- #[\Override]
- public function includeInGqlSchema(GqlSchema $schema): bool
- {
- return Gql::canQueryCategories($schema);
- }
-
- /**
- * {@inheritdoc}
- */
- #[\Override]
- public function getContentGqlType(): array
- {
- return [
- 'name' => $this->handle,
- 'type' => Type::nonNull(Type::listOf(CategoryInterface::getType())),
- 'args' => CategoryArguments::getArguments(),
- 'resolve' => CategoryResolver::class.'::resolve',
- 'complexity' => GqlHelper::relatedArgumentComplexity(GqlService::GRAPHQL_COMPLEXITY_EAGER_LOAD),
- ];
- }
-
- /**
- * {@inheritdoc}
- */
- #[\Override]
- public function getEagerLoadingGqlConditions(): ?array
- {
- $allowedEntities = Gql::extractAllowedEntitiesFromSchema();
- $categoryGroupUids = $allowedEntities['categorygroups'] ?? [];
-
- if (empty($categoryGroupUids)) {
- return null;
- }
-
- $categoriesService = Craft::$app->getCategories();
- $groupIds = array_filter(array_map(function (string $uid) use ($categoriesService) {
- $group = $categoriesService->getGroupByUid($uid);
-
- return $group->id ?? null;
- }, $categoryGroupUids));
-
- return [
- 'groupId' => $groupIds,
- ];
- }
-}
diff --git a/src/Field/Field.php b/src/Field/Field.php
index 624b17bc064..01dacabfde7 100644
--- a/src/Field/Field.php
+++ b/src/Field/Field.php
@@ -291,9 +291,7 @@ abstract class Field implements \Stringable, Actionable, Arrayable, FieldInterfa
'propagating',
'ref',
'relatedToAssets',
- 'relatedToCategories',
'relatedToEntries',
- 'relatedToTags',
'relatedToUsers',
'resaving',
'revisionId',
diff --git a/src/Field/Fields.php b/src/Field/Fields.php
index 17a0dfce64e..80acd36558d 100644
--- a/src/Field/Fields.php
+++ b/src/Field/Fields.php
@@ -22,7 +22,6 @@
use CraftCms\Cms\Database\Table;
use CraftCms\Cms\Field\Addresses as AddressesField;
use CraftCms\Cms\Field\Assets as AssetsField;
-use CraftCms\Cms\Field\Categories as CategoriesField;
use CraftCms\Cms\Field\Contracts\ElementContainerFieldInterface;
use CraftCms\Cms\Field\Contracts\FieldInterface;
use CraftCms\Cms\Field\Entries as EntriesField;
@@ -41,7 +40,6 @@
use CraftCms\Cms\Field\Events\RegisterNestedEntryFieldTypes;
use CraftCms\Cms\Field\Matrix as MatrixField;
use CraftCms\Cms\Field\Table as TableField;
-use CraftCms\Cms\Field\Tags as TagsField;
use CraftCms\Cms\Field\Users as UsersField;
use CraftCms\Cms\ProjectConfig\Events\ConfigEvent;
use CraftCms\Cms\ProjectConfig\ProjectConfig;
@@ -114,7 +112,6 @@ public function getAllFieldTypes(): Collection
AddressesField::class,
AssetsField::class,
ButtonGroup::class,
- CategoriesField::class,
Checkboxes::class,
Color::class,
ContentBlock::class,
@@ -135,7 +132,6 @@ public function getAllFieldTypes(): Collection
RadioButtons::class,
Range::class,
TableField::class,
- TagsField::class,
Time::class,
UsersField::class,
]);
diff --git a/src/Field/Link.php b/src/Field/Link.php
index e4fcdfd20c9..8fd349090fa 100644
--- a/src/Field/Link.php
+++ b/src/Field/Link.php
@@ -26,7 +26,6 @@
use CraftCms\Cms\Field\LinkTypes\Asset;
use CraftCms\Cms\Field\LinkTypes\BaseLinkType;
use CraftCms\Cms\Field\LinkTypes\BaseTextLinkType;
-use CraftCms\Cms\Field\LinkTypes\Category;
use CraftCms\Cms\Field\LinkTypes\Email as EmailType;
use CraftCms\Cms\Field\LinkTypes\Entry;
use CraftCms\Cms\Field\LinkTypes\Phone;
@@ -112,7 +111,6 @@ private static function types(): array
/** @var class-string[] $types */
$types = [
Asset::class,
- Category::class,
EmailType::class,
Entry::class,
Phone::class,
diff --git a/src/Field/LinkTypes/Category.php b/src/Field/LinkTypes/Category.php
deleted file mode 100644
index d32a39ee69a..00000000000
--- a/src/Field/LinkTypes/Category.php
+++ /dev/null
@@ -1,46 +0,0 @@
-getCategories()->getAllGroups();
- $sites = Sites::getAllSites();
-
- foreach ($groups as $group) {
- $siteSettings = $group->getSiteSettings();
- foreach ($sites as $site) {
- if (isset($siteSettings[$site->id]) && $siteSettings[$site->id]->hasUrls) {
- $sources[] = "group:$group->uid";
- break;
- }
- }
- }
-
- $sources = array_values(array_unique($sources));
-
- if (! empty($sources)) {
- array_unshift($sources, '*');
- }
-
- return $sources;
- }
-}
diff --git a/src/Field/Tags.php b/src/Field/Tags.php
deleted file mode 100644
index ae2d3e45335..00000000000
--- a/src/Field/Tags.php
+++ /dev/null
@@ -1,234 +0,0 @@
-', TagQuery::class, ElementCollection::class, Tag::class);
- }
-
- /**
- * {@inheritdoc}
- */
- public bool $allowMultipleSources = false;
-
- /**
- * {@inheritdoc}
- */
- public bool $allowLimit = false;
-
- /**
- * @see _getTagGroupUid()
- */
- private string|false $_tagGroupUid;
-
- /**
- * {@inheritdoc}
- */
- #[\Override]
- public function getSettingsHtml(): string
- {
- $html = parent::getSettingsHtml();
-
- // Remove the “Show the search input” field
- $crawler = new Crawler("$html");
- /** @var DOMElement $node */
- $node = $crawler->filter('#show-search-input-field')->getNode(0);
- $node->remove();
-
- return $crawler->filter('body')->first()->html();
- }
-
- /**
- * {@inheritdoc}
- */
- #[\Override]
- protected function inputHtml(mixed $value, ?ElementInterface $element, bool $inline): string
- {
- if ($element !== null && $element->hasEagerLoadedElements($this->handle)) {
- $value = $element->getEagerLoadedElements($this->handle)->all();
- }
-
- if ($value instanceof ElementQueryInterface) {
- $value = $value
- ->status(null)
- ->all();
- } elseif (! is_array($value)) {
- $value = [];
- }
-
- $tagGroup = $this->_getTagGroup();
-
- if ($tagGroup) {
- return Craft::$app->getView()->renderTemplate('_components/fieldtypes/Tags/input.twig',
- [
- 'elementType' => self::elementType(),
- 'id' => $this->getInputId(),
- 'describedBy' => $this->describedBy,
- 'labelId' => $this->getLabelId(),
- 'name' => $this->handle,
- 'elements' => $value,
- 'tagGroupId' => $tagGroup->id,
- 'targetSiteId' => $this->targetSiteId($element),
- 'sourceElementId' => $element?->id,
- 'selectionLabel' => $this->selectionLabel ? t($this->selectionLabel, category: 'site') : self::defaultSelectionLabel(),
- 'allowSelfRelations' => (bool) $this->allowSelfRelations,
- 'defaultPlacement' => $this->defaultPlacement,
- ]);
- }
-
- return ''.t('This field is not set to a valid source.').'
';
- }
-
- /**
- * {@inheritdoc}
- */
- #[\Override]
- protected function supportedViewModes(): array
- {
- return [
- 'list' => t('List'),
- ];
- }
-
- /**
- * {@inheritdoc}
- */
- #[\Override]
- public function includeInGqlSchema(GqlSchema $schema): bool
- {
- return Gql::canQueryTags($schema);
- }
-
- /**
- * {@inheritdoc}
- */
- #[\Override]
- public function getContentGqlType(): array
- {
- return [
- 'name' => $this->handle,
- 'type' => Type::nonNull(Type::listOf(TagInterface::getType())),
- 'args' => TagArguments::getArguments(),
- 'resolve' => TagResolver::class.'::resolve',
- 'complexity' => GqlHelper::relatedArgumentComplexity(GqlService::GRAPHQL_COMPLEXITY_EAGER_LOAD),
- ];
- }
-
- /**
- * {@inheritdoc}
- */
- #[\Override]
- public function getEagerLoadingGqlConditions(): ?array
- {
- $allowedEntities = Gql::extractAllowedEntitiesFromSchema();
- $tagGroupUids = $allowedEntities['taggroups'] ?? [];
-
- if (empty($tagGroupUids)) {
- return null;
- }
-
- $tagsService = Craft::$app->getTags();
- $tagGroupIds = array_filter(array_map(function (string $uid) use ($tagsService) {
- $tagGroup = $tagsService->getTagGroupByUid($uid);
-
- return $tagGroup->id ?? null;
- }, $tagGroupUids));
-
- return [
- 'groupId' => $tagGroupIds,
- ];
- }
-
- /**
- * Returns the tag group associated with this field.
- */
- private function _getTagGroup(): ?TagGroup
- {
- $groupUid = $this->_getTagGroupUid();
-
- return $groupUid ? Craft::$app->getTags()->getTagGroupByUid($groupUid) : null;
- }
-
- /**
- * Returns the tag group ID this field is associated with.
- */
- private function _getTagGroupUid(): ?string
- {
- if (! isset($this->_tagGroupUid)) {
- if (preg_match('/^taggroup:([0-9a-f\-]+)$/', (string) $this->source, $matches)) {
- $this->_tagGroupUid = $matches[1];
- } else {
- $this->_tagGroupUid = false;
- }
- }
-
- return $this->_tagGroupUid ?: null;
- }
-}
diff --git a/src/GarbageCollection/Actions/DeleteUnsupportedSiteEntries.php b/src/GarbageCollection/Actions/DeleteUnsupportedSiteEntries.php
index 66102714a08..a62e9604571 100644
--- a/src/GarbageCollection/Actions/DeleteUnsupportedSiteEntries.php
+++ b/src/GarbageCollection/Actions/DeleteUnsupportedSiteEntries.php
@@ -4,7 +4,6 @@
namespace CraftCms\Cms\GarbageCollection\Actions;
-use craft\elements\Category;
use CraftCms\Cms\Config\GeneralConfig;
use CraftCms\Cms\Database\Table;
use CraftCms\Cms\GarbageCollection\GarbageCollection;
diff --git a/src/GarbageCollection/GarbageCollection.php b/src/GarbageCollection/GarbageCollection.php
index 01a0626fe36..185a3b681b9 100644
--- a/src/GarbageCollection/GarbageCollection.php
+++ b/src/GarbageCollection/GarbageCollection.php
@@ -6,11 +6,8 @@
use craft\elements\Address;
use craft\elements\Asset;
-use craft\elements\Category;
use craft\elements\ContentBlock;
use craft\elements\Entry;
-use craft\elements\GlobalSet;
-use craft\elements\Tag;
use craft\elements\User;
use CraftCms\Cms\Database\Table;
use CraftCms\Cms\GarbageCollection\Actions\DeleteOrphanedDraftsAndRevisions;
@@ -89,26 +86,18 @@ public function run(bool $force = false): void
[HardDelete::class, [
'tables' => [
- Table::CATEGORYGROUPS,
Table::ENTRYTYPES,
Table::FIELDS,
Table::SECTIONS,
- Table::TAGGROUPS,
],
]],
[DeletePartialElements::class, ['elementType' => Address::class, 'table' => Table::ADDRESSES]],
[DeletePartialElements::class, ['elementType' => Asset::class, 'table' => Table::ASSETS]],
- [DeletePartialElements::class, ['elementType' => Category::class, 'table' => Table::CATEGORIES]],
[DeletePartialElements::class, ['elementType' => ContentBlock::class, 'table' => Table::CONTENTBLOCKS]],
[DeletePartialElements::class, ['elementType' => Entry::class, 'table' => Table::ENTRIES]],
- [DeletePartialElements::class, ['elementType' => GlobalSet::class, 'table' => Table::GLOBALSETS]],
- [DeletePartialElements::class, ['elementType' => Tag::class, 'table' => Table::TAGS]],
[DeletePartialElements::class, ['elementType' => User::class, 'table' => Table::USERS]],
[DeleteOrphanedFieldLayouts::class, ['elementType' => Asset::class, 'table' => Table::VOLUMES]],
- [DeleteOrphanedFieldLayouts::class, ['elementType' => Category::class, 'table' => Table::CATEGORYGROUPS]],
[DeleteOrphanedFieldLayouts::class, ['elementType' => Entry::class, 'table' => Table::ENTRYTYPES]],
- [DeleteOrphanedFieldLayouts::class, ['elementType' => GlobalSet::class, 'table' => Table::GLOBALSETS]],
- [DeleteOrphanedFieldLayouts::class, ['elementType' => Tag::class, 'table' => Table::TAGGROUPS]],
DeleteUnsupportedSiteEntries::class,
[DeleteOrphanedNestedElements::class, ['elementType' => Address::class, 'table' => Table::ADDRESSES]],
[DeleteOrphanedNestedElements::class, ['elementType' => ContentBlock::class, 'table' => Table::CONTENTBLOCKS]],
diff --git a/src/Http/Controllers/FieldsController.php b/src/Http/Controllers/FieldsController.php
index 4458a211900..6cb8cc24d29 100644
--- a/src/Http/Controllers/FieldsController.php
+++ b/src/Http/Controllers/FieldsController.php
@@ -9,7 +9,6 @@
use craft\base\FieldLayoutComponent;
use craft\base\FieldLayoutElement;
use craft\base\FieldLayoutProviderInterface;
-use craft\elements\GlobalSet;
use craft\fieldlayoutelements\CustomField;
use craft\helpers\Component;
use craft\helpers\Cp;
@@ -261,12 +260,7 @@ public function edit(Request $request, ?FieldInterface $field = null, ?int $fiel
/** @var FieldLayoutProviderInterface&Chippable $provider */
$provider = $layout->provider;
$label = $labels[] = $provider->getUiLabel();
- // special case for global sets, where we should link to the settings rather than the edit page
- if ($provider instanceof GlobalSet) {
- $url = "settings/globals/$provider->id";
- } else {
- $url = $provider instanceof CpEditable ? $provider->getCpEditUrl() : null;
- }
+ $url = $provider instanceof CpEditable ? $provider->getCpEditUrl() : null;
$icon = $provider instanceof Iconic ? $provider->getIcon() : null;
$labelHtml = Html::beginTag('span', [
diff --git a/src/ProjectConfig/ProjectConfig.php b/src/ProjectConfig/ProjectConfig.php
index 81bb6ef6a3b..30fb9820bfc 100644
--- a/src/ProjectConfig/ProjectConfig.php
+++ b/src/ProjectConfig/ProjectConfig.php
@@ -7,14 +7,11 @@
use Craft;
use craft\base\FsInterface;
use craft\elements\Address;
-use craft\elements\GlobalSet;
use craft\elements\User;
use craft\helpers\App;
use craft\helpers\DateTimeHelper;
use craft\helpers\FileHelper;
-use craft\models\CategoryGroup;
use craft\models\ImageTransform;
-use craft\models\TagGroup;
use craft\models\Volume;
use craft\services\ElementSources;
use CraftCms\Cms\Cms;
@@ -118,8 +115,6 @@ final class ProjectConfig
public const string PATH_ADDRESS_FIELD_LAYOUTS = self::PATH_ADDRESSES.'.'.'fieldLayouts';
- public const string PATH_CATEGORY_GROUPS = 'categoryGroups';
-
public const string PATH_DATE_MODIFIED = 'dateModified';
public const string PATH_ELEMENT_SOURCES = 'elementSources';
@@ -130,8 +125,6 @@ final class ProjectConfig
public const string PATH_FIELDS = 'fields';
- public const string PATH_GLOBAL_SETS = 'globalSets';
-
public const string PATH_FS = 'fs';
public const string PATH_GRAPHQL = 'graphql';
@@ -160,8 +153,6 @@ final class ProjectConfig
public const string PATH_SYSTEM = 'system';
- public const string PATH_TAG_GROUPS = 'tagGroups';
-
public const string PATH_USERS = 'users';
public const string PATH_USER_FIELD_LAYOUTS = self::PATH_USERS.'.'.'fieldLayouts';
@@ -1128,13 +1119,11 @@ public function rebuild(): void
unset($config[self::PATH_META]);
$config[self::PATH_ADDRESSES] = $this->_getAddressesData();
- $config[self::PATH_CATEGORY_GROUPS] = $this->_getCategoryGroupData();
$config[self::PATH_DATE_MODIFIED] = DateTimeHelper::currentTimeStamp();
$config[self::PATH_ELEMENT_SOURCES] = $this->_getElementSourceData($config[self::PATH_ELEMENT_SOURCES] ?? []);
$config[self::PATH_ENTRY_TYPES] = $this->_getEntryTypeData();
$config[self::PATH_FIELDS] = $this->_getFieldData();
$config[self::PATH_FS] = $this->_getFsData();
- $config[self::PATH_GLOBAL_SETS] = $this->_getGlobalSetData();
$config[self::PATH_GRAPHQL] = $this->_getGqlData();
$config[self::PATH_IMAGE_TRANSFORMS] = $this->_getTransformData();
$config[self::PATH_PLUGINS] = $this->_getPluginData($config[self::PATH_PLUGINS] ?? []);
@@ -1142,7 +1131,6 @@ public function rebuild(): void
$config[self::PATH_SITES] = $this->_getSiteData();
$config[self::PATH_SITE_GROUPS] = $this->_getSiteGroupData();
$config[self::PATH_SYSTEM] = $this->_systemConfig($config[self::PATH_SYSTEM] ?? []);
- $config[self::PATH_TAG_GROUPS] = $this->_getTagGroupData();
$config[self::PATH_USERS] = $this->_getUserData($config[self::PATH_USERS] ?? []);
$config[self::PATH_VOLUMES] = $this->_getVolumeData();
@@ -1882,36 +1870,6 @@ private function _getAddressesData(): array
return $data;
}
- /**
- * Return category group data config array.
- */
- private function _getCategoryGroupData(): array
- {
- return collect(Craft::$app->getCategories()->getAllGroups())
- ->mapWithKeys(fn (CategoryGroup $group) => [$group->uid => $group->getConfig()])
- ->all();
- }
-
- /**
- * Return tag group data config array.
- */
- private function _getTagGroupData(): array
- {
- return collect(Craft::$app->getTags()->getAllTagGroups())
- ->mapWithKeys(fn (TagGroup $group) => [$group->uid => $group->getConfig()])
- ->all();
- }
-
- /**
- * Return global set data config array.
- */
- private function _getGlobalSetData(): array
- {
- return collect(Craft::$app->getGlobals()->getAllSets())
- ->mapWithKeys(fn (GlobalSet $globalSet) => [$globalSet->uid => $globalSet->getConfig()])
- ->all();
- }
-
/**
* Return plugin data config array
*/
diff --git a/src/ProjectConfig/ProjectConfigServiceProvider.php b/src/ProjectConfig/ProjectConfigServiceProvider.php
index e44bae288b9..34a3b348703 100644
--- a/src/ProjectConfig/ProjectConfigServiceProvider.php
+++ b/src/ProjectConfig/ProjectConfigServiceProvider.php
@@ -79,14 +79,6 @@ public function boot(ProjectConfig $projectConfig): void
->onAdd(ProjectConfig::PATH_SITES.'.{uid}', fn (ConfigEvent $event) => Sites::handleChangedSite($event))
->onUpdate(ProjectConfig::PATH_SITES.'.{uid}', fn (ConfigEvent $event) => Sites::handleChangedSite($event))
->onRemove(ProjectConfig::PATH_SITES.'.{uid}', fn (ConfigEvent $event) => Sites::handleDeletedSite($event))
- // Tags
- ->onAdd(ProjectConfig::PATH_TAG_GROUPS.'.{uid}', $this->proxy('tags', 'handleChangedTagGroup'))
- ->onUpdate(ProjectConfig::PATH_TAG_GROUPS.'.{uid}', $this->proxy('tags', 'handleChangedTagGroup'))
- ->onRemove(ProjectConfig::PATH_TAG_GROUPS.'.{uid}', $this->proxy('tags', 'handleDeletedTagGroup'))
- // Categories
- ->onAdd(ProjectConfig::PATH_CATEGORY_GROUPS.'.{uid}', $this->proxy('categories', 'handleChangedCategoryGroup'))
- ->onUpdate(ProjectConfig::PATH_CATEGORY_GROUPS.'.{uid}', $this->proxy('categories', 'handleChangedCategoryGroup'))
- ->onRemove(ProjectConfig::PATH_CATEGORY_GROUPS.'.{uid}', $this->proxy('categories', 'handleDeletedCategoryGroup'))
// User group permissions
->onAdd(ProjectConfig::PATH_USER_GROUPS.'.{uid}.permissions', $this->proxy('userPermissions', 'handleChangedGroupPermissions'))
->onUpdate(ProjectConfig::PATH_USER_GROUPS.'.{uid}.permissions', $this->proxy('userPermissions', 'handleChangedGroupPermissions'))
@@ -99,10 +91,6 @@ public function boot(ProjectConfig $projectConfig): void
->onAdd(ProjectConfig::PATH_USER_FIELD_LAYOUTS, $this->proxy('users', 'handleChangedUserFieldLayout'))
->onUpdate(ProjectConfig::PATH_USER_FIELD_LAYOUTS, $this->proxy('users', 'handleChangedUserFieldLayout'))
->onRemove(ProjectConfig::PATH_USER_FIELD_LAYOUTS, $this->proxy('users', 'handleChangedUserFieldLayout'))
- // Global sets
- ->onAdd(ProjectConfig::PATH_GLOBAL_SETS.'.{uid}', $this->proxy('globals', 'handleChangedGlobalSet'))
- ->onUpdate(ProjectConfig::PATH_GLOBAL_SETS.'.{uid}', $this->proxy('globals', 'handleChangedGlobalSet'))
- ->onRemove(ProjectConfig::PATH_GLOBAL_SETS.'.{uid}', $this->proxy('globals', 'handleDeletedGlobalSet'))
// Sections
->onAdd(ProjectConfig::PATH_SECTIONS.'.{uid}', fn (ConfigEvent $event) => Sections::handleChangedSection($event))
->onUpdate(ProjectConfig::PATH_SECTIONS.'.{uid}', fn (ConfigEvent $event) => Sections::handleChangedSection($event))
diff --git a/src/Site/Sites.php b/src/Site/Sites.php
index 64b0813ebda..02245e1c488 100644
--- a/src/Site/Sites.php
+++ b/src/Site/Sites.php
@@ -6,9 +6,6 @@
use craft\base\ElementInterface;
use craft\elements\Asset;
-use craft\elements\Category;
-use craft\elements\GlobalSet;
-use craft\elements\Tag;
use craft\helpers\Queue;
use craft\queue\jobs\PropagateElements;
use CraftCms\Cms\Database\Table;
@@ -508,27 +505,11 @@ public function handleChangedSite(ConfigEvent $event): void
}
if ($isNewSite && $oldPrimarySiteId) {
- $oldPrimarySiteUid = DB::table(Table::SITES)->uidById($oldPrimarySiteId);
- $existingCategorySettings = $this->projectConfig->get(ProjectConfig::PATH_CATEGORY_GROUPS);
-
- if (! $this->projectConfig->isApplyingExternalChanges && is_array($existingCategorySettings)) {
- foreach ($existingCategorySettings as $categoryUid => $settings) {
- $this->projectConfig->set(
- path: ProjectConfig::PATH_CATEGORY_GROUPS.'.'.$categoryUid.'.siteSettings.'.$site->uid,
- value: $settings['siteSettings'][$oldPrimarySiteUid],
- message: 'Copy site settings for category groups',
- );
- }
- }
-
// Re-save most localizable element types
// (skip entries because they only support specific sites)
// (skip Matrix blocks because they will be re-saved when their owners are re-saved).
$elementTypes = [
- GlobalSet::class,
Asset::class,
- Category::class,
- Tag::class,
];
foreach ($elementTypes as $elementType) {
diff --git a/src/Structure/Commands/RepairCategoryGroupStructureCommand.php b/src/Structure/Commands/RepairCategoryGroupStructureCommand.php
deleted file mode 100644
index 5629ac4a4b3..00000000000
--- a/src/Structure/Commands/RepairCategoryGroupStructureCommand.php
+++ /dev/null
@@ -1,33 +0,0 @@
-getCategories()->getGroupByHandle($handle = $this->argument('handle'));
-
- if (! $group) {
- $this->components->error("Invalid category group handle: $handle");
-
- return self::FAILURE;
- }
-
- return $this->repairStructure($group->structureId, Category::find()->group($group));
- }
-}
diff --git a/src/Structure/StructureServiceProvider.php b/src/Structure/StructureServiceProvider.php
index e6d84b9d48d..0e10ec89c81 100644
--- a/src/Structure/StructureServiceProvider.php
+++ b/src/Structure/StructureServiceProvider.php
@@ -4,7 +4,6 @@
namespace CraftCms\Cms\Structure;
-use CraftCms\Cms\Structure\Commands\RepairCategoryGroupStructureCommand;
use CraftCms\Cms\Structure\Commands\RepairSectionStructureCommand;
use Illuminate\Support\ServiceProvider;
@@ -14,7 +13,6 @@ public function boot(): void
{
$this->commands([
RepairSectionStructureCommand::class,
- RepairCategoryGroupStructureCommand::class,
]);
}
}
diff --git a/tests/GarbageCollection/GarbageCollectionTest.php b/tests/GarbageCollection/GarbageCollectionTest.php
index 239444f4713..f2f16dd2cd5 100644
--- a/tests/GarbageCollection/GarbageCollectionTest.php
+++ b/tests/GarbageCollection/GarbageCollectionTest.php
@@ -4,11 +4,8 @@
use craft\elements\Address;
use craft\elements\Asset;
-use craft\elements\Category;
use craft\elements\ContentBlock;
use craft\elements\Entry;
-use craft\elements\GlobalSet;
-use craft\elements\Tag;
use craft\elements\User;
use CraftCms\Cms\Database\Table;
use CraftCms\Cms\GarbageCollection\Actions\DeleteOrphanedFieldLayouts;
@@ -76,11 +73,8 @@
})->with([
[Address::class, Table::ADDRESSES],
[Asset::class, Table::ASSETS],
- [Category::class, Table::CATEGORIES],
[ContentBlock::class, Table::CONTENTBLOCKS],
[Entry::class, Table::ENTRIES],
- [GlobalSet::class, Table::GLOBALSETS],
- [Tag::class, Table::TAGS],
[User::class, Table::USERS],
]);
@@ -98,10 +92,7 @@
app(GarbageCollection::class)->run(force: true);
})->with([
[Asset::class, Table::VOLUMES],
- [Category::class, Table::CATEGORYGROUPS],
[Entry::class, Table::ENTRYTYPES],
- [GlobalSet::class, Table::GLOBALSETS],
- [Tag::class, Table::TAGGROUPS],
]);
test('it deletes orphaned nested elements', function (string $elementType, string $table) {
@@ -136,11 +127,9 @@
app(GarbageCollection::class)->run(force: true);
})->with([
[[
- Table::CATEGORYGROUPS,
Table::ENTRYTYPES,
Table::FIELDS,
Table::SECTIONS,
- Table::TAGGROUPS,
]],
[[
Table::FIELDLAYOUTS,
diff --git a/yii2-adapter/legacy/base/ApplicationTrait.php b/yii2-adapter/legacy/base/ApplicationTrait.php
index 295c26988bd..02adbb16536 100644
--- a/yii2-adapter/legacy/base/ApplicationTrait.php
+++ b/yii2-adapter/legacy/base/ApplicationTrait.php
@@ -13,9 +13,7 @@
use craft\db\mysql\Schema;
use craft\elements\Address;
use craft\elements\Asset;
-use craft\elements\Category;
use craft\elements\Entry;
-use craft\elements\Tag;
use craft\elements\User;
use craft\errors\DbConnectException;
use craft\events\DefineFieldLayoutFieldsEvent;
@@ -52,7 +50,6 @@
use craft\services\AssetIndexer;
use craft\services\Assets;
use craft\services\Auth;
-use craft\services\Categories;
use craft\services\Conditions;
use craft\services\Config;
use craft\services\Dashboard;
@@ -64,7 +61,6 @@
use craft\services\Fields;
use craft\services\Fs;
use craft\services\Gc;
-use craft\services\Globals;
use craft\services\Gql;
use craft\services\Images;
use craft\services\ImageTransforms;
@@ -79,7 +75,6 @@
use craft\services\Sso;
use craft\services\Structures;
use craft\services\SystemMessages;
-use craft\services\Tags;
use craft\services\TemplateCaches;
use craft\services\Tokens;
use craft\services\UserGroups;
@@ -129,7 +124,6 @@
* @property-read AssetManager $assetManager The asset manager component
* @property-read Assets $assets The assets service
* @property-read Auth $auth The user authentication service
- * @property-read Categories $categories The categories service
* @property-read Conditions $conditions The conditions service
* @property-read Config $config The config service
* @property-read Connection $db The database connection component
@@ -144,7 +138,6 @@
* @property-read Formatter $formatter The formatter component
* @property-read Fs $fs The filesystems service
* @property-read Gc $gc The garbage collection service
- * @property-read Globals $globals The globals service
* @property-read Gql $gql The GraphQl service
* @property-read I18N $i18n The internationalization (i18n) component
* @property-read Images $images The images service
@@ -166,7 +159,6 @@
* @property-read Sso $sso The SSO service
* @property-read Structures $structures The structures service
* @property-read SystemMessages $systemMessages The system email messages service
- * @property-read Tags $tags The tags service
* @property-read TemplateCaches $templateCaches The template caches service
* @property-read Tokens $tokens The tokens service
* @property-read UrlManager $urlManager The URL manager for this application
@@ -854,16 +846,6 @@ public function getImageTransforms(): ImageTransforms
return $this->get('imageTransforms');
}
- /**
- * Returns the categories service.
- *
- * @return Categories The categories service
- */
- public function getCategories(): Categories
- {
- return $this->get('categories');
- }
-
/**
* Returns the conditions service.
*
@@ -1036,16 +1018,6 @@ public function getGc(): Gc
return $this->get('gc');
}
- /**
- * Returns the globals service.
- *
- * @return Globals The globals service
- */
- public function getGlobals(): Globals
- {
- return $this->get('globals');
- }
-
/**
* Returns the GraphQL service.
*
@@ -1221,16 +1193,6 @@ public function getStructures(): Structures
return $this->get('structures');
}
- /**
- * Returns the tags service.
- *
- * @return Tags The tags service
- */
- public function getTags(): Tags
- {
- return $this->get('tags');
- }
-
/**
* Returns the template cache service.
*
@@ -1357,9 +1319,6 @@ private function _postInit(): void
// Register field layout listeners
$this->_registerFieldLayoutListener();
- // Register all the listeners for config items
- $this->_registerConfigListeners();
-
$this->_isInitialized = true;
// Fire an 'init' event
@@ -1413,10 +1372,6 @@ private function _registerFieldLayoutListener(): void
$fieldLayout = $event->sender;
switch ($fieldLayout->type) {
- case Category::class:
- case Tag::class:
- $event->fields[] = TitleField::class;
- break;
case Address::class:
$event->fields[] = LabelField::class;
$event->fields[] = OrganizationField::class;
@@ -1447,17 +1402,4 @@ private function _registerFieldLayoutListener(): void
}
});
}
-
- /**
- * Register event listeners for config changes.
- */
- private function _registerConfigListeners(): void
- {
- // Prune deleted sites from site settings
- Event::on(Sites::class, Sites::EVENT_AFTER_DELETE_SITE, function(DeleteSiteEvent $event) {
- if (!app(ProjectConfig::class)->isApplyingExternalChanges) {
- $this->getCategories()->pruneDeletedSite($event);
- }
- });
- }
}
diff --git a/yii2-adapter/legacy/config/app.php b/yii2-adapter/legacy/config/app.php
index 2819634e42d..ad39ebc77a6 100644
--- a/yii2-adapter/legacy/config/app.php
+++ b/yii2-adapter/legacy/config/app.php
@@ -10,7 +10,6 @@
use craft\services\AssetIndexer;
use craft\services\Assets;
use craft\services\Auth;
-use craft\services\Categories;
use craft\services\Conditions;
use craft\services\Dashboard;
use craft\services\Deprecator;
@@ -21,7 +20,6 @@
use craft\services\Fields;
use craft\services\Fs;
use craft\services\Gc;
-use craft\services\Globals;
use craft\services\Gql;
use craft\services\Images;
use craft\services\ImageTransforms;
@@ -36,7 +34,6 @@
use craft\services\Sso;
use craft\services\Structures;
use craft\services\SystemMessages;
-use craft\services\Tags;
use craft\services\TemplateCaches;
use craft\services\Tokens;
use craft\services\UserGroups;
@@ -76,9 +73,6 @@
'auth' => [
'class' => Auth::class,
],
- 'categories' => [
- 'class' => Categories::class,
- ],
'conditions' => [
'class' => Conditions::class,
],
@@ -109,9 +103,6 @@
'gc' => [
'class' => Gc::class,
],
- 'globals' => [
- 'class' => Globals::class,
- ],
'gql' => [
'class' => Gql::class,
],
@@ -172,9 +163,6 @@
'systemMessages' => [
'class' => SystemMessages::class,
],
- 'tags' => [
- 'class' => Tags::class,
- ],
'templateCaches' => [
'class' => TemplateCaches::class,
],
diff --git a/yii2-adapter/legacy/config/cproutes/common.php b/yii2-adapter/legacy/config/cproutes/common.php
index 2959f39eaf4..28e7b2639b2 100644
--- a/yii2-adapter/legacy/config/cproutes/common.php
+++ b/yii2-adapter/legacy/config/cproutes/common.php
@@ -6,10 +6,6 @@
'assets/edit/' => 'elements/edit',
'assets' => 'assets/index',
'assets/' => 'assets/index',
- 'categories' => 'categories/category-index',
- 'categories/' => 'categories/category-index',
- 'categories//new' => 'categories/create',
- 'categories//' => 'elements/edit',
'edit/' => 'elements/redirect',
'edit/' => 'elements/redirect',
'entries//' => 'elements/edit',
@@ -18,8 +14,6 @@
'content///' => 'elements/edit',
'content////revisions' => 'elements/revisions',
- 'globals' => 'globals',
- 'globals/' => 'globals/edit-content',
'graphiql' => 'graphql/graphiql',
'graphql' => 'graphql/cp-index',
'graphql/schemas' => 'graphql/view-schemas',
@@ -40,20 +34,11 @@
'settings/assets/transforms' => 'image-transforms/index',
'settings/assets/transforms/new' => 'image-transforms/edit',
'settings/assets/transforms/' => 'image-transforms/edit',
- 'settings/categories' => 'categories/group-index',
- 'settings/categories/new' => 'categories/edit-category-group',
- 'settings/categories/' => 'categories/edit-category-group',
'settings/email' => 'system-settings/edit-email-settings',
'settings/fields/new' => 'fields/edit-field',
'settings/fields/edit/' => 'fields/edit-field',
'settings/general' => 'system-settings/general-settings',
- 'settings/globals' => 'system-settings/global-set-index',
- 'settings/globals/new' => 'system-settings/edit-global-set',
- 'settings/globals/' => 'system-settings/edit-global-set',
'settings/plugins/' => 'plugins/edit-plugin-settings',
- 'settings/tags' => 'tags/index',
- 'settings/tags/new' => 'tags/edit-tag-group',
- 'settings/tags/' => 'tags/edit-tag-group',
'settings/users' => ['template' => 'settings/users/fields'],
'preview/' => 'elements/preview',
];
diff --git a/yii2-adapter/legacy/console/controllers/EntrifyController.php b/yii2-adapter/legacy/console/controllers/EntrifyController.php
deleted file mode 100644
index 02ac2f921b1..00000000000
--- a/yii2-adapter/legacy/console/controllers/EntrifyController.php
+++ /dev/null
@@ -1,853 +0,0 @@
-
- * @since 4.4.0
- */
-class EntrifyController extends Controller
-{
- /**
- * @var string|null The section handle that entries should be saved in
- */
- public ?string $section = null;
-
- /**
- * @var string|null The entry type handle that entries should have
- */
- public ?string $entryType = null;
-
- /**
- * @var string|null The author username or email that entries should have
- */
- public ?string $author = null;
-
- private bool $_forSingle;
- private Section $_section;
- private EntryType $_entryType;
- private User $_author;
-
- /**
- * @inheritdoc
- */
- public function options($actionID): array
- {
- $options = parent::options($actionID);
- $options[] = 'section';
-
- if (in_array($actionID, ['categories', 'tags'])) {
- $options[] = 'entryType';
- $options[] = 'author';
- }
-
- return $options;
- }
-
- /**
- * @inheritdoc
- */
- public function beforeAction($action): bool
- {
- if (!parent::beforeAction($action)) {
- return false;
- }
-
- $this->_forSingle = $action->id === 'global-set';
-
- return true;
- }
-
- /**
- * Converts categories to entries.
- *
- * @param string|null $categoryGroup The category group handle
- * @return int
- */
- public function actionCategories(?string $categoryGroup = null): int
- {
- $categoriesService = Craft::$app->getCategories();
-
- if ($categoryGroup) {
- $categoryGroupHandle = $categoryGroup;
- $categoryGroup = $categoriesService->getGroupByHandle($categoryGroupHandle, true);
-
- if (!$categoryGroup) {
- $this->stderr("Invalid category group handle: $categoryGroupHandle\n", Console::FG_RED);
- return ExitCode::UNSPECIFIED_ERROR;
- }
- } else {
- if (!$this->interactive) {
- throw new InvalidConfigException('A category group handle is required when this command is run non-interactively.');
- }
-
- /** @var Collection $categoryGroups */
- $categoryGroups = Collection::make($categoriesService->getAllGroups())
- ->keyBy(fn(CategoryGroup $group) => $group->handle);
-
- if (empty($categoryGroups)) {
- $this->output('No category groups exist.', Console::FG_YELLOW);
- return ExitCode::OK;
- }
-
- $categoryGroupHandle = $this->select(
- 'Choose a category group:',
- $categoryGroups->map(fn(CategoryGroup $group) => $group->name)->all(),
- );
- $categoryGroup = $categoryGroups->get($categoryGroupHandle);
- }
-
- $projectConfigService = app(ProjectConfig::class);
- $projectConfigChanged = false;
- $sectionCreated = false;
-
- if (!isset($this->section)) {
- // Capture the new section handle
- Event::once(EntriesService::class, EntriesService::EVENT_AFTER_SAVE_SECTION, function(SectionEvent $event) {
- $this->section = $event->section->handle;
- });
- $this->run('sections/create', [
- 'fromCategoryGroup' => $categoryGroup->handle,
- ]);
-
- // Add it to a “Categories” page
- $this->_addSectionToPage('Categories', 'sitemap');
-
- $projectConfigChanged = true;
- $sectionCreated = true;
- }
-
- try {
- $section = $this->_section();
- $entryType = $this->_entryType();
- $author = $this->_author();
- } catch (InvalidConfigException $e) {
- $this->stderr($e->getMessage() . PHP_EOL, Console::FG_RED);
- if ($projectConfigChanged) {
- $projectConfigService->saveModifiedConfigData();
- }
- return ExitCode::UNSPECIFIED_ERROR;
- }
-
- if (
- $section->type === SectionType::Channel &&
- !$this->confirm("The categories’ structure data will be lost because “{$section->name}” is a Channel section. Are you sure you want to continue?\n")
- ) {
- $this->stdout("Aborted.\n");
- return ExitCode::OK;
- }
-
- $this->stdout(PHP_EOL);
-
- $categoryQuery = Category::find()
- ->group($categoryGroup)
- ->status(null);
-
- if ($categoryGroup->dateDeleted) {
- $categoryQuery
- ->trashed()
- ->andWhere(['categories.deletedWithGroup' => true]);
- }
-
- $entriesByLevel = [];
-
- foreach (Db::batch($categoryQuery) as $categories) {
- $authorData = [];
-
- foreach ($categories as $category) {
- /** @var Category $category */
- $this->do("Converting “{$category->title}” ($category->id)", function() use (
- $section,
- $entryType,
- $author,
- &$entriesByLevel,
- $categoryGroup,
- $category,
- &$authorData,
- ) {
- DbFacade::table(Table::ENTRIES)
- ->insert([
- 'id' => $category->id,
- 'sectionId' => $section->id,
- 'typeId' => $entryType->id,
- 'postDate' => $category->dateCreated,
- 'dateCreated' => $category->dateCreated,
- 'dateUpdated' => $category->dateUpdated,
- ]);
-
- DbFacade::table(Table::ELEMENTS)
- ->where('id', $category->id)
- ->update([
- 'type' => Entry::class,
- 'dateDeleted' => null,
- ]);
-
- DbFacade::table(Table::CATEGORIES)
- ->delete($category->id);
-
- DbFacade::table(Table::STRUCTUREELEMENTS)
- ->where('structureId', $categoryGroup->structureId)
- ->where('elementId', $category->id)
- ->delete();
-
- if ($section->type === SectionType::Structure) {
- $entry = Entry::find()
- ->id($category->id)
- ->drafts(null)
- ->revisions(null)
- ->status(null)
- ->one();
- $parentLevel = $category->level - 1;
- $parentEntry = null;
- while ($parentLevel >= 1) {
- if (isset($entriesByLevel[$parentLevel])) {
- $parentEntry = $entriesByLevel[$parentLevel];
- break;
- }
- $parentLevel--;
- }
- if ($parentEntry) {
- Structures::append($section->structureId, $entry, $parentEntry, Mode::Insert);
- } else {
- Structures::appendToRoot($section->structureId, $entry, Mode::Insert);
- }
- $entriesByLevel[$entry->level] = $entry;
- }
-
- $authorData[] = [
- 'entryId' => $category->id,
- 'authorId' => $author->id,
- 'sortOrder' => 1,
- ];
- });
- }
-
- DbFacade::table(Table::ENTRIES_AUTHORS)
- ->insert($authorData);
- }
-
- $this->success('Categories converted.');
-
- $this->_updateUserPermissions([
- "viewCategories:$categoryGroup->uid" => [
- "viewEntries:$section->uid",
- "viewPeerEntries:$section->uid",
- ],
- "saveCategories:$categoryGroup->uid" => [
- "createEntries:$section->uid",
- "saveEntries:$section->uid",
- "savePeerEntries:$section->uid",
- ],
- "deleteCategories:$categoryGroup->uid" => [
- "deleteEntries:$section->uid",
- "deletePeerEntries:$section->uid",
- ],
- "viewPeerCategoryDrafts:$categoryGroup->uid" => "viewPeerEntryDrafts:$section->uid",
- "savePeerCategoryDrafts:$categoryGroup->uid" => "savePeerEntryDrafts:$section->uid",
- "deletePeerCategoryDrafts:$categoryGroup->uid" => "deletePeerEntryDrafts:$section->uid",
- ], $sectionCreated);
-
- if (!$projectConfigService->readOnly) {
- if (!$categoryGroup->dateDeleted && $this->confirm("Delete the “{$categoryGroup}” category group?", true)) {
- $this->do('Deleting category group', function() use ($categoryGroup) {
- Craft::$app->getCategories()->deleteGroup($categoryGroup);
- });
- $this->success('Category group deleted.');
- $projectConfigChanged = true;
- }
-
- $fields = $this->_findInProjectConfig($projectConfigService, fn(array $config) => (
- ($config['type'] ?? null) === Categories::class &&
- ($config['settings']['source'] ?? null) === "group:$categoryGroup->uid"
- ));
- if (!empty($fields)) {
- $total = count($fields);
- $this->stdout(sprintf("Found %s relating to the “{$categoryGroup->name}” category group.\n", $total === 1 ? 'one Categories field' : "$total Categories fields"));
- if ($this->confirm($total === 1 ? 'Convert it to an Entries field?' : 'Convert them to Entries fields?', true)) {
- foreach ($fields as [$path, $config]) {
- $this->do(sprintf('Converting %s', ($config['name'] ?? null) ? "“{$config['name']}”" : 'Categories filed'), function() use ($section, $projectConfigService, $path, $config) {
- $config['type'] = Entries::class;
- $config['settings']['maintainHierarchy'] ??= true;
- $config['settings']['sources'] = ["section:$section->uid"];
- unset(
- $config['settings']['source'],
- $config['settings']['allowMultipleSources'],
- $config['settings']['allowLimit'],
- $config['settings']['allowLargeThumbsView'],
- );
- $projectConfigService->set($path, $config);
- });
- }
-
- $this->success(sprintf('Categories %s converted.', $total === 1 ? 'field' : 'fields'));
- $projectConfigChanged = true;
- }
- }
- }
-
- if ($projectConfigChanged) {
- $this->_deployTip('categories', $categoryGroup->handle);
- }
-
- return ExitCode::OK;
- }
-
- /**
- * Converts tags to entries.
- *
- * @param string|null $tagGroup The tag group handle
- * @return int
- */
- public function actionTags(?string $tagGroup = null): int
- {
- $tagsService = Craft::$app->getTags();
-
- if ($tagGroup) {
- $tagGroupHandle = $tagGroup;
-
- $tagGroup = $tagsService->getTagGroupByHandle($tagGroupHandle, true);
- if (!$tagGroup) {
- $this->stderr("Invalid tag group handle: $tagGroupHandle\n", Console::FG_RED);
- return ExitCode::UNSPECIFIED_ERROR;
- }
- } else {
- if (!$this->interactive) {
- throw new InvalidConfigException('A tag group handle is required when this command is run non-interactively.');
- }
-
- /** @var Collection $tagGroups */
- $tagGroups = Collection::make($tagsService->getAllTagGroups())
- ->keyBy(fn(TagGroup $group) => $group->handle);
-
- if (empty($tagGroups)) {
- $this->output('No tag groups exist.', Console::FG_YELLOW);
- return ExitCode::OK;
- }
-
- $tagGroupHandle = $this->select(
- 'Choose a tag group:',
- $tagGroups->map(fn(TagGroup $group) => $group->name)->all(),
- );
- $tagGroup = $tagGroups->get($tagGroupHandle);
- }
-
- $projectConfigService = app(ProjectConfig::class);
- $projectConfigChanged = false;
-
- if (!isset($this->section)) {
- // Capture the new section handle
- Event::once(EntriesService::class, EntriesService::EVENT_AFTER_SAVE_SECTION, function(SectionEvent $event) {
- $this->section = $event->section->handle;
- });
- $this->run('sections/create', [
- 'fromTagGroup' => $tagGroup->handle,
- ]);
-
- // Add it to a “Tags” page
- $this->_addSectionToPage('Tags', 'tags');
-
- $projectConfigChanged = true;
- }
-
- try {
- $section = $this->_section();
- $entryType = $this->_entryType();
- $author = $this->_author();
- } catch (InvalidConfigException $e) {
- $this->stderr($e->getMessage() . PHP_EOL, Console::FG_RED);
- if ($projectConfigChanged) {
- $projectConfigService->saveModifiedConfigData();
- }
- return ExitCode::UNSPECIFIED_ERROR;
- }
-
- $tagQuery = Tag::find()
- ->group($tagGroup)
- ->status(null);
-
- if ($tagGroup->dateDeleted) {
- $tagQuery
- ->trashed()
- ->andWhere(['tags.deletedWithGroup' => true]);
- }
-
- if ($tagGroup->dateDeleted) {
- $tagQuery
- ->trashed()
- ->andWhere(['tags.deletedWithGroup' => true]);
- }
-
- foreach (Db::batch($tagQuery) as $tags) {
- $authorData = [];
-
- foreach ($tags as $tag) {
- /** @var Tag $tag */
- $this->do("Converting “{$tag->title}” ($tag->id)", function() use (
- $section,
- $entryType,
- $author,
- $tag,
- &$authorData
- ) {
- DbFacade::table(Table::ENTRIES)
- ->insert([
- 'id' => $tag->id,
- 'sectionId' => $section->id,
- 'typeId' => $entryType->id,
- 'postDate' => $tag->dateCreated,
- 'dateCreated' => $tag->dateCreated,
- 'dateUpdated' => $tag->dateUpdated,
- ]);
-
- DbFacade::table(Table::ELEMENTS)
- ->where('id', $tag->id)
- ->update([
- 'type' => Entry::class,
- 'dateDeleted' => null,
- ]);
-
- DbFacade::table(Table::TAGS)->delete($tag->id);
-
- $authorData[] = [
- 'entryId' => $tag->id,
- 'authorId' => $author->id,
- 'sortOrder' => 1,
- ];
- });
- }
-
- DbFacade::table(Table::ENTRIES_AUTHORS)
- ->insert($authorData);
- }
-
- $this->success('Tags converted.');
-
- if (!$projectConfigService->readOnly) {
- if (!$tagGroup->dateDeleted && $this->confirm("Delete the “{$tagGroup}” tag group?", true)) {
- $this->do('Deleting tag group', function() use ($tagGroup) {
- Craft::$app->getTags()->deleteTagGroup($tagGroup);
- });
- $this->success('Tag group deleted.');
- $projectConfigChanged = true;
- }
-
- $fields = $this->_findInProjectConfig($projectConfigService, fn(array $config) => (
- ($config['type'] ?? null) === Tags::class &&
- ($config['settings']['source'] ?? null) === "taggroup:$tagGroup->uid"
- ));
- if (!empty($fields)) {
- $total = count($fields);
- $this->stdout(sprintf("Found %s relating to the “{$tagGroup->name}” tag group.\n", $total === 1 ? 'one Tags field' : "$total Tags fields"));
- if ($this->confirm($total === 1 ? 'Convert it to an Entries field?' : 'Convert them to Entries fields?', true)) {
- foreach ($fields as [$path, $config]) {
- $this->do(sprintf('Converting %s', ($config['name'] ?? null) ? "“{$config['name']}”" : 'Tags filed'), function() use ($section, $projectConfigService, $path, $config) {
- $config['type'] = Entries::class;
- $config['settings']['sources'] = ["section:$section->uid"];
- $config['settings']['viewMode'] = BaseRelationField::VIEW_MODE_LIST_INLINE;
- unset(
- $config['settings']['source'],
- $config['settings']['allowMultipleSources'],
- $config['settings']['allowLimit'],
- $config['settings']['allowLargeThumbsView'],
- );
- $projectConfigService->set($path, $config);
- });
- }
-
- $this->success(sprintf('Tags %s converted.', $total === 1 ? 'field' : 'fields'));
- $projectConfigChanged = true;
- }
- }
- }
-
- if ($projectConfigChanged) {
- $this->_deployTip('tags', $tagGroup->handle);
- }
-
- return ExitCode::OK;
- }
-
- /**
- * Converts a global set to a Single section.
- *
- * @param string|null $globalSet The global set handle
- * @return int
- */
- public function actionGlobalSet(?string $globalSet = null): int
- {
- $globalsService = Craft::$app->getGlobals();
-
- if ($globalSet) {
- $globalSetHandle = $globalSet;
-
- $globalSet = Craft::$app->getGlobals()->getSetByHandle($globalSetHandle, withTrashed: true);
- if (!$globalSet) {
- $this->stderr("Invalid global set handle: $globalSetHandle\n", Console::FG_RED);
- return ExitCode::UNSPECIFIED_ERROR;
- }
- } else {
- if (!$this->interactive) {
- throw new InvalidConfigException('A global set handle is required when this command is run non-interactively.');
- }
-
- /** @var Collection $globalSets */
- $globalSets = Collection::make($globalsService->getAllSets())
- ->keyBy(fn(GlobalSet $globalSet) => $globalSet->handle);
-
- if (empty($globalSets)) {
- $this->output('No global sets exist.', Console::FG_YELLOW);
- return ExitCode::OK;
- }
-
- $globalSetHandle = $this->select(
- 'Choose a global set:',
- $globalSets->map(fn(GlobalSet $globalSet) => $globalSet->name)->all(),
- );
- $globalSet = $globalSets->get($globalSetHandle);
- }
-
- $projectConfigChanged = false;
- $sectionCreated = false;
-
- if (!isset($this->section)) {
- // Capture the new section handle
- Event::once(EntriesService::class, EntriesService::EVENT_AFTER_SAVE_SECTION, function(SectionEvent $event) {
- $this->section = $event->section->handle;
- });
- $this->run('sections/create', [
- 'fromGlobalSet' => $globalSet->handle,
- ]);
- $projectConfigChanged = true;
- $sectionCreated = true;
- }
-
- try {
- $section = $this->_section();
- $entryType = $this->_entryType();
- } catch (InvalidConfigException $e) {
- $this->stderr($e->getMessage() . PHP_EOL, Console::FG_RED);
- if ($projectConfigChanged) {
- app(ProjectConfig::class)->saveModifiedConfigData();
- }
- return ExitCode::UNSPECIFIED_ERROR;
- }
-
- $this->do("Converting “{$globalSet->name}”", function() use (
- $section,
- $entryType,
- $globalSet,
- &$projectConfigChanged,
- ) {
- if (!$globalSet->dateDeleted) {
- Craft::$app->getGlobals()->deleteSet($globalSet);
- $projectConfigChanged = true;
- }
-
- $oldEntry = Entry::find()
- ->section($section)
- ->status(null)
- ->site('*')
- ->unique()
- ->one();
-
- if ($oldEntry) {
- Craft::$app->getElements()->deleteElement($oldEntry, true);
- }
-
- DbFacade::table(Table::ENTRIES)
- ->insert([
- 'id' => $globalSet->id,
- 'sectionId' => $section->id,
- 'typeId' => $entryType->id,
- 'postDate' => $globalSet->dateCreated,
- 'dateCreated' => $globalSet->dateCreated,
- 'dateUpdated' => $globalSet->dateUpdated,
- ]);
-
- DbFacade::table(Table::ELEMENTS)
- ->where('id', $globalSet->id)
- ->update([
- 'type' => Entry::class,
- 'dateDeleted' => null,
- ]);
-
- DbFacade::table(Table::ELEMENTS_SITES)
- ->where('elementId', $globalSet->id)
- ->update([
- 'title' => $globalSet->name,
- ]);
-
- DbFacade::table(Table::GLOBALSETS)->delete($globalSet->id);
- });
-
- $this->success('Global set converted.');
-
- $this->_updateUserPermissions([
- "editGlobalSet:$globalSet->uid" => [
- "viewEntries:$section->uid",
- "saveEntries:$section->uid",
- "viewPeerEntryDrafts:$section->uid",
- "savePeerEntryDrafts:$section->uid",
- "deletePeerEntryDrafts:$section->uid",
- ],
- ], $sectionCreated);
-
- if ($projectConfigChanged) {
- $this->_deployTip('global-set', $globalSet->handle);
- }
-
- return ExitCode::OK;
- }
-
- private function _section(): Section
- {
- if (!isset($this->_section)) {
- if (!$this->section) {
- throw new InvalidConfigException('The --section option is required when this command is run non-interactively.');
- }
-
- $section = Sections::getSectionByHandle($this->section);
- if (!$section) {
- throw new InvalidConfigException("Invalid section handle: $this->section");
- }
- if ($this->_forSingle) {
- if ($section->type !== SectionType::Single) {
- throw new InvalidConfigException("“{$section->name}” isn’t a Single section. You must specify a Single section.", Console::FG_RED);
- }
- } elseif ($section->type === SectionType::Single) {
- throw new InvalidConfigException("“{$section->name}” is a Single section. You must specify a Structure or Channel section.", Console::FG_RED);
- }
- $this->_section = $section;
- }
-
- return $this->_section;
- }
-
- private function _entryType(): EntryType
- {
- if (!isset($this->_entryType)) {
- $section = $this->_section();
- $allEntryTypes = Arr::keyBy($section->getEntryTypes(), 'handle');
- if (isset($this->entryType)) {
- if (!isset($allEntryTypes[$this->entryType])) {
- throw new InvalidConfigException("Invalid entry type handle for the section “{$section->name}”: $this->entryType");
- }
- $this->_entryType = $allEntryTypes[$this->entryType];
- } elseif (count($allEntryTypes) === 1) {
- $this->_entryType = reset($allEntryTypes);
- } else {
- if (!$this->interactive) {
- throw new InvalidConfigException('The --entry-type option is required when this command is run non-interactively.');
- }
- $entryTypeHandle = $this->select('Which entry type should the entries have?', array_map(
- fn(EntryType $entryType) => $entryType->name,
- $allEntryTypes,
- ));
- $this->_entryType = $allEntryTypes[$entryTypeHandle];
- }
- }
-
- return $this->_entryType;
- }
-
- private function _author(): User
- {
- if (!isset($this->_author)) {
- if (isset($this->author)) {
- $author = Craft::$app->getUsers()->getUserByUsernameOrEmail($this->author);
- if (!$author) {
- throw new InvalidConfigException("Invalid author username or email: $this->author");
- }
- $this->_author = $author;
- } else {
- if (!$this->interactive) {
- throw new InvalidConfigException('The --author option is required when this command is run non-interactively.');
- }
- $usersService = Craft::$app->getUsers();
- $what = Cms::config()->useEmailAsUsername ? 'email' : 'username or email';
- $usernameOrEmail = $this->prompt("Enter the $what of the author that the entries should have:", [
- 'required' => true,
- 'validator' => fn(string $value) => $usersService->getUserByUsernameOrEmail($value) !== null,
- 'error' => "Invalid $what.",
- ]);
- $this->_author = $usersService->getUserByUsernameOrEmail($usernameOrEmail);
- }
- }
-
- return $this->_author;
- }
-
- private function _updateUserPermissions(array $map, $updateUserGroups): void
- {
- // Normalize the permission map
- $map = array_combine(
- array_map('strtolower', array_keys($map)),
- array_map(fn($newPermissions) => array_map('strtolower', (array)$newPermissions), $map)
- );
-
- $this->do('Updating user permissions', function() use ($map, $updateUserGroups) {
- foreach ($map as $oldPermission => $newPermissions) {
- $userIds = DbFacade::table(Table::USERPERMISSIONS_USERS, 'upu')
- ->join(new Alias(Table::USERPERMISSIONS, 'up'), 'up.id', 'upu.permissionId')
- ->where('up.name', $oldPermission)
- ->pluck('upu.userId')
- ->unique();
-
- if ($userIds->isEmpty()) {
- continue;
- }
-
- $insert = [];
-
- foreach ($newPermissions as $newPermission) {
- $newPermissionId = DbFacade::table(Table::USERPERMISSIONS)
- ->where('name', $newPermission)
- ->value('id');
-
- if (!$newPermissionId) {
- $newPermissionId = DbFacade::table(Table::USERPERMISSIONS)
- ->insertGetId([
- 'name' => $newPermission,
- ]);
- }
-
- foreach ($userIds as $userId) {
- $insert[] = [
- 'permissionId' => $newPermissionId,
- 'userId' => $userId,
- ];
- }
- }
-
- DbFacade::table(Table::USERPERMISSIONS_USERS)
- ->insert($insert);
- }
-
- if ($updateUserGroups) {
- $projectConfig = app(ProjectConfig::class);
-
- foreach ($projectConfig->get('users.groups') ?? [] as $uid => $group) {
- $groupPermissions = array_flip($group['permissions'] ?? []);
- $changed = false;
-
- foreach ($map as $oldPermission => $newPermissions) {
- if (isset($groupPermissions[$oldPermission])) {
- foreach ($newPermissions as $newPermission) {
- $groupPermissions[$newPermission] = true;
- }
- $changed = true;
- }
- }
-
- if ($changed) {
- $projectConfig->set("users.groups.$uid.permissions", array_keys($groupPermissions));
- }
- }
- }
- });
-
- $this->stdout(PHP_EOL);
- }
-
- private function _findInProjectConfig(ProjectConfig $projectConfigService, callable $check): array
- {
- $results = [];
- $this->_findInProjectConfigInternal($projectConfigService->get(), $check, $results, null);
- return $results;
- }
-
- private function _findInProjectConfigInternal(array $config, callable $check, array &$results, ?string $path): void
- {
- foreach ($config as $key => $value) {
- if (is_array($value)) {
- $subpath = ($path !== null ? "$path." : '') . $key;
- if ($check($value)) {
- $results[] = [$subpath, $value];
- } else {
- $this->_findInProjectConfigInternal($value, $check, $results, $subpath);
- }
- }
- }
- }
-
- private function _deployTip(string $action, string $handle): void
- {
- $command = "php craft entrify/$action $handle --section={$this->_section->handle}";
-
- if (!$this->_forSingle) {
- $command .= " --entry-type={$this->_entryType->handle} --author={$this->_author->username}";
- }
-
- $this->tip(<<getProjectConfig();
-
- $sourceConfigPath = sprintf('%s.%s', ProjectConfig::PATH_ELEMENT_SOURCES, Entry::class);
- $sourceConfigs = Collection::make($projectConfig->get($sourceConfigPath))
- ->map(fn(array $config) => $config + ['page' => 'Entries'])
- ->all();
- $sourceConfigs[] = [
- 'key' => sprintf('section:%s', $this->_section()->uid),
- 'page' => $name,
- 'type' => 'native',
- ];
- $projectConfig->set($sourceConfigPath, $sourceConfigs);
-
- $pageSettings = app(ElementSources::class)->getPageSettings(Entry::class);
- $pageSettings[$name] = [
- 'icon' => $icon,
- ];
- $projectConfig->set(sprintf('%s.%s', ProjectConfig::PATH_ELEMENT_SOURCE_PAGES, Entry::class), $pageSettings);
- }
-}
diff --git a/yii2-adapter/legacy/console/controllers/ResaveController.php b/yii2-adapter/legacy/console/controllers/ResaveController.php
index de5cdbfda32..7acdfab0de8 100644
--- a/yii2-adapter/legacy/console/controllers/ResaveController.php
+++ b/yii2-adapter/legacy/console/controllers/ResaveController.php
@@ -13,20 +13,16 @@
use craft\console\Controller;
use craft\elements\Address;
use craft\elements\Asset;
-use craft\elements\Category;
use craft\elements\db\ElementQuery;
use craft\elements\db\ElementQueryInterface;
use craft\elements\Entry;
-use craft\elements\Tag;
use craft\elements\User;
use craft\errors\InvalidElementException;
use craft\events\MultiElementActionEvent;
use craft\helpers\Console;
use craft\helpers\ElementHelper;
use craft\helpers\Queue;
-use craft\models\CategoryGroup;
use craft\models\FieldLayout;
-use craft\models\TagGroup;
use craft\models\Volume;
use craft\queue\jobs\ResaveElements;
use craft\services\Elements;
@@ -175,7 +171,7 @@ final public static function normalizeTo(?string $to): callable
public bool $touch = false;
/**
- * @var string|null The group handle(s) to save categories/tags/users from. Can be set to multiple comma-separated groups.
+ * @var string|null The group handle(s) to save users from. Can be set to multiple comma-separated groups.
*/
public ?string $group = null;
@@ -292,9 +288,7 @@ public function options($actionID): array
$options[] = 'volume';
$options[] = 'withFields';
break;
- case 'tags':
case 'users':
- case 'categories':
$options[] = 'group';
$options[] = 'withFields';
break;
@@ -497,37 +491,6 @@ public function actionAssets(): int
return $this->resaveElements(Asset::class, $criteria);
}
- /**
- * Re-saves categories.
- *
- * @return int
- */
- public function actionCategories(): int
- {
- $criteria = [];
- if (isset($this->group)) {
- $criteria['group'] = explode(',', $this->group);
- }
-
- if (!empty($this->withFields)) {
- $handles = Collection::make(Craft::$app->getCategories()->getAllGroups())
- ->filter(fn(CategoryGroup $group) => $this->hasTheFields($group->getFieldLayout()))
- ->map(fn(CategoryGroup $group) => $group->handle)
- ->all();
- if (isset($criteria['group'])) {
- $criteria['group'] = array_intersect($criteria['group'], $handles);
- } else {
- $criteria['group'] = $handles;
- }
- if (empty($criteria['group'])) {
- $this->output($this->markdownToAnsi('No category groups satisfy `--with-fields`.'));
- return ExitCode::UNSPECIFIED_ERROR;
- }
- }
-
- return $this->resaveElements(Category::class, $criteria);
- }
-
/**
* Re-saves entries.
*
@@ -570,37 +533,6 @@ public function actionEntries(): int
return $this->resaveElements(Entry::class, $criteria);
}
- /**
- * Re-saves tags.
- *
- * @return int
- */
- public function actionTags(): int
- {
- $criteria = [];
- if (isset($this->group)) {
- $criteria['group'] = explode(',', $this->group);
- }
-
- if (!empty($this->withFields)) {
- $handles = Collection::make(Craft::$app->getTags()->getAllTagGroups())
- ->filter(fn(TagGroup $group) => $this->hasTheFields($group->getFieldLayout()))
- ->map(fn(TagGroup $group) => $group->handle)
- ->all();
- if (isset($criteria['group'])) {
- $criteria['group'] = array_intersect($criteria['group'], $handles);
- } else {
- $criteria['group'] = $handles;
- }
- if (empty($criteria['group'])) {
- $this->output($this->markdownToAnsi('No tag groups satisfy `--with-fields`.'));
- return ExitCode::UNSPECIFIED_ERROR;
- }
- }
-
- return $this->resaveElements(Tag::class, $criteria);
- }
-
/**
* Re-saves users.
*
diff --git a/yii2-adapter/legacy/controllers/CategoriesController.php b/yii2-adapter/legacy/controllers/CategoriesController.php
deleted file mode 100644
index 999443a8c6f..00000000000
--- a/yii2-adapter/legacy/controllers/CategoriesController.php
+++ /dev/null
@@ -1,540 +0,0 @@
-
- * @since 3.0.0
- */
-class CategoriesController extends Controller
-{
- /**
- * @event ElementEvent The event that is triggered when a category’s template is rendered for Live Preview.
- */
- public const EVENT_PREVIEW_CATEGORY = 'previewCategory';
-
- /**
- * @inheritdoc
- */
- protected array|bool|int $allowAnonymous = ['view-shared-category'];
-
- // Category Groups
- // -------------------------------------------------------------------------
-
- /**
- * Category groups index.
- *
- * @return Response
- */
- public function actionGroupIndex(): Response
- {
- $this->requireAdmin(false);
-
- $groups = Craft::$app->getCategories()->getAllGroups();
-
- return $this->renderTemplate('settings/categories/index.twig', [
- 'categoryGroups' => $groups,
- 'readOnly' => !Cms::config()->allowAdminChanges,
- ]);
- }
-
- /**
- * Edit a category group.
- *
- * @param int|null $groupId The category group’s ID, if editing an existing group.
- * @param CategoryGroup|null $categoryGroup The category group being edited, if there were any validation errors.
- * @return Response
- * @throws NotFoundHttpException if the requested category group cannot be found
- */
- public function actionEditCategoryGroup(?int $groupId = null, ?CategoryGroup $categoryGroup = null): Response
- {
- $this->requireAdmin(false);
-
- $readOnly = !Cms::config()->allowAdminChanges;
-
- if ($groupId === null && $readOnly) {
- throw new ForbiddenHttpException('Administrative changes are disallowed in this environment.');
- }
-
- $variables = [];
-
- // Breadcrumbs
- $variables['crumbs'] = [
- [
- 'label' => t('Settings'),
- 'url' => UrlHelper::url('settings'),
- ],
- [
- 'label' => t('Categories'),
- 'url' => UrlHelper::url('settings/categories'),
- ],
- ];
-
- $variables['brandNewGroup'] = false;
-
- if ($groupId !== null) {
- if ($categoryGroup === null) {
- $categoryGroup = Craft::$app->getCategories()->getGroupById($groupId);
-
- if (!$categoryGroup) {
- throw new NotFoundHttpException('Category group not found');
- }
- }
-
- $variables['title'] = trim($categoryGroup->name) ?: t('Edit Category Group');
- } else {
- if ($categoryGroup === null) {
- $categoryGroup = new CategoryGroup();
- $variables['brandNewGroup'] = true;
- }
-
- $variables['title'] = t('Create a new category group');
- }
-
- $variables['groupId'] = $groupId;
- $variables['categoryGroup'] = $categoryGroup;
- $variables['readOnly'] = $readOnly;
-
- return $this->renderTemplate('settings/categories/_edit.twig', $variables);
- }
-
- /**
- * Save a category group.
- *
- * @return Response|null
- * @throws BadRequestHttpException
- */
- public function actionSaveGroup(): ?Response
- {
- $this->requirePostRequest();
- $this->requireAdmin();
-
- $categoriesService = Craft::$app->getCategories();
- $groupId = $this->request->getBodyParam('groupId');
-
- if ($groupId) {
- $group = $categoriesService->getGroupById($groupId);
- if (!$group) {
- throw new BadRequestHttpException("Invalid category group ID: $groupId");
- }
- } else {
- $group = new CategoryGroup();
- }
-
- // Main group settings
- $group->name = $this->request->getBodyParam('name');
- $group->handle = $this->request->getBodyParam('handle');
- $group->maxLevels = (int)$this->request->getBodyParam('maxLevels') ?: null;
- $group->defaultPlacement = $this->request->getBodyParam('defaultPlacement') ?? $group->defaultPlacement;
-
- // Site-specific settings
- $allSiteSettings = [];
-
- foreach (Sites::getAllSites() as $site) {
- $postedSettings = $this->request->getBodyParam('sites.' . $site->handle);
-
- $siteSettings = new CategoryGroup_SiteSettings();
- $siteSettings->siteId = $site->id;
-
- if ($siteSettings->hasUrls = !empty($postedSettings['uriFormat'])) {
- $siteSettings->uriFormat = $postedSettings['uriFormat'];
- $siteSettings->template = $postedSettings['template'] ?? null;
- }
-
- $allSiteSettings[$site->id] = $siteSettings;
- }
-
- $group->setSiteSettings($allSiteSettings);
-
- // Group the field layout
- $fieldLayout = app(Fields::class)->assembleLayoutFromPost();
- $fieldLayout->type = Category::class;
- $group->setFieldLayout($fieldLayout);
-
- // Save it
- if (!$categoriesService->saveGroup($group)) {
- $this->setFailFlash(t('Couldn’t save the category group.'));
-
- // Send the category group back to the template
- Craft::$app->getUrlManager()->setRouteParams([
- 'categoryGroup' => $group,
- ]);
-
- return null;
- }
-
- $this->setSuccessFlash(t('Category group saved.'));
- return $this->redirectToPostedUrl($group);
- }
-
- /**
- * Deletes a category group.
- *
- * @return Response
- */
- public function actionDeleteCategoryGroup(): Response
- {
- $this->requirePostRequest();
- $this->requireAcceptsJson();
- $this->requireAdmin();
-
- $groupId = $this->request->getRequiredBodyParam('id');
-
- Craft::$app->getCategories()->deleteGroupById($groupId);
-
- return $this->asSuccess();
- }
-
- // Categories
- // -------------------------------------------------------------------------
-
- /**
- * Displays the category index page.
- *
- * @param string|null $groupHandle The category group’s handle.
- * @return Response
- * @throws ForbiddenHttpException if the user is not permitted to edit categories
- */
- public function actionCategoryIndex(?string $groupHandle = null): Response
- {
- $groups = Craft::$app->getCategories()->getEditableGroups();
-
- if (empty($groups)) {
- throw new ForbiddenHttpException('User not permitted to edit categories');
- }
-
- return $this->renderTemplate('categories/_index.twig', [
- 'groupHandle' => $groupHandle,
- 'groups' => $groups,
- ]);
- }
-
- /**
- * Creates a new unpublished draft and redirects to its edit page.
- *
- * @param string $groupHandle The group’s handle
- * @return Response|null
- * @throws BadRequestHttpException
- * @throws ForbiddenHttpException
- * @throws ServerErrorHttpException
- * @since 4.0.0
- */
- public function actionCreate(string $groupHandle): ?Response
- {
- $group = Craft::$app->getCategories()->getGroupByHandle($groupHandle);
- if (!$group) {
- throw new BadRequestHttpException("Invalid category group handle: $groupHandle");
- }
-
- $site = Cp::requestedSite();
-
- if (!$site) {
- throw new ForbiddenHttpException('User not authorized to edit content in any sites.');
- }
-
- // Create & populate the draft
- $category = Craft::createObject(Category::class);
- $category->siteId = $site->id;
- $category->groupId = $group->id;
-
- // Structure parent
- if ($group->maxLevels !== 1) {
- // Set the initially selected parent
- $category->setParentId($this->request->getParam('parentId'));
- }
-
- // Make sure the user is allowed to create this category
- if (!Craft::$app->getElements()->canSave($category)) {
- throw new ForbiddenHttpException('User not authorized to save this category.');
- }
-
- // Title & slug
- $category->title = $this->request->getQueryParam('title');
- $category->slug = $this->request->getQueryParam('slug');
- if ($category->title && !$category->slug) {
- $category->slug = ElementHelper::generateSlug($category->title, null, $site->getLanguage());
- }
- if (!$category->slug) {
- $category->slug = ElementHelper::tempSlug();
- }
-
- // Save it
- $category->setScenario(Element::SCENARIO_ESSENTIALS);
- if (!app(Drafts::class)->saveElementAsDraft($category, Craft::$app->getUser()->getId(), null, null, false)) {
- return $this->asModelFailure($category, mb_ucfirst(t('Couldn’t create {type}.', [
- 'type' => Category::lowerDisplayName(),
- ])), 'category');
- }
-
- // Set its position in the structure if a before/after param was passed
- if ($nextId = $this->request->getParam('before')) {
- $nextCategory = Craft::$app->getCategories()->getCategoryById($nextId, $site->id, [
- 'structureId' => $group->structureId,
- ]);
- Structures::moveBefore($group->structureId, $category, $nextCategory);
- } elseif ($prevId = $this->request->getParam('after')) {
- $prevCategory = Craft::$app->getCategories()->getCategoryById($prevId, $site->id, [
- 'structureId' => $group->structureId,
- ]);
- Structures::moveAfter($group->structureId, $category, $prevCategory);
- }
-
- $editUrl = $category->getCpEditUrl();
-
- $response = $this->asModelSuccess($category, t('{type} created.', [
- 'type' => Category::displayName(),
- ]), 'category', array_filter([
- 'cpEditUrl' => $this->request->isCpRequest ? $editUrl : null,
- ]));
-
- if (!$this->request->getAcceptsJson()) {
- $response->redirect(UrlHelper::urlWithParams($editUrl, [
- 'fresh' => 1,
- ]));
- }
-
- return $response;
- }
-
- /**
- * Saves an category.
- *
- * @return Response|null
- * @throws ServerErrorHttpException
- * @deprecated in 4.0.0
- */
- public function actionSaveCategory(): ?Response
- {
- $this->requirePostRequest();
-
- $category = $this->_getCategoryModel();
- $categoryVariable = $this->request->getValidatedBodyParam('categoryVariable') ?? 'category';
-
- // Permission enforcement
- $this->_enforceEditCategoryPermissions($category);
-
- // Are we duplicating the category?
- if ($this->request->getBodyParam('duplicate')) {
- // Swap $category with the duplicate
- try {
- $category = Craft::$app->getElements()->duplicateElement($category);
- } catch (InvalidElementException $e) {
- /** @var Category $clone */
- $clone = $e->element;
-
- if ($this->request->getAcceptsJson()) {
- return $this->asModelFailure($clone);
- }
-
- // Send the original category back to the template, with any validation errors on the clone
- $category->addErrors($clone->getErrors());
-
- return $this->asModelFailure(
- $category,
- t('Couldn’t duplicate {type}.', [
- 'type' => Category::lowerDisplayName(),
- ]),
- 'category'
- );
- } catch (Throwable $e) {
- throw new ServerErrorHttpException(t('An error occurred when duplicating the category.'), 0, $e);
- }
- }
-
- // Populate the category with post data
- $this->_populateCategoryModel($category);
-
- // Save the category
- if ($category->enabled && $category->getEnabledForSite()) {
- $category->setScenario(Element::SCENARIO_LIVE);
- }
-
- if (!Craft::$app->getElements()->saveElement($category)) {
- return $this->asModelFailure(
- $category,
- mb_ucfirst(t('Couldn’t save {type}.', [
- 'type' => Category::lowerDisplayName(),
- ])),
- $categoryVariable
- );
- }
-
- return $this->asModelSuccess(
- $category,
- t('{type} saved.', [
- 'type' => Category::displayName(),
- ]),
- data: [
- 'id' => $category->id,
- 'title' => $category->title,
- 'slug' => $category->slug,
- 'status' => $category->getStatus(),
- 'url' => $category->getUrl(),
- 'cpEditUrl' => $category->getCpEditUrl(),
- ],
- );
- }
-
- /**
- * Fetches or creates a Category.
- *
- * @return Category
- * @throws BadRequestHttpException if the requested category group doesn't exist
- * @throws NotFoundHttpException if the requested category cannot be found
- */
- private function _getCategoryModel(): Category
- {
- $categoryId = $this->request->getBodyParam('sourceId') ?? $this->request->getBodyParam('categoryId');
- $siteId = $this->request->getBodyParam('siteId');
-
- if ($categoryId) {
- $category = Craft::$app->getCategories()->getCategoryById($categoryId, $siteId);
-
- if (!$category) {
- throw new NotFoundHttpException('Category not found');
- }
- } else {
- $groupId = $this->request->getRequiredBodyParam('groupId');
- if (($group = Craft::$app->getCategories()->getGroupById($groupId)) === null) {
- throw new BadRequestHttpException('Invalid category group ID: ' . $groupId);
- }
-
- $category = new Category();
- $category->groupId = $group->id;
- $category->fieldLayoutId = $group->fieldLayoutId;
-
- if ($siteId) {
- $category->siteId = $siteId;
- }
- }
-
- return $category;
- }
-
- /**
- * Enforces all Edit Category permissions.
- *
- * @param Category $category
- */
- private function _enforceEditCategoryPermissions(Category $category): void
- {
- if (Sites::isMultiSite()) {
- // Make sure they have access to this site
- $this->requirePermission('editSite:' . $category->getSite()->uid);
- }
-
- // Make sure the user is allowed to edit categories in this group
- $group = $category->getGroup();
- $this->requirePermission("saveCategories:$group->uid");
- }
-
- /**
- * Populates an Category with post data.
- *
- * @param Category $category
- */
- private function _populateCategoryModel(Category $category): void
- {
- // Set the category attributes, defaulting to the existing values for whatever is missing from the post data
- $category->slug = $this->request->getBodyParam('slug', $category->slug);
- $category->title = $this->request->getBodyParam('title', $category->title);
-
- $enabledForSite = $this->request->getBodyParam('enabledForSite');
- if (is_array($enabledForSite)) {
- // Make sure they are allowed to edit all of the posted site IDs
- $editableSiteIds = Sites::getEditableSiteIds()->all();
- if (array_diff(array_keys($enabledForSite), $editableSiteIds)) {
- throw new ForbiddenHttpException('User not permitted to edit the statuses for all the submitted site IDs');
- }
- // Set the global status to true if it's enabled for *any* sites, or if already enabled.
- $category->enabled = in_array(true, $enabledForSite, false) || $category->enabled;
- } else {
- $category->enabled = (bool)$this->request->getBodyParam('enabled', $category->enabled);
- }
- $category->setEnabledForSite($enabledForSite ?? $category->getEnabledForSite());
-
- $fieldsLocation = $this->request->getParam('fieldsLocation', 'fields');
- $category->setFieldValuesFromRequest($fieldsLocation);
-
- // Parent
- if (($parentId = $this->request->getBodyParam('parentId')) !== null) {
- $category->setParentId($parentId);
- }
- }
-
- /**
- * Returns the HTML for a Categories field input, based on a given list of selected category IDs.
- *
- * @return Response
- * @since 4.0.0
- */
- public function actionInputHtml(): Response
- {
- $this->requireAcceptsJson();
-
- $categoryIds = $this->request->getParam('categoryIds', []);
-
- $categories = [];
-
- if (!empty($categoryIds)) {
- /** @var Category[] $categories */
- $categories = Category::find()
- ->id($categoryIds)
- ->siteId($this->request->getParam('siteId'))
- ->status(null)
- ->all();
-
- // Fill in the gaps
- Structures::fillGapsInElements($categories);
-
- // Enforce the branch limit
- if ($branchLimit = $this->request->getParam('branchLimit')) {
- Structures::applyBranchLimitToElements($categories, $branchLimit);
- }
- }
-
- $html = $this->getView()->renderTemplate('_components/fieldtypes/Categories/input.twig',
- [
- 'elements' => $categories,
- 'id' => $this->request->getParam('id'),
- 'name' => $this->request->getParam('name'),
- 'selectionLabel' => $this->request->getParam('selectionLabel'),
- ]);
-
- return $this->asJson([
- 'html' => $html,
- ]);
- }
-}
diff --git a/yii2-adapter/legacy/controllers/GlobalsController.php b/yii2-adapter/legacy/controllers/GlobalsController.php
deleted file mode 100644
index 73a4f35a9a3..00000000000
--- a/yii2-adapter/legacy/controllers/GlobalsController.php
+++ /dev/null
@@ -1,244 +0,0 @@
-
- * @since 3.0.0
- */
-class GlobalsController extends Controller
-{
- /**
- * Index
- *
- * @return Response
- * @throws ForbiddenHttpException if the user isn't authorized to edit any global sets
- */
- public function actionIndex(): Response
- {
- $editableSets = Craft::$app->getGlobals()->getEditableSets();
-
- if (empty($editableSets)) {
- throw new ForbiddenHttpException('User not permitted to edit any global content');
- }
-
- return $this->redirect('globals/' . $editableSets[0]->handle);
- }
-
- /**
- * Saves a global set.
- *
- * @return Response|null
- * @throws NotFoundHttpException if the requested global set cannot be found
- * @throws BadRequestHttpException
- */
- public function actionSaveSet(): ?Response
- {
- $this->requirePostRequest();
- $this->requireAdmin();
-
- $globalSetId = $this->request->getBodyParam('setId');
-
- if ($globalSetId) {
- $globalSet = Craft::$app->getGlobals()->getSetById($globalSetId);
- if (!$globalSet) {
- throw new BadRequestHttpException("Invalid global set ID: $globalSetId");
- }
- } else {
- $globalSet = new GlobalSet();
- }
-
- // Set the simple stuff
- $globalSet->name = $this->request->getBodyParam('name');
- $globalSet->handle = $this->request->getBodyParam('handle');
-
- // Set the field layout
- $fieldLayout = app(Fields::class)->assembleLayoutFromPost();
- $fieldLayout->type = GlobalSet::class;
- $globalSet->setFieldLayout($fieldLayout);
-
- // Save it
- if (!Craft::$app->getGlobals()->saveSet($globalSet)) {
- $this->setFailFlash(mb_ucfirst(t('Couldn’t save {type}.', [
- 'type' => GlobalSet::lowerDisplayName(),
- ])));
-
- // Send the global set back to the template
- Craft::$app->getUrlManager()->setRouteParams([
- 'globalSet' => $globalSet,
- ]);
-
- return null;
- }
-
- $this->setSuccessFlash(t('{type} saved.', [
- 'type' => GlobalSet::displayName(),
- ]));
- return $this->redirectToPostedUrl($globalSet);
- }
-
- /**
- * Reorders global sets.
- *
- * @return Response
- * @since 3.7.0
- */
- public function actionReorderSets(): Response
- {
- $this->requirePostRequest();
- $this->requireAcceptsJson();
-
- $setIds = Json::decode($this->request->getRequiredBodyParam('ids'));
- Craft::$app->getGlobals()->reorderSets($setIds);
-
- return $this->asSuccess();
- }
-
- /**
- * Deletes a global set.
- *
- * @return Response
- */
- public function actionDeleteSet(): Response
- {
- $this->requirePostRequest();
- $this->requireAcceptsJson();
- $this->requireAdmin();
-
- $globalSetId = $this->request->getRequiredBodyParam('id');
-
- Craft::$app->getGlobals()->deleteGlobalSetById($globalSetId);
-
- return $this->asSuccess();
- }
-
- /**
- * Edits a global set's content.
- *
- * @param string $globalSetHandle The global set’s handle.
- * @param GlobalSet|null $globalSet The global set being edited, if there were any validation errors.
- * @return Response
- * @throws ForbiddenHttpException if the user is not permitted to edit the global set
- * @throws NotFoundHttpException if the requested site handle is invalid
- */
- public function actionEditContent(string $globalSetHandle, ?GlobalSet $globalSet = null): Response
- {
- $site = Cp::requestedSite();
- if (!$site) {
- throw new ForbiddenHttpException('User not permitted to edit content in any sites');
- }
-
- // Get the global sets the user is allowed to edit, in the requested site
- $editableGlobalSets = [];
-
- /** @var GlobalSet[] $globalSets */
- $globalSets = GlobalSet::find()
- ->siteId($site->id)
- ->all();
-
- foreach ($globalSets as $thisGlobalSet) {
- if (Craft::$app->getUser()->checkPermission('editGlobalSet:' . $thisGlobalSet->uid)) {
- $editableGlobalSets[$thisGlobalSet->handle] = $thisGlobalSet;
- }
- }
-
- if (empty($editableGlobalSets) || !isset($editableGlobalSets[$globalSetHandle])) {
- throw new ForbiddenHttpException('User not permitted to edit global set');
- }
-
- if ($globalSet === null) {
- $globalSet = $editableGlobalSets[$globalSetHandle];
- }
-
- // Prep the form tabs & content
- $form = $globalSet->getFieldLayout()->createForm($globalSet, false, [
- 'registerDeltas' => true,
- ]);
-
- // Render the template!
- return $this->renderTemplate('globals/_edit.twig', [
- 'bodyClass' => 'edit-global-set',
- 'editableGlobalSets' => $editableGlobalSets,
- 'globalSet' => $globalSet,
- 'tabs' => $form->getTabMenu(),
- 'fieldsHtml' => $form->render(),
- ]);
- }
-
- /**
- * Saves a global set's content.
- *
- * @return Response|null
- * @throws NotFoundHttpException if the requested global set cannot be found
- */
- public function actionSaveContent(): ?Response
- {
- $this->requirePostRequest();
-
- $globalSetId = $this->request->getRequiredBodyParam('setId');
- $siteId = $this->request->getBodyParam('siteId') ?: Sites::getPrimarySite()->id;
-
- $site = Sites::getSiteById($siteId);
- $globalSet = Craft::$app->getGlobals()->getSetById($globalSetId, $siteId);
-
- if (!$globalSet) {
- throw new NotFoundHttpException('Global set not found');
- }
-
- if (!$site) {
- throw new NotFoundHttpException('Site not found');
- }
-
- // Make sure the user is allowed to edit this global set and site
- $this->requirePermission('editGlobalSet:' . $globalSet->uid);
-
- if (Sites::isMultiSite()) {
- $this->requirePermission('editSite:' . $site->uid);
- }
-
- $fieldsLocation = $this->request->getParam('fieldsLocation', 'fields');
- $globalSet->setFieldValuesFromRequest($fieldsLocation);
- $globalSet->setScenario(Element::SCENARIO_LIVE);
-
- if (!Craft::$app->getElements()->saveElement($globalSet)) {
- $this->setFailFlash(mb_ucfirst(t('Couldn’t save {type}.', [
- 'type' => GlobalSet::lowerDisplayName(),
- ])));
-
- // Send the global set back to the template
- Craft::$app->getUrlManager()->setRouteParams([
- 'globalSet' => $globalSet,
- ]);
-
- return null;
- }
-
- $this->setSuccessFlash(t('{type} saved.', [
- 'type' => GlobalSet::displayName(),
- ]));
- return $this->redirectToPostedUrl();
- }
-}
diff --git a/yii2-adapter/legacy/controllers/SystemSettingsController.php b/yii2-adapter/legacy/controllers/SystemSettingsController.php
index 92a7b5d7ac2..3103f4a6215 100644
--- a/yii2-adapter/legacy/controllers/SystemSettingsController.php
+++ b/yii2-adapter/legacy/controllers/SystemSettingsController.php
@@ -8,17 +8,14 @@
namespace craft\controllers;
use Craft;
-use craft\elements\GlobalSet;
use craft\errors\MissingComponentException;
use craft\helpers\App;
use craft\helpers\Component;
use craft\helpers\MailerHelper;
-use craft\helpers\UrlHelper;
use craft\mail\transportadapters\BaseTransportAdapter;
use craft\mail\transportadapters\Sendmail;
use craft\mail\transportadapters\TransportAdapterInterface;
use craft\models\MailSettings;
-use craft\web\assets\admintable\AdminTableAsset;
use craft\web\Controller;
use CraftCms\Cms\Cms;
use CraftCms\Cms\ProjectConfig\ProjectConfig;
@@ -26,8 +23,6 @@
use CraftCms\Cms\Support\Html;
use Illuminate\Support\Facades\Config;
use yii\base\Exception;
-use yii\web\ForbiddenHttpException;
-use yii\web\NotFoundHttpException;
use yii\web\Response;
use function CraftCms\Cms\t;
@@ -218,95 +213,6 @@ public function actionTestEmailSettings(): void
]);
}
- /**
- * Global Set index
- *
- * @return Response
- * @since 5.3.0
- */
- public function actionGlobalSetIndex(): Response
- {
- $view = $this->getView();
- $view->registerAssetBundle(AdminTableAsset::class);
- $view->registerTranslations('app', [
- 'Global Set Name',
- 'No global sets exist yet.',
- ]);
-
- return $this->renderTemplate('settings/globals/_index.twig', [
- 'title' => t('Globals'),
- 'crumbs' => [
- [
- 'label' => t('Settings'),
- 'url' => UrlHelper::cpUrl('settings'),
- ],
- ],
- 'globalSets' => Craft::$app->getGlobals()->getAllSets(),
- 'buttonLabel' => mb_ucfirst(t('New {type}', [
- 'type' => GlobalSet::lowerDisplayName(),
- ])),
- 'readOnly' => $this->readOnly,
- ]);
- }
-
- /**
- * Global Set edit form.
- *
- * @param int|null $globalSetId The global set’s ID, if any.
- * @param GlobalSet|null $globalSet The global set being edited, if there were any validation errors.
- * @return Response
- * @throws NotFoundHttpException if the requested global set cannot be found
- */
- public function actionEditGlobalSet(?int $globalSetId = null, ?GlobalSet $globalSet = null): Response
- {
- if ($globalSetId === null && $this->readOnly) {
- throw new ForbiddenHttpException('Administrative changes are disallowed in this environment.');
- }
-
- if ($globalSet === null) {
- if ($globalSetId !== null) {
- $globalSet = Craft::$app->getGlobals()->getSetById($globalSetId);
-
- if (!$globalSet) {
- throw new NotFoundHttpException('Global set not found');
- }
- } else {
- $globalSet = new GlobalSet();
- }
- }
-
- if ($globalSet->id) {
- $title = trim($globalSet->name) ?: t('Edit {type}', [
- 'type' => GlobalSet::displayName(),
- ]);
- } else {
- $title = t('Create a new {type}', [
- 'type' => GlobalSet::lowerDisplayName(),
- ]);
- }
-
- // Breadcrumbs
- $crumbs = [
- [
- 'label' => t('Settings'),
- 'url' => UrlHelper::url('settings'),
- ],
- [
- 'label' => t('Globals'),
- 'url' => UrlHelper::url('settings/globals'),
- ],
- ];
-
- // Render the template!
- return $this->renderTemplate('settings/globals/_edit.twig', [
- 'globalSetId' => $globalSetId,
- 'globalSet' => $globalSet,
- 'title' => $title,
- 'crumbs' => $crumbs,
- 'readOnly' => $this->readOnly,
- ]);
- }
-
/**
* Creates a MailSettings model, populated with post data.
*
diff --git a/yii2-adapter/legacy/controllers/TagsController.php b/yii2-adapter/legacy/controllers/TagsController.php
deleted file mode 100644
index f74f8514812..00000000000
--- a/yii2-adapter/legacy/controllers/TagsController.php
+++ /dev/null
@@ -1,274 +0,0 @@
-
- * @since 3.0.0
- */
-class TagsController extends Controller
-{
- /**
- * Called before displaying the tag settings index page.
- *
- * @return Response
- */
- public function actionIndex(): Response
- {
- $this->requireAdmin(false);
-
- $tagGroups = Craft::$app->getTags()->getAllTagGroups();
-
- return $this->renderTemplate('settings/tags/index.twig', [
- 'tagGroups' => $tagGroups,
- 'readOnly' => !Cms::config()->allowAdminChanges,
- ]);
- }
-
- /**
- * Edit a tag group.
- *
- * @param int|null $tagGroupId The tag group’s ID, if any.
- * @param TagGroup|null $tagGroup The tag group being edited, if there were any validation errors.
- * @return Response
- * @throws NotFoundHttpException if the requested tag group cannot be found
- */
- public function actionEditTagGroup(?int $tagGroupId = null, ?TagGroup $tagGroup = null): Response
- {
- $this->requireAdmin(false);
-
- $readOnly = !Cms::config()->allowAdminChanges;
-
- if ($tagGroupId === null && $readOnly) {
- throw new ForbiddenHttpException('Administrative changes are disallowed in this environment.');
- }
-
-
- if ($tagGroupId !== null) {
- if ($tagGroup === null) {
- $tagGroup = Craft::$app->getTags()->getTagGroupById($tagGroupId);
-
- if (!$tagGroup) {
- throw new NotFoundHttpException('Tag group not found');
- }
- }
-
- $title = trim($tagGroup->name) ?: t('Edit Tag Group');
- } else {
- if ($tagGroup === null) {
- $tagGroup = new TagGroup();
- }
-
- $title = t('Create a new tag group');
- }
-
- // Breadcrumbs
- $crumbs = [
- [
- 'label' => t('Settings'),
- 'url' => UrlHelper::url('settings'),
- ],
- [
- 'label' => t('Tags'),
- 'url' => UrlHelper::url('settings/tags'),
- ],
- ];
-
- return $this->renderTemplate('settings/tags/_edit.twig', [
- 'tagGroupId' => $tagGroupId,
- 'tagGroup' => $tagGroup,
- 'title' => $title,
- 'crumbs' => $crumbs,
- 'readOnly' => $readOnly,
- ]);
- }
-
- /**
- * Save a tag group.
- *
- * @return Response|null
- * @throws BadRequestHttpException
- */
- public function actionSaveTagGroup(): ?Response
- {
- $this->requirePostRequest();
- $this->requireAdmin();
-
- $tagsService = Craft::$app->getTags();
- $groupId = $this->request->getBodyParam('tagGroupId');
-
- if ($groupId) {
- $group = $tagsService->getTagGroupById($groupId);
- if (!$group) {
- throw new BadRequestHttpException("Invalid tag group ID: $groupId");
- }
- } else {
- $group = new TagGroup();
- }
-
- // Set the simple stuff
- $group->name = $this->request->getBodyParam('name');
- $group->handle = $this->request->getBodyParam('handle');
-
- // Set the field layout
- $fieldLayout = app(Fields::class)->assembleLayoutFromPost();
- $fieldLayout->type = Tag::class;
- $group->setFieldLayout($fieldLayout);
-
- // Save it
- if (!Craft::$app->getTags()->saveTagGroup($group)) {
- $this->setFailFlash(t('Couldn’t save the tag group.'));
-
- // Send the tag group back to the template
- Craft::$app->getUrlManager()->setRouteParams([
- 'tagGroup' => $group,
- ]);
-
- return null;
- }
-
- $this->setSuccessFlash(t('Tag group saved.'));
- return $this->redirectToPostedUrl($group);
- }
-
- /**
- * Deletes a tag group.
- *
- * @return Response
- */
- public function actionDeleteTagGroup(): Response
- {
- $this->requirePostRequest();
- $this->requireAcceptsJson();
- $this->requireAdmin();
-
- $groupId = $this->request->getRequiredBodyParam('id');
-
- Craft::$app->getTags()->deleteTagGroupById($groupId);
-
- return $this->asSuccess();
- }
-
- /**
- * Searches for tags.
- *
- * @return Response
- */
- public function actionSearchForTags(): Response
- {
- $this->requirePostRequest();
- $this->requireAcceptsJson();
-
- $search = trim($this->request->getBodyParam('search'));
- $tagGroupId = $this->request->getBodyParam('tagGroupId');
- $excludeIds = $this->request->getBodyParam('excludeIds', []);
- $allowSimilarTags = Cms::config()->allowSimilarTags;
-
- /** @var Tag[] $tags */
- $tags = Tag::find()
- ->groupId($tagGroupId)
- ->title(Db::escapeParam($search) . '*')
- ->orderBy(['LENGTH([[title]])' => SORT_ASC])
- ->limit(5)
- ->all();
-
- $return = [];
- $exactMatches = [];
- $excludes = [];
- $tagTitleLengths = [];
- $exactMatch = false;
-
- if ($allowSimilarTags) {
- $search = Search::normalizeKeywords($search, [], false);
- } else {
- $search = Search::normalizeKeywords($search);
- }
-
- foreach ($tags as $tag) {
- $exclude = in_array($tag->id, $excludeIds, false);
-
- $return[] = [
- 'id' => $tag->id,
- 'title' => $tag->title,
- 'exclude' => $exclude,
- ];
-
- $tagTitleLengths[] = mb_strlen($tag->title);
-
- if ($allowSimilarTags) {
- $title = Search::normalizeKeywords($tag->title, [], false);
- } else {
- $title = Search::normalizeKeywords($tag->title);
- }
-
- if ($title == $search) {
- $exactMatches[] = 1;
- $exactMatch = true;
- } else {
- $exactMatches[] = 0;
- }
-
- $excludes[] = $exclude ? 1 : 0;
- }
-
- array_multisort($excludes, SORT_ASC, $exactMatches, SORT_DESC, $tagTitleLengths, $return);
-
- return $this->asJson([
- 'tags' => $return,
- 'exactMatch' => $exactMatch,
- ]);
- }
-
- /**
- * Creates a new tag.
- *
- * @return Response
- * @throws BadRequestHttpException if the groupId param is missing or invalid
- */
- public function actionCreateTag(): Response
- {
- $this->requireAcceptsJson();
-
- $groupId = $this->request->getRequiredBodyParam('groupId');
- if (($group = Craft::$app->getTags()->getTagGroupById($groupId)) === null) {
- throw new BadRequestHttpException('Invalid tag group ID: ' . $groupId);
- }
-
- $tag = new Tag();
- $tag->groupId = $group->id;
- $tag->title = trim($this->request->getRequiredBodyParam('title'));
-
- // Don't validate required custom fields
- if (!Craft::$app->getElements()->saveElement($tag)) {
- return $this->asFailure();
- }
-
- return $this->asSuccess(data: [
- 'id' => $tag->id,
- ]);
- }
-}
diff --git a/yii2-adapter/legacy/db/Table.php b/yii2-adapter/legacy/db/Table.php
index 2a390d6cb6d..d62a5345dc4 100644
--- a/yii2-adapter/legacy/db/Table.php
+++ b/yii2-adapter/legacy/db/Table.php
@@ -32,9 +32,6 @@ abstract class Table
public const BULKOPEVENTS = '{{%bulkopevents}}';
/** @since 3.4.14 */
public const CACHE = '{{%cache}}';
- public const CATEGORIES = '{{%categories}}';
- public const CATEGORYGROUPS = '{{%categorygroups}}';
- public const CATEGORYGROUPS_SITES = '{{%categorygroups_sites}}';
/** @since 3.4.0 */
public const CHANGEDATTRIBUTES = '{{%changedattributes}}';
/** @since 3.4.0 */
@@ -59,7 +56,6 @@ abstract class Table
public const ENTRYTYPES = '{{%entrytypes}}';
public const FIELDLAYOUTS = '{{%fieldlayouts}}';
public const FIELDS = '{{%fields}}';
- public const GLOBALSETS = '{{%globalsets}}';
/** @since 3.3.0 */
public const GQLSCHEMAS = '{{%gqlschemas}}';
/** @since 3.4.0 */
@@ -101,8 +97,6 @@ abstract class Table
public const STRUCTUREELEMENTS = '{{%structureelements}}';
public const STRUCTURES = '{{%structures}}';
public const SYSTEMMESSAGES = '{{%systemmessages}}';
- public const TAGGROUPS = '{{%taggroups}}';
- public const TAGS = '{{%tags}}';
public const TOKENS = '{{%tokens}}';
public const USERGROUPS = '{{%usergroups}}';
public const USERGROUPS_USERS = '{{%usergroups_users}}';
diff --git a/yii2-adapter/legacy/elements/Category.php b/yii2-adapter/legacy/elements/Category.php
deleted file mode 100644
index d9218846ba4..00000000000
--- a/yii2-adapter/legacy/elements/Category.php
+++ /dev/null
@@ -1,1029 +0,0 @@
-
- * @since 3.0.0
- */
-class Category extends Element
-{
- /**
- * @inheritdoc
- */
- public static function displayName(): string
- {
- return t('Category');
- }
-
- /**
- * @inheritdoc
- */
- public static function lowerDisplayName(): string
- {
- return t('category');
- }
-
- /**
- * @inheritdoc
- */
- public static function pluralDisplayName(): string
- {
- return t('Categories');
- }
-
- /**
- * @inheritdoc
- */
- public static function pluralLowerDisplayName(): string
- {
- return t('categories');
- }
-
- /**
- * @inheritdoc
- */
- public static function refHandle(): ?string
- {
- return 'category';
- }
-
- /**
- * @inheritdoc
- */
- public static function hasDrafts(): bool
- {
- return true;
- }
-
- /**
- * @inheritdoc
- */
- public static function trackChanges(): bool
- {
- return true;
- }
-
- /**
- * @inheritdoc
- */
- public static function hasTitles(): bool
- {
- return true;
- }
-
- /**
- * @inheritdoc
- */
- public static function hasUris(): bool
- {
- return true;
- }
-
- /**
- * @inheritdoc
- */
- public static function isLocalized(): bool
- {
- return true;
- }
-
- /**
- * @inheritdoc
- */
- public static function hasStatuses(): bool
- {
- return true;
- }
-
- /**
- * @inheritdoc
- * @return CategoryQuery The newly created [[CategoryQuery]] instance.
- */
- public static function find(): CategoryQuery
- {
- return new CategoryQuery(static::class);
- }
-
- /**
- * @inheritdoc
- * @return CategoryCondition
- */
- public static function createCondition(): ElementConditionInterface
- {
- return Craft::createObject(CategoryCondition::class, [static::class]);
- }
-
- /**
- * Returns the GraphQL type name that categories should use, based on their category group.
- *
- * @since 5.0.0
- */
- public static function gqlTypeName(CategoryGroup $categoryGroup): string
- {
- return sprintf('%s_Category', $categoryGroup->handle);
- }
-
- /**
- * @inheritdoc
- */
- public static function baseGqlType(): Type
- {
- return CategoryInterface::getType();
- }
-
- /**
- * @inheritdoc
- * @since 3.3.0
- */
- public static function gqlScopesByContext(mixed $context): array
- {
- /** @var CategoryGroup $context */
- return ['categorygroups.' . $context->uid];
- }
-
- /**
- * @inheritdoc
- */
- protected static function defineSources(string $context): array
- {
- $sources = [];
-
- if ($context === ElementSources::CONTEXT_INDEX) {
- $groups = Craft::$app->getCategories()->getEditableGroups();
- } else {
- $groups = Craft::$app->getCategories()->getAllGroups();
- }
-
- foreach ($groups as $group) {
- $sources[] = [
- 'key' => 'group:' . $group->uid,
- 'label' => t($group->name, category: 'site'),
- 'data' => ['handle' => $group->handle],
- 'criteria' => ['groupId' => $group->id],
- 'structureId' => $group->structureId,
- 'structureEditable' => Craft::$app->getRequest()->getIsConsoleRequest() || Craft::$app->getUser()->checkPermission("viewCategories:$group->uid"),
- ];
- }
-
- return $sources;
- }
-
- /**
- * @inheritdoc
- */
- protected static function defineFieldLayouts(?string $source): array
- {
- if ($source !== null && preg_match('/^group:(.+)$/', $source, $matches)) {
- $groups = array_filter([
- Craft::$app->getCategories()->getGroupByUid($matches[1]),
- ]);
- } else {
- $groups = Craft::$app->getCategories()->getAllGroups();
- }
-
- return array_map(fn(CategoryGroup $group) => $group->getFieldLayout(), $groups);
- }
-
- /**
- * @inheritdoc
- */
- protected static function defineActions(string $source): array
- {
- // Get the selected site
- $controller = Craft::$app->controller;
- if ($controller instanceof ElementIndexesController) {
- /** @var ElementQuery $elementQuery */
- $elementQuery = $controller->getElementQuery();
- } else {
- $elementQuery = null;
- }
- $site = $elementQuery && $elementQuery->siteId
- ? Sites::getSiteById($elementQuery->siteId)
- : Sites::getCurrentSite();
-
- // Get the group we need to check permissions on
- if (preg_match('/^group:(\d+)$/', $source, $matches)) {
- $group = Craft::$app->getCategories()->getGroupById((int)$matches[1]);
- } elseif (preg_match('/^group:(.+)$/', $source, $matches)) {
- $group = Craft::$app->getCategories()->getGroupByUid($matches[1]);
- } else {
- $group = null;
- }
-
- // Now figure out what we can do with it
- $actions = [];
- $elementsService = Craft::$app->getElements();
-
- if ($group) {
- // New Child
- if ($group->maxLevels != 1) {
- $newChildUrl = 'categories/' . $group->handle . '/new';
-
- if (Sites::isMultiSite()) {
- $newChildUrl .= '?site=' . $site->handle;
- }
-
- $actions[] = $elementsService->createAction([
- 'type' => NewChild::class,
- 'maxLevels' => $group->maxLevels,
- 'newChildUrl' => $newChildUrl,
- ]);
- }
-
- // Duplicate
- $actions[] = Duplicate::class;
-
- if ($group->maxLevels != 1) {
- $actions[] = [
- 'type' => Duplicate::class,
- 'deep' => true,
- ];
- }
-
- // Delete
- $actions[] = Delete::class;
-
- if ($group->maxLevels != 1) {
- $actions[] = [
- 'type' => Delete::class,
- 'withDescendants' => true,
- ];
- }
- }
-
- // Restore
- $actions[] = Restore::class;
-
- return $actions;
- }
-
- /**
- * @inheritdoc
- */
- protected static function includeSetStatusAction(): bool
- {
- return true;
- }
-
- /**
- * @inheritdoc
- */
- protected static function defineSortOptions(): array
- {
- return [
- 'title' => t('Title'),
- 'slug' => t('Slug'),
- 'uri' => t('URI'),
- [
- 'label' => t('Date Created'),
- 'orderBy' => 'dateCreated',
- 'defaultDir' => 'desc',
- ],
- [
- 'label' => t('Date Updated'),
- 'orderBy' => 'dateUpdated',
- 'defaultDir' => 'desc',
- ],
- ];
- }
-
- /**
- * @inheritdoc
- */
- protected static function defineTableAttributes(): array
- {
- return array_merge(parent::defineTableAttributes(), [
- 'ancestors' => ['label' => t('Ancestors')],
- 'parent' => ['label' => t('Parent')],
- ]);
- }
-
- /**
- * @inheritdoc
- */
- protected static function defineDefaultTableAttributes(string $source): array
- {
- return [
- 'status',
- 'link',
- ];
- }
-
- /**
- * @inheritdoc
- */
- protected static function defineCardAttributes(): array
- {
- return array_merge(parent::defineCardAttributes(), [
- 'parent' => [
- 'label' => t('Parent'),
- 'placeholder' => fn() => Html::tag(
- 'span',
- t('Parent {type} Title', ['type' => self::displayName()]),
- ['class' => 'card-placeholder'],
- ),
- ],
- ]);
- }
-
- /**
- * @inheritdoc
- */
- public static function attributePreviewHtml(array $attribute): mixed
- {
- return match ($attribute['value']) {
- 'parent' => $attribute['placeholder'],
- default => parent::attributePreviewHtml($attribute),
- };
- }
-
- /**
- * @var int|null Group ID
- */
- public ?int $groupId = null;
-
- /**
- * @var bool Whether the category was deleted along with its group
- * @see beforeDelete()
- */
- public bool $deletedWithGroup = false;
-
- /**
- * @inheritdoc
- */
- public function extraFields(): array
- {
- $names = parent::extraFields();
- $names[] = 'group';
- return $names;
- }
-
- /**
- * @inheritdoc
- */
- protected function defineRules(): array
- {
- $rules = parent::defineRules();
- $rules[] = [['groupId'], 'number', 'integerOnly' => true];
- return $rules;
- }
-
- /**
- * @inheritdoc
- * @since 3.5.0
- */
- protected function cacheTags(): array
- {
- return [
- "group:$this->groupId",
- ];
- }
-
- /**
- * @inheritdoc
- */
- public function getUriFormat(): ?string
- {
- $categoryGroupSiteSettings = $this->getGroup()->getSiteSettings();
-
- if (!isset($categoryGroupSiteSettings[$this->siteId])) {
- throw new InvalidConfigException('Category’s group (' . $this->groupId . ') is not enabled for site ' . $this->siteId);
- }
-
- return $categoryGroupSiteSettings[$this->siteId]->uriFormat;
- }
-
- /**
- * @inheritdoc
- */
- protected function previewTargets(): array
- {
- $previewTargets = [];
-
- if ($url = $this->getUrl()) {
- $previewTargets[] = [
- 'label' => t('Primary {type} page', [
- 'type' => self::lowerDisplayName(),
- ]),
- 'url' => $url,
- ];
- }
-
- return $previewTargets;
- }
-
- /**
- * @inheritdoc
- */
- protected function route(): array|string|null
- {
- // Make sure the category group is set to have URLs for this site
- $categoryGroupSiteSettings = $this->getGroup()->getSiteSettings()[$this->siteId] ?? null;
-
- if (!$categoryGroupSiteSettings?->hasUrls) {
- return null;
- }
-
- return [
- 'templates/render', [
- 'template' => (string)$categoryGroupSiteSettings->template,
- 'variables' => [
- 'category' => $this,
- ],
- ],
- ];
- }
-
- /**
- * @inheritdoc
- */
- protected function crumbs(): array
- {
- $group = $this->getGroup();
-
- $crumbs = [
- [
- 'label' => t('Categories'),
- 'url' => UrlHelper::url('categories'),
- ],
- [
- 'label' => t($group->name, category: 'site'),
- 'url' => UrlHelper::url('categories/' . $group->handle),
- ],
- ];
-
- $elementsService = Craft::$app->getElements();
- $user = Craft::$app->getUser()->getIdentity();
-
- $ancestors = $this->getAncestors();
- if ($ancestors instanceof ElementQueryInterface) {
- $ancestors->status(null);
- }
-
- foreach ($ancestors->all() as $ancestor) {
- if ($elementsService->canView($ancestor, $user)) {
- $crumbs[] = [
- 'html' => Cp::elementChipHtml($ancestor, ['class' => 'chromeless']),
- ];
- }
- }
-
- return $crumbs;
- }
-
- /**
- * @inheritdoc
- */
- public function createAnother(): ?self
- {
- $group = $this->getGroup();
-
- /** @var self $category */
- $category = Craft::createObject([
- 'class' => self::class,
- 'groupId' => $this->groupId,
- 'siteId' => $this->siteId,
- ]);
-
- $category->enabled = $this->enabled;
- $category->setEnabledForSite($this->getEnabledForSite());
-
- // Structure parent
- if ($group->maxLevels !== 1) {
- $category->setParentId($this->getParentId());
- }
-
- return $category;
- }
-
- /**
- * @inheritdoc
- */
- public function canView(User $user): bool
- {
- if (parent::canView($user)) {
- return true;
- }
-
- $group = $this->getGroup();
-
- if ($this->getIsDraft() && $this->getIsDerivative()) {
- return (
- $this->draftCreatorId === $user->id ||
- $user->can("viewPeerCategoryDrafts:$group->uid")
- );
- }
-
- return $user->can("viewCategories:$group->uid");
- }
-
- /**
- * @inheritdoc
- */
- public function canSave(User $user): bool
- {
- if (parent::canSave($user)) {
- return true;
- }
-
- $group = $this->getGroup();
-
- if ($this->getIsDraft()) {
- return (
- $this->draftCreatorId === $user->id ||
- $user->can("savePeerCategoryDrafts:$group->uid")
- );
- }
-
- return $user->can("saveCategories:$group->uid");
- }
-
- /**
- * @inheritdoc
- */
- public function canDuplicate(User $user): bool
- {
- if (parent::canDuplicate($user)) {
- return true;
- }
-
- $group = $this->getGroup();
- return $user->can("saveCategories:$group->uid");
- }
-
- /**
- * @inheritdoc
- */
- public function canDelete(User $user): bool
- {
- $group = $this->getGroup();
-
- if (parent::canDelete($user)) {
- return true;
- }
-
- if ($this->getIsDraft() && $this->getIsDerivative()) {
- return (
- $this->draftCreatorId === $user->id ||
- $user->can("deletePeerCategoryDrafts:$group->uid")
- );
- }
-
- return $user->can("deleteCategories:$group->uid");
- }
-
- /**
- * @inheritdoc
- */
- public function canCreateDrafts(User $user): bool
- {
- // Everyone with view permissions can create drafts
- return true;
- }
-
- /**
- * @inheritdoc
- */
- protected function cpEditUrl(): ?string
- {
- $group = $this->getGroup();
-
- $path = sprintf('categories/%s/%s', $group->handle, $this->getCanonicalId());
-
- // Ignore homepage/temp slugs
- if ($this->slug && !str_starts_with($this->slug, '__')) {
- $path .= sprintf('-%s', str_replace('/', '-', $this->slug));
- }
-
- return UrlHelper::cpUrl($path);
- }
-
- /**
- * @inheritdoc
- */
- public function getPostEditUrl(): ?string
- {
- return UrlHelper::cpUrl('categories');
- }
-
- /**
- * @inheritdoc
- */
- public function getFieldLayout(): ?FieldLayout
- {
- try {
- return $this->getGroup()->getFieldLayout();
- } catch (InvalidConfigException) {
- return null;
- }
- }
-
- /**
- * @inheritdoc
- */
- protected function metaFieldsHtml(bool $static): string
- {
- $fields = [
- $this->slugFieldHtml($static),
- ];
-
- $group = $this->getGroup();
-
- if ($group->maxLevels !== 1) {
- $fields[] = (function() use ($static, $group) {
- if ($parentId = $this->getParentId()) {
- $parent = Craft::$app->getCategories()->getCategoryById($parentId, $this->siteId, [
- 'drafts' => null,
- 'draftOf' => false,
- ]);
- } else {
- // If the category already has structure data, use it. Otherwise, use its canonical category
- /** @var self|null $parent */
- $parent = self::find()
- ->siteId($this->siteId)
- ->ancestorOf($this->lft ? $this : ($this->getIsCanonical() ? $this->id : $this->getCanonical(true)))
- ->ancestorDist(1)
- ->drafts(null)
- ->draftOf(false)
- ->status(null)
- ->one();
- }
-
- return Cp::elementSelectFieldHtml([
- 'label' => t('Parent'),
- 'id' => 'parentId',
- 'name' => 'parentId',
- 'elementType' => self::class,
- 'selectionLabel' => t('Choose'),
- 'sources' => ["group:$group->uid"],
- 'criteria' => $this->_parentOptionCriteria($group),
- 'limit' => 1,
- 'elements' => $parent ? [$parent] : [],
- 'disabled' => $static,
- 'describedBy' => 'parentId-label',
- ]);
- })();
- }
-
- $fields[] = parent::metaFieldsHtml($static);
-
- return implode("\n", $fields);
- }
-
- private function _parentOptionCriteria(CategoryGroup $group): array
- {
- $parentOptionCriteria = [
- 'siteId' => $this->siteId,
- 'groupId' => $group->id,
- 'status' => null,
- 'drafts' => null,
- 'draftOf' => false,
- ];
-
- // Prevent the current entry, or any of its descendants, from being selected as a parent
- if ($this->id) {
- $excludeIds = self::find()
- ->descendantOf($this)
- ->drafts(null)
- ->draftOf(false)
- ->status(null)
- ->ids();
- $excludeIds[] = $this->getCanonicalId();
- $parentOptionCriteria['id'] = array_merge(['not'], $excludeIds);
- }
-
- if ($group->maxLevels) {
- if ($this->id) {
- // Figure out how deep the ancestors go
- $maxDepth = self::find()
- ->select('level')
- ->descendantOf($this)
- ->status(null)
- ->leaves()
- ->scalar();
- $depth = 1 + ($maxDepth ?: $this->level) - $this->level;
- } else {
- $depth = 1;
- }
-
- $parentOptionCriteria['level'] = sprintf('<=%s', $group->maxLevels - $depth);
- }
-
- return $parentOptionCriteria;
- }
-
- /**
- * Returns the category's group.
- *
- * @return CategoryGroup
- * @throws InvalidConfigException if [[groupId]] is missing or invalid
- */
- public function getGroup(): CategoryGroup
- {
- if (!isset($this->groupId)) {
- throw new InvalidConfigException('Category is missing its group ID');
- }
-
- $group = Craft::$app->getCategories()->getGroupById($this->groupId);
-
- if (!$group) {
- throw new InvalidConfigException('Invalid category group ID: ' . $this->groupId);
- }
-
- return $group;
- }
-
- // Indexes, etc.
- // -------------------------------------------------------------------------
-
- /**
- * @inheritdoc
- */
- protected function inlineAttributeInputHtml(string $attribute): string
- {
- switch ($attribute) {
- case 'slug':
- return Cp::textHtml([
- 'name' => 'slug',
- 'value' => $this->slug,
- ]);
- default:
- return parent::inlineAttributeInputHtml($attribute);
- }
- }
-
- /**
- * @inheritdoc
- * @since 3.3.0
- */
- public function getGqlTypeName(): string
- {
- return static::gqlTypeName($this->getGroup());
- }
-
- // Events
- // -------------------------------------------------------------------------
-
- /**
- * @inheritdoc
- * @throws Exception if reasons
- */
- public function beforeSave(bool $isNew): bool
- {
- // Set the structure ID for Element::attributes() and afterSave()
- $this->structureId = $this->getGroup()->structureId;
-
- // Has the category been assigned a new parent?
- if (!$this->duplicateOf && $this->hasNewParent()) {
- if ($parentId = $this->getParentId()) {
- $parentCategory = Craft::$app->getCategories()->getCategoryById($parentId, $this->siteId, [
- 'drafts' => null,
- 'draftOf' => false,
- ]);
-
- if (!$parentCategory) {
- throw new InvalidConfigException("Invalid category ID: $parentId");
- }
- } else {
- $parentCategory = null;
- }
-
- $this->setParent($parentCategory);
- }
-
- return parent::beforeSave($isNew);
- }
-
- /**
- * @inheritdoc
- * @throws InvalidConfigException
- */
- public function afterSave(bool $isNew): void
- {
- if (!$this->propagating) {
- $group = $this->getGroup();
-
- // Get the category record
- if (!$isNew) {
- $record = CategoryRecord::findOne($this->id);
-
- if (!$record) {
- throw new InvalidConfigException("Invalid category ID: $this->id");
- }
- } else {
- $record = new CategoryRecord();
- $record->id = (int)$this->id;
- }
-
- $record->groupId = (int)$this->groupId;
- $record->save(false);
-
- if (!$this->duplicateOf || $this->updatingFromDerivative) {
- // Has the parent changed?
- if ($this->hasNewParent()) {
- $this->_placeInStructure($isNew, $group);
- }
-
- // Update the category's descendants, who may be using this category's URI in their own URIs
- if (!$isNew && $this->getIsCanonical()) {
- Craft::$app->getElements()->updateDescendantSlugsAndUris($this, true, true);
- }
- }
- }
-
- parent::afterSave($isNew);
- }
-
- private function _placeInStructure(bool $isNew, CategoryGroup $group): void
- {
- $parentId = $this->getParentId();
-
- // If this is a provisional draft and its new parent matches the canonical entry’s, just drop it from the structure
- if ($this->isProvisionalDraft) {
- $canonicalParentId = self::find()
- ->select(['elements.id'])
- ->ancestorOf($this->getCanonicalId())
- ->ancestorDist(1)
- ->status(null)
- ->scalar();
-
- if ($parentId == $canonicalParentId) {
- Structures::remove($this->structureId, $this);
- return;
- }
- }
-
- $mode = $isNew ? Mode::Insert : Mode::Auto;
-
- if (!$parentId) {
- if ($group->defaultPlacement === CategoryGroup::DEFAULT_PLACEMENT_BEGINNING) {
- Structures::prependToRoot($this->structureId, $this, $mode);
- } else {
- Structures::appendToRoot($this->structureId, $this, $mode);
- }
- } else {
- if ($group->defaultPlacement === CategoryGroup::DEFAULT_PLACEMENT_BEGINNING) {
- Structures::prepend($this->structureId, $this, $this->getParent(), $mode);
- } else {
- Structures::append($this->structureId, $this, $this->getParent(), $mode);
- }
- }
- }
-
- /**
- * @inheritdoc
- */
- public function beforeDelete(): bool
- {
- if (!parent::beforeDelete()) {
- return false;
- }
-
- // Update the category record
- $data = [
- 'deletedWithGroup' => $this->deletedWithGroup,
- 'parentId' => null,
- ];
-
- if ($this->structureId) {
- // Remember the parent ID, in case the category needs to be restored later
- $parentId = $this->ancestors()
- ->ancestorDist(1)
- ->status(null)
- ->select(['elements.id'])
- ->scalar();
- if ($parentId) {
- $data['parentId'] = $parentId;
- }
- }
-
- DB::table(\CraftCms\Cms\Database\Table::CATEGORIES)
- ->where('id', $this->id)
- ->update($data);
-
- return true;
- }
-
- /**
- * @inheritdoc
- */
- public function afterRestore(): void
- {
- $structureId = $this->getGroup()->structureId;
-
- // Add the category back into its structure
- /** @var self|null $parent */
- $parent = self::find()
- ->structureId($structureId)
- ->innerJoin(['j' => Table::CATEGORIES], '[[j.parentId]] = [[elements.id]]')
- ->andWhere(['j.id' => $this->id])
- ->one();
-
- if (!$parent) {
- Structures::appendToRoot($structureId, $this);
- } else {
- Structures::append($structureId, $this, $parent);
- }
-
- parent::afterRestore();
- }
-
- /**
- * @inheritdoc
- */
- public function afterMoveInStructure(int $structureId): void
- {
- // Was the category moved within its group's structure?
- if ($this->getGroup()->structureId == $structureId) {
- // Update its URI
- Craft::$app->getElements()->updateElementSlugAndUri($this, true, true, true);
-
- // Make sure that each of the category's ancestors are related wherever the category is related
- $newRelationValues = [];
-
- $ancestorIds = $this->ancestors()
- ->status(null)
- ->ids();
-
- $sources = DB::table(\CraftCms\Cms\Database\Table::RELATIONS)
- ->select(['fieldId', 'sourceId', 'sourceSiteId'])
- ->where('targetId', $this->id)
- ->get();
-
- $now = now();
-
- foreach ($sources as $source) {
- $existingAncestorRelations = DB::table(\CraftCms\Cms\Database\Table::RELATIONS)
- ->where([
- 'fieldId' => $source->fieldId,
- 'sourceId' => $source->sourceId,
- 'sourceSiteId' => $source->sourceSiteId,
- 'targetId' => $ancestorIds,
- ])
- ->pluck('targetId')
- ->all();
-
- $missingAncestorRelations = array_diff($ancestorIds, $existingAncestorRelations);
-
- foreach ($missingAncestorRelations as $categoryId) {
- $newRelationValues[] = [
- 'fieldId' => $source->fieldId,
- 'sourceId' => $source->sourceId,
- 'sourceSiteId' => $source->sourceSiteId,
- 'targetId' => $categoryId,
- 'dateCreated' => $now,
- 'dateUpdated' => $now,
- 'uid' => Str::uuid(),
- ];
- }
- }
-
- if (!empty($newRelationValues)) {
- DB::table(\CraftCms\Cms\Database\Table::RELATIONS)
- ->insert($newRelationValues);
- }
- }
-
- parent::afterMoveInStructure($structureId);
- }
-}
diff --git a/yii2-adapter/legacy/elements/GlobalSet.php b/yii2-adapter/legacy/elements/GlobalSet.php
deleted file mode 100644
index 86d1af535f4..00000000000
--- a/yii2-adapter/legacy/elements/GlobalSet.php
+++ /dev/null
@@ -1,361 +0,0 @@
-
- * @since 3.0.0
- */
-class GlobalSet extends Element implements FieldLayoutProviderInterface
-{
- /**
- * @since 4.4.6
- */
- public const SCENARIO_SAVE_SET = 'saveSet';
-
- /**
- * @inheritdoc
- */
- public static function displayName(): string
- {
- return t('Global set');
- }
-
- /**
- * @inheritdoc
- */
- public static function lowerDisplayName(): string
- {
- return t('global set');
- }
-
- /**
- * @inheritdoc
- */
- public static function pluralDisplayName(): string
- {
- return t('Global sets');
- }
-
- /**
- * @inheritdoc
- */
- public static function pluralLowerDisplayName(): string
- {
- return t('global sets');
- }
-
- /**
- * @inheritdoc
- */
- public static function refHandle(): ?string
- {
- return 'globalset';
- }
-
- /**
- * @inheritdoc
- */
- public static function isLocalized(): bool
- {
- return true;
- }
-
- /**
- * @inheritdoc
- */
- protected static function defineFieldLayouts(?string $source): array
- {
- // fetch them through the global set instances so $provider gets set
- return array_map(fn(self $globalSet) => $globalSet->getFieldLayout(), self::findAll());
- }
-
- /**
- * @return string|null
- */
- public function getRef(): ?string
- {
- return $this->handle;
- }
-
- /**
- * @inheritdoc
- */
- public function canView(User $user): bool
- {
- return $user->can("editGlobalSet:$this->uid");
- }
-
- /**
- * @inheritdoc
- */
- public function canSave(User $user): bool
- {
- return true;
- }
-
- /**
- * @inheritdoc
- */
- public function canDuplicate(User $user): bool
- {
- return false;
- }
-
- /**
- * @inheritdoc
- */
- public function canDelete(User $user): bool
- {
- return false;
- }
-
- /**
- * @inheritdoc
- * @return GlobalSetQuery The newly created [[GlobalSetQuery]] instance.
- */
- public static function find(): GlobalSetQuery
- {
- return new GlobalSetQuery(static::class);
- }
-
- /**
- * @inheritdoc
- * @since 3.3.0
- */
- public static function gqlScopesByContext(mixed $context): array
- {
- /** @var self $context */
- return ['globalsets.' . $context->uid];
- }
-
- /**
- * @var string|null Name
- */
- public ?string $name = null;
-
- /**
- * @var string|null Handle
- */
- public ?string $handle = null;
-
- /**
- * @var int|null Sort order
- * @since 3.7.0
- */
- public ?int $sortOrder = null;
-
- /**
- * Use the global set's name as its string representation.
- *
- * @return string
- */
- public function __toString(): string
- {
- if ($this->name) {
- return $this->name;
- }
-
- return parent::__toString();
- }
-
- /**
- * @inheritdoc
- */
- protected function defineBehaviors(): array
- {
- $behaviors = parent::defineBehaviors();
- $behaviors['fieldLayout'] = [
- 'class' => FieldLayoutBehavior::class,
- 'elementType' => self::class,
- ];
- return $behaviors;
- }
-
- /**
- * @inheritdoc
- */
- public function attributeLabels(): array
- {
- return array_merge(parent::attributeLabels(), [
- 'handle' => t('Handle'),
- 'name' => t('Name'),
- ]);
- }
-
- /**
- * @inheritdoc
- */
- protected function defineRules(): array
- {
- $rules = parent::defineRules();
- $rules[] = [['fieldLayoutId'], 'number', 'integerOnly' => true];
- $rules[] = [['name', 'handle'], 'string', 'max' => 255];
- $rules[] = [['name', 'handle'], 'required'];
-
- $rules[] = [
- ['name', 'handle'],
- UniqueValidator::class,
- 'targetClass' => GlobalSetRecord::class,
- 'except' => [self::SCENARIO_ESSENTIALS],
- ];
-
- $rules[] = [
- ['handle'],
- HandleValidator::class,
- 'reservedWords' => ['id', 'dateCreated', 'dateUpdated', 'uid', 'title'],
- 'except' => [self::SCENARIO_ESSENTIALS],
- ];
-
- $rules[] = [['fieldLayout'], function() {
- $fieldLayout = $this->getFieldLayout();
- if (!$fieldLayout->validate()) {
- $this->addModelErrors($fieldLayout, 'fieldLayout');
- }
- }];
-
- return $rules;
- }
-
- /**
- * @inheritdoc
- */
- public function scenarios(): array
- {
- $scenarios = parent::scenarios();
- $scenarios[self::SCENARIO_SAVE_SET] = $scenarios[self::SCENARIO_DEFAULT];
-
- return $scenarios;
- }
-
- /**
- * @inheritdoc
- */
- public function getHandle(): ?string
- {
- return $this->handle;
- }
-
- /**
- * @inheritdoc
- */
- public function getFieldLayout(): FieldLayout
- {
- /** @var FieldLayoutBehavior $behavior */
- $behavior = $this->getBehavior('fieldLayout');
- return $behavior->getFieldLayout();
- }
-
- /**
- * @inheritdoc
- */
- protected function cpEditUrl(): ?string
- {
- return UrlHelper::cpUrl('globals/' . $this->handle);
- }
-
- /**
- * @inheritdoc
- */
- public function getPostEditUrl(): ?string
- {
- return $this->getCpEditUrl();
- }
-
- /**
- * @inheritdoc
- * @since 3.3.0
- */
- public function getGqlTypeName(): string
- {
- return "{$this->handle}_GlobalSet";
- }
-
- // Events
- // -------------------------------------------------------------------------
-
- /**
- * @inheritdoc
- */
- public function beforeDelete(): bool
- {
- if (!parent::beforeDelete()) {
- return false;
- }
-
- try {
- $fieldLayout = $this->getFieldLayout();
- } catch (InvalidConfigException $e) {
- $fieldLayout = null;
- }
-
- if ($fieldLayout !== null) {
- app(Fields::class)->deleteLayout($fieldLayout);
- }
-
- return true;
- }
-
- /**
- * @inheritdoc
- */
- public function afterRestore(): void
- {
- // Restore the field layout too
- if (
- $this->fieldLayoutId &&
- !app(Fields::class)->restoreLayoutById($this->fieldLayoutId)
- ) {
- Craft::warning("Global set $this->id restored, but its field layout ($this->fieldLayoutId) was not.");
- }
-
- parent::afterRestore();
- }
-
- /**
- * Returns the global set’s config.
- *
- * @return array
- * @since 3.5.0
- */
- public function getConfig(): array
- {
- $config = [
- 'name' => $this->name,
- 'handle' => $this->handle,
- 'sortOrder' => (int)$this->sortOrder,
- ];
-
- $fieldLayout = $this->getFieldLayout();
-
- if ($fieldLayoutConfig = $fieldLayout->getConfig()) {
- $config['fieldLayouts'] = [
- $fieldLayout->uid => $fieldLayoutConfig,
- ];
- }
-
- return $config;
- }
-}
diff --git a/yii2-adapter/legacy/elements/Tag.php b/yii2-adapter/legacy/elements/Tag.php
deleted file mode 100644
index 1ee1da238cf..00000000000
--- a/yii2-adapter/legacy/elements/Tag.php
+++ /dev/null
@@ -1,374 +0,0 @@
-
- * @since 3.0.0
- */
-class Tag extends Element
-{
- /**
- * @inheritdoc
- */
- public static function displayName(): string
- {
- return t('Tag');
- }
-
- /**
- * @inheritdoc
- */
- public static function lowerDisplayName(): string
- {
- return t('tag');
- }
-
- /**
- * @inheritdoc
- */
- public static function pluralDisplayName(): string
- {
- return t('Tags');
- }
-
- /**
- * @inheritdoc
- */
- public static function pluralLowerDisplayName(): string
- {
- return t('tags');
- }
-
- /**
- * @inheritdoc
- */
- public static function refHandle(): ?string
- {
- return 'tag';
- }
-
- /**
- * @inheritdoc
- */
- public static function hasTitles(): bool
- {
- return true;
- }
-
- /**
- * @inheritdoc
- */
- public static function hasUris(): bool
- {
- return true;
- }
-
- /**
- * @inheritdoc
- */
- public static function isLocalized(): bool
- {
- return true;
- }
-
- /**
- * @inheritdoc
- * @return TagQuery The newly created [[TagQuery]] instance.
- */
- public static function find(): TagQuery
- {
- return new TagQuery(static::class);
- }
-
- /**
- * @inheritdoc
- * @return TagCondition
- */
- public static function createCondition(): ElementConditionInterface
- {
- return Craft::createObject(TagCondition::class, [static::class]);
- }
-
- /**
- * @inheritdoc
- */
- protected static function defineSources(string $context): array
- {
- $sources = [];
-
- foreach (Craft::$app->getTags()->getAllTagGroups() as $tagGroup) {
- $sources[] = [
- 'key' => 'taggroup:' . $tagGroup->uid,
- 'label' => t($tagGroup->name, category: 'site'),
- 'criteria' => ['groupId' => $tagGroup->id],
- ];
- }
-
- return $sources;
- }
-
- /**
- * Returns the GraphQL type name that tags should use, based on their tag group.
- *
- * @since 5.0.0
- */
- public static function gqlTypeName(TagGroup $tagGroup): string
- {
- return sprintf('%s_Tag', $tagGroup->handle);
- }
-
- /**
- * @inheritdoc
- */
- public static function baseGqlType(): Type
- {
- return TagInterface::getType();
- }
-
- /**
- * @inheritdoc
- * @since 3.3.0
- */
- public static function gqlScopesByContext(mixed $context): array
- {
- /** @var TagGroup $context */
- return ['taggroups.' . $context->uid];
- }
-
- /**
- * @inheritdoc
- */
- protected static function defineFieldLayouts(?string $source): array
- {
- if ($source !== null) {
- $groups = [];
- if (preg_match('/^taggroup:(.+)$/', $source, $matches)) {
- $group = Craft::$app->getTags()->getTagGroupByUid($matches[1]);
- if ($group) {
- $groups[] = $group;
- }
- }
- } else {
- $groups = Craft::$app->getTags()->getAllTagGroups();
- }
-
- return array_map(fn(TagGroup $group) => $group->getFieldLayout(), $groups);
- }
-
- /**
- * @var int|null Group ID
- */
- public ?int $groupId = null;
-
- /**
- * @var bool Whether the tag was deleted along with its group
- * @see beforeDelete()
- */
- public bool $deletedWithGroup = false;
-
- /**
- * @inheritdoc
- */
- public function extraFields(): array
- {
- $names = parent::extraFields();
- $names[] = 'group';
- return $names;
- }
-
- /**
- * @inheritdoc
- */
- protected function defineRules(): array
- {
- $rules = parent::defineRules();
- $rules[] = [['groupId'], 'number', 'integerOnly' => true];
- $rules[] = [
- ['title'],
- 'validateTitle',
- 'when' => fn(): bool => !$this->hasErrors('groupId') && !$this->hasErrors('title'),
- 'on' => [self::SCENARIO_DEFAULT, self::SCENARIO_LIVE],
- ];
- return $rules;
- }
-
- /**
- * Validates the tag title.
- *
- * @param string $attribute
- * @param array|null $params
- * @param InlineValidator $validator
- * @since 3.4.12
- */
- public function validateTitle(string $attribute, ?array $params, InlineValidator $validator): void
- {
- $query = self::find()
- ->groupId($this->groupId)
- ->siteId($this->siteId)
- ->title(Db::escapeParam($this->title));
-
- if ($this->id) {
- $query->andWhere(['not', ['elements.id' => $this->id]]);
- }
-
- if ($query->exists()) {
- $validator->addError($this, $attribute, t('{attribute} "{value}" has already been taken.'));
- }
- }
-
- /**
- * @inheritdoc
- * @since 3.5.0
- */
- protected function cacheTags(): array
- {
- return [
- "group:$this->groupId",
- ];
- }
-
- /**
- * @inheritdoc
- */
- public function canView(User $user): bool
- {
- return true;
- }
-
- /**
- * @inheritdoc
- */
- public function canSave(User $user): bool
- {
- return true;
- }
-
- /**
- * @inheritdoc
- */
- public function canDuplicate(User $user): bool
- {
- return true;
- }
-
- /**
- * @inheritdoc
- */
- public function canDelete(User $user): bool
- {
- return true;
- }
-
- /**
- * @inheritdoc
- */
- public function getFieldLayout(): ?FieldLayout
- {
- try {
- return $this->getGroup()->getFieldLayout();
- } catch (InvalidConfigException) {
- return null;
- }
- }
-
- /**
- * Returns the tag's group.
- *
- * @return TagGroup
- * @throws InvalidConfigException if [[groupId]] is missing or invalid
- */
- public function getGroup(): TagGroup
- {
- if (!isset($this->groupId)) {
- throw new InvalidConfigException('Tag is missing its group ID');
- }
-
- if (($group = Craft::$app->getTags()->getTagGroupById($this->groupId)) === null) {
- throw new InvalidConfigException('Invalid tag group ID: ' . $this->groupId);
- }
-
- return $group;
- }
-
- /**
- * @inheritdoc
- * @since 3.3.0
- */
- public function getGqlTypeName(): string
- {
- return static::gqlTypeName($this->getGroup());
- }
-
- // Events
- // -------------------------------------------------------------------------
-
- /**
- * @inheritdoc
- * @throws InvalidConfigException
- */
- public function afterSave(bool $isNew): void
- {
- if (!$this->propagating) {
- // Get the tag record
- if (!$isNew) {
- $record = TagRecord::findOne($this->id);
-
- if (!$record) {
- throw new InvalidConfigException("Invalid tag ID: $this->id");
- }
- } else {
- $record = new TagRecord();
- $record->id = (int)$this->id;
- }
-
- $record->groupId = (int)$this->groupId;
- $record->save(false);
- }
-
- parent::afterSave($isNew);
- }
-
- /**
- * @inheritdoc
- */
- public function beforeDelete(): bool
- {
- if (!parent::beforeDelete()) {
- return false;
- }
-
- // Update the tag record
- \Illuminate\Support\Facades\DB::table(Table::TAGS)
- ->where('id', $this->id)
- ->update([
- 'deletedWithGroup' => $this->deletedWithGroup,
- ]);
-
- return true;
- }
-}
diff --git a/yii2-adapter/legacy/elements/conditions/categories/CategoryCondition.php b/yii2-adapter/legacy/elements/conditions/categories/CategoryCondition.php
deleted file mode 100644
index cc2f151b57b..00000000000
--- a/yii2-adapter/legacy/elements/conditions/categories/CategoryCondition.php
+++ /dev/null
@@ -1,28 +0,0 @@
-
- * @since 4.0.0
- */
-class CategoryCondition extends ElementCondition
-{
- /**
- * @inheritdoc
- */
- protected function selectableConditionRules(): array
- {
- return array_merge(parent::selectableConditionRules(), [
- GroupConditionRule::class,
- HasDescendantsRule::class,
- LevelConditionRule::class,
- ]);
- }
-}
diff --git a/yii2-adapter/legacy/elements/conditions/categories/GroupConditionRule.php b/yii2-adapter/legacy/elements/conditions/categories/GroupConditionRule.php
deleted file mode 100644
index d3015f8f143..00000000000
--- a/yii2-adapter/legacy/elements/conditions/categories/GroupConditionRule.php
+++ /dev/null
@@ -1,66 +0,0 @@
-
- * @since 4.0.0
- */
-class GroupConditionRule extends BaseMultiSelectConditionRule implements ElementConditionRuleInterface
-{
- /**
- * @inheritdoc
- */
- public function getLabel(): string
- {
- return t('Category Group');
- }
-
- /**
- * @inheritdoc
- */
- public function getExclusiveQueryParams(): array
- {
- return ['group', 'groupId'];
- }
-
- /**
- * @inheritdoc
- */
- protected function options(): array
- {
- $groups = Craft::$app->getCategories()->getAllGroups();
- return Arr::pluck($groups, 'name', 'uid');
- }
-
- /**
- * @inheritdoc
- */
- public function modifyQuery(ElementQueryInterface $query): void
- {
- /** @var CategoryQuery $query */
- $categories = Craft::$app->getCategories();
- $query->groupId($this->paramValue(fn(string $uid) => $categories->getGroupByUid($uid)->id ?? null));
- }
-
- /**
- * @inheritdoc
- */
- public function matchElement(ElementInterface $element): bool
- {
- /** @var Category $element */
- return $this->matchValue($element->getGroup()->uid);
- }
-}
diff --git a/yii2-adapter/legacy/elements/conditions/tags/GroupConditionRule.php b/yii2-adapter/legacy/elements/conditions/tags/GroupConditionRule.php
deleted file mode 100644
index 9cf45d2909f..00000000000
--- a/yii2-adapter/legacy/elements/conditions/tags/GroupConditionRule.php
+++ /dev/null
@@ -1,66 +0,0 @@
-
- * @since 4.0.0
- */
-class GroupConditionRule extends BaseMultiSelectConditionRule implements ElementConditionRuleInterface
-{
- /**
- * @inheritdoc
- */
- public function getLabel(): string
- {
- return t('Tag Group');
- }
-
- /**
- * @inheritdoc
- */
- public function getExclusiveQueryParams(): array
- {
- return ['group', 'groupId'];
- }
-
- /**
- * @inheritdoc
- */
- protected function options(): array
- {
- $groups = Craft::$app->getTags()->getAllTagGroups();
- return Arr::pluck($groups, 'name', 'uid');
- }
-
- /**
- * @inheritdoc
- */
- public function modifyQuery(ElementQueryInterface $query): void
- {
- /** @var TagQuery $query */
- $tags = Craft::$app->getTags();
- $query->groupId($this->paramValue(fn($uid) => $tags->getTagGroupByUid($uid)->id ?? null));
- }
-
- /**
- * @inheritdoc
- */
- public function matchElement(ElementInterface $element): bool
- {
- /** @var Tag $element */
- return $this->matchValue($element->getGroup()->uid);
- }
-}
diff --git a/yii2-adapter/legacy/elements/conditions/tags/TagCondition.php b/yii2-adapter/legacy/elements/conditions/tags/TagCondition.php
deleted file mode 100644
index a375698d150..00000000000
--- a/yii2-adapter/legacy/elements/conditions/tags/TagCondition.php
+++ /dev/null
@@ -1,24 +0,0 @@
-
- * @since 4.0.0
- */
-class TagCondition extends ElementCondition
-{
- /**
- * @inheritdoc
- */
- protected function selectableConditionRules(): array
- {
- return array_merge(parent::selectableConditionRules(), [
- GroupConditionRule::class,
- ]);
- }
-}
diff --git a/yii2-adapter/legacy/elements/db/CategoryQuery.php b/yii2-adapter/legacy/elements/db/CategoryQuery.php
deleted file mode 100644
index c780ee4deae..00000000000
--- a/yii2-adapter/legacy/elements/db/CategoryQuery.php
+++ /dev/null
@@ -1,351 +0,0 @@
-
- *
- * @property-write string|string[]|CategoryGroup|null $group The category group(s) that resulting categories must belong to
- * @author Pixel & Tonic, Inc.
- * @since 3.0.0
- * @doc-path categories.md
- * @supports-structure-params
- * @supports-site-params
- * @supports-title-param
- * @supports-slug-param
- * @supports-uri-param
- * @supports-status-param
- * @replace {element} category
- * @replace {elements} categories
- * @replace {twig-method} craft.categories()
- * @replace {myElement} myCategory
- * @replace {element-class} \craft\elements\Category
- */
-class CategoryQuery extends ElementQuery
-{
- // General parameters
- // -------------------------------------------------------------------------
-
- /**
- * @var bool Whether to only return categories that the user has permission to edit.
- * @used-by editable()
- */
- public bool $editable = false;
-
- /**
- * @var mixed The category group ID(s) that the resulting categories must be in.
- * @used-by group()
- * @used-by groupId()
- */
- public mixed $groupId = null;
-
- /**
- * @inheritdoc
- */
- public function __set($name, $value)
- {
- if ($name === 'group') {
- $this->group($value);
- } else {
- parent::__set($name, $value);
- }
- }
-
- /**
- * @inheritdoc
- */
- public function init(): void
- {
- if (!isset($this->withStructure)) {
- $this->withStructure = true;
- }
-
- parent::init();
- }
-
- /**
- * Sets the [[$editable]] property.
- *
- * @param bool $value The property value (defaults to true)
- * @return static self reference
- * @uses $editable
- */
- public function editable(bool $value = true): static
- {
- $this->editable = $value;
- return $this;
- }
-
- /**
- * Narrows the query results based on the category groups the categories belong to.
- *
- * Possible values include:
- *
- * | Value | Fetches categories…
- * | - | -
- * | `'foo'` | in a group with a handle of `foo`.
- * | `'not foo'` | not in a group with a handle of `foo`.
- * | `['foo', 'bar']` | in a group with a handle of `foo` or `bar`.
- * | `['not', 'foo', 'bar']` | not in a group with a handle of `foo` or `bar`.
- * | a [[CategoryGroup|CategoryGroup]] object | in a group represented by the object.
- *
- * ---
- *
- * ```twig
- * {# Fetch categories in the Foo group #}
- * {% set {elements-var} = {twig-method}
- * .group('foo')
- * .all() %}
- * ```
- *
- * ```php
- * // Fetch categories in the Foo group
- * ${elements-var} = {php-method}
- * ->group('foo')
- * ->all();
- * ```
- *
- * @param mixed $value The property value
- * @return static self reference
- * @uses $groupId
- */
- public function group(mixed $value): static
- {
- if ($value instanceof CategoryGroup) {
- // Special case for a single category group, since we also want to capture the structure ID
- $this->structureId = ($value->structureId ?: false);
- $this->groupId = [$value->id];
- } elseif (Db::normalizeParam($value, function($item) {
- if (is_string($item)) {
- $item = Craft::$app->getCategories()->getGroupByHandle($item);
- }
- return $item instanceof CategoryGroup ? $item->id : null;
- })) {
- $this->groupId = $value;
- } else {
- $this->groupId = (new Query())
- ->select(['id'])
- ->from(Table::CATEGORYGROUPS)
- ->where(Db::parseParam('handle', $value))
- ->column();
- }
-
- return $this;
- }
-
- /**
- * Narrows the query results based on the category groups the categories belong to, per the groups’ IDs.
- *
- * Possible values include:
- *
- * | Value | Fetches categories…
- * | - | -
- * | `1` | in a group with an ID of 1.
- * | `'not 1'` | not in a group with an ID of 1.
- * | `[1, 2]` | in a group with an ID of 1 or 2.
- * | `['not', 1, 2]` | not in a group with an ID of 1 or 2.
- *
- * ---
- *
- * ```twig
- * {# Fetch categories in the group with an ID of 1 #}
- * {% set {elements-var} = {twig-method}
- * .groupId(1)
- * .all() %}
- * ```
- *
- * ```php
- * // Fetch categories in the group with an ID of 1
- * ${elements-var} = {php-method}
- * ->groupId(1)
- * ->all();
- * ```
- *
- * @param mixed $value The property value
- * @return static self reference
- * @uses $groupId
- */
- public function groupId(mixed $value): static
- {
- $this->groupId = $value;
- return $this;
- }
-
- /**
- * @inheritdoc
- */
- protected function beforePrepare(): bool
- {
- if (!parent::beforePrepare()) {
- return false;
- }
-
- $this->_normalizeGroupId();
-
- // See if 'group' was set to an invalid handle
- if ($this->groupId === []) {
- return false;
- }
-
- $this->joinElementTable(Table::CATEGORIES);
-
- $this->query->addSelect([
- 'categories.groupId',
- ]);
-
- $this->_applyEditableParam();
- $this->_applyGroupIdParam();
- $this->_applyRefParam();
-
- return true;
- }
-
- /**
- * Applies the 'editable' param to the query being prepared.
- *
- * @throws QueryAbortedException
- */
- private function _applyEditableParam(): void
- {
- if ($this->editable) {
- // Limit the query to only the category groups the user has permission to edit
- $this->subQuery->andWhere([
- 'categories.groupId' => Craft::$app->getCategories()->getEditableGroupIds(),
- ]);
- }
- }
-
- /**
- * Applies the 'groupId' param to the query being prepared.
- */
- private function _applyGroupIdParam(): void
- {
- if ($this->groupId) {
- $this->subQuery->andWhere(['categories.groupId' => $this->groupId]);
-
- // Should we set the structureId param?
- if (!isset($this->structureId) && count($this->groupId) === 1) {
- $structureId = (new Query())
- ->select(['structureId'])
- ->from([Table::CATEGORYGROUPS])
- ->where(Db::parseNumericParam('id', $this->groupId))
- ->scalar();
- $this->structureId = (int)$structureId ?: false;
- }
- }
- }
-
- /**
- * Normalizes the groupId param to an array of IDs or null
- */
- private function _normalizeGroupId(): void
- {
- if (empty($this->groupId)) {
- $this->groupId = is_array($this->groupId) ? [] : null;
- } elseif (is_numeric($this->groupId)) {
- $this->groupId = [$this->groupId];
- } elseif (!is_array($this->groupId) || !Arr::isNumeric($this->groupId)) {
- $this->groupId = (new Query())
- ->select(['id'])
- ->from([Table::CATEGORYGROUPS])
- ->where(Db::parseNumericParam('id', $this->groupId))
- ->column();
- }
- }
-
- /**
- * Applies the 'ref' param to the query being prepared.
- */
- private function _applyRefParam(): void
- {
- if (!$this->ref) {
- return;
- }
-
- $refs = $this->ref;
- if (!is_array($refs)) {
- $refs = is_string($refs) ? str($refs)->explode(',') : [$refs];
- }
-
- $condition = ['or'];
- $joinCategoryGroups = false;
-
- foreach ($refs as $ref) {
- $parts = array_filter(explode('/', $ref));
-
- if (!empty($parts)) {
- if (count($parts) == 1) {
- $condition[] = Db::parseParam('elements_sites.slug', $parts[0]);
- } else {
- $condition[] = [
- 'and',
- Db::parseParam('categorygroups.handle', $parts[0]),
- Db::parseParam('elements_sites.slug', $parts[1]),
- ];
- $joinCategoryGroups = true;
- }
- }
- }
-
- $this->subQuery->andWhere($condition);
-
- if ($joinCategoryGroups) {
- $this->subQuery->innerJoin(['categorygroups' => Table::CATEGORYGROUPS], '[[categorygroups.id]] = [[categories.groupId]]');
- }
- }
-
- /**
- * @inheritdoc
- * @since 3.5.0
- */
- protected function cacheTags(): array
- {
- $tags = [];
- if ($this->groupId) {
- foreach ($this->groupId as $groupId) {
- $tags[] = "group:$groupId";
- }
- }
- return $tags;
- }
-
- /**
- * @inheritdoc
- */
- protected function fieldLayouts(): array
- {
- $this->_normalizeGroupId();
-
- if ($this->groupId) {
- $fieldLayouts = [];
- $categoriesService = Craft::$app->getCategories();
- foreach ($this->groupId as $groupId) {
- $group = $categoriesService->getGroupById($groupId);
- if ($group) {
- $fieldLayouts[] = $group->getFieldLayout();
- }
- }
- return $fieldLayouts;
- }
-
- return parent::fieldLayouts();
- }
-}
diff --git a/yii2-adapter/legacy/elements/db/GlobalSetQuery.php b/yii2-adapter/legacy/elements/db/GlobalSetQuery.php
deleted file mode 100644
index a2ac37201a3..00000000000
--- a/yii2-adapter/legacy/elements/db/GlobalSetQuery.php
+++ /dev/null
@@ -1,166 +0,0 @@
-
- *
- * @author Pixel & Tonic, Inc.
- * @since 3.0.0
- * @doc-path globals.md
- * @supports-site-params
- * @replace {element} global set
- * @replace {elements} global sets
- * @replace {twig-method} craft.globalSets()
- * @replace {myElement} myGlobalSet
- * @replace {element-class} \craft\elements\GlobalSet
- */
-class GlobalSetQuery extends ElementQuery
-{
- /**
- * @inheritdoc
- */
- protected array $defaultOrderBy = ['globalsets.sortOrder' => SORT_ASC];
-
- // General parameters
- // -------------------------------------------------------------------------
-
- /**
- * @var bool Whether to only return global sets that the user has permission to edit.
- * @used-by editable()
- */
- public bool $editable = false;
-
- /**
- * @var string|string[]|null The handle(s) that the resulting global sets must have.
- * @used-by handle()
- */
- public string|array|null $handle = null;
-
- /**
- * Sets the [[$editable]] property.
- *
- * @param bool $value The property value (defaults to true)
- * @return static self reference
- * @uses $editable
- */
- public function editable(bool $value = true): static
- {
- $this->editable = $value;
- return $this;
- }
-
- /**
- * Narrows the query results based on the global sets’ handles.
- *
- * Possible values include:
- *
- * | Value | Fetches global sets…
- * | - | -
- * | `'foo'` | with a handle of `foo`.
- * | `'not foo'` | not with a handle of `foo`.
- * | `['foo', 'bar']` | with a handle of `foo` or `bar`.
- * | `['not', 'foo', 'bar']` | not with a handle of `foo` or `bar`.
- *
- * ---
- *
- * ```twig
- * {# Fetch the global set with a handle of 'foo' #}
- * {% set {element-var} = {twig-method}
- * .handle('foo')
- * .one() %}
- * ```
- *
- * ```php
- * // Fetch the global set with a handle of 'foo'
- * ${element-var} = {php-method}
- * ->handle('foo')
- * ->one();
- * ```
- *
- * @param mixed $value The property value
- * @return static self reference
- * @uses $handle
- */
- public function handle(mixed $value): static
- {
- $this->handle = $value;
- return $this;
- }
-
- /**
- * @inheritdoc
- */
- protected function beforePrepare(): bool
- {
- if (!parent::beforePrepare()) {
- return false;
- }
-
- $this->joinElementTable(Table::GLOBALSETS);
-
- $this->query->addSelect([
- 'globalsets.name',
- 'globalsets.handle',
- 'globalsets.sortOrder',
- 'globalsets.uid',
- ]);
-
- if ($this->handle) {
- $this->subQuery->andWhere(Db::parseParam('globalsets.handle', $this->handle));
- }
-
- $this->_applyEditableParam();
- $this->_applyRefParam();
-
- return true;
- }
-
-
- /**
- * Applies the 'ref' param to the query being prepared.
- */
- private function _applyRefParam(): void
- {
- if (!$this->ref) {
- return;
- }
-
- $this->subQuery->andWhere(Db::parseParam('globalsets.handle', $this->ref));
- }
-
- /**
- * Applies the 'editable' param to the query being prepared.
- *
- * @throws QueryAbortedException
- */
- private function _applyEditableParam(): void
- {
- if ($this->editable) {
- // Limit the query to only the global sets the user has permission to edit
- $editableSetIds = Craft::$app->getGlobals()->getEditableSetIds();
- $this->subQuery->andWhere(['elements.id' => $editableSetIds]);
- }
- }
-
- public function getCacheTags(): array
- {
- // no need to register cache tags for global set queries
- return [];
- }
-}
diff --git a/yii2-adapter/legacy/elements/db/TagQuery.php b/yii2-adapter/legacy/elements/db/TagQuery.php
deleted file mode 100644
index 574189fb47f..00000000000
--- a/yii2-adapter/legacy/elements/db/TagQuery.php
+++ /dev/null
@@ -1,254 +0,0 @@
-
- *
- * @property-write string|string[]|TagGroup|null $group The tag group(s) that resulting tags must belong to
- * @author Pixel & Tonic, Inc.
- * @since 3.0.0
- * @doc-path tags.md
- * @supports-site-params
- * @supports-title-param
- * @supports-uri-param
- * @replace {element} tag
- * @replace {elements} tags
- * @replace {twig-method} craft.tags()
- * @replace {myElement} myTag
- * @replace {element-class} \craft\elements\Tag
- */
-class TagQuery extends ElementQuery
-{
- /**
- * @inheritdoc
- */
- protected array $defaultOrderBy = ['elements_sites.title' => SORT_ASC];
-
- // General parameters
- // -------------------------------------------------------------------------
-
- /**
- * @var mixed The tag group ID(s) that the resulting tags must be in.
- * ---
- * ```php
- * // fetch tags in the Topics group
- * $tags = \craft\elements\Tag::find()
- * ->group('topics')
- * ->all();
- * ```
- * ```twig
- * {# fetch tags in the Topics group #}
- * {% set tags = craft.tags()
- * .group('topics')
- * .all() %}
- * ```
- * @used-by group()
- * @used-by groupId()
- */
- public mixed $groupId = null;
-
- /**
- * @inheritdoc
- */
- public function __set($name, $value)
- {
- if ($name === 'group') {
- $this->group($value);
- } else {
- parent::__set($name, $value);
- }
- }
-
- /**
- * Narrows the query results based on the tag groups the tags belong to.
- *
- * Possible values include:
- *
- * | Value | Fetches tags…
- * | - | -
- * | `'foo'` | in a group with a handle of `foo`.
- * | `'not foo'` | not in a group with a handle of `foo`.
- * | `['foo', 'bar']` | in a group with a handle of `foo` or `bar`.
- * | `['not', 'foo', 'bar']` | not in a group with a handle of `foo` or `bar`.
- * | a [[TagGroup|TagGroup]] object | in a group represented by the object.
- *
- * ---
- *
- * ```twig
- * {# Fetch tags in the Foo group #}
- * {% set {elements-var} = {twig-method}
- * .group('foo')
- * .all() %}
- * ```
- *
- * ```php
- * // Fetch tags in the Foo group
- * ${elements-var} = {php-method}
- * ->group('foo')
- * ->all();
- * ```
- *
- * @param mixed $value The property value
- * @return static self reference
- * @uses $groupId
- */
- public function group(mixed $value): static
- {
- if (Db::normalizeParam($value, function($item) {
- if (is_string($item)) {
- $item = Craft::$app->getTags()->getTagGroupByHandle($item);
- }
- return $item instanceof TagGroup ? $item->id : null;
- })) {
- $this->groupId = $value;
- } else {
- $this->groupId = (new Query())
- ->select(['id'])
- ->from([Table::TAGGROUPS])
- ->where(Db::parseParam('handle', $value))
- ->column() ?: false;
- }
-
- return $this;
- }
-
- /**
- * Narrows the query results based on the tag groups the tags belong to, per the groups’ IDs.
- *
- * Possible values include:
- *
- * | Value | Fetches tags…
- * | - | -
- * | `1` | in a group with an ID of 1.
- * | `'not 1'` | not in a group with an ID of 1.
- * | `[1, 2]` | in a group with an ID of 1 or 2.
- * | `['not', 1, 2]` | not in a group with an ID of 1 or 2.
- *
- * ---
- *
- * ```twig
- * {# Fetch tags in the group with an ID of 1 #}
- * {% set {elements-var} = {twig-method}
- * .groupId(1)
- * .all() %}
- * ```
- *
- * ```php
- * // Fetch tags in the group with an ID of 1
- * ${elements-var} = {php-method}
- * ->groupId(1)
- * ->all();
- * ```
- *
- * @param mixed $value The property value
- * @return static self reference
- * @uses $groupId
- */
- public function groupId(mixed $value): static
- {
- $this->groupId = $value;
- return $this;
- }
-
- /**
- * @inheritdoc
- */
- protected function beforePrepare(): bool
- {
- if (!parent::beforePrepare()) {
- return false;
- }
-
- $this->_normalizeGroupId();
-
- $this->joinElementTable(Table::TAGS);
-
- $this->query->addSelect([
- 'tags.groupId',
- ]);
-
- if ($this->groupId) {
- $this->subQuery->andWhere(['tags.groupId' => $this->groupId]);
- }
-
- return true;
- }
-
- /**
- * Normalizes the groupId param to an array of IDs or null
- *
- * @throws QueryAbortedException
- */
- private function _normalizeGroupId(): void
- {
- if ($this->groupId === false) {
- throw new QueryAbortedException();
- }
-
- if (empty($this->groupId)) {
- $this->groupId = null;
- } elseif (is_numeric($this->groupId)) {
- $this->groupId = [$this->groupId];
- } elseif (!is_array($this->groupId) || !Arr::isNumeric($this->groupId)) {
- $this->groupId = (new Query())
- ->select(['id'])
- ->from([Table::TAGGROUPS])
- ->where(Db::parseNumericParam('id', $this->groupId))
- ->column();
- }
- }
-
- /**
- * @inheritdoc
- * @since 3.5.0
- */
- protected function cacheTags(): array
- {
- $tags = [];
- if ($this->groupId) {
- foreach ($this->groupId as $groupId) {
- $tags[] = "group:$groupId";
- }
- }
- return $tags;
- }
-
- /**
- * @inheritdoc
- */
- protected function fieldLayouts(): array
- {
- if ($this->groupId) {
- $fieldLayouts = [];
- $tagsService = Craft::$app->getTags();
- foreach ($this->groupId as $groupId) {
- $group = $tagsService->getTagGroupById($groupId);
- if ($group) {
- $fieldLayouts[] = $group->getFieldLayout();
- }
- }
- return $fieldLayouts;
- }
-
- return parent::fieldLayouts();
- }
-}
diff --git a/yii2-adapter/legacy/errors/CategoryGroupNotFoundException.php b/yii2-adapter/legacy/errors/CategoryGroupNotFoundException.php
deleted file mode 100644
index 069cc25f3e3..00000000000
--- a/yii2-adapter/legacy/errors/CategoryGroupNotFoundException.php
+++ /dev/null
@@ -1,27 +0,0 @@
-
- * @since 3.0.0
- */
-class CategoryGroupNotFoundException extends Exception
-{
- /**
- * @return string the user-friendly name of this exception
- */
- public function getName(): string
- {
- return 'Category group not found';
- }
-}
diff --git a/yii2-adapter/legacy/errors/GlobalSetNotFoundException.php b/yii2-adapter/legacy/errors/GlobalSetNotFoundException.php
deleted file mode 100644
index bd09fc21ca8..00000000000
--- a/yii2-adapter/legacy/errors/GlobalSetNotFoundException.php
+++ /dev/null
@@ -1,25 +0,0 @@
-
- * @since 3.0.0
- */
-class GlobalSetNotFoundException extends ElementNotFoundException
-{
- /**
- * @return string the user-friendly name of this exception
- */
- public function getName(): string
- {
- return 'Global set not found';
- }
-}
diff --git a/yii2-adapter/legacy/errors/TagGroupNotFoundException.php b/yii2-adapter/legacy/errors/TagGroupNotFoundException.php
deleted file mode 100644
index 657dcc5d0eb..00000000000
--- a/yii2-adapter/legacy/errors/TagGroupNotFoundException.php
+++ /dev/null
@@ -1,27 +0,0 @@
-
- * @since 3.0.0
- */
-class TagGroupNotFoundException extends Exception
-{
- /**
- * @return string the user-friendly name of this exception
- */
- public function getName(): string
- {
- return 'Tag group not found';
- }
-}
diff --git a/yii2-adapter/legacy/events/CategoryGroupEvent.php b/yii2-adapter/legacy/events/CategoryGroupEvent.php
deleted file mode 100644
index 0a427219786..00000000000
--- a/yii2-adapter/legacy/events/CategoryGroupEvent.php
+++ /dev/null
@@ -1,30 +0,0 @@
-
- * @since 3.0.0
- */
-class CategoryGroupEvent extends Event
-{
- /**
- * @var CategoryGroup The category group model associated with the event.
- */
- public CategoryGroup $categoryGroup;
-
- /**
- * @var bool Whether the category group is brand new
- */
- public bool $isNew = false;
-}
diff --git a/yii2-adapter/legacy/events/GlobalSetEvent.php b/yii2-adapter/legacy/events/GlobalSetEvent.php
deleted file mode 100644
index ad232937d79..00000000000
--- a/yii2-adapter/legacy/events/GlobalSetEvent.php
+++ /dev/null
@@ -1,30 +0,0 @@
-
- * @since 3.0.0
- */
-class GlobalSetEvent extends Event
-{
- /**
- * @var GlobalSet The global set model associated with the event.
- */
- public GlobalSet $globalSet;
-
- /**
- * @var bool Whether the global set is brand new
- */
- public bool $isNew = false;
-}
diff --git a/yii2-adapter/legacy/events/TagGroupEvent.php b/yii2-adapter/legacy/events/TagGroupEvent.php
deleted file mode 100644
index 34063ec33f3..00000000000
--- a/yii2-adapter/legacy/events/TagGroupEvent.php
+++ /dev/null
@@ -1,30 +0,0 @@
-
- * @since 3.0.0
- */
-class TagGroupEvent extends Event
-{
- /**
- * @var TagGroup The tag group model associated with the event.
- */
- public TagGroup $tagGroup;
-
- /**
- * @var bool Whether the tag group is brand new
- */
- public bool $isNew = false;
-}
diff --git a/yii2-adapter/legacy/fields/Categories.php b/yii2-adapter/legacy/fields/Categories.php
deleted file mode 100644
index dfd73792680..00000000000
--- a/yii2-adapter/legacy/fields/Categories.php
+++ /dev/null
@@ -1,21 +0,0 @@
-_argumentHandlers = [
'relatedToEntries' => RelatedEntries::class,
'relatedToAssets' => RelatedAssets::class,
- 'relatedToCategories' => RelatedCategories::class,
- 'relatedToTags' => RelatedTags::class,
'relatedToUsers' => RelatedUsers::class,
'site' => Site::class,
'siteId' => SiteId::class,
diff --git a/yii2-adapter/legacy/gql/ElementQueryConditionBuilder.php b/yii2-adapter/legacy/gql/ElementQueryConditionBuilder.php
index e094f2806bd..66a0a6d57e1 100644
--- a/yii2-adapter/legacy/gql/ElementQueryConditionBuilder.php
+++ b/yii2-adapter/legacy/gql/ElementQueryConditionBuilder.php
@@ -19,7 +19,6 @@
use craft\services\Gql;
use CraftCms\Cms\Field\Assets as AssetField;
use CraftCms\Cms\Field\BaseRelationField;
-use CraftCms\Cms\Field\Categories as CategoryField;
use CraftCms\Cms\Field\Contracts\EagerLoadingFieldInterface;
use CraftCms\Cms\Field\Contracts\FieldInterface;
use CraftCms\Cms\Field\Entries as EntryField;
@@ -304,7 +303,7 @@ private function _getKnownSpecialEagerLoadNodes(): array
'drafts' => [BaseRelationField::class, 'canBeAliased' => false],
'revisions' => [BaseRelationField::class, 'canBeAliased' => false],
'revisionCreator' => [BaseRelationField::class, 'canBeAliased' => false],
- self::LOCALIZED_NODENAME => [CategoryField::class, EntryField::class],
+ self::LOCALIZED_NODENAME => [EntryField::class],
];
// Fire a 'registerGqlEagerLoadableFields' event
diff --git a/yii2-adapter/legacy/gql/arguments/elements/Category.php b/yii2-adapter/legacy/gql/arguments/elements/Category.php
deleted file mode 100644
index 53fbcdded12..00000000000
--- a/yii2-adapter/legacy/gql/arguments/elements/Category.php
+++ /dev/null
@@ -1,72 +0,0 @@
-
- * @since 3.3.0
- */
-class Category extends StructureElementArguments
-{
- /**
- * @inheritdoc
- */
- public static function getArguments(): array
- {
- return array_merge(parent::getArguments(), self::getContentArguments(), [
- 'editable' => [
- 'name' => 'editable',
- 'type' => Type::boolean(),
- 'description' => 'Whether to only return categories that the user has permission to edit.',
- ],
- 'group' => [
- 'name' => 'group',
- 'type' => Type::listOf(Type::string()),
- 'description' => 'Narrows the query results based on the category groups the categories belong to per the group’s handles.',
- ],
- 'groupId' => [
- 'name' => 'groupId',
- 'type' => Type::listOf(QueryArgument::getType()),
- 'description' => 'Narrows the query results based on the category groups the categories belong to, per the groups’ IDs.',
- ],
- ]);
- }
-
- /**
- * @inheritdoc
- */
- public static function getContentArguments(): array
- {
- $categoryGroupFieldArguments = Craft::$app->getGql()->getContentArguments(Craft::$app->getCategories()->getAllGroups(), CategoryElement::class);
- return array_merge(parent::getContentArguments(), $categoryGroupFieldArguments);
- }
-
- /**
- * @inheritdoc
- */
- public static function getDraftArguments(): array
- {
- return [];
- }
-
- /**
- * @inheritdoc
- */
- public static function getRevisionArguments(): array
- {
- return [];
- }
-}
diff --git a/yii2-adapter/legacy/gql/arguments/elements/GlobalSet.php b/yii2-adapter/legacy/gql/arguments/elements/GlobalSet.php
deleted file mode 100644
index 887c33c21d7..00000000000
--- a/yii2-adapter/legacy/gql/arguments/elements/GlobalSet.php
+++ /dev/null
@@ -1,61 +0,0 @@
-
- * @since 3.3.0
- */
-class GlobalSet extends ElementArguments
-{
- /**
- * @inheritdoc
- */
- public static function getArguments(): array
- {
- return array_merge(parent::getArguments(), self::getContentArguments(), [
- 'handle' => [
- 'name' => 'handle',
- 'type' => Type::listOf(Type::string()),
- 'description' => 'Narrows the query results based on the global sets’ handles.',
- ],
- ]);
- }
-
- /**
- * @inheritdoc
- */
- public static function getContentArguments(): array
- {
- $globalSetFieldArgument = Craft::$app->getGql()->getContentArguments(Craft::$app->getGlobals()->getAllSets(), GlobalSetElement::class);
- return array_merge(parent::getContentArguments(), $globalSetFieldArgument);
- }
-
- /**
- * @inheritdoc
- */
- public static function getDraftArguments(): array
- {
- return [];
- }
-
- /**
- * @inheritdoc
- */
- public static function getRevisionArguments(): array
- {
- return [];
- }
-}
diff --git a/yii2-adapter/legacy/gql/arguments/elements/Tag.php b/yii2-adapter/legacy/gql/arguments/elements/Tag.php
deleted file mode 100644
index 2e790bfad78..00000000000
--- a/yii2-adapter/legacy/gql/arguments/elements/Tag.php
+++ /dev/null
@@ -1,67 +0,0 @@
-
- * @since 3.3.0
- */
-class Tag extends ElementArguments
-{
- /**
- * @inheritdoc
- */
- public static function getArguments(): array
- {
- return array_merge(parent::getArguments(), self::getContentArguments(), [
- 'group' => [
- 'name' => 'group',
- 'type' => Type::listOf(Type::string()),
- 'description' => 'Narrows the query results based on the tag groups the tags belong to per the group’s handles.',
- ],
- 'groupId' => [
- 'name' => 'groupId',
- 'type' => Type::listOf(QueryArgument::getType()),
- 'description' => 'Narrows the query results based on the tag groups the tags belong to, per the groups’ IDs.',
- ],
- ]);
- }
-
- /**
- * @inheritdoc
- */
- public static function getContentArguments(): array
- {
- $tagGroupFieldArguments = Craft::$app->getGql()->getContentArguments(Craft::$app->getTags()->getAllTagGroups(), TagElement::class);
- return array_merge(parent::getContentArguments(), $tagGroupFieldArguments);
- }
-
- /**
- * @inheritdoc
- */
- public static function getDraftArguments(): array
- {
- return [];
- }
-
- /**
- * @inheritdoc
- */
- public static function getRevisionArguments(): array
- {
- return [];
- }
-}
diff --git a/yii2-adapter/legacy/gql/base/ElementArguments.php b/yii2-adapter/legacy/gql/base/ElementArguments.php
index f810a9bf32b..1943b02a9c8 100644
--- a/yii2-adapter/legacy/gql/base/ElementArguments.php
+++ b/yii2-adapter/legacy/gql/base/ElementArguments.php
@@ -9,9 +9,7 @@
use craft\gql\GqlEntityRegistry;
use craft\gql\types\input\criteria\AssetRelation;
-use craft\gql\types\input\criteria\CategoryRelation;
use craft\gql\types\input\criteria\EntryRelation;
-use craft\gql\types\input\criteria\TagRelation;
use craft\gql\types\input\criteria\UserRelation;
use craft\gql\types\QueryArgument;
use craft\helpers\Gql;
@@ -125,18 +123,6 @@ public static function getArguments(): array
'type' => Type::listOf(UserRelation::getType()),
'description' => 'Narrows the query results to elements that relate to a use list defined with this argument.',
],
- 'relatedToCategories' => [
- 'name' => 'relatedToCategories',
- // don't lazy load the type (see https://github.com/craftcms/cms/issues/17858)
- 'type' => Type::listOf(CategoryRelation::getType()),
- 'description' => 'Narrows the query results to elements that relate to a category list defined with this argument.',
- ],
- 'relatedToTags' => [
- 'name' => 'relatedToTags',
- // don't lazy load the type (see https://github.com/craftcms/cms/issues/17858)
- 'type' => Type::listOf(TagRelation::getType()),
- 'description' => 'Narrows the query results to elements that relate to a tag list defined with this argument.',
- ],
'relatedToAll' => [
'name' => 'relatedToAll',
'type' => Type::listOf(QueryArgument::getType()),
diff --git a/yii2-adapter/legacy/gql/handlers/RelatedCategories.php b/yii2-adapter/legacy/gql/handlers/RelatedCategories.php
deleted file mode 100644
index e1163622cf4..00000000000
--- a/yii2-adapter/legacy/gql/handlers/RelatedCategories.php
+++ /dev/null
@@ -1,31 +0,0 @@
-
- * @since 3.6.0
- */
-class RelatedCategories extends RelationArgumentHandler
-{
- protected string $argumentName = 'relatedToCategories';
-
- /**
- * @inheritdoc
- */
- protected function handleArgument($argumentValue): mixed
- {
- $argumentValue = parent::handleArgument($argumentValue);
- return $this->getIds(Category::class, $argumentValue);
- }
-}
diff --git a/yii2-adapter/legacy/gql/handlers/RelatedTags.php b/yii2-adapter/legacy/gql/handlers/RelatedTags.php
deleted file mode 100644
index 2a9c7eebd5c..00000000000
--- a/yii2-adapter/legacy/gql/handlers/RelatedTags.php
+++ /dev/null
@@ -1,31 +0,0 @@
-
- * @since 3.6.0
- */
-class RelatedTags extends RelationArgumentHandler
-{
- protected string $argumentName = 'relatedToTags';
-
- /**
- * @inheritdoc
- */
- protected function handleArgument($argumentValue): mixed
- {
- $argumentValue = parent::handleArgument($argumentValue);
- return $this->getIds(Tag::class, $argumentValue);
- }
-}
diff --git a/yii2-adapter/legacy/gql/interfaces/elements/Category.php b/yii2-adapter/legacy/gql/interfaces/elements/Category.php
deleted file mode 100644
index 79d74a935e6..00000000000
--- a/yii2-adapter/legacy/gql/interfaces/elements/Category.php
+++ /dev/null
@@ -1,138 +0,0 @@
-
- * @since 3.3.0
- */
-class Category extends Structure
-{
- /**
- * @inheritdoc
- */
- public static function getTypeGenerator(): string
- {
- return CategoryType::class;
- }
-
- /**
- * @inheritdoc
- */
- public static function getType(): Type
- {
- if ($type = GqlEntityRegistry::getEntity(self::getName())) {
- return $type;
- }
-
- $type = GqlEntityRegistry::createEntity(self::getName(), new InterfaceType([
- 'name' => static::getName(),
- 'fields' => self::class . '::getFieldDefinitions',
- 'description' => 'This is the interface implemented by all categories.',
- 'resolveType' => self::class . '::resolveElementTypeName',
- ]));
-
- CategoryType::generateTypes();
-
- return $type;
- }
-
- /**
- * @inheritdoc
- */
- public static function getName(): string
- {
- return 'CategoryInterface';
- }
-
- /**
- * @inheritdoc
- */
- public static function getFieldDefinitions(): array
- {
- return Craft::$app->getGql()->prepareFieldDefinitions(array_merge(parent::getFieldDefinitions(), [
- 'groupId' => [
- 'name' => 'groupId',
- 'type' => Type::nonNull(Type::int()),
- 'description' => 'The ID of the group that contains the category.',
- ],
- 'groupHandle' => [
- 'name' => 'groupHandle',
- 'type' => Type::nonNull(Type::string()),
- 'description' => 'The handle of the group that contains the category.',
- 'complexity' => Gql::singleQueryComplexity(),
- ],
- 'children' => [
- 'name' => 'children',
- 'args' => CategoryArguments::getArguments(),
- 'type' => Type::nonNull(Type::listOf(Type::nonNull(static::getType()))),
- 'description' => 'The category’s children.',
- 'complexity' => Gql::relatedArgumentComplexity(GqlService::GRAPHQL_COMPLEXITY_EAGER_LOAD),
- ],
- 'descendants' => [
- 'name' => 'descendants',
- 'args' => CategoryArguments::getArguments(),
- 'type' => Type::nonNull(Type::listOf(Type::nonNull(static::getType()))),
- 'description' => 'The category’s descendants, if the section is a structure. Accepts the same arguments as the `entries` query.',
- 'complexity' => Gql::relatedArgumentComplexity(GqlService::GRAPHQL_COMPLEXITY_EAGER_LOAD),
- ],
- 'ancestors' => [
- 'name' => 'ancestors',
- 'args' => CategoryArguments::getArguments(),
- 'type' => Type::nonNull(Type::listOf(Type::nonNull(static::getType()))),
- 'description' => 'The category’s ancestors, if the section is a structure. Accepts the same arguments as the `entries` query.',
- 'complexity' => Gql::relatedArgumentComplexity(GqlService::GRAPHQL_COMPLEXITY_EAGER_LOAD),
- ],
- 'parent' => [
- 'name' => 'parent',
- 'args' => CategoryArguments::getArguments(),
- 'type' => static::getType(),
- 'description' => 'The category’s parent.',
- 'complexity' => Gql::relatedArgumentComplexity(GqlService::GRAPHQL_COMPLEXITY_EAGER_LOAD),
- ],
- 'url' => [
- 'name' => 'url',
- 'type' => Type::string(),
- 'description' => 'The element’s full URL',
- ],
- 'localized' => [
- 'name' => 'localized',
- 'args' => CategoryArguments::getArguments(),
- 'type' => Type::nonNull(Type::listOf(Type::nonNull(static::getType()))),
- 'description' => 'The same element in other locales.',
- 'complexity' => Gql::eagerLoadComplexity(),
- ],
- 'prev' => [
- 'name' => 'prev',
- 'type' => self::getType(),
- 'args' => CategoryArguments::getArguments(),
- 'description' => 'Returns the previous element relative to this one, from a given set of criteria.',
- 'complexity' => fn($childrenComplexity, $args) => $childrenComplexity + GqlService::GRAPHQL_COMPLEXITY_NPLUS1 * (int)!empty($args),
- ],
- 'next' => [
- 'name' => 'next',
- 'type' => self::getType(),
- 'args' => CategoryArguments::getArguments(),
- 'description' => 'Returns the next element relative to this one, from a given set of criteria.',
- 'complexity' => fn($childrenComplexity, $args) => $childrenComplexity + GqlService::GRAPHQL_COMPLEXITY_NPLUS1 * (int)!empty($args),
- ],
- ]), self::getName());
- }
-}
diff --git a/yii2-adapter/legacy/gql/interfaces/elements/GlobalSet.php b/yii2-adapter/legacy/gql/interfaces/elements/GlobalSet.php
deleted file mode 100644
index b5125fcc01c..00000000000
--- a/yii2-adapter/legacy/gql/interfaces/elements/GlobalSet.php
+++ /dev/null
@@ -1,80 +0,0 @@
-
- * @since 3.3.0
- */
-class GlobalSet extends Element
-{
- /**
- * @inheritdoc
- */
- public static function getTypeGenerator(): string
- {
- return GlobalSetType::class;
- }
-
- /**
- * @inheritdoc
- */
- public static function getType(): Type
- {
- if ($type = GqlEntityRegistry::getEntity(self::getName())) {
- return $type;
- }
-
- $type = GqlEntityRegistry::createEntity(self::getName(), new InterfaceType([
- 'name' => static::getName(),
- 'fields' => self::class . '::getFieldDefinitions',
- 'description' => 'This is the interface implemented by all global sets.',
- 'resolveType' => self::class . '::resolveElementTypeName',
- ]));
-
- GlobalSetType::generateTypes();
-
- return $type;
- }
-
- /**
- * @inheritdoc
- */
- public static function getName(): string
- {
- return 'GlobalSetInterface';
- }
-
- /**
- * @inheritdoc
- */
- public static function getFieldDefinitions(): array
- {
- return Craft::$app->getGql()->prepareFieldDefinitions(array_merge(parent::getFieldDefinitions(), [
- 'name' => [
- 'name' => 'name',
- 'type' => Type::nonNull(Type::string()),
- 'description' => 'The name of the global set.',
- ],
- 'handle' => [
- 'name' => 'handle',
- 'type' => Type::nonNull(Type::string()),
- 'description' => 'The handle of the global set.',
- ],
- ]), self::getName());
- }
-}
diff --git a/yii2-adapter/legacy/gql/interfaces/elements/Tag.php b/yii2-adapter/legacy/gql/interfaces/elements/Tag.php
deleted file mode 100644
index 2dddcc8b8f4..00000000000
--- a/yii2-adapter/legacy/gql/interfaces/elements/Tag.php
+++ /dev/null
@@ -1,82 +0,0 @@
-
- * @since 3.3.0
- */
-class Tag extends Element
-{
- /**
- * @inheritdoc
- */
- public static function getTypeGenerator(): string
- {
- return TagType::class;
- }
-
- /**
- * @inheritdoc
- */
- public static function getType(): Type
- {
- if ($type = GqlEntityRegistry::getEntity(self::getName())) {
- return $type;
- }
-
- $type = GqlEntityRegistry::createEntity(self::getName(), new InterfaceType([
- 'name' => static::getName(),
- 'fields' => self::class . '::getFieldDefinitions',
- 'description' => 'This is the interface implemented by all tags.',
- 'resolveType' => self::class . '::resolveElementTypeName',
- ]));
-
- TagType::generateTypes();
-
- return $type;
- }
-
- /**
- * @inheritdoc
- */
- public static function getName(): string
- {
- return 'TagInterface';
- }
-
- /**
- * @inheritdoc
- */
- public static function getFieldDefinitions(): array
- {
- return Craft::$app->getGql()->prepareFieldDefinitions(array_merge(parent::getFieldDefinitions(), [
- 'groupId' => [
- 'name' => 'groupId',
- 'type' => Type::nonNull(Type::int()),
- 'description' => 'The ID of the group that contains the tag.',
- ],
- 'groupHandle' => [
- 'name' => 'groupHandle',
- 'type' => Type::nonNull(Type::string()),
- 'description' => 'The handle of the group that contains the tag.',
- 'complexity' => Gql::singleQueryComplexity(),
- ],
- ]), self::getName());
- }
-}
diff --git a/yii2-adapter/legacy/gql/mutations/Category.php b/yii2-adapter/legacy/gql/mutations/Category.php
deleted file mode 100644
index 9343fb420dd..00000000000
--- a/yii2-adapter/legacy/gql/mutations/Category.php
+++ /dev/null
@@ -1,98 +0,0 @@
-
- * @since 3.5.0
- */
-class Category extends Mutation
-{
- /**
- * @inheritdoc
- */
- public static function getMutations(): array
- {
- if (!GqlHelper::canMutateCategories()) {
- return [];
- }
-
- $mutationList = [];
-
- $createDeleteMutation = false;
-
- foreach (Craft::$app->getCategories()->getAllGroups() as $categoryGroup) {
- $scope = 'categorygroups.' . $categoryGroup->uid;
-
- if (Gql::canSchema($scope, 'save')) {
- // Create a mutation for each category group
- $mutation = static::createSaveMutation($categoryGroup);
- $mutationList[$mutation['name']] = $mutation;
- }
-
- if (!$createDeleteMutation && Gql::canSchema($scope, 'delete')) {
- $createDeleteMutation = true;
- }
- }
-
- if ($createDeleteMutation) {
- $mutationList['deleteCategory'] = [
- 'name' => 'deleteCategory',
- 'args' => ['id' => Type::nonNull(Type::int())],
- 'resolve' => [Craft::createObject(CategoryResolver::class), 'deleteCategory'],
- 'description' => 'Delete a category.',
- 'type' => Type::boolean(),
- ];
- }
-
- return $mutationList;
- }
-
- /**
- * Create the per-category-group save mutation.
- *
- * @param CategoryGroup $categoryGroup
- * @return array
- * @throws InvalidConfigException
- */
- public static function createSaveMutation(CategoryGroup $categoryGroup): array
- {
- $mutationArguments = array_merge(ElementMutationArguments::getArguments(), StructureArguments::getArguments());
- $generatedType = CategoryType::generateType($categoryGroup);
-
- /** @var CategoryResolver $resolver */
- $resolver = Craft::createObject(CategoryResolver::class);
- $resolver->setResolutionData('categoryGroup', $categoryGroup);
- static::prepareResolver($resolver, $categoryGroup->getCustomFields());
-
- $mutationArguments = array_merge($mutationArguments, $resolver->getResolutionData(ElementMutationResolver::CONTENT_FIELD_KEY));
-
- return [
- 'name' => "save_{$categoryGroup->handle}_Category",
- 'description' => 'Save the “' . $categoryGroup->name . '” category.',
- 'args' => $mutationArguments,
- 'resolve' => [$resolver, 'saveCategory'],
- 'type' => $generatedType,
- ];
- }
-}
diff --git a/yii2-adapter/legacy/gql/mutations/GlobalSet.php b/yii2-adapter/legacy/gql/mutations/GlobalSet.php
deleted file mode 100644
index 4e6e34fd4d6..00000000000
--- a/yii2-adapter/legacy/gql/mutations/GlobalSet.php
+++ /dev/null
@@ -1,75 +0,0 @@
-
- * @since 3.5.0
- */
-class GlobalSet extends Mutation
-{
- /**
- * @inheritdoc
- */
- public static function getMutations(): array
- {
- if (!GqlHelper::canMutateGlobalSets()) {
- return [];
- }
-
- $mutationList = [];
-
- foreach (Craft::$app->getGlobals()->getAllSets() as $globalSet) {
- $scope = 'globalsets.' . $globalSet->uid;
-
- if (Gql::canSchema($scope, 'edit')) {
- $mutation = static::createSaveMutation($globalSet);
- $mutationList[$mutation['name']] = $mutation;
- }
- }
-
- return $mutationList;
- }
-
- /**
- * Create the per-global-set save mutation.
- *
- * @param GlobalSetElement $globalSet
- * @return array
- */
- public static function createSaveMutation(GlobalSetElement $globalSet): array
- {
- $generatedType = GlobalSetType::generateType($globalSet);
-
- /** @var GlobalSetResolver $resolver */
- $resolver = Craft::createObject(GlobalSetResolver::class);
- $resolver->setResolutionData('globalSet', $globalSet);
- static::prepareResolver($resolver, $globalSet->getCustomFields());
-
- $mutationArguments = $resolver->getResolutionData(ElementMutationResolver::CONTENT_FIELD_KEY);
-
- return [
- 'name' => "save_{$globalSet->handle}_GlobalSet",
- 'description' => 'Update the ”' . $globalSet . '“ global set.',
- 'args' => $mutationArguments,
- 'resolve' => [$resolver, 'saveGlobalSet'],
- 'type' => $generatedType,
- ];
- }
-}
diff --git a/yii2-adapter/legacy/gql/mutations/Tag.php b/yii2-adapter/legacy/gql/mutations/Tag.php
deleted file mode 100644
index 4d4c8004a7d..00000000000
--- a/yii2-adapter/legacy/gql/mutations/Tag.php
+++ /dev/null
@@ -1,97 +0,0 @@
-
- * @since 3.5.0
- */
-class Tag extends Mutation
-{
- /**
- * @inheritdoc
- */
- public static function getMutations(): array
- {
- if (!GqlHelper::canMutateTags()) {
- return [];
- }
-
- $mutationList = [];
-
- $createDeleteMutation = false;
-
- foreach (Craft::$app->getTags()->getAllTagGroups() as $tagGroup) {
- $scope = 'taggroups.' . $tagGroup->uid;
-
- if (Gql::canSchema($scope, 'save')) {
- // Create a mutation for the tag group
- $mutation = static::createSaveMutation($tagGroup);
- $mutationList[$mutation['name']] = $mutation;
- }
-
- if (!$createDeleteMutation && Gql::canSchema($scope, 'delete')) {
- $createDeleteMutation = true;
- }
- }
-
- if ($createDeleteMutation) {
- $mutationList['deleteTag'] = [
- 'name' => 'deleteTag',
- 'args' => ['id' => Type::nonNull(Type::int())],
- 'resolve' => [Craft::createObject(TagResolver::class), 'deleteTag'],
- 'description' => 'Delete a tag.',
- 'type' => Type::boolean(),
- ];
- }
-
- return $mutationList;
- }
-
- /**
- * Create the per-tag-group save mutation.
- *
- * @param TagGroup $tagGroup
- * @return array
- * @throws InvalidConfigException
- */
- public static function createSaveMutation(TagGroup $tagGroup): array
- {
- $mutationArguments = ElementMutationArguments::getArguments();
- $generatedType = TagType::generateType($tagGroup);
-
- /** @var TagResolver $resolver */
- $resolver = Craft::createObject(TagResolver::class);
- $resolver->setResolutionData('tagGroup', $tagGroup);
- static::prepareResolver($resolver, $tagGroup->getCustomFields());
-
- $mutationArguments = array_merge($mutationArguments, $resolver->getResolutionData(ElementMutationResolver::CONTENT_FIELD_KEY));
-
- return [
- 'name' => "save_{$tagGroup->handle}_Tag",
- 'description' => 'Save the “' . $tagGroup->name . '” tag.',
- 'args' => $mutationArguments,
- 'resolve' => [$resolver, 'saveTag'],
- 'type' => $generatedType,
- ];
- }
-}
diff --git a/yii2-adapter/legacy/gql/queries/Category.php b/yii2-adapter/legacy/gql/queries/Category.php
deleted file mode 100644
index 98060bb4692..00000000000
--- a/yii2-adapter/legacy/gql/queries/Category.php
+++ /dev/null
@@ -1,58 +0,0 @@
-
- * @since 3.3.0
- */
-class Category extends Query
-{
- /**
- * @inheritdoc
- */
- public static function getQueries(bool $checkToken = true): array
- {
- if ($checkToken && !GqlHelper::canQueryCategories()) {
- return [];
- }
-
- return [
- 'categories' => [
- 'type' => Type::listOf(CategoryInterface::getType()),
- 'args' => CategoryArguments::getArguments(),
- 'resolve' => CategoryResolver::class . '::resolve',
- 'description' => 'This query is used to query for categories.',
- 'complexity' => GqlHelper::relatedArgumentComplexity(),
- ],
- 'categoryCount' => [
- 'type' => Type::nonNull(Type::int()),
- 'args' => CategoryArguments::getArguments(),
- 'resolve' => CategoryResolver::class . '::resolveCount',
- 'description' => 'This query is used to return the number of categories.',
- 'complexity' => GqlHelper::relatedArgumentComplexity(),
- ],
- 'category' => [
- 'type' => CategoryInterface::getType(),
- 'args' => CategoryArguments::getArguments(),
- 'resolve' => CategoryResolver::class . '::resolveOne',
- 'description' => 'This query is used to query for a single category.',
- 'complexity' => GqlHelper::relatedArgumentComplexity(),
- ],
- ];
- }
-}
diff --git a/yii2-adapter/legacy/gql/queries/GlobalSet.php b/yii2-adapter/legacy/gql/queries/GlobalSet.php
deleted file mode 100644
index 99c72237ce6..00000000000
--- a/yii2-adapter/legacy/gql/queries/GlobalSet.php
+++ /dev/null
@@ -1,51 +0,0 @@
-
- * @since 3.3.0
- */
-class GlobalSet extends Query
-{
- /**
- * @inheritdoc
- */
- public static function getQueries(bool $checkToken = true): array
- {
- if ($checkToken && !GqlHelper::canQueryGlobalSets()) {
- return [];
- }
-
- return [
- 'globalSets' => [
- 'type' => Type::listOf(GlobalSetInterface::getType()),
- 'args' => GlobalSetArguments::getArguments(),
- 'resolve' => GlobalSetResolver::class . '::resolve',
- 'description' => 'This query is used to query for global sets.',
- 'complexity' => GqlHelper::relatedArgumentComplexity(),
- ],
- 'globalSet' => [
- 'type' => GlobalSetInterface::getType(),
- 'args' => GlobalSetArguments::getArguments(),
- 'resolve' => GlobalSetResolver::class . '::resolveOne',
- 'description' => 'This query is used to query for a single global set.',
- 'complexity' => GqlHelper::relatedArgumentComplexity(),
- ],
- ];
- }
-}
diff --git a/yii2-adapter/legacy/gql/queries/Tag.php b/yii2-adapter/legacy/gql/queries/Tag.php
deleted file mode 100644
index 6f9d5f40db8..00000000000
--- a/yii2-adapter/legacy/gql/queries/Tag.php
+++ /dev/null
@@ -1,58 +0,0 @@
-
- * @since 3.3.0
- */
-class Tag extends Query
-{
- /**
- * @inheritdoc
- */
- public static function getQueries(bool $checkToken = true): array
- {
- if ($checkToken && !GqlHelper::canQueryTags()) {
- return [];
- }
-
- return [
- 'tags' => [
- 'type' => Type::listOf(TagInterface::getType()),
- 'args' => TagArguments::getArguments(),
- 'resolve' => TagResolver::class . '::resolve',
- 'description' => 'This query is used to query for tags.',
- 'complexity' => GqlHelper::relatedArgumentComplexity(),
- ],
- 'tagCount' => [
- 'type' => Type::nonNull(Type::int()),
- 'args' => TagArguments::getArguments(),
- 'resolve' => TagResolver::class . '::resolveCount',
- 'description' => 'This query is used to return the number of tags.',
- 'complexity' => GqlHelper::relatedArgumentComplexity(),
- ],
- 'tag' => [
- 'type' => TagInterface::getType(),
- 'args' => TagArguments::getArguments(),
- 'resolve' => TagResolver::class . '::resolveOne',
- 'description' => 'This query is used to query for a single tag.',
- 'complexity' => GqlHelper::relatedArgumentComplexity(),
- ],
- ];
- }
-}
diff --git a/yii2-adapter/legacy/gql/resolvers/elements/Category.php b/yii2-adapter/legacy/gql/resolvers/elements/Category.php
deleted file mode 100644
index 24faf1757f6..00000000000
--- a/yii2-adapter/legacy/gql/resolvers/elements/Category.php
+++ /dev/null
@@ -1,70 +0,0 @@
-
- * @since 3.3.0
- */
-class Category extends ElementResolver
-{
- /**
- * @inheritdoc
- */
- public static function prepareQuery(mixed $source, array $arguments, ?string $fieldName = null): mixed
- {
- // If this is the beginning of a resolver chain, start fresh
- if ($source === null) {
- $query = CategoryElement::find();
- // If not, get the prepared element query
- } else {
- $query = $source->$fieldName;
- }
-
- // If it's preloaded, it's preloaded.
- if (!$query instanceof ElementQuery) {
- return $query;
- }
-
- foreach ($arguments as $key => $value) {
- try {
- $query->$key($value);
- } catch (UnknownMethodException $e) {
- if ($value !== null) {
- throw $e;
- }
- }
- }
-
- $pairs = GqlHelper::extractAllowedEntitiesFromSchema('read');
-
- if (!GqlHelper::canQueryCategories()) {
- return ElementCollection::empty();
- }
-
- $categoriesService = Craft::$app->getCategories();
- $groupIds = array_filter(array_map(function(string $uid) use ($categoriesService) {
- $group = $categoriesService->getGroupByUid($uid);
- return $group->id ?? null;
- }, $pairs['categorygroups']));
-
- $query->andWhere(['in', 'categories.groupId', $groupIds]);
-
- return $query;
- }
-}
diff --git a/yii2-adapter/legacy/gql/resolvers/elements/GlobalSet.php b/yii2-adapter/legacy/gql/resolvers/elements/GlobalSet.php
deleted file mode 100644
index e7cbf8f6f73..00000000000
--- a/yii2-adapter/legacy/gql/resolvers/elements/GlobalSet.php
+++ /dev/null
@@ -1,51 +0,0 @@
-
- * @since 3.3.0
- */
-class GlobalSet extends ElementResolver
-{
- /**
- * @inheritdoc
- */
- public static function prepareQuery(mixed $source, array $arguments, ?string $fieldName = null): mixed
- {
- $query = GlobalSetElement::find();
-
- foreach ($arguments as $key => $value) {
- try {
- $query->$key($value);
- } catch (UnknownMethodException $e) {
- if ($value !== null) {
- throw $e;
- }
- }
- }
-
- $pairs = GqlHelper::extractAllowedEntitiesFromSchema('read');
-
- if (!GqlHelper::canQueryGlobalSets()) {
- return ElementCollection::empty();
- }
-
- $query->andWhere(['in', 'globalsets.uid', $pairs['globalsets']]);
-
- return $query;
- }
-}
diff --git a/yii2-adapter/legacy/gql/resolvers/elements/Tag.php b/yii2-adapter/legacy/gql/resolvers/elements/Tag.php
deleted file mode 100644
index cc7091465e0..00000000000
--- a/yii2-adapter/legacy/gql/resolvers/elements/Tag.php
+++ /dev/null
@@ -1,70 +0,0 @@
-
- * @since 3.3.0
- */
-class Tag extends ElementResolver
-{
- /**
- * @inheritdoc
- */
- public static function prepareQuery(mixed $source, array $arguments, ?string $fieldName = null): mixed
- {
- // If this is the beginning of a resolver chain, start fresh
- if ($source === null) {
- $query = TagElement::find();
- // If not, get the prepared element query
- } else {
- $query = $source->$fieldName;
- }
-
- // If it's preloaded, it's preloaded.
- if (!$query instanceof ElementQuery) {
- return $query;
- }
-
- foreach ($arguments as $key => $value) {
- try {
- $query->$key($value);
- } catch (UnknownMethodException $e) {
- if ($value !== null) {
- throw $e;
- }
- }
- }
-
- $pairs = GqlHelper::extractAllowedEntitiesFromSchema('read');
-
- if (!GqlHelper::canQueryTags()) {
- return ElementCollection::empty();
- }
-
- $tagsService = Craft::$app->getTags();
- $tagGroupIds = array_filter(array_map(function(string $uid) use ($tagsService) {
- $tagGroup = $tagsService->getTagGroupByUid($uid);
- return $tagGroup->id ?? null;
- }, $pairs['taggroups']));
-
- $query->andWhere(['in', 'tags.groupId', $tagGroupIds]);
-
- return $query;
- }
-}
diff --git a/yii2-adapter/legacy/gql/resolvers/mutations/Category.php b/yii2-adapter/legacy/gql/resolvers/mutations/Category.php
deleted file mode 100644
index 87aac505535..00000000000
--- a/yii2-adapter/legacy/gql/resolvers/mutations/Category.php
+++ /dev/null
@@ -1,106 +0,0 @@
-
- * @since 3.5.0
- */
-class Category extends ElementMutationResolver
-{
- use StructureMutationTrait;
-
- /** @inheritdoc */
- protected array $immutableAttributes = ['id', 'uid', 'groupId'];
-
- /**
- * Save a category using the passed arguments.
- *
- * @param mixed $source
- * @param array $arguments
- * @param mixed $context
- * @param ResolveInfo $resolveInfo
- * @return CategoryElement
- * @throws Throwable if reasons.
- */
- public function saveCategory(mixed $source, array $arguments, mixed $context, ResolveInfo $resolveInfo): CategoryElement
- {
- /** @var CategoryGroup $categoryGroup */
- $categoryGroup = $this->getResolutionData('categoryGroup');
- $canIdentify = !empty($arguments['id']) || !empty($arguments['uid']);
- $elementService = Craft::$app->getElements();
-
- if ($canIdentify) {
- if (!empty($arguments['uid'])) {
- $category = $elementService->createElementQuery(CategoryElement::class)->uid($arguments['uid'])->one();
- } else {
- $category = $elementService->getElementById($arguments['id'], CategoryElement::class);
- }
-
- if (!$category) {
- throw new Error('No such category exists');
- }
- } else {
- $category = $elementService->createElement(['type' => CategoryElement::class, 'groupId' => $categoryGroup->id]);
- }
-
- if ($category->groupId != $categoryGroup->id) {
- throw new Error('Impossible to change the group of an existing category');
- }
-
- $this->requireSchemaAction('categorygroups.' . $categoryGroup->uid, 'save');
-
- $category = $this->populateElementWithData($category, $arguments, $resolveInfo);
-
- $category = $this->saveElement($category);
-
- $this->performStructureOperations($category, $arguments);
-
- return $elementService->getElementById($category->id, CategoryElement::class);
- }
-
- /**
- * Delete a category identified by the arguments.
- *
- * @param mixed $source
- * @param array $arguments
- * @param mixed $context
- * @param ResolveInfo $resolveInfo
- * @return bool
- * @throws Throwable if reasons.
- */
- public function deleteCategory(mixed $source, array $arguments, mixed $context, ResolveInfo $resolveInfo): bool
- {
- $categoryId = $arguments['id'];
-
- $elementService = Craft::$app->getElements();
- $category = $elementService->getElementById($categoryId, CategoryElement::class);
-
- if (!$category) {
- return false;
- }
-
- $categoryGroupUid = DB::table(Table::CATEGORYGROUPS)->uidById($category->groupId);
- $this->requireSchemaAction('categorygroups.' . $categoryGroupUid, 'delete');
-
- return $elementService->deleteElementById($categoryId);
- }
-}
diff --git a/yii2-adapter/legacy/gql/resolvers/mutations/GlobalSet.php b/yii2-adapter/legacy/gql/resolvers/mutations/GlobalSet.php
deleted file mode 100644
index ac065853892..00000000000
--- a/yii2-adapter/legacy/gql/resolvers/mutations/GlobalSet.php
+++ /dev/null
@@ -1,46 +0,0 @@
-
- * @since 3.5.0
- */
-class GlobalSet extends ElementMutationResolver
-{
- /**
- * Save the global set identified by resolver data.
- *
- * @param mixed $source
- * @param array $arguments
- * @param mixed $context
- * @param ResolveInfo $resolveInfo
- * @return GlobalSetElement
- * @throws Throwable if reasons.
- */
- public function saveGlobalSet(mixed $source, array $arguments, mixed $context, ResolveInfo $resolveInfo): GlobalSetElement
- {
- $globalSet = $this->getResolutionData('globalSet');
-
- $this->requireSchemaAction('globalsets.' . $globalSet->uid, 'edit');
-
- $globalSet = $this->populateElementWithData($globalSet, $arguments, $resolveInfo);
-
- $globalSet = $this->saveElement($globalSet);
-
- return Craft::$app->getGlobals()->getSetById($globalSet->id);
- }
-}
diff --git a/yii2-adapter/legacy/gql/resolvers/mutations/Tag.php b/yii2-adapter/legacy/gql/resolvers/mutations/Tag.php
deleted file mode 100644
index 2fd248d8abe..00000000000
--- a/yii2-adapter/legacy/gql/resolvers/mutations/Tag.php
+++ /dev/null
@@ -1,100 +0,0 @@
-
- * @since 3.5.0
- */
-class Tag extends ElementMutationResolver
-{
- /** @inheritdoc */
- protected array $immutableAttributes = ['id', 'uid', 'groupId'];
-
- /**
- * Save a tag using the passed arguments.
- *
- * @param mixed $source
- * @param array $arguments
- * @param mixed $context
- * @param ResolveInfo $resolveInfo
- * @return TagElement
- * @throws Throwable if reasons.
- */
- public function saveTag(mixed $source, array $arguments, mixed $context, ResolveInfo $resolveInfo): TagElement
- {
- /** @var TagGroup $tagGroup */
- $tagGroup = $this->getResolutionData('tagGroup');
- $canIdentify = !empty($arguments['id']) || !empty($arguments['uid']);
- $elementService = Craft::$app->getElements();
-
- if ($canIdentify) {
- if (!empty($arguments['uid'])) {
- $tag = $elementService->createElementQuery(TagElement::class)->uid($arguments['uid'])->one();
- } else {
- $tag = $elementService->getElementById($arguments['id'], TagElement::class);
- }
-
- if (!$tag) {
- throw new Error('No such tag exists');
- }
- } else {
- $tag = $elementService->createElement(['type' => TagElement::class, 'groupId' => $tagGroup->id]);
- }
-
- if ($tag->groupId != $tagGroup->id) {
- throw new Error('Impossible to change the group of an existing tag');
- }
-
- $this->requireSchemaAction('taggroups.' . $tagGroup->uid, 'save');
-
- $tag = $this->populateElementWithData($tag, $arguments, $resolveInfo);
- $tag = $this->saveElement($tag);
-
- return $elementService->getElementById($tag->id, TagElement::class);
- }
-
- /**
- * Delete a tag identified by the arguments.
- *
- * @param mixed $source
- * @param array $arguments
- * @param mixed $context
- * @param ResolveInfo $resolveInfo
- * @return bool
- * @throws Throwable if reasons.
- */
- public function deleteTag(mixed $source, array $arguments, mixed $context, ResolveInfo $resolveInfo): bool
- {
- $tagId = $arguments['id'];
-
- $elementService = Craft::$app->getElements();
- $tag = $elementService->getElementById($tagId, TagElement::class);
-
- if (!$tag) {
- return false;
- }
-
- $tagGroupUid = DB::table(Table::TAGGROUPS)->uidById($tag->groupId);
- $this->requireSchemaAction('taggroups.' . $tagGroupUid, 'delete');
-
- return $elementService->deleteElementById($tagId);
- }
-}
diff --git a/yii2-adapter/legacy/gql/types/elements/Category.php b/yii2-adapter/legacy/gql/types/elements/Category.php
deleted file mode 100644
index 14f70bcd91e..00000000000
--- a/yii2-adapter/legacy/gql/types/elements/Category.php
+++ /dev/null
@@ -1,47 +0,0 @@
-
- * @since 3.3.0
- */
-class Category extends Element
-{
- /**
- * @inheritdoc
- */
- public function __construct(array $config)
- {
- $config['interfaces'] = [
- CategoryInterface::getType(),
- ];
-
- parent::__construct($config);
- }
-
- /**
- * @inheritdoc
- */
- protected function resolve(mixed $source, array $arguments, mixed $context, ResolveInfo $resolveInfo): mixed
- {
- /** @var CategoryElement $source */
- $fieldName = $resolveInfo->fieldName;
-
- return match ($fieldName) {
- 'groupHandle' => $source->getGroup()->handle,
- default => parent::resolve($source, $arguments, $context, $resolveInfo),
- };
- }
-}
diff --git a/yii2-adapter/legacy/gql/types/elements/GlobalSet.php b/yii2-adapter/legacy/gql/types/elements/GlobalSet.php
deleted file mode 100644
index 5b20e7dc178..00000000000
--- a/yii2-adapter/legacy/gql/types/elements/GlobalSet.php
+++ /dev/null
@@ -1,31 +0,0 @@
-
- * @since 3.3.0
- */
-class GlobalSet extends Element
-{
- /**
- * @inheritdoc
- */
- public function __construct(array $config)
- {
- $config['interfaces'] = [
- GlobalSetInterface::getType(),
- ];
-
- parent::__construct($config);
- }
-}
diff --git a/yii2-adapter/legacy/gql/types/elements/Tag.php b/yii2-adapter/legacy/gql/types/elements/Tag.php
deleted file mode 100644
index 881df0711e6..00000000000
--- a/yii2-adapter/legacy/gql/types/elements/Tag.php
+++ /dev/null
@@ -1,47 +0,0 @@
-
- * @since 3.3.0
- */
-class Tag extends Element
-{
- /**
- * @inheritdoc
- */
- public function __construct(array $config)
- {
- $config['interfaces'] = [
- TagInterface::getType(),
- ];
-
- parent::__construct($config);
- }
-
- /**
- * @inheritdoc
- */
- protected function resolve(mixed $source, array $arguments, mixed $context, ResolveInfo $resolveInfo): mixed
- {
- /** @var TagElement $source */
- $fieldName = $resolveInfo->fieldName;
-
- return match ($fieldName) {
- 'groupHandle' => $source->getGroup()->handle,
- default => parent::resolve($source, $arguments, $context, $resolveInfo),
- };
- }
-}
diff --git a/yii2-adapter/legacy/gql/types/generators/CategoryType.php b/yii2-adapter/legacy/gql/types/generators/CategoryType.php
deleted file mode 100644
index 62f5410f7b4..00000000000
--- a/yii2-adapter/legacy/gql/types/generators/CategoryType.php
+++ /dev/null
@@ -1,68 +0,0 @@
-
- * @since 3.3.0
- */
-class CategoryType extends Generator implements GeneratorInterface, SingleGeneratorInterface
-{
- /**
- * @inheritdoc
- */
- public static function generateTypes(mixed $context = null): array
- {
- $categoryGroups = Craft::$app->getCategories()->getAllGroups();
- $gqlTypes = [];
-
- foreach ($categoryGroups as $categoryGroup) {
- $requiredContexts = CategoryElement::gqlScopesByContext($categoryGroup);
-
- if (!GqlHelper::isSchemaAwareOf($requiredContexts)) {
- continue;
- }
-
- // Generate a type for each category group
- $type = static::generateType($categoryGroup);
- $gqlTypes[$type->name] = $type;
- }
-
- return $gqlTypes;
- }
-
- /**
- * @inheritdoc
- */
- public static function generateType(mixed $context): ObjectType
- {
- $typeName = CategoryElement::gqlTypeName($context);
-
- return GqlEntityRegistry::getOrCreate($typeName, fn() => new Category([
- 'name' => $typeName,
- 'fields' => function() use ($context, $typeName) {
- $contentFieldGqlTypes = self::getContentFields($context);
- $categoryGroupFields = array_merge(CategoryInterface::getFieldDefinitions(), $contentFieldGqlTypes);
- return Craft::$app->getGql()->prepareFieldDefinitions($categoryGroupFields, $typeName);
- },
- ]));
- }
-}
diff --git a/yii2-adapter/legacy/gql/types/generators/GlobalSetType.php b/yii2-adapter/legacy/gql/types/generators/GlobalSetType.php
deleted file mode 100644
index a95efc6a996..00000000000
--- a/yii2-adapter/legacy/gql/types/generators/GlobalSetType.php
+++ /dev/null
@@ -1,77 +0,0 @@
-
- * @since 3.3.0
- */
-class GlobalSetType extends Generator implements GeneratorInterface, SingleGeneratorInterface
-{
- /**
- * @inheritdoc
- */
- public static function generateTypes(mixed $context = null): array
- {
- $globalSets = Craft::$app->getGlobals()->getAllSets();
- $gqlTypes = [];
-
- foreach ($globalSets as $globalSet) {
- $requiredContexts = GlobalSetElement::gqlScopesByContext($globalSet);
-
- if (!GqlHelper::isSchemaAwareOf($requiredContexts)) {
- continue;
- }
-
- // Generate a type for each global set
- $type = static::generateType($globalSet);
- $gqlTypes[$type->name] = $type;
- }
-
- return $gqlTypes;
- }
-
- /**
- * Returns the generator name.
- */
- public static function getName($context = null): string
- {
- /** @var GlobalSetElement $context */
- return $context->handle . '_GlobalSet';
- }
-
- /**
- * @inheritdoc
- */
- public static function generateType(mixed $context): ObjectType
- {
- $typeName = self::getName($context);
-
- return GqlEntityRegistry::getOrCreate($typeName, fn() => new GlobalSet([
- 'name' => $typeName,
- 'fields' => function() use ($context, $typeName) {
- $contentFieldGqlTypes = self::getContentFields($context);
- $globalSetFields = array_merge(GlobalSetInterface::getFieldDefinitions(), $contentFieldGqlTypes);
- return Craft::$app->getGql()->prepareFieldDefinitions($globalSetFields, $typeName);
- },
- ]));
- }
-}
diff --git a/yii2-adapter/legacy/gql/types/generators/TagType.php b/yii2-adapter/legacy/gql/types/generators/TagType.php
deleted file mode 100644
index 1f16b4bd718..00000000000
--- a/yii2-adapter/legacy/gql/types/generators/TagType.php
+++ /dev/null
@@ -1,68 +0,0 @@
-
- * @since 3.3.0
- */
-class TagType extends Generator implements GeneratorInterface, SingleGeneratorInterface
-{
- /**
- * @inheritdoc
- */
- public static function generateTypes(mixed $context = null): array
- {
- $tagGroups = Craft::$app->getTags()->getAllTagGroups();
- $gqlTypes = [];
-
- foreach ($tagGroups as $tagGroup) {
- $requiredContexts = TagElement::gqlScopesByContext($tagGroup);
-
- if (!GqlHelper::isSchemaAwareOf($requiredContexts)) {
- continue;
- }
-
- // Generate a type for each tag group
- $type = static::generateType($tagGroup);
- $gqlTypes[$type->name] = $type;
- }
-
- return $gqlTypes;
- }
-
- /**
- * @inheritdoc
- */
- public static function generateType(mixed $context): ObjectType
- {
- $typeName = TagElement::gqlTypeName($context);
-
- return GqlEntityRegistry::getOrCreate($typeName, fn() => new Tag([
- 'name' => $typeName,
- 'fields' => function() use ($context, $typeName) {
- $contentFieldGqlTypes = self::getContentFields($context);
- $tagGroupFields = array_merge(TagInterface::getFieldDefinitions(), $contentFieldGqlTypes);
- return Craft::$app->getGql()->prepareFieldDefinitions($tagGroupFields, $typeName);
- },
- ]));
- }
-}
diff --git a/yii2-adapter/legacy/gql/types/input/criteria/Category.php b/yii2-adapter/legacy/gql/types/input/criteria/Category.php
deleted file mode 100644
index cdb2d7fff8f..00000000000
--- a/yii2-adapter/legacy/gql/types/input/criteria/Category.php
+++ /dev/null
@@ -1,34 +0,0 @@
-
- * @since 3.6.0
- */
-class Category extends InputObjectType
-{
- /**
- * @return mixed
- */
- public static function getType(): mixed
- {
- $typeName = 'CategoryCriteriaInput';
-
- return GqlEntityRegistry::getOrCreate($typeName, fn() => new InputObjectType([
- 'name' => $typeName,
- 'fields' => fn() => CategoryArguments::getArguments(),
- ]));
- }
-}
diff --git a/yii2-adapter/legacy/gql/types/input/criteria/CategoryRelation.php b/yii2-adapter/legacy/gql/types/input/criteria/CategoryRelation.php
deleted file mode 100644
index 72703c757d9..00000000000
--- a/yii2-adapter/legacy/gql/types/input/criteria/CategoryRelation.php
+++ /dev/null
@@ -1,35 +0,0 @@
-
- * @since 5.4.0
- */
-class CategoryRelation extends InputObjectType
-{
- /**
- * @return mixed
- */
- public static function getType(): mixed
- {
- $typeName = 'CategoryRelationCriteriaInput';
-
- return GqlEntityRegistry::getOrCreate($typeName, fn() => new InputObjectType([
- 'name' => $typeName,
- 'fields' => fn() => CategoryArguments::getArguments() + RelationCriteria::getArguments(),
- ]));
- }
-}
diff --git a/yii2-adapter/legacy/gql/types/input/criteria/Tag.php b/yii2-adapter/legacy/gql/types/input/criteria/Tag.php
deleted file mode 100644
index 193f15dce9b..00000000000
--- a/yii2-adapter/legacy/gql/types/input/criteria/Tag.php
+++ /dev/null
@@ -1,34 +0,0 @@
-
- * @since 3.6.0
- */
-class Tag extends InputObjectType
-{
- /**
- * @return mixed
- */
- public static function getType(): mixed
- {
- $typeName = 'TagCriteriaInput';
-
- return GqlEntityRegistry::getOrCreate($typeName, fn() => new InputObjectType([
- 'name' => $typeName,
- 'fields' => fn() => TagArguments::getArguments(),
- ]));
- }
-}
diff --git a/yii2-adapter/legacy/gql/types/input/criteria/TagRelation.php b/yii2-adapter/legacy/gql/types/input/criteria/TagRelation.php
deleted file mode 100644
index a67bfca1991..00000000000
--- a/yii2-adapter/legacy/gql/types/input/criteria/TagRelation.php
+++ /dev/null
@@ -1,35 +0,0 @@
-
- * @since 5.4.0
- */
-class TagRelation extends InputObjectType
-{
- /**
- * @return mixed
- */
- public static function getType(): mixed
- {
- $typeName = 'TagRelationCriteriaInput';
-
- return GqlEntityRegistry::getOrCreate($typeName, fn() => new InputObjectType([
- 'name' => $typeName,
- 'fields' => fn() => TagArguments::getArguments() + RelationCriteria::getArguments(),
- ]));
- }
-}
diff --git a/yii2-adapter/legacy/helpers/Gql.php b/yii2-adapter/legacy/helpers/Gql.php
index 374583399f8..4e3e5bfb6bb 100644
--- a/yii2-adapter/legacy/helpers/Gql.php
+++ b/yii2-adapter/legacy/helpers/Gql.php
@@ -142,45 +142,6 @@ public static function extractEntityAllowedActions(string $entity, ?GqlSchema $s
return array_keys($actions);
}
- /**
- * Return true if active schema can mutate tags.
- *
- * @param GqlSchema|null $schema The GraphQL schema. If none is provided, the active schema will be used.
- * @return bool
- * @since 3.5.0
- */
- public static function canMutateTags(?GqlSchema $schema = null): bool
- {
- $allowedEntities = self::extractAllowedEntitiesFromSchema('edit', $schema);
- return isset($allowedEntities['taggroups']);
- }
-
- /**
- * Return true if active schema can mutate global sets.
- *
- * @param GqlSchema|null $schema The GraphQL schema. If none is provided, the active schema will be used.
- * @return bool
- * @since 3.5.0
- */
- public static function canMutateGlobalSets(?GqlSchema $schema = null): bool
- {
- $allowedEntities = self::extractAllowedEntitiesFromSchema('edit', $schema);
- return isset($allowedEntities['globalsets']);
- }
-
- /**
- * Return true if active schema can mutate categories.
- *
- * @param GqlSchema|null $schema The GraphQL schema. If none is provided, the active schema will be used.
- * @return bool
- * @since 3.5.0
- */
- public static function canMutateCategories(?GqlSchema $schema = null): bool
- {
- $allowedEntities = self::extractAllowedEntitiesFromSchema('edit', $schema);
- return isset($allowedEntities['categorygroups']);
- }
-
/**
* Return true if active schema can mutate assets.
*
@@ -221,42 +182,6 @@ public static function canQueryAssets(?GqlSchema $schema = null): bool
return isset($allowedEntities['volumes']);
}
- /**
- * Return true if active schema can query categories.
- *
- * @param GqlSchema|null $schema The GraphQL schema. If none is provided, the active schema will be used.
- * @return bool
- */
- public static function canQueryCategories(?GqlSchema $schema = null): bool
- {
- $allowedEntities = self::extractAllowedEntitiesFromSchema('read', $schema);
- return isset($allowedEntities['categorygroups']);
- }
-
- /**
- * Return true if active schema can query tags.
- *
- * @param GqlSchema|null $schema The GraphQL schema. If none is provided, the active schema will be used.
- * @return bool
- */
- public static function canQueryTags(?GqlSchema $schema = null): bool
- {
- $allowedEntities = self::extractAllowedEntitiesFromSchema('read', $schema);
- return isset($allowedEntities['taggroups']);
- }
-
- /**
- * Return true if active schema can query global sets.
- *
- * @param GqlSchema|null $schema The GraphQL schema. If none is provided, the active schema will be used.
- * @return bool
- */
- public static function canQueryGlobalSets(?GqlSchema $schema = null): bool
- {
- $allowedEntities = self::extractAllowedEntitiesFromSchema('read', $schema);
- return isset($allowedEntities['globalsets']);
- }
-
/**
* Return true if active schema can query users.
*
@@ -536,7 +461,7 @@ public static function relatedArgumentComplexity(int $baseComplexity = GqlServic
{
return static function($childComplexity, $args) use ($baseComplexity) {
$complexityScore = $childComplexity + $baseComplexity;
- $relatedArguments = ['relatedToAssets', 'relatedToEntries', 'relatedToUsers', 'relatedToCategories', 'relatedToTags'];
+ $relatedArguments = ['relatedToAssets', 'relatedToEntries', 'relatedToUsers'];
foreach ($relatedArguments as $argumentName) {
if (!empty($args[$argumentName])) {
diff --git a/yii2-adapter/legacy/models/CategoryGroup.php b/yii2-adapter/legacy/models/CategoryGroup.php
deleted file mode 100644
index b70304282e7..00000000000
--- a/yii2-adapter/legacy/models/CategoryGroup.php
+++ /dev/null
@@ -1,306 +0,0 @@
-
- * @since 3.0.0
- */
-class CategoryGroup extends Model implements
- Chippable,
- CpEditable,
- FieldLayoutProviderInterface
-{
- /** @since 3.7.0 */
- public const DEFAULT_PLACEMENT_BEGINNING = 'beginning';
- /** @since 3.7.0 */
- public const DEFAULT_PLACEMENT_END = 'end';
-
- /**
- * @inheritdoc
- */
- public static function get(int|string $id): ?static
- {
- /** @phpstan-ignore-next-line */
- return Craft::$app->getCategories()->getGroupById($id);
- }
-
- /**
- * @var int|null ID
- */
- public ?int $id = null;
-
- /**
- * @var int|null Structure ID
- */
- public ?int $structureId = null;
-
- /**
- * @var int|null Field layout ID
- */
- public ?int $fieldLayoutId = null;
-
- /**
- * @var string|null Name
- */
- public ?string $name = null;
-
- /**
- * @var string|null Handle
- */
- public ?string $handle = null;
-
- /**
- * @var int|null Max levels
- */
- public ?int $maxLevels = null;
-
- /**
- * @var string Default placement
- * @phpstan-var self::DEFAULT_PLACEMENT_BEGINNING|self::DEFAULT_PLACEMENT_END
- * @since 3.7.0
- */
- public string $defaultPlacement = self::DEFAULT_PLACEMENT_END;
-
- /**
- * @var string|null UID
- */
- public ?string $uid = null;
-
- /**
- * @var DateTime|null The date that the category group was trashed
- * @since 4.4.0
- */
- public ?DateTime $dateDeleted = null;
-
- /**
- * @var CategoryGroup_SiteSettings[]
- */
- private array $_siteSettings;
-
- /**
- * @inheritdoc
- */
- protected function defineBehaviors(): array
- {
- return [
- 'fieldLayout' => [
- 'class' => FieldLayoutBehavior::class,
- 'elementType' => Category::class,
- ],
- ];
- }
-
- /**
- * @inheritdoc
- */
- public function getId(): ?int
- {
- return $this->id;
- }
-
- /**
- * @inheritdoc
- */
- public function getUiLabel(): string
- {
- return t($this->name, category: 'site');
- }
-
- /**
- * @inheritdoc
- */
- public function getCpEditUrl(): ?string
- {
- if (!$this->id || !Craft::$app->getUser()->getIsAdmin()) {
- return null;
- }
- return UrlHelper::cpUrl("settings/categories/$this->id");
- }
-
- /**
- * @inheritdoc
- */
- public function attributeLabels(): array
- {
- return [
- 'handle' => t('Handle'),
- 'name' => t('Name'),
- ];
- }
-
- /**
- * @inheritdoc
- */
- protected function defineRules(): array
- {
- $rules = parent::defineRules();
- $rules[] = [['id', 'structureId', 'fieldLayoutId', 'maxLevels'], 'number', 'integerOnly' => true];
- $rules[] = [['handle'], HandleValidator::class, 'reservedWords' => ['id', 'dateCreated', 'dateUpdated', 'uid', 'title']];
- $rules[] = [['name', 'handle'], UniqueValidator::class, 'targetClass' => CategoryGroupRecord::class];
- $rules[] = [['name', 'handle', 'siteSettings'], 'required'];
- $rules[] = [['name', 'handle'], 'string', 'max' => 255];
- $rules[] = [['defaultPlacement'], 'in', 'range' => [self::DEFAULT_PLACEMENT_BEGINNING, self::DEFAULT_PLACEMENT_END]];
- $rules[] = [['fieldLayout'], 'validateFieldLayout'];
- $rules[] = [['siteSettings'], 'validateSiteSettings'];
- return $rules;
- }
-
- /**
- * Validates the field layout.
- *
- * @since 3.7.0
- */
- public function validateFieldLayout(): void
- {
- $fieldLayout = $this->getFieldLayout();
- $fieldLayout->reservedFieldHandles = [
- 'group',
- ];
-
- if (!$fieldLayout->validate()) {
- $this->addModelErrors($fieldLayout, 'fieldLayout');
- }
- }
-
- /**
- * Validates the site settings.
- */
- public function validateSiteSettings(): void
- {
- foreach ($this->getSiteSettings() as $i => $siteSettings) {
- if (!$siteSettings->validate()) {
- $this->addModelErrors($siteSettings, "siteSettings[$i]");
- }
- }
- }
-
- /**
- * Use the translated category group's name as the string representation.
- *
- * @return string
- */
- public function __toString(): string
- {
- return t($this->name, category: 'site') ?: static::class;
- }
-
- /**
- * @inheritdoc
- */
- public function getHandle(): ?string
- {
- return $this->handle;
- }
-
- /**
- * @inheritdoc
- */
- public function getFieldLayout(): FieldLayout
- {
- /** @var FieldLayoutBehavior $behavior */
- $behavior = $this->getBehavior('fieldLayout');
- return $behavior->getFieldLayout();
- }
-
- /**
- * Returns the group's site-specific settings.
- *
- * @return CategoryGroup_SiteSettings[]
- */
- public function getSiteSettings(): array
- {
- if (isset($this->_siteSettings)) {
- return $this->_siteSettings;
- }
-
- if (!$this->id) {
- return [];
- }
-
- // Set them with setSiteSettings() so setGroup() gets called on them
- $this->setSiteSettings(Craft::$app->getCategories()->getGroupSiteSettings($this->id));
-
- return $this->_siteSettings;
- }
-
- /**
- * Sets the group's site-specific settings.
- *
- * @param CategoryGroup_SiteSettings[] $siteSettings
- */
- public function setSiteSettings(array $siteSettings): void
- {
- $this->_siteSettings = Arr::keyBy($siteSettings, 'siteId');
-
- foreach ($this->_siteSettings as $settings) {
- $settings->setGroup($this);
- }
- }
-
- /**
- * Returns the category group’s config.
- *
- * @return array
- * @since 3.5.0
- */
- public function getConfig(): array
- {
- $config = [
- 'name' => $this->name,
- 'handle' => $this->handle,
- 'structure' => [
- 'uid' => $this->structureId ? DB::table(Table::STRUCTURES)->uidById($this->structureId) : Str::uuid()->toString(),
- 'maxLevels' => (int)$this->maxLevels ?: null,
- ],
- 'siteSettings' => [],
- 'defaultPlacement' => $this->defaultPlacement ?? self::DEFAULT_PLACEMENT_END,
- ];
-
- $fieldLayout = $this->getFieldLayout();
-
- if ($fieldLayoutConfig = $fieldLayout->getConfig()) {
- $config['fieldLayouts'] = [
- $fieldLayout->uid => $fieldLayoutConfig,
- ];
- }
-
- foreach ($this->getSiteSettings() as $siteId => $settings) {
- $siteUid = DB::table(Table::SITES)->uidById($siteId);
- $config['siteSettings'][$siteUid] = [
- 'hasUrls' => (bool)$settings['hasUrls'],
- 'uriFormat' => $settings['uriFormat'] ?: null,
- 'template' => $settings['template'] ?: null,
- ];
- }
-
- return $config;
- }
-}
diff --git a/yii2-adapter/legacy/models/CategoryGroup_SiteSettings.php b/yii2-adapter/legacy/models/CategoryGroup_SiteSettings.php
deleted file mode 100644
index fb82f7041f2..00000000000
--- a/yii2-adapter/legacy/models/CategoryGroup_SiteSettings.php
+++ /dev/null
@@ -1,142 +0,0 @@
-
- * @since 3.0.0
- */
-class CategoryGroup_SiteSettings extends Model
-{
- /**
- * @var int|null ID
- */
- public ?int $id = null;
-
- /**
- * @var int|null Group ID
- */
- public ?int $groupId = null;
-
- /**
- * @var int|null Site ID
- */
- public ?int $siteId = null;
-
- /**
- * @var bool|null Has URLs?
- */
- public ?bool $hasUrls = null;
-
- /**
- * @var string|null URI format
- */
- public ?string $uriFormat = null;
-
- /**
- * @var string|null Entry template
- */
- public ?string $template = null;
-
- /**
- * @var CategoryGroup|null
- */
- private ?CategoryGroup $_group = null;
-
- /**
- * Returns the group.
- *
- * @return CategoryGroup
- * @throws InvalidConfigException if [[groupId]] is missing or invalid
- */
- public function getGroup(): CategoryGroup
- {
- if (isset($this->_group)) {
- return $this->_group;
- }
-
- if (!$this->groupId) {
- throw new InvalidConfigException('Category is missing its group ID');
- }
-
- if (($this->_group = Craft::$app->getCategories()->getGroupById($this->groupId)) === null) {
- throw new InvalidConfigException('Invalid group ID: ' . $this->groupId);
- }
-
- return $this->_group;
- }
-
- /**
- * Sets the group.
- *
- * @param CategoryGroup $group
- */
- public function setGroup(CategoryGroup $group): void
- {
- $this->_group = $group;
- }
-
- /**
- * Returns the site.
- *
- * @return Site
- * @throws InvalidConfigException if [[siteId]] is missing or invalid
- */
- public function getSite(): Site
- {
- if (!$this->siteId) {
- throw new InvalidConfigException('Category group site settings model is missing its site ID');
- }
-
- if (($site = Sites::getSiteById($this->siteId)) === null) {
- throw new InvalidConfigException('Invalid site ID: ' . $this->siteId);
- }
-
- return $site;
- }
-
- /**
- * @inheritdoc
- */
- public function attributeLabels(): array
- {
- return [
- 'template' => t('Template'),
- 'uriFormat' => t('URI Format'),
- ];
- }
-
- /**
- * @inheritdoc
- */
- protected function defineRules(): array
- {
- $rules = parent::defineRules();
- $rules[] = [['id', 'groupId', 'siteId'], 'number', 'integerOnly' => true];
- $rules[] = [['siteId'], SiteIdValidator::class];
- $rules[] = [['template'], 'string', 'max' => 500];
- $rules[] = [['uriFormat'], UriFormatValidator::class];
-
- if ($this->hasUrls) {
- $rules[] = [['uriFormat'], 'required'];
- }
-
- return $rules;
- }
-}
diff --git a/yii2-adapter/legacy/models/TagGroup.php b/yii2-adapter/legacy/models/TagGroup.php
deleted file mode 100644
index 460e6d69a96..00000000000
--- a/yii2-adapter/legacy/models/TagGroup.php
+++ /dev/null
@@ -1,167 +0,0 @@
-
- * @since 3.0.0
- */
-class TagGroup extends Model implements FieldLayoutProviderInterface
-{
- /**
- * @var int|null ID
- */
- public ?int $id = null;
-
- /**
- * @var string|null Name
- */
- public ?string $name = null;
-
- /**
- * @var string|null Handle
- */
- public ?string $handle = null;
-
- /**
- * @var int|null Field layout ID
- */
- public ?int $fieldLayoutId = null;
-
- /**
- * @var string|null Field layout ID
- */
- public ?string $uid = null;
-
- /**
- * @var DateTime|null The date that the tag group was trashed
- * @since 4.4.0
- */
- public ?DateTime $dateDeleted = null;
-
- /**
- * @inheritdoc
- */
- protected function defineBehaviors(): array
- {
- return [
- 'fieldLayout' => [
- 'class' => FieldLayoutBehavior::class,
- 'elementType' => Tag::class,
- ],
- ];
- }
-
- /**
- * @inheritdoc
- */
- public function attributeLabels(): array
- {
- return [
- 'handle' => t('Handle'),
- 'name' => t('Name'),
- ];
- }
-
- /**
- * @inheritdoc
- */
- protected function defineRules(): array
- {
- $rules = parent::defineRules();
- $rules[] = [['id', 'fieldLayoutId'], 'number', 'integerOnly' => true];
- $rules[] = [['handle'], HandleValidator::class, 'reservedWords' => ['id', 'dateCreated', 'dateUpdated', 'uid', 'title']];
- $rules[] = [['name', 'handle'], UniqueValidator::class, 'targetClass' => TagGroupRecord::class];
- $rules[] = [['name', 'handle'], 'required'];
- $rules[] = [['name', 'handle'], 'string', 'max' => 255];
- $rules[] = [['fieldLayout'], 'validateFieldLayout'];
- return $rules;
- }
-
- /**
- * Validates the field layout.
- *
- * @since 3.7.0
- */
- public function validateFieldLayout(): void
- {
- $fieldLayout = $this->getFieldLayout();
- $fieldLayout->reservedFieldHandles = [
- 'group',
- ];
-
- if (!$fieldLayout->validate()) {
- $this->addModelErrors($fieldLayout, 'fieldLayout');
- }
- }
-
- /**
- * Use the translated tag group's name as the string representation.
- *
- * @return string
- */
- public function __toString(): string
- {
- return t($this->name, category: 'site') ?: static::class;
- }
-
- /**
- * @inheritdoc
- */
- public function getHandle(): ?string
- {
- return $this->handle;
- }
-
- /**
- * @inheritdoc
- */
- public function getFieldLayout(): FieldLayout
- {
- /** @var FieldLayoutBehavior $behavior */
- $behavior = $this->getBehavior('fieldLayout');
- return $behavior->getFieldLayout();
- }
-
- /**
- * Returns the tag group’s config.
- *
- * @return array
- * @since 3.5.0
- */
- public function getConfig(): array
- {
- $config = [
- 'name' => $this->name,
- 'handle' => $this->handle,
- ];
-
- $fieldLayout = $this->getFieldLayout();
-
- if ($fieldLayoutConfig = $fieldLayout->getConfig()) {
- $config['fieldLayouts'] = [
- $fieldLayout->uid => $fieldLayoutConfig,
- ];
- }
-
- return $config;
- }
-}
diff --git a/yii2-adapter/legacy/records/Category.php b/yii2-adapter/legacy/records/Category.php
deleted file mode 100644
index 7970ba3a53f..00000000000
--- a/yii2-adapter/legacy/records/Category.php
+++ /dev/null
@@ -1,54 +0,0 @@
-
- * @since 3.0.0
- */
-class Category extends ActiveRecord
-{
- /**
- * @inheritdoc
- * @return string
- */
- public static function tableName(): string
- {
- return Table::CATEGORIES;
- }
-
- /**
- * Returns the category’s element.
- *
- * @return ActiveQueryInterface The relational query object.
- */
- public function getElement(): ActiveQueryInterface
- {
- return $this->hasOne(Element::class, ['id' => 'id']);
- }
-
- /**
- * Returns the category’s group.
- *
- * @return ActiveQueryInterface The relational query object.
- */
- public function getGroup(): ActiveQueryInterface
- {
- return $this->hasOne(CategoryGroup::class, ['id' => 'groupId']);
- }
-}
diff --git a/yii2-adapter/legacy/records/CategoryGroup.php b/yii2-adapter/legacy/records/CategoryGroup.php
deleted file mode 100644
index 0e157dd9111..00000000000
--- a/yii2-adapter/legacy/records/CategoryGroup.php
+++ /dev/null
@@ -1,88 +0,0 @@
-
- * @since 3.0.0
- */
-class CategoryGroup extends ActiveRecord
-{
- use SoftDeleteTrait;
-
- /**
- * @inheritdoc
- * @return string
- */
- public static function tableName(): string
- {
- return Table::CATEGORYGROUPS;
- }
-
- /**
- * Returns the category group’s structure.
- *
- * @return ActiveQueryInterface The relational query object.
- */
- public function getStructure(): ActiveQueryInterface
- {
- return $this->hasOne(Structure::class, ['id' => 'structureId']);
- }
-
- /**
- * Returns the category group’s fieldLayout.
- *
- * @return ActiveQueryInterface The relational query object.
- */
- public function getFieldLayout(): ActiveQueryInterface
- {
- return $this->hasOne(FieldLayout::class,
- ['id' => 'fieldLayoutId']);
- }
-
- /**
- * Returns the category group’s site settings.
- *
- * @return ActiveQueryInterface The relational query object.
- */
- public function getSiteSettings(): ActiveQueryInterface
- {
- return $this->hasMany(CategoryGroup_SiteSettings::class, ['groupId' => 'id']);
- }
-
- /**
- * Returns the category group’s categories.
- *
- * @return ActiveQueryInterface The relational query object.
- */
- public function getCategories(): ActiveQueryInterface
- {
- return $this->hasMany(Category::class, ['groupId' => 'id']);
- }
-}
diff --git a/yii2-adapter/legacy/records/CategoryGroup_SiteSettings.php b/yii2-adapter/legacy/records/CategoryGroup_SiteSettings.php
deleted file mode 100644
index eae978a9b8e..00000000000
--- a/yii2-adapter/legacy/records/CategoryGroup_SiteSettings.php
+++ /dev/null
@@ -1,58 +0,0 @@
-
- * @since 3.0.0
- */
-class CategoryGroup_SiteSettings extends ActiveRecord
-{
- /**
- * @inheritdoc
- * @return string
- */
- public static function tableName(): string
- {
- return Table::CATEGORYGROUPS_SITES;
- }
-
- /**
- * Returns the associated category group.
- *
- * @return ActiveQueryInterface The relational query object.
- */
- public function getGroup(): ActiveQueryInterface
- {
- return $this->hasOne(CategoryGroup::class, ['id' => 'groupId']);
- }
-
- /**
- * Returns the associated site.
- *
- * @return ActiveQueryInterface The relational query object.
- */
- public function getSite(): ActiveQueryInterface
- {
- return $this->hasOne(Site::class, ['id' => 'siteId']);
- }
-}
diff --git a/yii2-adapter/legacy/records/GlobalSet.php b/yii2-adapter/legacy/records/GlobalSet.php
deleted file mode 100644
index 6d40830ed27..00000000000
--- a/yii2-adapter/legacy/records/GlobalSet.php
+++ /dev/null
@@ -1,95 +0,0 @@
-
- * @since 3.0.0
- */
-class GlobalSet extends ActiveRecord
-{
- /**
- * @inheritdoc
- * @return string
- */
- public static function tableName(): string
- {
- return Table::GLOBALSETS;
- }
-
- /**
- * @return ActiveQuery
- */
- public static function find(): ActiveQuery
- {
- return parent::find()
- ->where([
- 'exists', (new Query())
- ->from(['e' => Table::ELEMENTS])
- ->where('[[e.id]] = ' . static::tableName() . '.[[id]]')
- ->andWhere(['e.dateDeleted' => null]),
- ]);
- }
-
- /**
- * @return ActiveQuery
- */
- public static function findWithTrashed(): ActiveQuery
- {
- return static::find()->where([]);
- }
-
- /**
- * @return ActiveQuery
- */
- public static function findTrashed(): ActiveQuery
- {
- return static::find()->where([
- 'not exists', (new Query())
- ->from(['e' => Table::ELEMENTS])
- ->where('[[e.id]] = ' . static::tableName() . '.[[id]]')
- ->andWhere(['e.dateDeleted' => null]),
- ]);
- }
-
- /**
- * Returns the global set’s element.
- *
- * @return ActiveQueryInterface The relational query object.
- */
- public function getElement(): ActiveQueryInterface
- {
- return $this->hasOne(Element::class, ['id' => 'id']);
- }
-
- /**
- * Returns the global set’s fieldLayout.
- *
- * @return ActiveQueryInterface The relational query object.
- */
- public function getFieldLayout(): ActiveQueryInterface
- {
- return $this->hasOne(FieldLayout::class,
- ['id' => 'fieldLayoutId']);
- }
-}
diff --git a/yii2-adapter/legacy/records/Tag.php b/yii2-adapter/legacy/records/Tag.php
deleted file mode 100644
index c2007d2188a..00000000000
--- a/yii2-adapter/legacy/records/Tag.php
+++ /dev/null
@@ -1,54 +0,0 @@
-
- * @since 3.0.0
- */
-class Tag extends ActiveRecord
-{
- /**
- * @inheritdoc
- * @return string
- */
- public static function tableName(): string
- {
- return Table::TAGS;
- }
-
- /**
- * Returns the tag’s element.
- *
- * @return ActiveQueryInterface The relational query object.
- */
- public function getElement(): ActiveQueryInterface
- {
- return $this->hasOne(Element::class, ['id' => 'id']);
- }
-
- /**
- * Returns the tag’s group.
- *
- * @return ActiveQueryInterface The relational query object.
- */
- public function getGroup(): ActiveQueryInterface
- {
- return $this->hasOne(TagGroup::class, ['id' => 'groupId']);
- }
-}
diff --git a/yii2-adapter/legacy/records/TagGroup.php b/yii2-adapter/legacy/records/TagGroup.php
deleted file mode 100644
index ebc81bd1043..00000000000
--- a/yii2-adapter/legacy/records/TagGroup.php
+++ /dev/null
@@ -1,64 +0,0 @@
-
- * @since 3.0.0
- */
-class TagGroup extends ActiveRecord
-{
- use SoftDeleteTrait;
-
- /**
- * @inheritdoc
- * @return string
- */
- public static function tableName(): string
- {
- return Table::TAGGROUPS;
- }
-
- /**
- * Returns the tag group’s fieldLayout.
- *
- * @return ActiveQueryInterface The relational query object.
- */
- public function getFieldLayout(): ActiveQueryInterface
- {
- return $this->hasOne(FieldLayout::class,
- ['id' => 'fieldLayoutId']);
- }
-
- /**
- * Returns the tag group’s tags.
- *
- * @return ActiveQueryInterface The relational query object.
- */
- public function getTags(): ActiveQueryInterface
- {
- return $this->hasMany(Tag::class, ['groupId' => 'id']);
- }
-}
diff --git a/yii2-adapter/legacy/services/Categories.php b/yii2-adapter/legacy/services/Categories.php
deleted file mode 100644
index e743adfeccd..00000000000
--- a/yii2-adapter/legacy/services/Categories.php
+++ /dev/null
@@ -1,800 +0,0 @@
-getCategories()`]].
- *
- * @author Pixel & Tonic, Inc.
- * @since 3.0.0
- */
-class Categories extends Component
-{
- /**
- * @event CategoryGroupEvent The event that is triggered before a category group is saved.
- */
- public const EVENT_BEFORE_SAVE_GROUP = 'beforeSaveGroup';
-
- /**
- * @event CategoryGroupEvent The event that is triggered after a category group is saved.
- */
- public const EVENT_AFTER_SAVE_GROUP = 'afterSaveGroup';
-
- /**
- * @event CategoryGroupEvent The event that is triggered before a category group is deleted.
- */
- public const EVENT_BEFORE_DELETE_GROUP = 'beforeDeleteGroup';
-
- /**
- * @event CategoryGroupEvent The event that is triggered before a category group delete is applied to the database.
- * @since 3.1.0
- */
- public const EVENT_BEFORE_APPLY_GROUP_DELETE = 'beforeApplyGroupDelete';
-
- /**
- * @event CategoryGroupEvent The event that is triggered after a category group is deleted.
- */
- public const EVENT_AFTER_DELETE_GROUP = 'afterDeleteGroup';
-
- /**
- * @var MemoizableArray|null
- * @see _groups()
- */
- private ?MemoizableArray $_groups = null;
-
- /**
- * Serializer
- */
- public function __serialize()
- {
- $vars = get_object_vars($this);
- unset($vars['_groups']);
- return $vars;
- }
-
- // Category groups
- // -------------------------------------------------------------------------
-
- /**
- * Returns all of the group IDs.
- *
- * @return int[]
- */
- public function getAllGroupIds(): array
- {
- return array_map(fn(CategoryGroup $group) => $group->id, $this->getAllGroups());
- }
-
- /**
- * Returns all of the category group IDs that are editable by the current user.
- *
- * @return int[]
- */
- public function getEditableGroupIds(): array
- {
- return array_map(fn(CategoryGroup $group) => $group->id, $this->getEditableGroups());
- }
-
- /**
- * Returns a memoizable array of all category groups.
- *
- * @return MemoizableArray
- */
- private function _groups(): MemoizableArray
- {
- if (!isset($this->_groups)) {
- $groupRecords = CategoryGroupRecord::find()
- ->orderBy(['name' => SORT_ASC])
- ->with('structure')
- ->all();
-
- $this->_groups = new MemoizableArray(
- $groupRecords,
- fn(CategoryGroupRecord $record) => $this->_createCategoryGroupFromRecord($record),
- );
- }
-
- return $this->_groups;
- }
-
- /**
- * Returns all category groups.
- *
- * @return CategoryGroup[]
- */
- public function getAllGroups(): array
- {
- return $this->_groups()->all();
- }
-
- /**
- * Returns all editable groups.
- *
- * @return CategoryGroup[]
- */
- public function getEditableGroups(): array
- {
- if (Craft::$app->getRequest()->getIsConsoleRequest()) {
- return $this->getAllGroups();
- }
-
- $user = Craft::$app->getUser()->getIdentity();
-
- if (!$user) {
- return [];
- }
-
- return Collection::make($this->getAllGroups())
- ->filter(fn(CategoryGroup $group) => $user->can("viewCategories:$group->uid"))
- ->values()
- ->all();
- }
-
- /**
- * Gets the total number of category groups.
- *
- * @return int
- */
- public function getTotalGroups(): int
- {
- return count($this->getAllGroups());
- }
-
- /**
- * Returns a group by its ID.
- *
- * @param int $groupId
- *
- * @return CategoryGroup|null
- */
- public function getGroupById(int $groupId): ?CategoryGroup
- {
- return $this->_groups()->firstWhere('id', $groupId);
- }
-
- /**
- * Returns a group by its UID.
- *
- * @param string $uid
- *
- * @return CategoryGroup|null
- * @since 3.1.0
- */
- public function getGroupByUid(string $uid): ?CategoryGroup
- {
- return $this->_groups()->firstWhere('uid', $uid, true);
- }
-
- /**
- * Returns a group by its handle.
- *
- * @param string $groupHandle
- * @param bool $withTrashed
- *
- * @return CategoryGroup|null
- */
- public function getGroupByHandle(string $groupHandle, bool $withTrashed = false): ?CategoryGroup
- {
- /** @var CategoryGroup|null $group */
- $group = $this->_groups()->firstWhere('handle', $groupHandle, true);
-
- if (!$group && $withTrashed) {
- /** @var CategoryGroupRecord|null $record */
- $record = CategoryGroupRecord::findWithTrashed()
- ->andWhere(['handle' => $groupHandle])
- ->one();
- if ($record) {
- $group = $this->_createCategoryGroupFromRecord($record);
- }
- }
-
- return $group;
- }
-
- /**
- * Returns a group's site settings.
- *
- * @param int $groupId
- *
- * @return CategoryGroup_SiteSettings[]
- */
- public function getGroupSiteSettings(int $groupId): array
- {
- /** @var CategoryGroup_SiteSettingsRecord[] $results */
- $results = CategoryGroup_SiteSettingsRecord::find()
- ->where(['groupId' => $groupId])
- ->all();
- $siteSettings = [];
-
- foreach ($results as $result) {
- $siteSettings[] = new CategoryGroup_SiteSettings($result->toArray([
- 'id',
- 'groupId',
- 'siteId',
- 'hasUrls',
- 'uriFormat',
- 'template',
- ]));
- }
-
- return $siteSettings;
- }
-
- /**
- * Saves a category group.
- *
- * @param CategoryGroup $group The category group to be saved
- * @param bool $runValidation Whether the category group should be validated
- *
- * @return bool Whether the category group was saved successfully
- * @throws CategoryGroupNotFoundException if $group has an invalid ID
- * @throws Throwable if reasons
- */
- public function saveGroup(CategoryGroup $group, bool $runValidation = true): bool
- {
- $isNewCategoryGroup = !$group->id;
-
- // Fire a 'beforeSaveGroup' event
- if ($this->hasEventHandlers(self::EVENT_BEFORE_SAVE_GROUP)) {
- $this->trigger(self::EVENT_BEFORE_SAVE_GROUP, new CategoryGroupEvent([
- 'categoryGroup' => $group,
- 'isNew' => $isNewCategoryGroup,
- ]));
- }
-
- if ($runValidation && !$group->validate()) {
- Craft::info('Category group not saved due to validation error.', __METHOD__);
- return false;
- }
-
- if ($isNewCategoryGroup && !$group->uid) {
- $group->uid = Str::uuid()->toString();
- }
-
- // If they've set maxLevels to 0 (don't ask why), then pretend like there are none.
- if ((int)$group->maxLevels === 0) {
- $group->maxLevels = null;
- }
-
- // Make sure the group isn't missing any site settings
- $allSiteSettings = $group->getSiteSettings();
- foreach (Sites::getAllSiteIds() as $siteId) {
- if (!isset($allSiteSettings[$siteId])) {
- throw new Exception('Tried to save a category group that is missing site settings');
- }
- }
-
- $configPath = ProjectConfig::PATH_CATEGORY_GROUPS . '.' . $group->uid;
- $configData = $group->getConfig();
- app(ProjectConfig::class)->set($configPath, $configData, "Save category group “{$group->handle}”");
-
- if ($isNewCategoryGroup) {
- $group->id = DB::table(Table::CATEGORYGROUPS)->idByUid($group->uid);
- }
-
- return true;
- }
-
- /**
- * Handle category group change
- *
- * @param ConfigEvent $event
- */
- public function handleChangedCategoryGroup(ConfigEvent $event): void
- {
- $categoryGroupUid = $event->tokenMatches[0];
- $data = $event->newValue;
-
- // Make sure fields and sites are processed
- ProjectConfigHelper::ensureAllSitesProcessed();
- ProjectConfigHelper::ensureAllFieldsProcessed();
-
- DB::beginTransaction();
-
- try {
- $structureData = $data['structure'];
- $siteData = $data['siteSettings'];
- $structureUid = $structureData['uid'];
-
- // Basic data
- $groupRecord = $this->_getCategoryGroupRecord($categoryGroupUid, true);
- $isNewCategoryGroup = $groupRecord->getIsNewRecord();
-
- $groupRecord->name = $data['name'];
- $groupRecord->handle = $data['handle'];
- $groupRecord->uid = $categoryGroupUid;
- $groupRecord->defaultPlacement = $data['defaultPlacement'] ?? CategoryGroup::DEFAULT_PLACEMENT_END;
-
- // Structure
- $structure = Structures::getStructureByUid($structureUid,
- true) ?? new Structure(uid: $structureUid);
- $structure->maxLevels = $structureData['maxLevels'];
- Structures::saveStructure($structure);
-
- $groupRecord->structureId = $structure->id;
-
- // Save the field layout
- if (!empty($data['fieldLayouts'])) {
- // Save the field layout
- $layout = FieldLayout::createFromConfig(reset($data['fieldLayouts']));
- $layout->id = $groupRecord->fieldLayoutId;
- $layout->type = Category::class;
- $layout->uid = key($data['fieldLayouts']);
- app(Fields::class)->saveLayout($layout, false);
- $groupRecord->fieldLayoutId = $layout->id;
- } elseif ($groupRecord->fieldLayoutId) {
- // Delete the field layout
- app(Fields::class)->deleteLayoutById($groupRecord->fieldLayoutId);
- $groupRecord->fieldLayoutId = null;
- }
-
- // Save the category group
- if ($wasTrashed = (bool)$groupRecord->dateDeleted) {
- $groupRecord->restore();
- } else {
- $groupRecord->save(false);
- }
-
- // Update the site settings
- // -----------------------------------------------------------------
-
- $sitesNowWithoutUrls = [];
- $sitesWithNewUriFormats = [];
-
- if (!$isNewCategoryGroup) {
- // Get the old category group site settings
- /** @var CategoryGroup_SiteSettingsRecord[] $allOldSiteSettingsRecords */
- $allOldSiteSettingsRecords = CategoryGroup_SiteSettingsRecord::find()
- ->where(['groupId' => $groupRecord->id])
- ->indexBy('siteId')
- ->all();
- }
-
- $siteIdMap = DB::table(Table::SITES)
- ->whereIn('uid', array_keys($siteData))
- ->pluck('id', 'uid')
- ->all();
-
- foreach ($siteData as $siteUid => $siteSettings) {
- $siteId = $siteIdMap[$siteUid];
-
- // Was this already selected?
- if (!$isNewCategoryGroup && isset($allOldSiteSettingsRecords[$siteId])) {
- $siteSettingsRecord = $allOldSiteSettingsRecords[$siteId];
- } else {
- $siteSettingsRecord = new CategoryGroup_SiteSettingsRecord();
- $siteSettingsRecord->groupId = $groupRecord->id;
- $siteSettingsRecord->siteId = $siteId;
- }
-
- if ($siteSettingsRecord->hasUrls = $siteSettings['hasUrls']) {
- $siteSettingsRecord->uriFormat = $siteSettings['uriFormat'];
- $siteSettingsRecord->template = $siteSettings['template'];
- } else {
- $siteSettingsRecord->uriFormat = null;
- $siteSettingsRecord->template = null;
- }
-
- if (!$siteSettingsRecord->getIsNewRecord()) {
- // Did it used to have URLs, but not anymore?
- if ($siteSettingsRecord->isAttributeChanged('hasUrls', false) && !$siteSettings['hasUrls']) {
- $sitesNowWithoutUrls[] = $siteId;
- }
-
- // Does it have URLs, and has its URI format changed?
- if ($siteSettings['hasUrls'] && $siteSettingsRecord->isAttributeChanged('uriFormat', false)) {
- $sitesWithNewUriFormats[] = $siteId;
- }
- }
-
- $siteSettingsRecord->save(false);
- }
-
- if (!$isNewCategoryGroup) {
- // Drop any site settings that are no longer being used, as well as the associated category/element
- // site rows
- $affectedSiteUids = array_keys($siteData);
-
- foreach ($allOldSiteSettingsRecords as $siteId => $siteSettingsRecord) {
- $siteUid = array_search($siteId, $siteIdMap, false);
- if (!in_array($siteUid, $affectedSiteUids, false)) {
- $siteSettingsRecord->delete();
- }
- }
- }
-
- // Finally, deal with the existing categories...
- // -----------------------------------------------------------------
-
- if (!$isNewCategoryGroup) {
- // Get all of the category IDs in this group
- $categoryIds = Category::find()
- ->groupId($groupRecord->id)
- ->status(null)
- ->ids();
-
- // Are there any sites left?
- if (!empty($siteData)) {
- // Drop the old category URIs for any site settings that don't have URLs
- if (!empty($sitesNowWithoutUrls)) {
- DB::table(Table::ELEMENTS_SITES)
- ->whereIn('elementId', $categoryIds)
- ->whereIn('siteId', $sitesNowWithoutUrls)
- ->update([
- 'uri' => null,
- 'dateUpdated' => now(),
- ]);
- } elseif (!empty($sitesWithNewUriFormats)) {
- foreach ($categoryIds as $categoryId) {
- maxPowerCaptain();
-
- // Loop through each of the changed sites and update all of the categories’ slugs and URIs
- foreach ($sitesWithNewUriFormats as $siteId) {
- /** @var Category|null $category */
- $category = Category::find()
- ->id($categoryId)
- ->siteId($siteId)
- ->status(null)
- ->one();
-
- if ($category) {
- Craft::$app->getElements()->updateElementSlugAndUri($category, false, false);
- }
- }
- }
- }
- }
- }
-
- DB::commit();
- } catch (Throwable $e) {
- DB::rollBack();
- throw $e;
- }
-
- // Clear caches
- $this->_groups = null;
-
- if ($wasTrashed) {
- // Restore the categories that were deleted with the group
- /** @var Category[] $categories */
- $categories = Category::find()
- ->groupId($groupRecord->id)
- ->trashed()
- ->andWhere(['categories.deletedWithGroup' => true])
- ->all();
- Craft::$app->getElements()->restoreElements($categories);
- }
-
- // Fire an 'afterSaveGroup' event
- if ($this->hasEventHandlers(self::EVENT_AFTER_SAVE_GROUP)) {
- $this->trigger(self::EVENT_AFTER_SAVE_GROUP, new CategoryGroupEvent([
- 'categoryGroup' => $this->getGroupById($groupRecord->id),
- 'isNew' => $isNewCategoryGroup,
- ]));
- }
-
- // Invalidate category caches
- Craft::$app->getElements()->invalidateCachesForElementType(Category::class);
- }
-
- /**
- * Deletes a category group by its ID.
- *
- * @param int $groupId The category group's ID
- *
- * @return bool Whether the category group was deleted successfully
- * @throws Throwable if reasons
- * @since 3.0.12
- */
- public function deleteGroupById(int $groupId): bool
- {
- if (!$groupId) {
- return false;
- }
-
- $group = $this->getGroupById($groupId);
-
- if (!$group) {
- return false;
- }
-
- return $this->deleteGroup($group);
- }
-
- /**
- * Deletes a category group.
- *
- * @param CategoryGroup $group The category group
- *
- * @return bool Whether the category group was deleted successfully
- */
- public function deleteGroup(CategoryGroup $group): bool
- {
- // Fire a 'beforeDeleteGroup' event
- if ($this->hasEventHandlers(self::EVENT_BEFORE_DELETE_GROUP)) {
- $this->trigger(self::EVENT_BEFORE_DELETE_GROUP, new CategoryGroupEvent([
- 'categoryGroup' => $group,
- ]));
- }
-
- app(ProjectConfig::class)->remove(
- ProjectConfig::PATH_CATEGORY_GROUPS . '.' . $group->uid,
- "Delete category group “{$group->handle}”"
- );
-
- return true;
- }
-
- /**
- * Returns whether a group’s categories have URLs for the given site ID, and if the group’s template path is valid.
- *
- * @param CategoryGroup $group
- * @param int $siteId
- *
- * @return bool
- */
- public function isGroupTemplateValid(CategoryGroup $group, int $siteId): bool
- {
- $categoryGroupSiteSettings = $group->getSiteSettings();
-
- if (!isset($categoryGroupSiteSettings[$siteId]) || !$categoryGroupSiteSettings[$siteId]->hasUrls) {
- return false;
- }
-
- $template = (string)$categoryGroupSiteSettings[$siteId]->template;
- return Craft::$app->getView()->doesTemplateExist($template, View::TEMPLATE_MODE_SITE);
- }
-
- /**
- * Handle Category group getting deleted
- *
- * @param ConfigEvent $event
- */
- public function handleDeletedCategoryGroup(ConfigEvent $event): void
- {
- $uid = $event->tokenMatches[0];
- $categoryGroupRecord = $this->_getCategoryGroupRecord($uid);
-
- if (!$categoryGroupRecord->id) {
- return;
- }
-
- /** @var CategoryGroup $group */
- $group = $this->getGroupById($categoryGroupRecord->id);
-
- // Fire a 'beforeApplyGroupDelete' event
- if ($this->hasEventHandlers(self::EVENT_BEFORE_APPLY_GROUP_DELETE)) {
- $this->trigger(self::EVENT_BEFORE_APPLY_GROUP_DELETE, new CategoryGroupEvent([
- 'categoryGroup' => $group,
- ]));
- }
-
- DB::beginTransaction();
- try {
- // Delete the categories
- $elementsTable = Table::ELEMENTS;
- $categoriesTable = Table::CATEGORIES;
- $now = now();
-
- $condition = fn(Builder $query): Builder => $query
- ->where('categories.groupId', $group->id)
- ->whereColumn('categories.id', 'elements.id')
- ->whereNull(['elements.canonicalId', 'elements.revisionId', 'elements.dateDeleted']);
-
- DB::table(new Alias($categoriesTable, 'categories'))
- ->join(new Alias($elementsTable, 'elements'), 'elements.id', 'categories.id')
- ->where($condition)
- ->update(['deletedWithGroup' => true]);
-
- DB::table(new Alias($elementsTable, 'elements'))
- ->join(new Alias($categoriesTable, 'categories'), 'elements.id', 'categories.id')
- ->where($condition)
- ->update(['dateDeleted' => $now]);
-
- // Delete the structure
- Structures::deleteStructureById($categoryGroupRecord->structureId);
-
- // Delete the field layout
- if ($categoryGroupRecord->fieldLayoutId) {
- app(Fields::class)->deleteLayoutById($categoryGroupRecord->fieldLayoutId);
- }
-
- // Delete the category group
- DB::table(Table::CATEGORYGROUPS)->softDelete($categoryGroupRecord->id);
-
- DB::commit();
- } catch (Throwable $e) {
- DB::rollBack();
- throw $e;
- }
-
- // Clear caches
- $this->_groups = null;
-
- // Fire an 'afterDeleteGroup' event
- if ($this->hasEventHandlers(self::EVENT_AFTER_DELETE_GROUP)) {
- $this->trigger(self::EVENT_AFTER_DELETE_GROUP, new CategoryGroupEvent([
- 'categoryGroup' => $group,
- ]));
- }
-
- // Invalidate category caches
- Craft::$app->getElements()->invalidateCachesForElementType(Category::class);
- }
-
- /**
- * @deprecated in 4.0.5. Unused fields will be pruned automatically as field layouts are resaved.
- */
- public function pruneDeletedField(): void
- {
- }
-
- /**
- * Prune a deleted site from category group site settings.
- *
- * @param DeleteSiteEvent $event
- */
- public function pruneDeletedSite(DeleteSiteEvent $event): void
- {
- $siteUid = $event->site->uid;
-
- $projectConfig = app(ProjectConfig::class);
- $categoryGroups = $projectConfig->get(ProjectConfig::PATH_CATEGORY_GROUPS);
-
- // Loop through the category groups and prune the UID from field layouts.
- if (is_array($categoryGroups)) {
- foreach ($categoryGroups as $categoryGroupUid => $categoryGroup) {
- $projectConfig->remove(ProjectConfig::PATH_CATEGORY_GROUPS . '.' . $categoryGroupUid . '.siteSettings.' . $siteUid,
- 'Prune deleted site settings');
- }
- }
- }
-
- // Categories
- // -------------------------------------------------------------------------
-
- /**
- * Returns a category by its ID.
- *
- * @param int $categoryId
- * @param int|int[]|string|null $siteId
- * @param array $criteria
- *
- * @return Category|null
- */
- public function getCategoryById(int $categoryId, mixed $siteId = null, array $criteria = []): ?Category
- {
- if (!$categoryId) {
- return null;
- }
-
- // Get the structure ID
- if (!isset($criteria['structureId'])) {
- $criteria['structureId'] = DB::table(new Alias(Table::CATEGORIES,
- 'categories'))
- ->join(new Alias(Table::CATEGORYGROUPS, 'categorygroups'), 'categorygroups.id',
- 'categories.groupId')
- ->where('categories.id', $categoryId)
- ->value('categorygroups.structureId');
- }
-
- // All categories are part of a structure
- if (!$criteria['structureId']) {
- return null;
- }
-
- return Craft::$app->getElements()->getElementById($categoryId, Category::class, $siteId, $criteria);
- }
-
- /**
- * Patches an array of categories, filling in any gaps in the tree.
- *
- * @param Category[] $categories
- *
- * @deprecated in 3.6.0. Use [[\craft\services\Structures::fillGapsInElements()]] instead.
- */
- public function fillGapsInCategories(array &$categories): void
- {
- Structures::fillGapsInElements($categories);
- }
-
- /**
- * Filters an array of categories down to only <= X branches.
- *
- * @param Category[] $categories
- * @param int $branchLimit
- *
- * @deprecated in 3.6.0. Use [[\craft\services\Structures::applyBranchLimitToElements()]] instead.
- */
- public function applyBranchLimitToCategories(array &$categories, int $branchLimit): void
- {
- Structures::applyBranchLimitToElements($categories, $branchLimit);
- }
-
- /**
- * Creates a CategoryGroup with attributes from a CategoryGroupRecord.
- *
- * @param CategoryGroupRecord|null $groupRecord
- *
- * @return CategoryGroup|null
- */
- private function _createCategoryGroupFromRecord(?CategoryGroupRecord $groupRecord = null): ?CategoryGroup
- {
- if (!$groupRecord) {
- return null;
- }
-
- $group = new CategoryGroup($groupRecord->toArray([
- 'id',
- 'structureId',
- 'fieldLayoutId',
- 'name',
- 'handle',
- 'defaultPlacement',
- 'dateDeleted',
- 'uid',
- ]));
-
- if ($groupRecord->structure) {
- $group->maxLevels = $groupRecord->structure->maxLevels;
- }
-
- return $group;
- }
-
- /**
- * Gets a category group's record by uid.
- *
- * @param string $uid
- * @param bool $withTrashed Whether to include trashed category groups in search
- *
- * @return CategoryGroupRecord
- */
- private function _getCategoryGroupRecord(string $uid, bool $withTrashed = false): CategoryGroupRecord
- {
- $query = $withTrashed ? CategoryGroupRecord::findWithTrashed() : CategoryGroupRecord::find();
- $query->andWhere(['uid' => $uid]);
- /** @noinspection PhpIncompatibleReturnTypeInspection */
- /** @var CategoryGroupRecord */
- return $query->one() ?? new CategoryGroupRecord();
- }
-}
diff --git a/yii2-adapter/legacy/services/Elements.php b/yii2-adapter/legacy/services/Elements.php
index b0ef4180725..34aa9bd4294 100644
--- a/yii2-adapter/legacy/services/Elements.php
+++ b/yii2-adapter/legacy/services/Elements.php
@@ -19,15 +19,12 @@
use craft\db\QueryAbortedException;
use craft\elements\Address;
use craft\elements\Asset;
-use craft\elements\Category;
use craft\elements\db\EagerLoadInfo;
use craft\elements\db\EagerLoadPlan;
use craft\elements\db\ElementQuery;
use craft\elements\db\ElementQueryInterface;
use craft\elements\ElementCollection;
use craft\elements\Entry;
-use craft\elements\GlobalSet;
-use craft\elements\Tag;
use craft\elements\User;
use craft\errors\ElementNotFoundException;
use craft\errors\FieldNotFoundException;
@@ -2954,10 +2951,7 @@ public function getAllElementTypes(): array
$elementTypes = [
Address::class,
Asset::class,
- Category::class,
Entry::class,
- GlobalSet::class,
- Tag::class,
User::class,
];
@@ -3016,16 +3010,6 @@ public function getElementTypeByRefHandle(string $refHandle): ?string
{
if (!isset($this->_elementTypesByRefHandle[$refHandle])) {
$class = $this->elementTypeByRefHandle($refHandle);
-
- // Special cases for categories/tags/globals, if they’ve been entrified
- if (
- ($class === Category::class && empty(Craft::$app->getCategories()->getAllGroups())) ||
- ($class === Tag::class && empty(Craft::$app->getTags()->getAllTagGroups())) ||
- ($class === GlobalSet::class && empty(Craft::$app->getGlobals()->getAllSets()))
- ) {
- $class = Entry::class;
- }
-
$this->_elementTypesByRefHandle[$refHandle] = $class;
}
diff --git a/yii2-adapter/legacy/services/Globals.php b/yii2-adapter/legacy/services/Globals.php
deleted file mode 100644
index eb35dcbf67d..00000000000
--- a/yii2-adapter/legacy/services/Globals.php
+++ /dev/null
@@ -1,576 +0,0 @@
-getGlobals()`]].
- *
- * @author Pixel & Tonic, Inc.
- * @since 3.0.0
- */
-class Globals extends Component
-{
- /**
- * @event GlobalSetEvent The event that is triggered before a global set is saved.
- */
- public const EVENT_BEFORE_SAVE_GLOBAL_SET = 'beforeSaveGlobalSet';
-
- /**
- * @event GlobalSetEvent The event that is triggered after a global set is saved.
- */
- public const EVENT_AFTER_SAVE_GLOBAL_SET = 'afterSaveGlobalSet';
-
- /**
- * @var MemoizableArray[]|null
- * @see _allSets()
- */
- private ?array $_allGlobalSets = null;
-
- /**
- * @var GlobalSet[][]|null
- * @see getEditableSets()
- */
- private ?array $_editableGlobalSets = null;
-
- /**
- * Serializer
- *
- * @since 3.5.14
- */
- public function __serialize()
- {
- $vars = get_object_vars($this);
- unset($vars['_allGlobalSets']);
- return $vars;
- }
-
- /**
- * Returns all of the global set IDs.
- *
- * ---
- *
- * ```php
- * $globalSetIds = Craft::$app->globals->allSetIds;
- * ```
- * ```twig
- * {% set globalSetIds = craft.app.globals.allSetIds %}
- * ```
- *
- * @return array
- */
- public function getAllSetIds(): array
- {
- return array_map(fn(GlobalSet $globalSet) => $globalSet->id, $this->getAllSets());
- }
-
- /**
- * Returns all of the global set IDs that are editable by the current user.
- *
- * ---
- *
- * ```php
- * $globalSetIds = Craft::$app->globals->editableSetIds;
- * ```
- * ```twig
- * {% set globalSetIds = craft.app.globals.editableSetIds %}
- * ```
- *
- * @return array
- */
- public function getEditableSetIds(): array
- {
- return array_map(fn(GlobalSet $globalSet) => $globalSet->id, $this->getEditableSets());
- }
-
- /**
- * Returns all global sets.
- *
- * ---
- *
- * ```php
- * $globalSets = Craft::$app->globals->allSets;
- * ```
- * ```twig
- * {% set globalSets = craft.app.globals.allSets %}
- * ```
- *
- * @return GlobalSet[]
- */
- public function getAllSets(): array
- {
- /** @noinspection PhpUnhandledExceptionInspection */
- return $this->_allSets(Sites::getCurrentSite()->id)->all();
- }
-
- /**
- * Returns all global sets that are editable by the current user.
- *
- * ---
- *
- * ```php
- * $globalSets = Craft::$app->globals->editableSets;
- * ```
- * ```twig
- * {% set globalSets = craft.app.globals.editableSets %}
- * ```
- *
- * @return GlobalSet[]
- */
- public function getEditableSets(): array
- {
- /** @noinspection PhpUnhandledExceptionInspection */
- $currentSiteId = Sites::getCurrentSite()->id;
-
- if (!isset($this->_editableGlobalSets[$currentSiteId])) {
- $session = Craft::$app->getUser();
-
- $this->_editableGlobalSets[$currentSiteId] = Collection::make($this->_allSets($currentSiteId))
- ->filter(fn(GlobalSet $globalSet) => $session->checkPermission("editGlobalSet:$globalSet->uid"))
- ->values()
- ->all();
- }
-
- return $this->_editableGlobalSets[$currentSiteId];
- }
-
- /**
- * Returns the total number of global sets.
- *
- * ---
- *
- * ```php
- * $total = Craft::$app->globals->totalSets;
- * ```
- * ```twig
- * {% set total = craft.app.globals.totalSets %}
- * ```
- *
- * @return int
- */
- public function getTotalSets(): int
- {
- return count($this->getAllSets());
- }
-
- /**
- * Returns the total number of global sets that are editable by the current user.
- *
- * ---
- *
- * ```php
- * $total = Craft::$app->globals->totalEditableSets;
- * ```
- * ```twig
- * {% set total = craft.app.globals.totalEditableSets %}
- * ```
- *
- * @return int
- */
- public function getTotalEditableSets(): int
- {
- return count($this->getEditableSets());
- }
-
- /**
- * Returns a global set by its ID.
- *
- * ---
- *
- * ```php
- * $globalSet = Craft::$app->globals->getSetById(1);
- * ```
- * ```twig
- * {% set globalSet = craft.app.globals.getSetById(1) %}
- * ```
- *
- * @param int $globalSetId
- * @param int|null $siteId
- * @return GlobalSet|null
- */
- public function getSetById(int $globalSetId, ?int $siteId = null): ?GlobalSet
- {
- /** @noinspection PhpUnhandledExceptionInspection */
- $currentSiteId = Sites::getCurrentSite()->id;
-
- if ($siteId === null) {
- $siteId = $currentSiteId;
- }
-
- if ($siteId == $currentSiteId) {
- return $this->_allSets($siteId)->firstWhere('id', $globalSetId);
- }
-
- /** @var GlobalSet|null */
- return GlobalSet::find()
- ->siteId($siteId)
- ->id($globalSetId)
- ->one();
- }
-
- /**
- * Returns a global set by its handle.
- *
- * ---
- *
- * ```php
- * $globalSet = Craft::$app->globals->getSetByHandle('footerInfo');
- * ```
- * ```twig
- * {% set globalSet = craft.app.globals.getSetByHandle('footerInfo') %}
- * ```
- *
- * @param string $globalSetHandle
- * @param int|null $siteId
- * @param bool $withTrashed
- * @return GlobalSet|null
- */
- public function getSetByHandle(string $globalSetHandle, ?int $siteId = null, bool $withTrashed = false): ?GlobalSet
- {
- /** @noinspection PhpUnhandledExceptionInspection */
- $currentSiteId = Sites::getCurrentSite()->id;
-
- if ($siteId === null) {
- $siteId = $currentSiteId;
- }
-
- if ($siteId == $currentSiteId) {
- /** @var GlobalSet|null $globalSet */
- $globalSet = $this->_allSets($siteId)->firstWhere('handle', $globalSetHandle, true);
- if ($globalSet) {
- return $globalSet;
- }
- }
-
- $globalSetQuery = GlobalSet::find()
- ->handle($globalSetHandle)
- ->siteId($siteId);
-
- if ($withTrashed) {
- $globalSetQuery->trashed(null);
- }
-
- /** @var GlobalSet|null */
- return $globalSetQuery->one();
- }
-
- /**
- * Saves a global set.
- *
- * @param GlobalSet $globalSet The global set to be saved
- * @param bool $runValidation Whether the global set should be validated
- * @return bool
- * @throws GlobalSetNotFoundException if $globalSet->id is invalid
- * @throws Throwable if reasons
- */
- public function saveSet(GlobalSet $globalSet, bool $runValidation = true): bool
- {
- $isNewSet = !$globalSet->id;
-
- // Fire a 'beforeSaveGlobalSet' event
- if ($this->hasEventHandlers(self::EVENT_BEFORE_SAVE_GLOBAL_SET)) {
- $this->trigger(self::EVENT_BEFORE_SAVE_GLOBAL_SET, new GlobalSetEvent([
- 'globalSet' => $globalSet,
- 'isNew' => $isNewSet,
- ]));
- }
-
- // Prevent most custom field validators
- $globalSet->setScenario(GlobalSet::SCENARIO_SAVE_SET);
-
- if ($runValidation && !$globalSet->validate()) {
- Craft::info('Global set not saved due to validation error.', __METHOD__);
- return false;
- }
-
- if ($isNewSet) {
- $globalSet->uid = $globalSet->uid ?: Str::uuid()->toString();
- $globalSet->sortOrder = DB::table(Table::GLOBALSETS)->max('sortOrder') + 1;
- } elseif (!$globalSet->uid) {
- $globalSet->uid = DB::table(Table::GLOBALSETS)->uidById($globalSet->id);
- }
-
- $configPath = ProjectConfig::PATH_GLOBAL_SETS . '.' . $globalSet->uid;
- $configData = $globalSet->getConfig();
- app(ProjectConfig::class)->set($configPath, $configData, "Save global set “{$globalSet->handle}”");
-
- if ($isNewSet) {
- $globalSet->id = DB::table(Table::GLOBALSETS)->idByUid($globalSet->uid);
- }
-
- return true;
- }
-
- /**
- * Handle global set change
- *
- * @param ConfigEvent $event
- */
- public function handleChangedGlobalSet(ConfigEvent $event): void
- {
- $globalSetUid = $event->tokenMatches[0];
- $data = $event->newValue;
-
- // Make sure fields are processed
- ProjectConfigHelper::ensureAllSitesProcessed();
- ProjectConfigHelper::ensureAllFieldsProcessed();
-
- DB::beginTransaction();
- try {
- $globalSetRecord = $this->_getGlobalSetRecord($globalSetUid, true);
- $isNewSet = $globalSetRecord->getIsNewRecord();
-
- $globalSetRecord->name = $data['name'];
- $globalSetRecord->handle = $data['handle'];
- $globalSetRecord->sortOrder = $data['sortOrder'] ?? 0;
- $globalSetRecord->uid = $globalSetUid;
-
- if (!empty($data['fieldLayouts'])) {
- // Save the field layout
- $layout = FieldLayout::createFromConfig(reset($data['fieldLayouts']));
- $layout->id = $globalSetRecord->fieldLayoutId;
- $layout->type = GlobalSet::class;
- $layout->uid = key($data['fieldLayouts']);
- app(Fields::class)->saveLayout($layout, false);
- $globalSetRecord->fieldLayoutId = $layout->id;
- } elseif ($globalSetRecord->fieldLayoutId) {
- // Delete the field layout
- app(Fields::class)->deleteLayoutById($globalSetRecord->fieldLayoutId);
- $globalSetRecord->fieldLayoutId = null;
- }
-
- // Make sure there's an element for it.
- $element = null;
- $elementsService = Craft::$app->getElements();
- if (!$globalSetRecord->getIsNewRecord()) {
- /** @var GlobalSet|null $element */
- $element = GlobalSet::find()
- ->id($globalSetRecord->id)
- ->trashed(null)
- ->one();
-
- // If it's trashed, attempt to restore it, otherwise create a new element
- if ($element && $element->trashed) {
- $element->fieldLayoutId = $globalSetRecord->fieldLayoutId;
- if (
- !$elementsService->saveElement($element) ||
- !$elementsService->restoreElement($element)
- ) {
- $element = null;
- }
- }
- }
-
- if (!$element) {
- $element = new GlobalSet();
- }
-
- $element->name = $globalSetRecord->name;
- $element->handle = $globalSetRecord->handle;
- $element->fieldLayoutId = $globalSetRecord->fieldLayoutId;
-
- if (!$elementsService->saveElement($element, false)) {
- throw new ElementNotFoundException('Unable to save the element required for global set.');
- }
-
- // Save the global set
- $globalSetRecord->id = $element->id;
- $globalSetRecord->save(false);
-
- DB::commit();
- } catch (Throwable $e) {
- DB::rollBack();
- throw $e;
- }
-
- // Clear caches
- $this->_allGlobalSets = null;
- $this->_editableGlobalSets = null;
-
- // Fire an 'afterSaveGlobalSet' event
- if ($this->hasEventHandlers(self::EVENT_AFTER_SAVE_GLOBAL_SET)) {
- $this->trigger(self::EVENT_AFTER_SAVE_GLOBAL_SET, new GlobalSetEvent([
- 'globalSet' => $this->getSetById($globalSetRecord->id),
- 'isNew' => $isNewSet,
- ]));
- }
-
- // Invalidate all element caches
- Craft::$app->getElements()->invalidateAllCaches();
- }
-
- /**
- * Reorders global sets.
- *
- * @param array $setIds
- * @return bool
- * @throws Throwable
- * @since 3.7.0
- */
- public function reorderSets(array $setIds): bool
- {
- $projectConfig = app(ProjectConfig::class);
-
- $uidsByIds = DB::table(Table::GLOBALSETS)->uidsByIds($setIds);
-
- foreach ($setIds as $i => $setId) {
- if (!empty($uidsByIds[$setId])) {
- $setUid = $uidsByIds[$setId];
- $projectConfig->set(ProjectConfig::PATH_GLOBAL_SETS . ".$setUid.sortOrder", $i + 1, 'Reorder global sets');
- }
- }
-
- return true;
- }
-
- /**
- * Deletes a global set by its ID.
- *
- * @param int $globalSetId
- * @return bool Whether the global set was deleted successfully
- * @throws Throwable if reasons
- */
- public function deleteGlobalSetById(int $globalSetId): bool
- {
- if (!$globalSetId) {
- return false;
- }
-
- $globalSet = $this->getSetById($globalSetId);
-
- if (!$globalSet) {
- return false;
- }
-
- $this->deleteSet($globalSet);
- return true;
- }
-
- /**
- * Deletes a global set by its ID.
- *
- * @param GlobalSet $globalSet
- * @since 3.6.0
- */
- public function deleteSet(GlobalSet $globalSet): void
- {
- app(ProjectConfig::class)->remove(ProjectConfig::PATH_GLOBAL_SETS . '.' . $globalSet->uid, "Delete the “{$globalSet->handle}” global set");
- }
-
- /**
- * Handle global set getting deleted
- *
- * @param ConfigEvent $event
- */
- public function handleDeletedGlobalSet(ConfigEvent $event): void
- {
- $uid = $event->tokenMatches[0];
- $globalSetRecord = $this->_getGlobalSetRecord($uid);
-
- if (!$globalSetRecord->id) {
- return;
- }
-
- DB::beginTransaction();
-
- try {
- // Get the field layout
- $fieldLayoutId = DB::table(Table::GLOBALSETS)
- ->where('id', $globalSetRecord->id)
- ->value('fieldLayoutId');
-
- Craft::$app->getElements()->deleteElementById($globalSetRecord->id);
-
- if ($fieldLayoutId) {
- $fieldLayout = app(Fields::class)->getLayoutById($fieldLayoutId);
-
- // Delete the field layout after the element has been deleted
- if ($fieldLayout) {
- app(Fields::class)->deleteLayout($fieldLayout);
- }
- }
-
- DB::commit();
- } catch (Throwable $e) {
- DB::rollBack();
- throw $e;
- }
-
- // Invalidate all element caches
- Craft::$app->getElements()->invalidateAllCaches();
- }
-
- /**
- * @deprecated in 4.0.5. Unused fields will be pruned automatically as field layouts are resaved.
- */
- public function pruneDeletedField(): void
- {
- }
-
- /**
- * Resets the memoized globals.
- *
- * @since 3.6.0
- */
- public function reset(): void
- {
- $this->_allGlobalSets = $this->_editableGlobalSets = null;
- }
-
- /**
- * Returns a memoizable array of all global sets for the given site.
- *
- * @param int $siteId
- * @return MemoizableArray
- */
- private function _allSets(int $siteId): MemoizableArray
- {
- if (!isset($this->_allGlobalSets[$siteId])) {
- $this->_allGlobalSets[$siteId] = new MemoizableArray(GlobalSet::find()->siteId($siteId)->all());
- }
-
- return $this->_allGlobalSets[$siteId];
- }
-
- /**
- * Gets a global set's record by uid.
- *
- * @param string $uid
- * @param bool $withTrashed Whether to include trashed sections in search
- * @return GlobalSetRecord
- */
- private function _getGlobalSetRecord(string $uid, bool $withTrashed = false): GlobalSetRecord
- {
- $query = $withTrashed ? GlobalSetRecord::findWithTrashed() : GlobalSetRecord::find();
- $query->andWhere(['uid' => $uid]);
- /** @noinspection PhpIncompatibleReturnTypeInspection */
- /** @var GlobalSetRecord */
- return $query->one() ?? new GlobalSetRecord();
- }
-}
diff --git a/yii2-adapter/legacy/services/Gql.php b/yii2-adapter/legacy/services/Gql.php
index 90290dfe8d5..d5d7e2fdd9a 100644
--- a/yii2-adapter/legacy/services/Gql.php
+++ b/yii2-adapter/legacy/services/Gql.php
@@ -36,24 +36,15 @@
use craft\gql\interfaces\Element as ElementInterface;
use craft\gql\interfaces\elements\Address as AddressInterface;
use craft\gql\interfaces\elements\Asset as AssetInterface;
-use craft\gql\interfaces\elements\Category as CategoryInterface;
use craft\gql\interfaces\elements\Entry as EntryInterface;
-use craft\gql\interfaces\elements\GlobalSet as GlobalSetInterface;
-use craft\gql\interfaces\elements\Tag as TagInterface;
use craft\gql\interfaces\elements\User as UserInterface;
use craft\gql\mutations\Asset as AssetMutation;
-use craft\gql\mutations\Category as CategoryMutation;
use craft\gql\mutations\Entry as EntryMutation;
-use craft\gql\mutations\GlobalSet as GlobalSetMutation;
use craft\gql\mutations\Ping as PingMutation;
-use craft\gql\mutations\Tag as TagMutation;
use craft\gql\queries\Address as AddressQuery;
use craft\gql\queries\Asset as AssetQuery;
-use craft\gql\queries\Category as CategoryQuery;
use craft\gql\queries\Entry as EntryQuery;
-use craft\gql\queries\GlobalSet as GlobalSetQuery;
use craft\gql\queries\Ping as PingQuery;
-use craft\gql\queries\Tag as TagQuery;
use craft\gql\queries\User as UserQuery;
use craft\gql\TypeLoader;
use craft\gql\TypeManager;
@@ -725,22 +716,10 @@ public function getAllSchemaComponents(): array
$label = t('Assets');
[$queries[$label], $mutations[$label]] = $this->assetSchemaComponents();
- // Global Sets
- $label = t('Global Sets');
- [$queries[$label], $mutations[$label]] = $this->globalSetSchemaComponents();
-
// Users
$label = t('Users');
[$queries[$label], $mutations[$label]] = $this->userSchemaComponents();
- // Categories
- $label = t('Categories');
- [$queries[$label], $mutations[$label]] = $this->categorySchemaComponents();
-
- // Tags
- $label = t('Tags');
- [$queries[$label], $mutations[$label]] = $this->tagSchemaComponents();
-
// Fire a 'registerGqlSchemaComponents' event
if ($this->hasEventHandlers(self::EVENT_REGISTER_GQL_SCHEMA_COMPONENTS)) {
$event = new RegisterGqlSchemaComponentsEvent([
@@ -1474,9 +1453,6 @@ private function _registerGqlTypes(): array
EntryInterface::class,
AssetInterface::class,
UserInterface::class,
- GlobalSetInterface::class,
- CategoryInterface::class,
- TagInterface::class,
];
// Fire a 'registerGqlTypes' event
@@ -1506,9 +1482,6 @@ private function _registerGqlQueries(): void
EntryQuery::getQueries(),
AssetQuery::getQueries(),
UserQuery::getQueries(),
- GlobalSetQuery::getQueries(),
- CategoryQuery::getQueries(),
- TagQuery::getQueries(),
];
// Flatten them
@@ -1533,9 +1506,6 @@ private function _registerGqlMutations(): void
// Mutations
PingMutation::getMutations(),
EntryMutation::getMutations(),
- TagMutation::getMutations(),
- CategoryMutation::getMutations(),
- GlobalSetMutation::getMutations(),
AssetMutation::getMutations(),
];
@@ -1779,34 +1749,6 @@ private function assetSchemaComponents(): array
return [$queryComponents, $mutationComponents];
}
- /**
- * Return global set permissions.
- *
- * @return array
- */
- private function globalSetSchemaComponents(): array
- {
- $queryComponents = [];
- $mutationComponents = [];
-
- $globalSets = Craft::$app->getGlobals()->getAllSets();
-
- if (!empty($globalSets)) {
- foreach ($globalSets as $globalSet) {
- $name = t($globalSet->name, category: 'site');
- $prefix = "globalsets.$globalSet->uid";
- $queryComponents["$prefix:read"] = [
- 'label' => t('Query for the “{name}” global set', ['name' => $name]),
- ];
- $mutationComponents["$prefix:edit"] = [
- 'label' => t('Edit the “{globalSet}” global set.', ['globalSet' => $name]),
- ];
- }
- }
-
- return [$queryComponents, $mutationComponents];
- }
-
/**
* Return user permissions.
*
@@ -1839,84 +1781,6 @@ private function userSchemaComponents(): array
return [$queryComponents, []];
}
- /**
- * Return category group permissions.
- *
- * @return array
- */
- private function categorySchemaComponents(): array
- {
- $queryComponents = [];
- $mutationComponents = [];
-
- $categoryGroups = Craft::$app->getCategories()->getAllGroups();
-
- if (!empty($categoryGroups)) {
- foreach ($categoryGroups as $categoryGroup) {
- $name = t($categoryGroup->name, category: 'site');
- $prefix = "categorygroups.$categoryGroup->uid";
- $queryComponents["$prefix:read"] = [
- 'label' => t('Query for categories in the “{name}” category group',
- ['name' => $name]),
- ];
- $mutationComponents["$prefix:edit"] = [
- 'label' => t('Edit categories in the “{categoryGroup}” category group',
- ['categoryGroup' => $name]),
- 'nested' => [
- "$prefix:save" => [
- 'label' => t('Save categories in the “{categoryGroup}” category group',
- ['categoryGroup' => $name]),
- ],
- "$prefix:delete" => [
- 'label' => t('Delete categories from the “{categoryGroup}” category group',
- ['categoryGroup' => $name]),
- ],
- ],
- ];
- }
- }
-
- return [$queryComponents, $mutationComponents];
- }
-
- /**
- * Return tag group permissions.
- *
- * @return array
- */
- private function tagSchemaComponents(): array
- {
- $queryComponents = [];
- $mutationComponents = [];
-
- $tagGroups = Craft::$app->getTags()->getAllTagGroups();
-
- if (!empty($tagGroups)) {
- foreach ($tagGroups as $tagGroup) {
- $name = t($tagGroup->name, category: 'site');
- $prefix = "taggroups.$tagGroup->uid";
- $queryComponents["$prefix:read"] = [
- 'label' => t('Query for tags in the “{name}” tag group', ['name' => $name]),
- ];
- $mutationComponents["$prefix:edit"] = [
- 'label' => t('Edit tags in the “{tagGroup}” tag group', ['tagGroup' => $name]),
- 'nested' => [
- "$prefix:save" => [
- 'label' => t('Save tags in the “{tagGroup}” tag group',
- ['tagGroup' => $name]),
- ],
- "$prefix:delete" => [
- 'label' => t('Delete tags from the “{tagGroup}” tag group',
- ['tagGroup' => $name]),
- ],
- ],
- ];
- }
- }
-
- return [$queryComponents, $mutationComponents];
- }
-
private function _createTokenQuery(): Builder
{
return DB::table(Table::GQLTOKENS)
diff --git a/yii2-adapter/legacy/services/ProjectConfig.php b/yii2-adapter/legacy/services/ProjectConfig.php
index c7454dca694..20063ef0c8c 100644
--- a/yii2-adapter/legacy/services/ProjectConfig.php
+++ b/yii2-adapter/legacy/services/ProjectConfig.php
@@ -97,12 +97,10 @@ class ProjectConfig extends Component
public const PATH_ADDRESSES = \CraftCms\Cms\ProjectConfig\ProjectConfig::PATH_ADDRESSES;
public const PATH_ADDRESS_FIELD_LAYOUTS = \CraftCms\Cms\ProjectConfig\ProjectConfig::PATH_ADDRESS_FIELD_LAYOUTS;
- public const PATH_CATEGORY_GROUPS = \CraftCms\Cms\ProjectConfig\ProjectConfig::PATH_CATEGORY_GROUPS;
public const PATH_DATE_MODIFIED = \CraftCms\Cms\ProjectConfig\ProjectConfig::PATH_DATE_MODIFIED;
public const PATH_ELEMENT_SOURCES = \CraftCms\Cms\ProjectConfig\ProjectConfig::PATH_ELEMENT_SOURCES;
public const PATH_ENTRY_TYPES = \CraftCms\Cms\ProjectConfig\ProjectConfig::PATH_ENTRY_TYPES;
public const PATH_FIELDS = \CraftCms\Cms\ProjectConfig\ProjectConfig::PATH_FIELDS;
- public const PATH_GLOBAL_SETS = \CraftCms\Cms\ProjectConfig\ProjectConfig::PATH_GLOBAL_SETS;
public const PATH_FS = \CraftCms\Cms\ProjectConfig\ProjectConfig::PATH_FS;
public const PATH_GRAPHQL = \CraftCms\Cms\ProjectConfig\ProjectConfig::PATH_GRAPHQL;
public const PATH_GRAPHQL_PUBLIC_TOKEN = \CraftCms\Cms\ProjectConfig\ProjectConfig::PATH_GRAPHQL_PUBLIC_TOKEN;
@@ -118,7 +116,6 @@ class ProjectConfig extends Component
public const PATH_SITES = \CraftCms\Cms\ProjectConfig\ProjectConfig::PATH_SITES;
public const PATH_SITE_GROUPS = \CraftCms\Cms\ProjectConfig\ProjectConfig::PATH_SITE_GROUPS;
public const PATH_SYSTEM = \CraftCms\Cms\ProjectConfig\ProjectConfig::PATH_SYSTEM;
- public const PATH_TAG_GROUPS = \CraftCms\Cms\ProjectConfig\ProjectConfig::PATH_TAG_GROUPS;
public const PATH_USERS = \CraftCms\Cms\ProjectConfig\ProjectConfig::PATH_USERS;
public const PATH_USER_FIELD_LAYOUTS = \CraftCms\Cms\ProjectConfig\ProjectConfig::PATH_USER_FIELD_LAYOUTS;
public const PATH_USER_GROUPS = \CraftCms\Cms\ProjectConfig\ProjectConfig::PATH_USER_GROUPS;
diff --git a/yii2-adapter/legacy/services/Tags.php b/yii2-adapter/legacy/services/Tags.php
deleted file mode 100644
index 0b913bfcc70..00000000000
--- a/yii2-adapter/legacy/services/Tags.php
+++ /dev/null
@@ -1,488 +0,0 @@
-getTags()`]].
- *
- * @author Pixel & Tonic, Inc.
- * @since 3.0.0
- */
-class Tags extends Component
-{
- /**
- * @event TagGroupEvent The event that is triggered before a tag group is saved.
- */
- public const EVENT_BEFORE_SAVE_GROUP = 'beforeSaveGroup';
-
- /**
- * @event TagGroupEvent The event that is triggered after a tag group is saved.
- */
- public const EVENT_AFTER_SAVE_GROUP = 'afterSaveGroup';
-
- /**
- * @event TagGroupEvent The event that is triggered before a tag group is deleted.
- */
- public const EVENT_BEFORE_DELETE_GROUP = 'beforeDeleteGroup';
-
- /**
- * @event TagGroupEvent The event that is triggered before a tag group delete is applied to the database.
- * @since 3.1.0
- */
- public const EVENT_BEFORE_APPLY_GROUP_DELETE = 'beforeApplyGroupDelete';
-
- /**
- * @event TagGroupEvent The event that is triggered after a tag group is deleted.
- */
- public const EVENT_AFTER_DELETE_GROUP = 'afterDeleteGroup';
-
- /**
- * @var MemoizableArray|null
- * @see _tagGroups()
- */
- private ?MemoizableArray $_tagGroups = null;
-
- /**
- * Serializer
- *
- * @since 3.5.14
- */
- public function __serialize()
- {
- $vars = get_object_vars($this);
- unset($vars['_tagGroups']);
- return $vars;
- }
-
- // Tag groups
- // -------------------------------------------------------------------------
-
- /**
- * Returns all of the group IDs.
- *
- * @return array
- */
- public function getAllTagGroupIds(): array
- {
- return array_map(fn(TagGroup $group) => $group->id, $this->getAllTagGroups());
- }
-
- /**
- * Returns a memoizable array of all tag groups.
- *
- * @return MemoizableArray
- */
- private function _tagGroups(): MemoizableArray
- {
- if (!isset($this->_tagGroups)) {
- $records = TagGroupRecord::find()
- ->orderBy(['name' => SORT_ASC])
- ->all();
-
- $this->_tagGroups = new MemoizableArray(
- $records,
- fn(TagGroupRecord $record) => $this->_createTagGroupFromRecord($record),
- );
- }
-
- return $this->_tagGroups;
- }
-
- private function _createTagGroupFromRecord(TagGroupRecord $record): TagGroup
- {
- return new TagGroup($record->toArray([
- 'id',
- 'name',
- 'handle',
- 'fieldLayoutId',
- 'dateDeleted',
- 'uid',
- ]));
- }
-
- /**
- * Returns all tag groups.
- *
- * @return TagGroup[]
- */
- public function getAllTagGroups(): array
- {
- return $this->_tagGroups()->all();
- }
-
- /**
- * Gets the total number of tag groups.
- *
- * @return int
- */
- public function getTotalTagGroups(): int
- {
- return count($this->getAllTagGroups());
- }
-
- /**
- * Returns a group by its ID.
- *
- * @param int $groupId
- * @return TagGroup|null
- */
- public function getTagGroupById(int $groupId): ?TagGroup
- {
- return $this->_tagGroups()->firstWhere('id', $groupId);
- }
-
- /**
- * Returns a group by its UID.
- *
- * @param string $groupUid
- * @return TagGroup|null
- */
- public function getTagGroupByUid(string $groupUid): ?TagGroup
- {
- return $this->_tagGroups()->firstWhere('uid', $groupUid, true);
- }
-
-
- /**
- * Gets a group by its handle.
- *
- * @param string $groupHandle
- * @param bool $withTrashed
- * @return TagGroup|null
- */
- public function getTagGroupByHandle(string $groupHandle, bool $withTrashed = false): ?TagGroup
- {
- /** @var TagGroup|null $group */
- $group = $this->_tagGroups()->firstWhere('handle', $groupHandle, true);
-
- if (!$group && $withTrashed) {
- /** @var TagGroupRecord|null $record */
- $record = TagGroupRecord::findWithTrashed()
- ->andWhere(['handle' => $groupHandle])
- ->one();
- if ($record) {
- $group = $this->_createTagGroupFromRecord($record);
- }
- }
-
- return $group;
- }
-
- /**
- * Saves a tag group.
- *
- * @param TagGroup $tagGroup The tag group to be saved
- * @param bool $runValidation Whether the tag group should be validated
- * @return bool Whether the tag group was saved successfully
- * @throws TagGroupNotFoundException if $tagGroup->id is invalid
- * @throws Throwable if reasons
- */
- public function saveTagGroup(TagGroup $tagGroup, bool $runValidation = true): bool
- {
- $isNewTagGroup = !$tagGroup->id;
-
- // Fire a 'beforeSaveGroup' event
- if ($this->hasEventHandlers(self::EVENT_BEFORE_SAVE_GROUP)) {
- $this->trigger(self::EVENT_BEFORE_SAVE_GROUP, new TagGroupEvent([
- 'tagGroup' => $tagGroup,
- 'isNew' => $isNewTagGroup,
- ]));
- }
-
- if ($runValidation && !$tagGroup->validate()) {
- Craft::info('Tag group not saved due to validation error.', __METHOD__);
- return false;
- }
-
- if ($isNewTagGroup) {
- if (!$tagGroup->uid) {
- $tagGroup->uid = Str::uuid()->toString();
- }
- } elseif (!$tagGroup->uid) {
- $tagGroup->uid = \Illuminate\Support\Facades\DB::table(\CraftCms\Cms\Database\Table::TAGGROUPS)->uidById($tagGroup->id);
- }
-
- $configPath = ProjectConfig::PATH_TAG_GROUPS . '.' . $tagGroup->uid;
- $configData = $tagGroup->getConfig();
- app(ProjectConfig::class)->set($configPath, $configData, "Save the “{$tagGroup->handle}” tag group");
-
- if ($isNewTagGroup) {
- $tagGroup->id = \Illuminate\Support\Facades\DB::table(\CraftCms\Cms\Database\Table::TAGGROUPS)->idByUid($tagGroup->uid);
- }
-
- return true;
- }
-
- /**
- * Handle tag group change
- *
- * @param ConfigEvent $event
- */
- public function handleChangedTagGroup(ConfigEvent $event): void
- {
- $tagGroupUid = $event->tokenMatches[0];
- $data = $event->newValue;
-
- // Make sure fields are processed
- ProjectConfigHelper::ensureAllFieldsProcessed();
-
- \Illuminate\Support\Facades\DB::beginTransaction();
- try {
- $tagGroupRecord = $this->_getTagGroupRecord($tagGroupUid, true);
- $isNewTagGroup = $tagGroupRecord->getIsNewRecord();
-
- $tagGroupRecord->name = $data['name'];
- $tagGroupRecord->handle = $data['handle'];
- $tagGroupRecord->uid = $tagGroupUid;
-
- if (!empty($data['fieldLayouts'])) {
- // Save the field layout
- $layout = FieldLayout::createFromConfig(reset($data['fieldLayouts']));
- $layout->id = $tagGroupRecord->fieldLayoutId;
- $layout->type = Tag::class;
- $layout->uid = key($data['fieldLayouts']);
- app(Fields::class)->saveLayout($layout, false);
- $tagGroupRecord->fieldLayoutId = $layout->id;
- } elseif ($tagGroupRecord->fieldLayoutId) {
- // Delete the field layout
- app(Fields::class)->deleteLayoutById($tagGroupRecord->fieldLayoutId);
- $tagGroupRecord->fieldLayoutId = null;
- }
-
- // Save the tag group
- if ($wasTrashed = (bool)$tagGroupRecord->dateDeleted) {
- $tagGroupRecord->restore();
- } else {
- $tagGroupRecord->save(false);
- }
-
- \Illuminate\Support\Facades\DB::commit();
- } catch (Throwable $e) {
- \Illuminate\Support\Facades\DB::rollBack();
- throw $e;
- }
-
- // Clear caches
- $this->_tagGroups = null;
-
- if ($wasTrashed) {
- // Restore the tags that were deleted with the group
- /** @var Tag[] $tags */
- $tags = Tag::find()
- ->groupId($tagGroupRecord->id)
- ->trashed()
- ->andWhere(['tags.deletedWithGroup' => true])
- ->all();
- Craft::$app->getElements()->restoreElements($tags);
- }
-
- // Fire an 'afterSaveGroup' event
- if ($this->hasEventHandlers(self::EVENT_AFTER_SAVE_GROUP)) {
- $this->trigger(self::EVENT_AFTER_SAVE_GROUP, new TagGroupEvent([
- 'tagGroup' => $this->getTagGroupById($tagGroupRecord->id),
- 'isNew' => $isNewTagGroup,
- ]));
- }
-
- // Invalidate tag caches
- Craft::$app->getElements()->invalidateCachesForElementType(Tag::class);
- }
-
- /**
- * Deletes a tag group by its ID.
- *
- * @param int $groupId The tag group's ID
- * @return bool Whether the tag group was deleted successfully
- * @throws Throwable if reasons
- * @since 3.0.12
- */
- public function deleteTagGroupById(int $groupId): bool
- {
- if (!$groupId) {
- return false;
- }
-
- $group = $this->getTagGroupById($groupId);
-
- if (!$group) {
- return false;
- }
-
- return $this->deleteTagGroup($group);
- }
-
- /**
- * Deletes a tag group.
- *
- * @param TagGroup $tagGroup The tag group
- * @return bool Whether the tag group was deleted successfully
- * @throws Throwable if reasons
- */
- public function deleteTagGroup(TagGroup $tagGroup): bool
- {
- // Fire a 'beforeDeleteGroup' event
- if ($this->hasEventHandlers(self::EVENT_BEFORE_DELETE_GROUP)) {
- $this->trigger(self::EVENT_BEFORE_DELETE_GROUP, new TagGroupEvent([
- 'tagGroup' => $tagGroup,
- ]));
- }
-
- app(ProjectConfig::class)->remove(ProjectConfig::PATH_TAG_GROUPS . '.' . $tagGroup->uid, "Delete the “{$tagGroup->handle}” tag group");
- return true;
- }
-
- /**
- * Handle Tag group getting deleted
- *
- * @param ConfigEvent $event
- */
- public function handleDeletedTagGroup(ConfigEvent $event): void
- {
- $uid = $event->tokenMatches[0];
- $tagGroupRecord = $this->_getTagGroupRecord($uid);
-
- if (!$tagGroupRecord->id) {
- return;
- }
-
- /** @var TagGroup $tagGroup */
- $tagGroup = $this->getTagGroupById($tagGroupRecord->id);
-
- // Fire a 'beforeApplyGroupDelete' event
- if ($this->hasEventHandlers(self::EVENT_BEFORE_APPLY_GROUP_DELETE)) {
- $this->trigger(self::EVENT_BEFORE_APPLY_GROUP_DELETE, new TagGroupEvent([
- 'tagGroup' => $tagGroup,
- ]));
- }
-
- \Illuminate\Support\Facades\DB::beginTransaction();
- try {
- // Delete the tags
- $elementsTable = Table::ELEMENTS;
- $tagsTable = Table::TAGS;
- $now = Db::prepareDateForDb(new DateTime());
- $db = Craft::$app->getDb();
-
- $conditionSql = <<id AND
-[[tags.id]] = [[elements.id]] AND
-[[elements.canonicalId]] IS NULL AND
-[[elements.revisionId]] IS NULL AND
-[[elements.dateDeleted]] IS NULL
-SQL;
-
- if ($db->getIsMysql()) {
- $db->createCommand(<<execute();
- } else {
- // Not possible to update two tables simultaneously with Postgres
- $db->createCommand(<<execute();
- $db->createCommand(<<execute();
- }
-
- // Delete the field layout
- if ($tagGroupRecord->fieldLayoutId) {
- app(Fields::class)->deleteLayoutById($tagGroupRecord->fieldLayoutId);
- }
-
- // Delete the tag group
- \Illuminate\Support\Facades\DB::table(\CraftCms\Cms\Database\Table::TAGGROUPS)->softDelete($tagGroupRecord->id);
-
- \Illuminate\Support\Facades\DB::commit();
- } catch (Throwable $e) {
- \Illuminate\Support\Facades\DB::rollBack();
- throw $e;
- }
-
- // Clear caches
- $this->_tagGroups = null;
-
- // Fire an 'afterDeleteGroup' event
- if ($this->hasEventHandlers(self::EVENT_AFTER_DELETE_GROUP)) {
- $this->trigger(self::EVENT_AFTER_DELETE_GROUP, new TagGroupEvent([
- 'tagGroup' => $tagGroup,
- ]));
- }
-
- // Invalidate tag caches
- Craft::$app->getElements()->invalidateCachesForElementType(Tag::class);
- }
-
- /**
- * @deprecated in 4.0.5. Unused fields will be pruned automatically as field layouts are resaved.
- */
- public function pruneDeletedField(): void
- {
- }
-
- // Tags
- // -------------------------------------------------------------------------
-
- /**
- * Returns a tag by its ID.
- *
- * @param int $tagId
- * @param int|null $siteId
- * @return Tag|null
- */
- public function getTagById(int $tagId, ?int $siteId = null): ?Tag
- {
- return Craft::$app->getElements()->getElementById($tagId, Tag::class, $siteId);
- }
-
- /**
- * Gets a tag group's record by uid.
- *
- * @param string $uid
- * @param bool $withTrashed Whether to include trashed tag groups in search
- * @return TagGroupRecord
- */
- private function _getTagGroupRecord(string $uid, bool $withTrashed = false): TagGroupRecord
- {
- $query = $withTrashed ? TagGroupRecord::findWithTrashed() : TagGroupRecord::find();
- $query->andWhere(['uid' => $uid]);
- /** @noinspection PhpIncompatibleReturnTypeInspection */
- /** @var TagGroupRecord */
- return $query->one() ?? new TagGroupRecord();
- }
-}
diff --git a/yii2-adapter/legacy/services/UserPermissions.php b/yii2-adapter/legacy/services/UserPermissions.php
index af4dba3c825..ad5ccfa232e 100644
--- a/yii2-adapter/legacy/services/UserPermissions.php
+++ b/yii2-adapter/legacy/services/UserPermissions.php
@@ -9,7 +9,6 @@
use Craft;
use craft\elements\Asset;
-use craft\elements\Category;
use craft\elements\Entry;
use craft\elements\User;
use craft\events\RegisterUserPermissionsEvent;
@@ -116,8 +115,6 @@ public function getAllPermissions(): array
$this->_userPermissions($this->_allPermissions);
$this->_sitePermissions($this->_allPermissions);
$this->_entryPermissions($this->_allPermissions);
- $this->_globalSetPermissions($this->_allPermissions);
- $this->_categoryPermissions($this->_allPermissions);
$this->_volumePermissions($this->_allPermissions);
$this->_utilityPermissions($this->_allPermissions);
@@ -711,79 +708,6 @@ private function _entryPermissions(array &$permissions): void
}
}
- private function _globalSetPermissions(array &$permissions): void
- {
- $globalSets = Craft::$app->getGlobals()->getAllSets();
-
- if (!$globalSets) {
- return;
- }
-
- $globalSetPermissions = [];
-
- foreach ($globalSets as $globalSet) {
- $globalSetPermissions["editGlobalSet:$globalSet->uid"] = [
- 'label' => t('Edit “{title}”', [
- 'title' => t($globalSet->name, category: 'site'),
- ]),
- ];
- }
-
- $permissions[] = [
- 'heading' => t('Global Sets'),
- 'permissions' => $globalSetPermissions,
- ];
- }
-
- private function _categoryPermissions(array &$permissions): void
- {
- $categoryGroups = Craft::$app->getCategories()->getAllGroups();
-
- if (!$categoryGroups) {
- return;
- }
-
- $type = Category::pluralLowerDisplayName();
-
- foreach ($categoryGroups as $group) {
- $permissions[] = [
- 'heading' => t('Category Group - {name}', [
- 'name' => t($group->name, category: 'site'),
- ]),
- 'permissions' => [
- "viewCategories:$group->uid" => [
- 'label' => mb_ucfirst(t('View {type}', ['type' => $type])),
- 'nested' => [
- "saveCategories:$group->uid" => [
- 'label' => mb_ucfirst(t('Save {type}', ['type' => $type])),
- ],
- "deleteCategories:$group->uid" => [
- 'label' => mb_ucfirst(t('Delete {type}', ['type' => $type])),
- ],
- "viewPeerCategoryDrafts:$group->uid" => [
- 'label' => mb_ucfirst(t('View other users’ {type}', [
- 'type' => t('drafts'),
- ])),
- 'nested' => [
- "savePeerCategoryDrafts:$group->uid" => [
- 'label' => mb_ucfirst(t('Save other users’ {type}', [
- 'type' => t('drafts'),
- ])),
- ],
- "deletePeerCategoryDrafts:$group->uid" => [
- 'label' => t('Delete other users’ {type}', [
- 'type' => t('drafts'),
- ]),
- ],
- ],
- ],
- ],
- ],
- ],
- ];
- }
- }
-
private function _volumePermissions(array &$permissions): void
{
$volumes = Craft::$app->getVolumes()->getAllVolumes();
diff --git a/yii2-adapter/legacy/test/TestSetup.php b/yii2-adapter/legacy/test/TestSetup.php
index 31dba303180..1b466b8e2b6 100644
--- a/yii2-adapter/legacy/test/TestSetup.php
+++ b/yii2-adapter/legacy/test/TestSetup.php
@@ -20,7 +20,6 @@
use craft\queue\Queue;
use craft\services\AssetIndexer;
use craft\services\Assets;
-use craft\services\Categories;
use craft\services\Config;
use craft\services\Dashboard;
use craft\services\Deprecator;
@@ -28,7 +27,6 @@
use craft\services\ElementSources;
use craft\services\Entries;
use craft\services\Fields;
-use craft\services\Globals;
use craft\services\Images;
use craft\services\ImageTransforms;
use craft\services\Path;
@@ -39,7 +37,6 @@
use craft\services\Sites;
use craft\services\Structures;
use craft\services\SystemMessages;
-use craft\services\Tags;
use craft\services\TemplateCaches;
use craft\services\Tokens;
use craft\services\UserGroups;
@@ -512,7 +509,6 @@ public static function getCraftServiceMap(): array
[Assets::class, ['getAssets', 'assets']],
[AssetIndexer::class, ['getAssetIndexer', 'assetIndexer']],
[ImageTransforms::class, ['getImageTransforms', 'imageTransforms']],
- [Categories::class, ['getCategories', 'categories']],
[Config::class, ['getConfig', 'config']],
[Dashboard::class, ['getDashboard', 'dashboard']],
[Deprecator::class, ['getDeprecator', 'deprecator']],
@@ -521,7 +517,6 @@ public static function getCraftServiceMap(): array
[SystemMessages::class, ['getSystemMessages', 'systemMessages']],
[Entries::class, ['getEntries', 'entries']],
[Fields::class, ['getFields', 'fields']],
- [Globals::class, ['getGlobals', 'globals']],
[Images::class, ['getImages', 'images']],
[Locale::class, ['getLocale', 'locale']],
[Mailer::class, ['getMailer', 'mailer']],
@@ -536,7 +531,6 @@ public static function getCraftServiceMap(): array
[Sites::class, ['getSites', 'sites']],
[Structures::class, ['getStructures', 'structures']],
[SystemMessages::class, ['getSystemMessages', 'systemMessages']],
- [Tags::class, ['getTags', 'tags']],
[TemplateCaches::class, ['getTemplateCaches', 'templateCaches']],
[Tokens::class, ['getTokens', 'tokens']],
[UserGroups::class, ['getUserGroups', 'userGroups']],
diff --git a/yii2-adapter/legacy/test/fixtures/elements/CategoryFixture.php b/yii2-adapter/legacy/test/fixtures/elements/CategoryFixture.php
deleted file mode 100644
index cc06ae47e55..00000000000
--- a/yii2-adapter/legacy/test/fixtures/elements/CategoryFixture.php
+++ /dev/null
@@ -1,48 +0,0 @@
-
- * @author Robuust digital | Bob Olde Hampsink
- * @author Global Network Group | Giel Tettelaar
- * @since 3.2
- */
-abstract class CategoryFixture extends BaseElementFixture
-{
- /**
- * @var array
- */
- protected array $groupIds = [];
-
- /**
- * @inheritdoc
- */
- public function init(): void
- {
- parent::init();
-
- foreach (Craft::$app->getCategories()->getAllGroups() as $group) {
- $this->groupIds[$group->handle] = $group->id;
- }
- }
-
- /**
- * @inheritdoc
- */
- protected function createElement(): ElementInterface
- {
- return new Category();
- }
-}
diff --git a/yii2-adapter/legacy/test/fixtures/elements/GlobalSetFixture.php b/yii2-adapter/legacy/test/fixtures/elements/GlobalSetFixture.php
deleted file mode 100644
index 051c2411935..00000000000
--- a/yii2-adapter/legacy/test/fixtures/elements/GlobalSetFixture.php
+++ /dev/null
@@ -1,73 +0,0 @@
-
- * @author Robuust digital | Bob Olde Hampsink
- * @author Global Network Group | Giel Tettelaar
- * @since 3.2.0
- */
-abstract class GlobalSetFixture extends BaseElementFixture
-{
- /**
- * @inheritdoc
- */
- public function load(): void
- {
- parent::load();
- Craft::$app->getGlobals()->reset();
- }
-
- /**
- * @inheritdoc
- */
- public function unload(): void
- {
- parent::unload();
- Craft::$app->getGlobals()->reset();
- }
-
- /**
- * @inheritdoc
- */
- protected function createElement(): ElementInterface
- {
- return new GlobalSet();
- }
-
- /**
- * @inheritdoc
- */
- protected function saveElement(ElementInterface $element): bool
- {
- /** @var GlobalSet $element */
- if (!parent::saveElement($element)) {
- return false;
- }
-
- // Add the globalsets table row manually rather than going through Globals::saveSet(),
- // since the field layout should not be created/removed exclusively for this global set
- $record = new GlobalSetRecord();
- $record->id = $element->id;
- $record->uid = $element->uid;
- $record->name = $element->name;
- $record->handle = $element->handle;
- $record->fieldLayoutId = $element->fieldLayoutId;
- $record->save();
-
- return true;
- }
-}
diff --git a/yii2-adapter/legacy/test/fixtures/elements/TagFixture.php b/yii2-adapter/legacy/test/fixtures/elements/TagFixture.php
deleted file mode 100644
index db1d671de47..00000000000
--- a/yii2-adapter/legacy/test/fixtures/elements/TagFixture.php
+++ /dev/null
@@ -1,48 +0,0 @@
-
- * @author Robuust digital | Bob Olde Hampsink
- * @author Global Network Group | Giel Tettelaar
- * @since 3.2
- */
-abstract class TagFixture extends BaseElementFixture
-{
- /**
- * @var array
- */
- protected array $groupIds = [];
-
- /**
- * @inheritdoc
- */
- public function init(): void
- {
- parent::init();
-
- foreach (Craft::$app->getTags()->getAllTagGroups() as $group) {
- $this->groupIds[$group->handle] = $group->id;
- }
- }
-
- /**
- * @inheritdoc
- */
- protected function createElement(): ElementInterface
- {
- return new Tag();
- }
-}
diff --git a/yii2-adapter/legacy/web/View.php b/yii2-adapter/legacy/web/View.php
index 036dda245f3..c9843143fc1 100644
--- a/yii2-adapter/legacy/web/View.php
+++ b/yii2-adapter/legacy/web/View.php
@@ -21,7 +21,6 @@
use craft\web\twig\Environment;
use craft\web\twig\Extension;
use craft\web\twig\FeExtension;
-use craft\web\twig\GlobalsExtension;
use craft\web\twig\SafeHtml;
use craft\web\twig\SinglePreloaderExtension;
use craft\web\twig\TemplateLoader;
@@ -438,7 +437,6 @@ public function createTwig(): Environment
$twig->addExtension(new CpExtension());
} elseif (Craft::$app->getIsInstalled()) {
$twig->addExtension(new FeExtension());
- $twig->addExtension(new GlobalsExtension());
if (Cms::config()->preloadSingles) {
$twig->addExtension(new SinglePreloaderExtension());
diff --git a/yii2-adapter/legacy/web/assets/cp/CpAsset.php b/yii2-adapter/legacy/web/assets/cp/CpAsset.php
index 16b70fedfde..574aec0dc0e 100644
--- a/yii2-adapter/legacy/web/assets/cp/CpAsset.php
+++ b/yii2-adapter/legacy/web/assets/cp/CpAsset.php
@@ -291,8 +291,6 @@ private function _registerTranslations(View $view): void
'Move up',
'Move',
'Name',
- 'New category in the {group} category group',
- 'New category, choose a category group',
'New child',
'New custom source',
'New entry in the {section} section',
@@ -305,7 +303,6 @@ private function _registerTranslations(View $view): void
'New position saved.',
'New position saved.',
'New subfolder',
- 'New {group} category',
'New {section} entry',
'New {type}',
'Next Page',
@@ -610,7 +607,6 @@ private function _craftData(): array
?? $generalConfig->accessibilityDefaults['disableAutofocus']
?? false
),
- 'editableCategoryGroups' => $upToDate ? $this->_editableCategoryGroups() : [],
'edition' => Edition::get()->value,
'elementTypeNames' => $elementTypeNames,
'elevatedSessionDuration' => $generalConfig->elevatedSessionDuration,
@@ -681,22 +677,6 @@ private function _defaultCookieOptions(): array
];
}
- private function _editableCategoryGroups(): array
- {
- $groups = [];
-
- foreach (Craft::$app->getCategories()->getEditableGroups() as $group) {
- $groups[] = [
- 'handle' => $group->handle,
- 'id' => (int)$group->id,
- 'name' => t($group->name, category: 'site'),
- 'uid' => $group->uid,
- ];
- }
-
- return $groups;
- }
-
/**
* @param GeneralConfig $generalConfig
* @return array|null
diff --git a/yii2-adapter/legacy/web/twig/GlobalsExtension.php b/yii2-adapter/legacy/web/twig/GlobalsExtension.php
deleted file mode 100644
index c640ac5f7ac..00000000000
--- a/yii2-adapter/legacy/web/twig/GlobalsExtension.php
+++ /dev/null
@@ -1,33 +0,0 @@
-
- * @since 3.7.8
- */
-class GlobalsExtension extends AbstractExtension implements GlobalsInterface
-{
- /**
- * @inheritdoc
- */
- public function getGlobals(): array
- {
- $globals = [];
- foreach (Craft::$app->getGlobals()->getAllSets() as $globalSet) {
- $globals[$globalSet->handle] = $globalSet;
- }
- return $globals;
- }
-}
diff --git a/yii2-adapter/legacy/web/twig/variables/Cp.php b/yii2-adapter/legacy/web/twig/variables/Cp.php
index f7d80eb9a34..577e48a83c7 100644
--- a/yii2-adapter/legacy/web/twig/variables/Cp.php
+++ b/yii2-adapter/legacy/web/twig/variables/Cp.php
@@ -264,22 +264,6 @@ public function nav(): array
}
}
- if (!empty(Craft::$app->getGlobals()->getEditableSets())) {
- $navItems[] = [
- 'label' => t('Globals'),
- 'url' => 'globals',
- 'icon' => 'globe',
- ];
- }
-
- if (Craft::$app->getCategories()->getEditableGroupIds()) {
- $navItems[] = [
- 'label' => t('Categories'),
- 'url' => 'categories',
- 'icon' => 'sitemap',
- ];
- }
-
if (Craft::$app->getVolumes()->getTotalViewableVolumes()) {
$navItems[] = [
'label' => t('Assets'),
@@ -487,18 +471,6 @@ public function settings(): array
'iconMask' => '@craftcms/resources/icons/light/pen-to-square.svg',
'label' => t('Fields'),
];
- $settings[$label]['globals'] = [
- 'iconMask' => '@craftcms/resources/icons/light/globe.svg',
- 'label' => t('Globals'),
- ];
- $settings[$label]['categories'] = [
- 'iconMask' => '@craftcms/resources/icons/light/sitemap.svg',
- 'label' => t('Categories'),
- ];
- $settings[$label]['tags'] = [
- 'iconMask' => '@craftcms/resources/icons/light/tags.svg',
- 'label' => t('Tags'),
- ];
$label = t('Media');
diff --git a/yii2-adapter/legacy/web/twig/variables/CraftVariable.php b/yii2-adapter/legacy/web/twig/variables/CraftVariable.php
index 3aff839a7fb..c52e949832b 100644
--- a/yii2-adapter/legacy/web/twig/variables/CraftVariable.php
+++ b/yii2-adapter/legacy/web/twig/variables/CraftVariable.php
@@ -12,17 +12,11 @@
use craft\db\Query;
use craft\elements\Address;
use craft\elements\Asset;
-use craft\elements\Category;
use craft\elements\db\AddressQuery;
use craft\elements\db\AssetQuery;
-use craft\elements\db\CategoryQuery;
use craft\elements\db\EntryQuery;
-use craft\elements\db\GlobalSetQuery;
-use craft\elements\db\TagQuery;
use craft\elements\db\UserQuery;
use craft\elements\Entry;
-use craft\elements\GlobalSet;
-use craft\elements\Tag;
use craft\elements\User;
use craft\events\DefineBehaviorsEvent;
use craft\web\Application as WebApplication;
@@ -186,19 +180,6 @@ public function assets(array $criteria = []): AssetQuery
return $query;
}
- /**
- * Returns a new [category query](https://craftcms.com/docs/5.x/reference/element-types/categories.html#querying-categories).
- *
- * @param array $criteria
- * @return CategoryQuery
- */
- public function categories(array $criteria = []): CategoryQuery
- {
- $query = Category::find();
- Craft::configure($query, $criteria);
- return $query;
- }
-
public function elementSources(): ElementSources
{
return app(ElementSources::class);
@@ -232,20 +213,6 @@ public function fields(): Fields
return app(Fields::class);
}
- /**
- * Returns a new [global set query](https://craftcms.com/docs/5.x/reference/element-types/globals.html#querying-globals).
- *
- * @param array $criteria
- * @return GlobalSetQuery
- * @since 3.0.4
- */
- public function globalSets(array $criteria = []): GlobalSetQuery
- {
- $query = GlobalSet::find();
- Craft::configure($query, $criteria);
- return $query;
- }
-
public function i18n(): I18N
{
return app(I18N::class);
@@ -282,19 +249,6 @@ public function query(): Query
return new Query();
}
- /**
- * Returns a new [tag query](https://craftcms.com/docs/5.x/reference/element-types/tags.html#querying-tags).
- *
- * @param array $criteria
- * @return TagQuery
- */
- public function tags(array $criteria = []): TagQuery
- {
- $query = Tag::find();
- Craft::configure($query, $criteria);
- return $query;
- }
-
/**
* Returns a new [user query](https://craftcms.com/docs/5.x/reference/element-types/users.html#querying-users).
*
diff --git a/yii2-adapter/tests/fixtures/GlobalSetFixture.php b/yii2-adapter/tests/fixtures/GlobalSetFixture.php
deleted file mode 100644
index f7db5b8999a..00000000000
--- a/yii2-adapter/tests/fixtures/GlobalSetFixture.php
+++ /dev/null
@@ -1,30 +0,0 @@
-
- * @author Global Network Group | Giel Tettelaar
- * @since 3.2.0
- */
-class GlobalSetFixture extends BaseGlobalSetFixture
-{
- /**
- * @inheritdoc
- */
- public $dataFile = __DIR__ . '/data/global-sets.php';
-
- /**
- * @inheritdoc
- */
- public $depends = [FieldLayoutFixture::class];
-}
diff --git a/yii2-adapter/tests/fixtures/data/field-layout.php b/yii2-adapter/tests/fixtures/data/field-layout.php
index 9214152d9c8..f5ced84269a 100644
--- a/yii2-adapter/tests/fixtures/data/field-layout.php
+++ b/yii2-adapter/tests/fixtures/data/field-layout.php
@@ -7,7 +7,6 @@
use craft\elements\Asset;
use craft\elements\Entry;
-use craft\elements\GlobalSet;
use craft\elements\User;
use craft\fieldlayoutelements\entries\EntryTitleField;
use CraftCms\Cms\Field\Color;
@@ -180,71 +179,6 @@
],
],
],
- [
- 'uid' => 'field-layout-1004----------------uid',
- 'type' => GlobalSet::class,
- 'tabs' => [
- [
- 'name' => 'Tab 1',
- 'fields' => [
- // MATRIX FIELD 3
- [
- 'uid' => 'field-1006-----------------------uid',
- 'name' => 'Matrix 3',
- 'handle' => 'matrixThird',
- 'type' => Matrix::class,
- 'entryTypes' => [
- 'entry-type-1010------------------uid',
- ],
- 'required' => false,
- ],
-
- // PLAIN TEXT FIELD THREE
- [
- 'uid' => 'field-1007-----------------------uid',
- 'name' => 'Plain Text Field3',
- 'handle' => 'plainTextField3',
- 'type' => PlainText::class,
- 'required' => true,
- ],
-
- // TABLE FIELD TWO
- [
- 'uid' => 'field-1008-----------------------uid',
- 'name' => 'Appointments2',
- 'handle' => 'appointments2',
- 'type' => Table::class,
- 'addRowLabel' => 'Add a row',
- 'minRows' => 1,
- 'maxRows' => 5,
- 'columns' => [
- 'col1' => [
- 'heading' => 'What',
- 'handle' => 'one',
- 'type' => 'singleline',
- ],
- 'col2' => [
- 'heading' => 'When',
- 'handle' => 'two',
- 'type' => 'date',
- ],
- 'col3' => [
- 'heading' => 'How many',
- 'handle' => 'howMany',
- 'type' => 'number',
- ],
- 'col4' => [
- 'heading' => 'Allow?',
- 'handle' => 'allow',
- 'type' => 'lightswitch',
- ],
- ],
- 'required' => true,
- ],
- ],
- ],
- ],
- ],
[
'uid' => 'field-layout-1005----------------uid',
'type' => Entry::class,
diff --git a/yii2-adapter/tests/fixtures/data/global-sets.php b/yii2-adapter/tests/fixtures/data/global-sets.php
deleted file mode 100644
index 572ac23e5b4..00000000000
--- a/yii2-adapter/tests/fixtures/data/global-sets.php
+++ /dev/null
@@ -1,43 +0,0 @@
- 'A global set',
- 'handle' => 'aGlobalSet',
- 'fieldLayoutUid' => 'field-layout-1004----------------uid',
- 'plainTextField3' => 'There is some information here',
- 'uid' => 'globalset-1000-------------------uid',
- 'appointments2' => [['col1' => 'foo', 'col2' => '2019-06-25 07:00:00', 'col3' => '7', 'col4' => '1']],
- ],
-
- [
- 'name' => 'A different global set',
- 'handle' => 'aDifferentGlobalSet',
- 'fieldLayoutUid' => 'field-layout-1004----------------uid',
- 'plainTextField3' => 'No more information to give.',
- 'uid' => 'globalset-1001-------------------uid',
- 'appointments2' => [['col1' => 'foo', 'col2' => '2019-06-25 07:00:00', 'col3' => '7', 'col4' => '1']],
- ],
-
- [
- 'name' => 'A third global set',
- 'handle' => 'aThirdGlobalSet',
- 'fieldLayoutUid' => 'field-layout-1004----------------uid',
- 'plainTextField3' => 'No more information to give.',
- 'uid' => 'globalset-1002-------------------uid',
- 'appointments2' => [['col1' => 'foo', 'col2' => '2019-06-25 07:00:00', 'col3' => '7', 'col4' => '1']],
- ],
-
- // Deleted
- [
- 'name' => 'A deleted global set',
- 'handle' => 'aDeletedGlobalSet',
- 'dateDeleted' => (new DateTime('now'))->format('Y-m-d H:i:s'),
- 'uid' => 'globalset-1003-------------------uid',
- ],
-];
diff --git a/yii2-adapter/tests/fixtures/data/gql-schemas.php b/yii2-adapter/tests/fixtures/data/gql-schemas.php
index c13e63e69bf..d5996be4141 100644
--- a/yii2-adapter/tests/fixtures/data/gql-schemas.php
+++ b/yii2-adapter/tests/fixtures/data/gql-schemas.php
@@ -10,9 +10,6 @@
'id' => '1000',
'name' => 'Sneakers',
'scope' => [
- 'globalsets.globalset-1000-------------------uid:read',
- 'globalsets.globalset-1001-------------------uid:read',
- 'globalsets.globalset-1002-------------------uid:read',
'volumes.volume-1000----------------------uid:read',
'volumes.volume-1001----------------------uid:read',
'volumes.volume-1002----------------------uid:read',
@@ -30,15 +27,4 @@
'dateUpdated' => '2018-08-08 20:00:00',
'uid' => 'gql-main-token-------------------uid',
],
- [
- 'id' => '1001',
- 'name' => 'Peter Parker',
- 'scope' => [
- 'globalsets.globalset-1001-------------------uid:read',
- 'globalsets.globalset-1002-------------------uid:read',
- ],
- 'dateCreated' => '2018-08-08 20:00:00',
- 'dateUpdated' => '2018-08-08 20:00:00',
- 'uid' => 'gql-sub-token--------------------uid',
- ],
];
diff --git a/yii2-adapter/tests/gql/GqlCest.php b/yii2-adapter/tests/gql/GqlCest.php
index f08e25cacea..4ef65e0e1d3 100644
--- a/yii2-adapter/tests/gql/GqlCest.php
+++ b/yii2-adapter/tests/gql/GqlCest.php
@@ -10,7 +10,6 @@
use Craft;
use craft\models\GqlSchema;
use crafttests\fixtures\EntryWithFieldsFixture;
-use crafttests\fixtures\GlobalSetFixture;
use crafttests\fixtures\GqlSchemasFixture;
use FunctionalTester;
use yii\base\Exception;
@@ -29,9 +28,6 @@ public function _fixtures(): array
'gqlSchemas' => [
'class' => GqlSchemasFixture::class,
],
- 'globalSets' => [
- 'class' => GlobalSetFixture::class,
- ],
];
}
diff --git a/yii2-adapter/tests/unit/gql/ArgumentHandlerTest.php b/yii2-adapter/tests/unit/gql/ArgumentHandlerTest.php
index b887a6fb53f..f0f07652e15 100644
--- a/yii2-adapter/tests/unit/gql/ArgumentHandlerTest.php
+++ b/yii2-adapter/tests/unit/gql/ArgumentHandlerTest.php
@@ -9,18 +9,14 @@
use Craft;
use craft\elements\Asset;
-use craft\elements\Category;
use craft\elements\Entry;
-use craft\elements\Tag;
use craft\elements\User;
use craft\events\RegisterGqlArgumentHandlersEvent;
use craft\events\RegisterGqlQueriesEvent;
use craft\gql\ArgumentManager;
use craft\gql\base\ArgumentHandlerInterface;
use craft\gql\handlers\RelatedAssets;
-use craft\gql\handlers\RelatedCategories;
use craft\gql\handlers\RelatedEntries;
-use craft\gql\handlers\RelatedTags;
use craft\gql\handlers\RelatedUsers;
use craft\models\GqlSchema;
use craft\services\Gql;
@@ -123,8 +119,6 @@ public function relationArgumentHandlerProvider(): array
$handlers = [
'relatedToAssets' => $this->make(RelatedAssets::class, ['getIds' => $getIds]),
'relatedToEntries' => $this->make(RelatedEntries::class, ['getIds' => $getIds]),
- 'relatedToCategories' => $this->make(RelatedCategories::class, ['getIds' => $getIds]),
- 'relatedToTags' => $this->make(RelatedTags::class, ['getIds' => $getIds]),
'relatedToUsers' => $this->make(RelatedUsers::class, ['getIds' => $getIds]),
];
@@ -132,8 +126,6 @@ public function relationArgumentHandlerProvider(): array
[[], ['relatedToAll' => [1, 2, 3]], ['and', ['element' => 1], ['element' => 2], ['element' => 3]]],
[$handlers, ['relatedToAssets' => ['expected' => Asset::class, 'return' => [[1, 2]]]], ['and', ['element' => [1, 2]]]],
[$handlers, ['relatedToEntries' => ['expected' => Entry::class, 'return' => [[3], [4]]]], ['and', ['element' => [3]], ['element' => [4]]]],
- [$handlers, ['relatedToCategories' => ['expected' => Category::class, 'return' => []]], ['and', ['element' => [0]]]],
- [$handlers, ['relatedToTags' => ['expected' => Tag::class, 'return' => [[7], [8]]]], ['and', ['element' => [7]], ['element' => [8]]]],
[$handlers, ['relatedToUsers' => ['expected' => User::class, 'return' => [[9, 10]]]], ['and', ['element' => [9, 10]]]],
[
diff --git a/yii2-adapter/tests/unit/gql/ElementFieldResolverTest.php b/yii2-adapter/tests/unit/gql/ElementFieldResolverTest.php
index b839921994d..275a205298a 100644
--- a/yii2-adapter/tests/unit/gql/ElementFieldResolverTest.php
+++ b/yii2-adapter/tests/unit/gql/ElementFieldResolverTest.php
@@ -10,22 +10,16 @@
use Craft;
use craft\elements\Asset;
use craft\elements\Asset as AssetElement;
-use craft\elements\Category as CategoryElement;
use craft\elements\Entry;
use craft\elements\Entry as EntryElement;
-use craft\elements\GlobalSet as GlobalSetElement;
use craft\elements\User as UserElement;
use craft\errors\GqlException;
use craft\fs\Local;
use craft\gql\base\ObjectType;
use craft\gql\types\elements\Asset as AssetGqlType;
-use craft\gql\types\elements\Category as CategoryGqlType;
use craft\gql\types\elements\Entry as EntryGqlType;
-use craft\gql\types\elements\GlobalSet as GlobalSetGqlType;
-use craft\gql\types\elements\Tag as TagGqlType;
use craft\gql\types\elements\User as UserGqlType;
use craft\imagetransforms\ImageTransformer;
-use craft\models\CategoryGroup;
use craft\models\GqlSchema;
use craft\models\ImageTransform;
use craft\models\UserGroup;
@@ -118,79 +112,6 @@ public function testAssetFieldResolving(string $gqlTypeClass, string $propertyNa
$this->_runTest($mockElement, $gqlTypeClass, $propertyName, $result);
}
- /**
- * Test resolving fields on global sets.
- *
- * @dataProvider globalSetFieldTestDataProvider
- * @param string $gqlTypeClass The Gql type class
- * @phpstan-param class-string $gqlTypeClass
- * @param string $propertyName The property being tested
- * @param mixed $result True for exact match, false for non-existing or a callback for fetching the data
- */
- public function testGlobalSetFieldResolving(string $gqlTypeClass, string $propertyName, mixed $result): void
- {
- $mockElement = $this->make(
- GlobalSetElement::class, [
- '__get' => fn($property) =>
- // Assume a content field named 'plainTextField'
- $property == 'plainTextField' ? 'ok' : $this->$property,
- 'handle' => 'aHandle',
- ]
- );
-
- $this->_runTest($mockElement, $gqlTypeClass, $propertyName, $result);
- }
-
- /**
- * Test resolving fields on categories
- *
- * @dataProvider categoryFieldTestDataProvider
- * @param string $gqlTypeClass The Gql type class
- * @phpstan-param class-string $gqlTypeClass
- * @param string $propertyName The property being tested
- * @param mixed $result True for exact match, false for non-existing or a callback for fetching the data
- */
- public function testCategoryFieldResolving(string $gqlTypeClass, string $propertyName, mixed $result): void
- {
- $groupHandle = Str::uuid()->toString();
-
- $mockElement = $this->make(
- CategoryElement::class, [
- '__get' => fn($property) =>
- // Assume a content field named 'plainTextField'
- $property == 'plainTextField' ? 'ok' : $this->$property,
- 'getGroup' => fn() => $this->make(CategoryGroup::class, ['handle' => $groupHandle]),
- ]
- );
-
- $this->_runTest($mockElement, $gqlTypeClass, $propertyName, $result);
- }
-
- /**
- * Test resolving fields on tags
- *
- * @dataProvider tagFieldTestDataProvider
- * @param string $gqlTypeClass The Gql type class
- * @phpstan-param class-string $gqlTypeClass
- * @param string $propertyName The property being tested
- * @param mixed $result True for exact match, false for non-existing or a callback for fetching the data
- */
- public function testTagFieldResolving(string $gqlTypeClass, string $propertyName, mixed $result): void
- {
- $groupHandle = Str::uuid()->toString();
-
- $mockElement = $this->make(
- CategoryElement::class, [
- '__get' => fn($property) =>
- // Assume a content field named 'plainTextField'
- $property == 'plainTextField' ? 'ok' : $this->$property,
- 'getGroup' => fn() => $this->make(CategoryGroup::class, ['handle' => $groupHandle]),
- ]
- );
-
- $this->_runTest($mockElement, $gqlTypeClass, $propertyName, $result);
- }
-
/**
* Test resolving fields on Matrix entries.
*
@@ -351,37 +272,6 @@ public static function assetFieldTestDataProvider(): array
];
}
- public static function globalSetFieldTestDataProvider(): array
- {
- return [
- [GlobalSetGqlType::class, 'missingProperty', false],
- [GlobalSetGqlType::class, 'plainTextField', true],
- [GlobalSetGqlType::class, 'handle', true],
- ];
- }
-
- public static function categoryFieldTestDataProvider(): array
- {
- return [
- [CategoryGqlType::class, 'missingProperty', false],
- [CategoryGqlType::class, 'plainTextField', true],
- [
- CategoryGqlType::class, 'groupHandle', fn($source) => $source->getGroup()->handle,
- ],
- ];
- }
-
- public static function tagFieldTestDataProvider(): array
- {
- return [
- [TagGqlType::class, 'missingProperty', false],
- [TagGqlType::class, 'plainTextField', true],
- [
- TagGqlType::class, 'groupHandle', fn($source) => $source->getGroup()->handle,
- ],
- ];
- }
-
public static function matrixEntryFieldTestDataProvider(): array
{
return [
diff --git a/yii2-adapter/tests/unit/gql/InterfaceAndGeneratorTest.php b/yii2-adapter/tests/unit/gql/InterfaceAndGeneratorTest.php
index fbfbafaf1ba..d1952a15312 100644
--- a/yii2-adapter/tests/unit/gql/InterfaceAndGeneratorTest.php
+++ b/yii2-adapter/tests/unit/gql/InterfaceAndGeneratorTest.php
@@ -10,26 +10,19 @@
use Codeception\Stub;
use Craft;
use craft\elements\Asset as AssetElement;
-use craft\elements\Category as CategoryElement;
use craft\elements\Entry as EntryElement;
-use craft\elements\GlobalSet as GlobalSetElement;
-use craft\elements\Tag as TagElement;
use craft\errors\GqlException;
use craft\gql\base\SingularTypeInterface;
use craft\gql\GqlEntityRegistry;
use craft\gql\interfaces\elements\Asset as AssetInterface;
-use craft\gql\interfaces\elements\Category as CategoryInterface;
use craft\gql\interfaces\elements\Entry as EntryInterface;
-use craft\gql\interfaces\elements\Tag as TagInterface;
use craft\gql\TypeLoader;
use craft\gql\types\generators\EntryType as EntryTypeGenerator;
use craft\gql\types\generators\TableRowType;
-use craft\models\CategoryGroup;
use craft\models\EntryType;
use craft\models\FieldLayout;
use craft\models\GqlSchema;
use craft\models\Section;
-use craft\models\TagGroup;
use craft\models\Volume;
use craft\test\TestCase;
use CraftCms\Cms\Field\PlainText;
@@ -61,9 +54,6 @@ protected function _before(): void
'volumes.volume-uid-2:read',
'sections.section-uid-1:read',
'sections.section-uid-2:read',
- 'categorygroups.categoyGroup-uid-1:read',
- 'taggroups.tagGroup-uid-1:read',
- 'globalsets.globalset-uid-1:read',
],
]),
]
@@ -89,30 +79,6 @@ protected function _before(): void
'getAllEntryTypes' => fn() => array_filter(array_map(fn(array $context) => $context['entryType'], $contexts)),
],
);
-
- $this->tester->mockMethods(
- Craft::$app,
- 'globals',
- [
- 'getAllSets' => fn() => static::mockGlobalSets(),
- ]
- );
-
- $this->tester->mockMethods(
- Craft::$app,
- 'categories',
- [
- 'getAllGroups' => fn() => static::mockCategoryGroups(),
- ]
- );
-
- $this->tester->mockMethods(
- Craft::$app,
- 'tags',
- [
- 'getAllTagGroups' => fn() => static::mockTagGroups(),
- ]
- );
}
protected function _after(): void
@@ -206,8 +172,6 @@ public static function interfaceDataProvider(): array
[EntryTypeGenerator::class, 'generateType'],
false,
],
- [CategoryInterface::class, fn() => static::mockCategoryGroups(), [CategoryElement::class, 'gqlTypeName']],
- [TagInterface::class, fn() => static::mockTagGroups(), [TagElement::class, 'gqlTypeName']],
];
}
@@ -325,70 +289,4 @@ public static function mockEntryContexts(): array
],
];
}
-
- /**
- * Mock the global sets for tests.
- *
- * @return array
- * @throws Exception
- */
- public static function mockGlobalSets(): array
- {
- return [
- Stub::make(GlobalSetElement::class, [
- 'uid' => 'globalset-uid-1',
- 'handle' => 'mockGlobal',
- '__call' => fn($name) => match ($name) {
- 'getCustomFields' => [
- Stub::make(PlainText::class, ['name' => 'Mock Field', 'handle' => 'mockField']),
- ],
- default => throw new UnknownMethodException("Calling unknown method: $name()"),
- },
- ]),
- ];
- }
-
- /**
- * Mock a category group for tests.
- *
- * @return array
- * @throws Exception
- */
- public static function mockCategoryGroups(): array
- {
- return [
- Stub::make(CategoryGroup::class, [
- 'uid' => 'categoyGroup-uid-1',
- 'handle' => 'mockCategoryGroup',
- '__call' => fn($name) => match ($name) {
- 'getCustomFields' => [
- Stub::make(PlainText::class, ['name' => 'Mock Field', 'handle' => 'mockField']),
- ],
- default => throw new UnknownMethodException("Calling unknown method: $name()"),
- },
- ]),
- ];
- }
-
- /**
- * Mock a tag group for tests.
- *
- * @return array
- * @throws Exception
- */
- public static function mockTagGroups(): array
- {
- return [
- Stub::make(TagGroup::class, [
- 'uid' => 'tagGroup-uid-1',
- 'handle' => 'mockTagGroup',
- '__call' => fn($name) => match ($name) {
- 'getCustomFields' => [
- Stub::make(PlainText::class, ['name' => 'Mock Field', 'handle' => 'mockField']),
- ],
- default => throw new UnknownMethodException("Calling unknown method: $name()"),
- },
- ]),
- ];
- }
}
diff --git a/yii2-adapter/tests/unit/gql/PrepareQueryTest.php b/yii2-adapter/tests/unit/gql/PrepareQueryTest.php
index abf9067cf9b..11fb776b850 100644
--- a/yii2-adapter/tests/unit/gql/PrepareQueryTest.php
+++ b/yii2-adapter/tests/unit/gql/PrepareQueryTest.php
@@ -9,18 +9,10 @@
use Craft;
use craft\gql\resolvers\elements\Asset as AssetResolver;
-use craft\gql\resolvers\elements\Category as CategoryResolver;
use craft\gql\resolvers\elements\Entry as EntryResolver;
-use craft\gql\resolvers\elements\GlobalSet as GlobalSetResolver;
-use craft\gql\resolvers\elements\Tag as TagResolver;
use craft\gql\resolvers\elements\User as UserResolver;
use craft\models\GqlSchema;
-use craft\records\CategoryGroup;
-use craft\records\Element;
use craft\records\EntryType;
-use craft\records\GlobalSet;
-use craft\records\Structure;
-use craft\records\TagGroup;
use craft\records\UserGroup;
use craft\records\Volume;
use craft\services\Entries;
@@ -43,13 +35,8 @@ class PrepareQueryTest extends TestCase
protected UnitTester $tester;
private Volume $_volume;
- private Structure $_structure;
- private CategoryGroup $_categoryGroup;
private Section $_section;
private EntryType $_entryType;
- private Element $_element;
- private GlobalSet $_globalSet;
- private TagGroup $_tagGroup;
private UserGroup $_userGroup;
@@ -66,10 +53,7 @@ protected function _before(): void
'getActiveSchema' => $this->make(GqlSchema::class, [
'scope' => [
'volumes.' . self::VOLUME_UID . ':read',
- 'categorygroups.' . self::CATEGORY_GROUP_UID . ':read',
'sections.' . self::SECTION_UID . ':read',
- 'globalsets.' . self::GLOBAL_SET_UID . ':read',
- 'taggroups.' . self::TAG_GROUP_UID . ':read',
'usergroups.' . self::USER_GROUP_UID . ':read',
],
]),
@@ -77,10 +61,7 @@ protected function _before(): void
);
$this->_setupAssets();
- $this->_setupCategories();
$this->_setupEntries();
- $this->_setupGlobals();
- $this->_setupTags();
$this->_setupUsers();
}
@@ -90,24 +71,16 @@ protected function _before(): void
protected function _after(): void
{
$this->_volume->delete();
- $this->_structure->delete();
- $this->_categoryGroup->delete();
Sections::deleteSection($this->_section);
$this->_entryType->delete();
- $this->_element->delete();
- $this->_globalSet->delete();
- $this->_tagGroup->delete();
$this->_userGroup->delete();
Craft::$app->set('entries', new Entries());
}
public const VOLUME_UID = 'volume-uid--------------------------';
- public const CATEGORY_GROUP_UID = 'categoryGroup-uid-------------------';
public const SECTION_UID = 'section-uid-------------------------';
public const ENTRY_TYPE_UID = 'entryType-uid-----------------------';
- public const GLOBAL_SET_UID = 'globalSet-uid-----------------------';
- public const TAG_GROUP_UID = 'tagGroup-uid------------------------';
public const USER_GROUP_UID = 'userGroup-uid-----------------------';
/**
@@ -155,17 +128,6 @@ public function relationalFieldQueryPreparationProvider(): array
AssetResolver::class, [null, []], fn($result) => $result->where[0] === 'in' && !empty($result->where[2]),
],
- // Category
- [
- CategoryResolver::class, [(object)['field' => ['foo', 'bar']], [], 'field'], fn($result) => $result === ['foo', 'bar'],
- ],
- [
- CategoryResolver::class, [null, ['groupId' => 2]], fn($result) => $result->groupId == 2,
- ],
- [
- CategoryResolver::class, [null, []], fn($result) => $result->where[0] === 'in' && !empty($result->where[2]),
- ],
-
// Entries
[
EntryResolver::class, [(object)['field' => ['foo', 'bar']], [], 'field'], fn($result) => $result === ['foo', 'bar'],
@@ -180,25 +142,6 @@ public function relationalFieldQueryPreparationProvider(): array
},
],
- // Global Sets
- [
- GlobalSetResolver::class, [null, ['handle' => 'foo']], fn($result) => $result->handle == 'foo',
- ],
- [
- GlobalSetResolver::class, [null, []], fn($result) => $result->where[0] === 'in' && !empty($result->where[2]),
- ],
-
- // Tags
- [
- TagResolver::class, [(object)['field' => ['foo', 'bar']], [], 'field'], fn($result) => $result === ['foo', 'bar'],
- ],
- [
- TagResolver::class, [null, ['groupId' => 2]], fn($result) => $result->groupId == 2,
- ],
- [
- TagResolver::class, [null, []], fn($result) => $result->where[0] === 'in' && !empty($result->where[2]),
- ],
-
// Users
[
UserResolver::class, [(object)['field' => ['foo', 'bar']], [], 'field'], fn($result) => $result === ['foo', 'bar'],
@@ -237,38 +180,6 @@ private function _setupAssets()
]);
}
- private function _setupCategories()
- {
- $this->_structure = new Structure();
- $this->_structure->save();
-
- $this->_categoryGroup = new CategoryGroup([
- 'uid' => self::CATEGORY_GROUP_UID,
- 'name' => Str::random(),
- 'handle' => Str::random(),
- 'structureId' => $this->_structure->id,
- ]);
-
- $this->_categoryGroup->save();
-
- $categoriesService = Craft::$app->getCategories();
-
- $this->tester->mockCraftMethods('categories', [
- 'getGroupByUid' => function($uid) use ($categoriesService) {
- if ($uid === self::CATEGORY_GROUP_UID) {
- return new \craft\models\CategoryGroup([
- 'id' => $this->_categoryGroup->id,
- 'uid' => self::CATEGORY_GROUP_UID,
- 'name' => $this->_categoryGroup->name,
- 'handle' => $this->_categoryGroup->handle,
- 'structureId' => $this->_structure->id,
- ]);
- }
- return $categoriesService->getGroupByUid($uid);
- },
- ]);
- }
-
private function _setupEntries()
{
$this->_entryType = new EntryType([
@@ -300,51 +211,6 @@ private function _setupEntries()
]);
}
- private function _setupGlobals()
- {
- $this->_element = new Element([
- 'type' => Str::random(),
- 'enabled' => true,
- 'archived' => false,
- ]);
- $this->_element->save();
-
- $this->_globalSet = new GlobalSet([
- 'uid' => self::GLOBAL_SET_UID,
- 'name' => Str::random(),
- 'handle' => Str::random(),
- 'id' => $this->_element->id,
- ]);
- $this->_globalSet->save();
- }
-
- private function _setupTags()
- {
- $this->_tagGroup = new TagGroup([
- 'uid' => self::TAG_GROUP_UID,
- 'name' => Str::random(),
- 'handle' => Str::random(),
- ]);
-
- $this->_tagGroup->save();
-
- $tagsService = Craft::$app->getTags();
-
- $this->tester->mockCraftMethods('tags', [
- 'getTagGroupByUid' => function($uid) use ($tagsService) {
- if ($uid === self::TAG_GROUP_UID) {
- return new \craft\models\TagGroup([
- 'id' => $this->_tagGroup->id,
- 'uid' => self::TAG_GROUP_UID,
- 'name' => $this->_tagGroup->name,
- 'handle' => $this->_tagGroup->handle,
- ]);
- }
- return $tagsService->getTagGroupByUid($uid);
- },
- ]);
- }
-
private function _setupUsers()
{
$this->_userGroup = new UserGroup([
diff --git a/yii2-adapter/tests/unit/gql/TypeResolverTest.php b/yii2-adapter/tests/unit/gql/TypeResolverTest.php
index 5a0c0d39b83..7cdca80c7b8 100644
--- a/yii2-adapter/tests/unit/gql/TypeResolverTest.php
+++ b/yii2-adapter/tests/unit/gql/TypeResolverTest.php
@@ -12,19 +12,16 @@
use craft\base\ElementInterface;
use craft\elements\Asset;
use craft\elements\Entry;
-use craft\elements\GlobalSet;
use craft\elements\User;
use craft\gql\base\Resolver;
use craft\gql\resolvers\elements\Asset as AssetResolver;
use craft\gql\resolvers\elements\Entry as EntryResolver;
-use craft\gql\resolvers\elements\GlobalSet as GlobalSetResolver;
use craft\gql\resolvers\elements\User as UserResolver;
use craft\test\mockclasses\elements\ExampleElement;
use craft\test\TestCase;
use CraftCms\Cms\Support\Str;
use crafttests\fixtures\AssetFixture;
use crafttests\fixtures\EntryFixture;
-use crafttests\fixtures\GlobalSetFixture;
use crafttests\fixtures\GqlSchemasFixture;
use crafttests\fixtures\UserFixture;
use Exception;
@@ -56,9 +53,6 @@ public function _fixtures(): array
'users' => [
'class' => UserFixture::class,
],
- 'globalSets' => [
- 'class' => GlobalSetFixture::class,
- ],
'gqlSchemas' => [
'class' => GqlSchemasFixture::class,
],
@@ -81,12 +75,6 @@ public function testRunGqlResolveTest(): void
[Entry::class, ['title' => 'Theories of life'], EntryResolver::class],
[Entry::class, ['title' => Str::random(128)], EntryResolver::class],
- // Globals
- [GlobalSet::class, ['handle' => 'aGlobalSet'], GlobalSetResolver::class, true],
- [GlobalSet::class, ['handle' => ['aGlobalSet', 'aDifferentGlobalSet']], GlobalSetResolver::class, true],
- [GlobalSet::class, ['handle' => 'aDeletedGlobalSet'], GlobalSetResolver::class, true],
- [GlobalSet::class, ['handle' => Str::random(128)], GlobalSetResolver::class, true],
-
// Users
[User::class, ['username' => 'user1'], UserResolver::class],
[User::class, ['username' => ['user1', 'admin']], UserResolver::class],
diff --git a/yii2-adapter/tests/unit/gql/mutations/CreateMutationsTest.php b/yii2-adapter/tests/unit/gql/mutations/CreateMutationsTest.php
index 21fad722000..e55d7d2b207 100644
--- a/yii2-adapter/tests/unit/gql/mutations/CreateMutationsTest.php
+++ b/yii2-adapter/tests/unit/gql/mutations/CreateMutationsTest.php
@@ -7,20 +7,11 @@
namespace crafttests\unit\gql\mutations;
-use craft\elements\GlobalSet;
use craft\gql\mutations\Asset as AssetMutations;
-use craft\gql\mutations\Category as CategoryMutations;
use craft\gql\mutations\Entry as EntryMutations;
-use craft\gql\mutations\GlobalSet as GlobalSetMutations;
-use craft\gql\mutations\Tag as TagMutations;
use craft\gql\types\elements\Asset as AssetGqlType;
-use craft\gql\types\elements\Category as CategoryGqlType;
use craft\gql\types\elements\Entry as EntryGqlType;
-use craft\gql\types\elements\GlobalSet as GlobalSetGqlType;
-use craft\gql\types\elements\Tag as TagGqlType;
-use craft\models\CategoryGroup;
use craft\models\GqlSchema;
-use craft\models\TagGroup;
use craft\models\Volume;
use craft\test\TestCase;
use CraftCms\Cms\Field\Number;
@@ -50,24 +41,6 @@ protected function _before(): void
],
]);
- $this->tester->mockCraftMethods('categories', [
- 'getAllGroups' => [
- new CategoryGroup(['uid' => 'uid', 'handle' => 'someGroup']),
- ],
- ]);
-
- $this->tester->mockCraftMethods('tags', [
- 'getAllTagGroups' => [
- new TagGroup(['uid' => 'uid', 'handle' => 'someGroup']),
- ],
- ]);
-
- $this->tester->mockCraftMethods('globals', [
- 'getAllSets' => [
- new GlobalSet(['uid' => 'uid', 'handle' => 'gSet']),
- ],
- ]);
-
$entryType = new \CraftCms\Cms\Entry\Data\EntryType(
handle: 'article',
uid: 'uid',
@@ -138,140 +111,6 @@ public function testCreateAssetSaveMutation(): void
self::assertArrayHasKey('_file', $mutation['args']);
}
- /**
- * For a list of scopes, test whether the right Category mutations are created.
- *
- * @param array $scopes
- * @param array $mutationNames
- * @dataProvider categoryMutationDataProvider
- * @throws InvalidConfigException
- */
- public function testCreateCategoryMutations(array $scopes, array $mutationNames): void
- {
- $this->_mockScope($scopes);
-
- // Create mutations
- $mutations = CategoryMutations::getMutations();
- $actualMutationNames = array_keys($mutations);
-
- // Verify
- sort($mutationNames);
- sort($actualMutationNames);
-
- self::assertEquals($mutationNames, $actualMutationNames);
- }
-
- /**
- * Check if a created save mutation for a given category group has expected arguments and returns a certain type
- */
- public function testCreateCategorySaveMutation(): void
- {
- $categoryGroup = $this->make(CategoryGroup::class, [
- '__call' => fn($name) => match ($name) {
- 'getCustomFields' => [
- new PlainText(['handle' => 'someTextField']),
- ],
- default => throw new UnknownMethodException("Calling unknown method: $name()"),
- },
- ]);
-
- $mutation = CategoryMutations::createSaveMutation($categoryGroup);
-
- self::assertInstanceOf(CategoryGqlType::class, $mutation['type']);
- self::assertArrayHasKey('someTextField', $mutation['args']);
- self::assertArrayHasKey('prependToRoot', $mutation['args']);
- self::assertArrayHasKey('title', $mutation['args']);
- }
-
- /**
- * For a list of scopes, test whether the right Tag mutations are created.
- *
- * @param array $scopes
- * @param array $mutationNames
- * @dataProvider tagMutationDataProvider
- * @throws InvalidConfigException
- */
- public function testCreateTagMutations(array $scopes, array $mutationNames): void
- {
- $this->_mockScope($scopes);
-
- // Create mutations
- $mutations = TagMutations::getMutations();
- $actualMutationNames = array_keys($mutations);
-
- // Verify
- sort($mutationNames);
- sort($actualMutationNames);
-
- self::assertEquals($mutationNames, $actualMutationNames);
- }
-
- /**
- * Check if a created save mutation for a given tag group has expected arguments and returns a certain type
- */
- public function testCreateTagSaveMutation(): void
- {
- $tagGroup = $this->make(TagGroup::class, [
- '__call' => fn($name) => match ($name) {
- 'getCustomFields' => [
- new PlainText(['handle' => 'someTextField']),
- ],
- default => throw new UnknownMethodException("Calling unknown method: $name()"),
- },
- ]);
-
- $mutation = TagMutations::createSaveMutation($tagGroup);
-
- self::assertInstanceOf(TagGqlType::class, $mutation['type']);
- self::assertArrayHasKey('someTextField', $mutation['args']);
- self::assertArrayHasKey('uid', $mutation['args']);
- }
-
- /**
- * For a list of scopes, test whether the right global set mutations are created.
- *
- * @param array $scopes
- * @param array $mutationNames
- * @dataProvider globalSetMutationDataProvider
- * @throws InvalidConfigException
- */
- public function testCreateGlobalSetMutations(array $scopes, array $mutationNames): void
- {
- $this->_mockScope($scopes);
-
- // Create mutations
- $mutations = GlobalSetMutations::getMutations();
- $actualMutationNames = array_keys($mutations);
-
- // Verify
- sort($mutationNames);
- sort($actualMutationNames);
-
- self::assertEquals($mutationNames, $actualMutationNames);
- }
-
- /**
- * Check if a created save mutation for a given global set has expected arguments and returns a certain type
- */
- public function testCreateGlobalSetSaveMutation(): void
- {
- $globalSet = $this->make(GlobalSet::class, [
- '__call' => fn($name) => match ($name) {
- 'getCustomFields' => [
- new PlainText(['handle' => 'someTextField']),
- ],
- default => throw new UnknownMethodException("Calling unknown method: $name()"),
- },
- ]);
-
- $mutation = GlobalSetMutations::createSaveMutation($globalSet);
-
- self::assertInstanceOf(GlobalSetGqlType::class, $mutation['type']);
- self::assertArrayHasKey('someTextField', $mutation['args']);
- self::assertArrayNotHasKey('uid', $mutation['args']);
- }
-
-
/**
* For a list of scopes, test whether getting the right entry and draft mutations are created.
*
@@ -384,50 +223,6 @@ public static function assetMutationDataProvider(): array
];
}
- public static function categoryMutationDataProvider(): array
- {
- return [
- [
- ['categorygroups.uid:edit', 'categorygroups.uid:delete'],
- ['deleteCategory'],
- ],
- [
- ['categorygroups.uid:edit', 'categorygroups.uid:save', 'categorygroups.uid:delete'],
- ['deleteCategory', 'save_someGroup_Category'],
- ],
- [
- ['categorygroups.uid:edit', 'categorygroups.uid:save'],
- ['save_someGroup_Category'],
- ],
- [
- ['categorygroups.nope:edit', 'categorygroups.nope:save'],
- [],
- ],
- ];
- }
-
- public static function tagMutationDataProvider(): array
- {
- return [
- [
- ['taggroups.uid:edit', 'taggroups.uid:delete'],
- ['deleteTag'],
- ],
- [
- ['taggroups.uid:edit', 'taggroups.uid:save', 'taggroups.uid:delete'],
- ['deleteTag', 'save_someGroup_Tag'],
- ],
- [
- ['taggroups.uid:edit', 'taggroups.uid:save'],
- ['save_someGroup_Tag'],
- ],
- [
- ['taggroups.nope:edit', 'taggroups.nope:save'],
- [],
- ],
- ];
- }
-
public static function entryMutationDataProvider(): array
{
return [
@@ -454,20 +249,6 @@ public static function entryMutationDataProvider(): array
];
}
- public static function globalSetMutationDataProvider(): array
- {
- return [
- [
- ['globalsets.uid:edit'],
- ['save_gSet_GlobalSet'],
- ],
- [
- ['globalsets.uid:edit', 'globalsets.uid2:edit'],
- ['save_gSet_GlobalSet'],
- ],
- ];
- }
-
/**
* @param array $scopes
* @throws Exception
diff --git a/yii2-adapter/tests/unit/gql/mutations/StructureOperationMutationTest.php b/yii2-adapter/tests/unit/gql/mutations/StructureOperationMutationTest.php
index 1af405cf341..32f4e703a69 100644
--- a/yii2-adapter/tests/unit/gql/mutations/StructureOperationMutationTest.php
+++ b/yii2-adapter/tests/unit/gql/mutations/StructureOperationMutationTest.php
@@ -7,8 +7,8 @@
namespace crafttests\unit\gql\mutations;
-use craft\elements\Category;
-use craft\gql\resolvers\mutations\Category as CategoryResolver;
+use craft\elements\Entry;
+use craft\gql\resolvers\mutations\Entry as EntryResolver;
use craft\test\TestCase;
use CraftCms\Cms\Support\Facades\Structures;
use UnitTester;
@@ -39,7 +39,7 @@ protected function _after(): void
*/
public function testStructureOperations(array $elementProperties, array $arguments, ?string $requiredMethod = null, ?string $exception = null): void
{
- $element = $this->make(Category::class, $elementProperties);
+ $element = $this->make(Entry::class, $elementProperties);
$structuresMock = Structures::partialMock();
@@ -59,14 +59,14 @@ public function testStructureOperations(array $elementProperties, array $argumen
}
$this->tester->mockCraftMethods('elements', [
- 'getElementById' => fn($elementId) => $elementId > 0 ? new Category() : null,
+ 'getElementById' => fn($elementId) => $elementId > 0 ? new Entry() : null,
]);
if ($exception) {
$this->expectExceptionMessage($exception);
}
- $resolver = new CategoryResolver();
+ $resolver = new EntryResolver();
$this->invokeMethod($resolver, 'performStructureOperations', [$element, $arguments]);
diff --git a/yii2-adapter/tests/unit/helpers/GqlHelperTest.php b/yii2-adapter/tests/unit/helpers/GqlHelperTest.php
index e1313951883..ff2be6255d2 100644
--- a/yii2-adapter/tests/unit/helpers/GqlHelperTest.php
+++ b/yii2-adapter/tests/unit/helpers/GqlHelperTest.php
@@ -91,11 +91,8 @@ public function testSchemaQueryAbility(): void
$this->_setSchemaWithPermissions($permissionSet);
self::assertTrue(GqlHelper::canQueryEntries());
- self::assertTrue(GqlHelper::canQueryGlobalSets());
self::assertTrue(GqlHelper::canQueryUsers());
self::assertFalse(GqlHelper::canQueryAssets());
- self::assertFalse(GqlHelper::canQueryCategories());
- self::assertFalse(GqlHelper::canQueryTags());
}
/**
diff --git a/yii2-adapter/tests/unit/services/ElementsTest.php b/yii2-adapter/tests/unit/services/ElementsTest.php
index e567e77dd62..aafdf3aa82d 100644
--- a/yii2-adapter/tests/unit/services/ElementsTest.php
+++ b/yii2-adapter/tests/unit/services/ElementsTest.php
@@ -17,7 +17,6 @@
use craft\test\TestSetup;
use crafttests\fixtures\AssetFixture;
use crafttests\fixtures\EntryFixture;
-use crafttests\fixtures\GlobalSetFixture;
use crafttests\fixtures\settings\GeneralConfigSettingFixture;
use crafttests\fixtures\SitesFixture;
use crafttests\fixtures\UserFixture;
@@ -98,15 +97,10 @@ public function _fixtures(): array
'assets' => [
'class' => AssetFixture::class,
],
- // Category?
// ContentBlock?
'entries' => [
'class' => EntryFixture::class,
],
- 'globalSet' => [
- 'class' => GlobalSetFixture::class,
- ],
- // Tag?
'users' => [
'class' => UserFixture::class,
],
diff --git a/yii2-adapter/tests/unit/services/GlobalsTest.php b/yii2-adapter/tests/unit/services/GlobalsTest.php
deleted file mode 100644
index 099c9a3afe7..00000000000
--- a/yii2-adapter/tests/unit/services/GlobalsTest.php
+++ /dev/null
@@ -1,57 +0,0 @@
-
- * @since 3.3.16
- */
-class GlobalsTest extends TestCase
-{
- /**
- * @var UnitTester
- */
- protected UnitTester $tester;
-
- protected function _before(): void
- {
- }
-
- // @TODO: more tests, obviously.
-
- /**
- * Test if rebuilding project congif ignores the `readOnly` flag.
- */
- public function testAbortOnUnsavedElement(): void
- {
- $configEvent = new ItemUpdated(
- path: 'globalSets.testUid',
- oldValue: [],
- newValue: [
- 'name' => 'Test ' . Str::uuid()->toString(),
- 'handle' => 'test' . Str::uuid()->toString(),
- ],
- tokenMatches: ['testuid'],
- );
-
- $this->tester->mockMethods(Craft::$app, 'elements', ['saveElement' => false]);
-
- $this->tester->expectThrowable(ElementNotFoundException::class, function() use ($configEvent) {
- Craft::$app->getGlobals()->handleChangedGlobalSet($configEvent);
- });
- }
-}
diff --git a/yii2-adapter/tests/unit/services/GqlTest.php b/yii2-adapter/tests/unit/services/GqlTest.php
index 7675c2a1680..b70e277c9ac 100644
--- a/yii2-adapter/tests/unit/services/GqlTest.php
+++ b/yii2-adapter/tests/unit/services/GqlTest.php
@@ -8,7 +8,6 @@
namespace crafttests\unit\services;
use Craft;
-use craft\elements\GlobalSet;
use craft\elements\User;
use craft\errors\GqlException;
use craft\events\ExecuteGqlQueryEvent;
@@ -20,18 +19,13 @@
use craft\gql\GqlEntityRegistry;
use craft\gql\interfaces\elements\User as UserInterface;
use craft\gql\TypeLoader;
-use craft\models\CategoryGroup;
use craft\models\EntryType;
use craft\models\GqlSchema;
use craft\models\GqlToken;
use craft\models\Section;
-use craft\models\TagGroup;
use craft\models\UserGroup;
-use craft\services\Categories;
use craft\services\Entries;
-use craft\services\Globals;
use craft\services\Gql;
-use craft\services\Tags;
use craft\services\UserGroups;
use craft\services\Volumes;
use craft\test\mockclasses\gql\MockDirective;
@@ -328,34 +322,6 @@ public function testPermissionListGenerated(): void
],
]);
- $globalService = $this->make(Globals::class, [
- 'getAllSets' => [
- new GlobalSet([
- 'id' => 1,
- 'name' => 'Test global',
- 'uid' => 'globalUid',
- ]),
- ],
- ]);
- $categoryService = $this->make(Categories::class, [
- 'getAllGroups' => [
- new CategoryGroup([
- 'id' => 1,
- 'name' => 'Test category group',
- 'uid' => 'categoryGroupUid',
- ]),
- ],
- ]);
- $tagService = $this->make(Tags::class, [
- 'getAllTagGroups' => [
- new TagGroup([
- 'id' => 1,
- 'name' => 'Test tag group',
- 'uid' => 'tagGroupUid',
- ]),
- ],
- ]);
-
$userGroupService = $this->make(UserGroups::class, [
'getAllGroups' => [
new UserGroup([
@@ -368,9 +334,6 @@ public function testPermissionListGenerated(): void
Craft::$app->set('entries', $entriesService);
Craft::$app->set('volumes', $volumeService);
- Craft::$app->set('globals', $globalService);
- Craft::$app->set('categories', $categoryService);
- Craft::$app->set('tags', $tagService);
Craft::$app->set('userGroups', $userGroupService);
$edition = Edition::get();
@@ -383,16 +346,10 @@ public function testPermissionListGenerated(): void
self::assertNotEmpty($allSchemaComponents['queries']['Entries'] ?? []);
self::assertNotEmpty($allSchemaComponents['queries']['Assets'] ?? []);
- self::assertNotEmpty($allSchemaComponents['queries']['Global Sets'] ?? []);
self::assertNotEmpty($allSchemaComponents['queries']['Users'] ?? []);
- self::assertNotEmpty($allSchemaComponents['queries']['Categories'] ?? []);
- self::assertNotEmpty($allSchemaComponents['queries']['Tags'] ?? []);
self::assertNotEmpty($allSchemaComponents['mutations']['Entries'] ?? []);
self::assertNotEmpty($allSchemaComponents['mutations']['Assets'] ?? []);
- self::assertNotEmpty($allSchemaComponents['mutations']['Global Sets'] ?? []);
- self::assertNotEmpty($allSchemaComponents['mutations']['Categories'] ?? []);
- self::assertNotEmpty($allSchemaComponents['mutations']['Tags'] ?? []);
Edition::set($edition);
}
diff --git a/yii2-adapter/tests/unit/services/UserPermissionsTest.php b/yii2-adapter/tests/unit/services/UserPermissionsTest.php
index fe00b74117e..506e51007a0 100644
--- a/yii2-adapter/tests/unit/services/UserPermissionsTest.php
+++ b/yii2-adapter/tests/unit/services/UserPermissionsTest.php
@@ -18,7 +18,6 @@
use CraftCms\Cms\Edition\Exceptions\WrongEditionException;
use CraftCms\Cms\ProjectConfig\ProjectConfig;
use CraftCms\Cms\Support\Arr;
-use crafttests\fixtures\GlobalSetFixture;
use crafttests\fixtures\SectionsFixture;
use crafttests\fixtures\SitesFixture;
use crafttests\fixtures\UserFixture;
@@ -61,9 +60,6 @@ public function _fixtures(): array
'sections' => [
'class' => SectionsFixture::class,
],
- 'globals' => [
- 'class' => GlobalSetFixture::class,
- ],
'volumes' => [
'class' => VolumesFixture::class,
],
@@ -88,7 +84,6 @@ public function testGetAllPermissions(): void
self::assertContains('Sites', $headings);
self::assertContains('Section - Single', $headings);
self::assertContains('Section - Test 1', $headings);
- self::assertContains('Global Sets', $headings);
self::assertContains('Volume - Test volume 1', $headings);
self::assertContains('Utilities', $headings);
}
diff --git a/yii2-adapter/tests/unit/web/twig/ExtensionTest.php b/yii2-adapter/tests/unit/web/twig/ExtensionTest.php
index ce9756c09ba..a734386cd0c 100644
--- a/yii2-adapter/tests/unit/web/twig/ExtensionTest.php
+++ b/yii2-adapter/tests/unit/web/twig/ExtensionTest.php
@@ -24,12 +24,11 @@
use CraftCms\Cms\Field\PlainText;
use CraftCms\Cms\ProjectConfig\ProjectConfig;
use CraftCms\Cms\Support\Facades\EntryTypes;
-use crafttests\fixtures\GlobalSetFixture;
+use crafttests\fixtures\FieldLayoutFixture;
use DateInterval;
use DateTime;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Session;
-use Throwable;
use Twig\Error\LoaderError;
use Twig\Error\RuntimeError;
use Twig\Error\SyntaxError;
@@ -56,7 +55,7 @@ public function _fixtures(): array
{
return [
'globals' => [
- 'class' => GlobalSetFixture::class,
+ 'class' => FieldLayoutFixture::class,
],
];
}
@@ -142,19 +141,6 @@ public function test_site_globals(): void
);
}
- /**
- * @throws LoaderError
- * @throws SyntaxError
- * @throws Throwable
- */
- public function test_element_globals(): void
- {
- $this->testRenderResult(
- 'A global set | A different global set',
- '{{ aGlobalSet }} | {{ aDifferentGlobalSet }}'
- );
- }
-
public function test_instance_of_test(): void
{
$this->testRenderResult(