diff --git a/CHANGELOG.md b/CHANGELOG.md index ab34f13..8956f51 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,20 @@ # Bulk Edit Changelog -## Unreleased +## 2.0.0 - 2020-01-24 +{warning} The FieldProcessorInterface has slightly changed to better +support old versions of PHP. If you wrote your own FieldProcessor, +ensure it has been updates prior to updating to 2.0.0 + ### Added - Add support for new strategies: ADD, MULTIPLY, DIVIDE - Number fields now have the following strategies: REPLACE, ADD, SUBTRACT, MULTIPLY, DIVIDE +### Changed +- Changed FieldProcessorInterface to remove void declaration + +### Fixed +- Improved compatibility with older versions of php + ## 1.1.1 - 2019-07-22 ### Fixed - Fixed potential issues with merge strategies defaulting to replace diff --git a/composer.json b/composer.json index 715f749..4f2ad6f 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "venveo/craft-bulkedit", "description": "Bulk edit entries", "type": "craft-plugin", - "version": "1.1.1", + "version": "2.0.0", "keywords": [ "craft", "cms", diff --git a/src/Plugin.php b/src/Plugin.php index 9780089..f63d9a4 100644 --- a/src/Plugin.php +++ b/src/Plugin.php @@ -10,6 +10,7 @@ namespace venveo\bulkedit; +use Craft; use craft\base\Element; use craft\base\Plugin as BasePlugin; use craft\commerce\elements\Product; @@ -34,19 +35,17 @@ class Plugin extends BasePlugin { + const PERMISSION_BULKEDIT_ENTRIES = 'PERMISSION_BULKEDIT_ENTRIES'; + const PERMISSION_BULKEDIT_PRODUCTS = 'PERMISSION_BULKEDIT_PRODUCTS'; + const PERMISSION_BULKEDIT_ASSETS = 'PERMISSION_BULKEDIT_ASSETS'; + const PERMISSION_BULKEDIT_CATEGORIES = 'PERMISSION_BULKEDIT_CATEGORIES'; + const PERMISSION_BULKEDIT_USERS = 'PERMISSION_BULKEDIT_USERS'; /** * @var Plugin */ public static $plugin; - public $schemaVersion = '1.1.0'; - public const PERMISSION_BULKEDIT_ENTRIES = 'PERMISSION_BULKEDIT_ENTRIES'; - public const PERMISSION_BULKEDIT_PRODUCTS = 'PERMISSION_BULKEDIT_PRODUCTS'; - public const PERMISSION_BULKEDIT_ASSETS = 'PERMISSION_BULKEDIT_ASSETS'; - public const PERMISSION_BULKEDIT_CATEGORIES = 'PERMISSION_BULKEDIT_CATEGORIES'; - public const PERMISSION_BULKEDIT_USERS = 'PERMISSION_BULKEDIT_USERS'; - public function init() { parent::init(); @@ -57,29 +56,29 @@ public function init() Event::on(UserPermissions::class, UserPermissions::EVENT_REGISTER_PERMISSIONS, function (RegisterUserPermissionsEvent $event) { $permissions = []; $permissions[self::PERMISSION_BULKEDIT_ENTRIES] = [ - 'label' => \Craft::t('venveo-bulk-edit', 'Bulk Edit Entries') + 'label' => Craft::t('venveo-bulk-edit', 'Bulk Edit Entries') ]; $permissions[self::PERMISSION_BULKEDIT_ASSETS] = [ - 'label' => \Craft::t('venveo-bulk-edit', 'Bulk Edit Assets') + 'label' => Craft::t('venveo-bulk-edit', 'Bulk Edit Assets') ]; $permissions[self::PERMISSION_BULKEDIT_CATEGORIES] = [ - 'label' => \Craft::t('venveo-bulk-edit', 'Bulk Edit Categories') + 'label' => Craft::t('venveo-bulk-edit', 'Bulk Edit Categories') ]; $permissions[self::PERMISSION_BULKEDIT_USERS] = [ - 'label' => \Craft::t('venveo-bulk-edit', 'Bulk Edit Users') + 'label' => Craft::t('venveo-bulk-edit', 'Bulk Edit Users') ]; - if (\Craft::$app->plugins->isPluginInstalled('commerce')) { + if (Craft::$app->plugins->isPluginInstalled('commerce')) { $permissions[self::PERMISSION_BULKEDIT_PRODUCTS] = [ - 'label' => \Craft::t('venveo-bulk-edit', 'Bulk Edit Products') + 'label' => Craft::t('venveo-bulk-edit', 'Bulk Edit Products') ]; } - $event->permissions[\Craft::t('venveo-bulk-edit', 'Bulk Edit')] = $permissions; + $event->permissions[Craft::t('venveo-bulk-edit', 'Bulk Edit')] = $permissions; }); - if (\Craft::$app->request->isCpRequest) { - if (\Craft::$app->user->checkPermission(self::PERMISSION_BULKEDIT_ENTRIES)) { + if (Craft::$app->request->isCpRequest) { + if (Craft::$app->user->checkPermission(self::PERMISSION_BULKEDIT_ENTRIES)) { Event::on(Entry::class, Element::EVENT_REGISTER_ACTIONS, function (RegisterElementActionsEvent $event) { $event->actions[] = BulkEditElementAction::class; @@ -87,7 +86,7 @@ function (RegisterElementActionsEvent $event) { ); } - if (\Craft::$app->user->checkPermission(self::PERMISSION_BULKEDIT_CATEGORIES)) { + if (Craft::$app->user->checkPermission(self::PERMISSION_BULKEDIT_CATEGORIES)) { Event::on(Category::class, Element::EVENT_REGISTER_ACTIONS, function (RegisterElementActionsEvent $event) { $event->actions[] = BulkEditElementAction::class; @@ -95,7 +94,7 @@ function (RegisterElementActionsEvent $event) { ); } - if (\Craft::$app->user->checkPermission(self::PERMISSION_BULKEDIT_ASSETS)) { + if (Craft::$app->user->checkPermission(self::PERMISSION_BULKEDIT_ASSETS)) { Event::on(Asset::class, Element::EVENT_REGISTER_ACTIONS, function (RegisterElementActionsEvent $event) { $event->actions[] = BulkEditElementAction::class; @@ -103,7 +102,7 @@ function (RegisterElementActionsEvent $event) { ); } - if (\Craft::$app->user->checkPermission(self::PERMISSION_BULKEDIT_USERS)) { + if (Craft::$app->user->checkPermission(self::PERMISSION_BULKEDIT_USERS)) { Event::on(User::class, Element::EVENT_REGISTER_ACTIONS, function (RegisterElementActionsEvent $event) { $event->actions[] = BulkEditElementAction::class; @@ -111,8 +110,8 @@ function (RegisterElementActionsEvent $event) { ); } - if (\Craft::$app->user->checkPermission(self::PERMISSION_BULKEDIT_PRODUCTS)) { - if (\Craft::$app->plugins->isPluginInstalled('commerce') && class_exists(Product::class)) { + if (Craft::$app->user->checkPermission(self::PERMISSION_BULKEDIT_PRODUCTS)) { + if (Craft::$app->plugins->isPluginInstalled('commerce') && class_exists(Product::class)) { Event::on(Product::class, Element::EVENT_REGISTER_ACTIONS, function (RegisterElementActionsEvent $event) { $event->actions[] = BulkEditElementAction::class; diff --git a/src/assetbundles/bulkeditelementaction/BulkEditElementActionAsset.php b/src/assetbundles/bulkeditelementaction/BulkEditElementActionAsset.php index dc1540e..64db99f 100644 --- a/src/assetbundles/bulkeditelementaction/BulkEditElementActionAsset.php +++ b/src/assetbundles/bulkeditelementaction/BulkEditElementActionAsset.php @@ -1,7 +1,7 @@ handle; - $element->setFieldValue($fieldHandle, $value); - } - - public static function performSubtraction(Element $element, Field $field, $value): void - { - throw new \RuntimeException('Subtraction not implemented for this field type'); - } - - public static function performAddition(Element $element, Field $field, $value): void - { - throw new \RuntimeException('Addition not implemented for this field type'); - } - - public static function performDivision(Element $element, Field $field, $value): void - { - throw new \RuntimeException('Division not implemented for this field type'); - } - - public static function performMultiplication(Element $element, Field $field, $value): void - { - throw new \RuntimeException('Multiplication not implemented for this field type'); - } - /** * @param FieldInterface $field * @return bool @@ -54,7 +24,7 @@ public static function supportsField(FieldInterface $field): bool return false; } - public static function processElementField(Element $element, Field $field, $strategy, $newValue): void + public static function processElementField(Element $element, Field $field, $strategy, $newValue) { switch ($strategy) { case BulkEdit::STRATEGY_REPLACE: @@ -77,4 +47,35 @@ public static function processElementField(Element $element, Field $field, $stra break; } } + + public static function performReplacement(Element $element, Field $field, $value) + { + $fieldHandle = $field->handle; + $element->setFieldValue($fieldHandle, $value); + } + + public static function performMerge(Element $element, Field $field, $value) + { + throw new RuntimeException('Merge not implemented for this field type'); + } + + public static function performSubtraction(Element $element, Field $field, $value) + { + throw new RuntimeException('Subtraction not implemented for this field type'); + } + + public static function performAddition(Element $element, Field $field, $value) + { + throw new RuntimeException('Addition not implemented for this field type'); + } + + public static function performMultiplication(Element $element, Field $field, $value) + { + throw new RuntimeException('Multiplication not implemented for this field type'); + } + + public static function performDivision(Element $element, Field $field, $value) + { + throw new RuntimeException('Division not implemented for this field type'); + } } \ No newline at end of file diff --git a/src/base/ElementTypeProcessorInterface.php b/src/base/ElementTypeProcessorInterface.php index fd08b54..39f068e 100644 --- a/src/base/ElementTypeProcessorInterface.php +++ b/src/base/ElementTypeProcessorInterface.php @@ -1,9 +1,11 @@ requirePostRequest(); $this->requireAcceptsJson(); - $site = Craft::$app->getSites()->getCurrentSite(); $elementIds = Craft::$app->getRequest()->getRequiredParam('elementIds'); - $elementType = Craft::$app->getRequest()->getRequiredParam('elementType'); $requestId = Craft::$app->getRequest()->getRequiredParam('requestId'); + $viewParams = Craft::$app->getRequest()->getRequiredParam('viewParams'); + + $elementType = $viewParams['elementType']; + $siteId = $viewParams['criteria']['siteId']; + $site = Craft::$app->sites->getSiteById($siteId); + +// $sourceKey = $viewParams['source']; +// $criteria = $viewParams['criteria']; +// +// $query = $elementType::find(); +// $source = ElementHelper::findSource($elementType, $sourceKey, 'index'); +// +// +// if ($source === null) { +// throw new BadRequestHttpException('Invalid source key: ' . $sourceKey); +// } +// +// // Does the source specify any criteria attributes? +// if (isset($source['criteria'])) { +// Craft::configure($query, $source['criteria']); +// } +// +// // Override with the request's params +// if ($criteria !== null) { +// if (isset($criteria['trashed'])) { +// $criteria['trashed'] = (bool)$criteria['trashed']; +// } +// Craft::configure($query, $criteria); +// } /** @var BulkEditService $service */ $service = Plugin::$plugin->bulkEdit; $fields = $service->getFieldsForElementIds($elementIds, $elementType); - $view = \Craft::$app->getView(); + $view = Craft::$app->getView(); $modalHtml = $view->renderTemplate('venveo-bulk-edit/elementactions/BulkEdit/_fields', [ 'fieldWrappers' => $fields, 'elementType' => $elementType, @@ -79,9 +109,9 @@ public function actionGetFields(): Response /** * @return \yii\web\Response * @throws BadRequestHttpException - * @throws \Twig\Error\LoaderError - * @throws \Twig\Error\RuntimeError - * @throws \Twig\Error\SyntaxError + * @throws LoaderError + * @throws RuntimeError + * @throws SyntaxError */ public function actionGetEditScreen() { @@ -95,6 +125,8 @@ public function actionGetEditScreen() $siteId = Craft::$app->getRequest()->getRequiredParam('siteId'); $fields = Craft::$app->getRequest()->getRequiredParam('fields'); + $viewParams = Craft::$app->getRequest()->getParam('viewParams'); + // Pull out the enabled fields $enabledFields = []; foreach ($fields as $fieldId => $field) { @@ -106,35 +138,35 @@ public function actionGetEditScreen() $site = Craft::$app->getSites()->getSiteById($siteId); if (!$site) { - throw new \Exception('Site does not exist'); + throw new Exception('Site does not exist'); } $fields = Field::findAll(array_keys($enabledFields)); if (count($fields) !== count($enabledFields)) { - throw new \Exception('Could not find all fields requested'); + throw new Exception('Could not find all fields requested'); } $elementIds = explode(',', $elementIds); $elements = Element::findAll($elementIds); if (count($elements) !== count($elementIds)) { - throw new \Exception('Could not find all elements requested'); + throw new Exception('Could not find all elements requested'); } try { $fieldModels = []; /** @var Field $field */ foreach ($fields as $field) { - $fieldModel = \Craft::$app->fields->getFieldById($field->id); + $fieldModel = Craft::$app->fields->getFieldById($field->id); if ($fieldModel && Plugin::$plugin->bulkEdit->isFieldSupported($fieldModel)) { $fieldModels[] = $fieldModel; } } - } catch (\Exception $e) { + } catch (Exception $e) { throw $e; } - $view = \Craft::$app->getView(); + $view = Craft::$app->getView(); // We've gotta register any asset bundles - this won't actually be rendered foreach ($fieldModels as $fieldModel) { @@ -195,7 +227,7 @@ public function actionSaveContext() } } if (!isset($fieldId)) { - throw new \Exception('Failed to locate field'); + throw new Exception('Failed to locate field'); } $keyedFieldValues[$fieldId] = $value; } @@ -208,7 +240,7 @@ public function actionSaveContext() return $this->asJson([ 'success' => true ]); - } catch (\Exception $e) { + } catch (Exception $e) { return $this->asErrorJson('Failed to save context'); } } diff --git a/src/elements/actions/BulkEditElementAction.php b/src/elements/actions/BulkEditElementAction.php index c443658..e751efe 100644 --- a/src/elements/actions/BulkEditElementAction.php +++ b/src/elements/actions/BulkEditElementAction.php @@ -12,7 +12,6 @@ use Craft; use craft\base\ElementAction; -use craft\elements\db\ElementQueryInterface; use craft\helpers\Json; use venveo\bulkedit\assetbundles\bulkeditelementaction\BulkEditElementActionAsset; diff --git a/src/elements/processors/UserProcessor.php b/src/elements/processors/UserProcessor.php index 980ec56..6af457d 100644 --- a/src/elements/processors/UserProcessor.php +++ b/src/elements/processors/UserProcessor.php @@ -2,6 +2,7 @@ namespace venveo\bulkedit\elements\processors; +use Craft; use craft\elements\User; use craft\records\FieldLayout; use craft\services\Users; @@ -18,7 +19,7 @@ class UserProcessor extends AbstractElementTypeProcessor */ public static function getLayoutsFromElementIds($elementIds): array { - $projectConfig = \Craft::$app->projectConfig; + $projectConfig = Craft::$app->projectConfig; $fieldLayouts = $projectConfig->get(Users::CONFIG_USERLAYOUT_KEY); $fieldLayoutsUIDs = array_keys($fieldLayouts); $layouts = FieldLayout::find() diff --git a/src/fields/processors/NumberFieldProcessor.php b/src/fields/processors/NumberFieldProcessor.php index 4b9a71d..838ff42 100644 --- a/src/fields/processors/NumberFieldProcessor.php +++ b/src/fields/processors/NumberFieldProcessor.php @@ -31,7 +31,7 @@ public static function getSupportedStrategies(): array return [BulkEdit::STRATEGY_REPLACE, BulkEdit::STRATEGY_SUBTRACT, BulkEdit::STRATEGY_ADD, BulkEdit::STRATEGY_MULTIPLY, BulkEdit::STRATEGY_DIVIDE]; } - public static function performAddition(Element $element, Field $field, $value): void + public static function performAddition(Element $element, Field $field, $value) { $fieldHandle = $field->handle; $originalValue = (int)$element->getFieldValue($fieldHandle); @@ -39,7 +39,7 @@ public static function performAddition(Element $element, Field $field, $value): $element->setFieldValue($fieldHandle, $originalValue + (int)$value); } - public static function performSubtraction(Element $element, Field $field, $value): void + public static function performSubtraction(Element $element, Field $field, $value) { $fieldHandle = $field->handle; $originalValue = (int)$element->getFieldValue($fieldHandle); @@ -48,7 +48,7 @@ public static function performSubtraction(Element $element, Field $field, $value $element->setFieldValue($fieldHandle, $originalValue - (int)$value); } - public static function performMultiplication(Element $element, Field $field, $value): void + public static function performMultiplication(Element $element, Field $field, $value) { $fieldHandle = $field->handle; $originalValue = (int)$element->getFieldValue($fieldHandle); @@ -57,7 +57,7 @@ public static function performMultiplication(Element $element, Field $field, $va $element->setFieldValue($fieldHandle, $originalValue * (int)$value); } - public static function performDivision(Element $element, Field $field, $value): void + public static function performDivision(Element $element, Field $field, $value) { $fieldHandle = $field->handle; $originalValue = (int)$element->getFieldValue($fieldHandle); diff --git a/src/fields/processors/PlainTextProcessor.php b/src/fields/processors/PlainTextProcessor.php index d316319..4db004f 100644 --- a/src/fields/processors/PlainTextProcessor.php +++ b/src/fields/processors/PlainTextProcessor.php @@ -2,8 +2,7 @@ namespace venveo\bulkedit\fields\processors; -use craft\base\Element; -use craft\base\Field; +use Craft; use craft\fields\Checkboxes; use craft\fields\Color; use craft\fields\Date; @@ -11,12 +10,11 @@ use craft\fields\Email; use craft\fields\Lightswitch; use craft\fields\MultiSelect; -use craft\fields\Number; use craft\fields\PlainText; use craft\fields\RadioButtons; use craft\fields\Table; -use craft\redactor\Field as RedactorField; use craft\fields\Url; +use craft\redactor\Field as RedactorField; use venveo\bulkedit\base\AbstractFieldProcessor; use venveo\bulkedit\services\BulkEdit; @@ -43,7 +41,7 @@ public static function getSupportedFields(): array MultiSelect::class ]; - if (\Craft::$app->plugins->isPluginInstalled('redactor')) { + if (Craft::$app->plugins->isPluginInstalled('redactor')) { $fields[] = RedactorField::class; } diff --git a/src/fields/processors/RelationFieldProcessor.php b/src/fields/processors/RelationFieldProcessor.php index 232b01e..fd073ba 100644 --- a/src/fields/processors/RelationFieldProcessor.php +++ b/src/fields/processors/RelationFieldProcessor.php @@ -4,7 +4,6 @@ use craft\base\Element; use craft\base\Field; -use craft\base\FieldInterface; use craft\fields\BaseRelationField; use venveo\bulkedit\base\AbstractFieldProcessor; use venveo\bulkedit\services\BulkEdit; @@ -34,7 +33,7 @@ public static function getSupportedStrategies(): array return [BulkEdit::STRATEGY_REPLACE, BulkEdit::STRATEGY_SUBTRACT, BulkEdit::STRATEGY_MERGE]; } - public static function performSubtraction(Element $element, Field $field, $value): void + public static function performSubtraction(Element $element, Field $field, $value) { $fieldHandle = $field->handle; $originalValue = $element->getFieldValue($fieldHandle); @@ -43,7 +42,7 @@ public static function performSubtraction(Element $element, Field $field, $value $element->setFieldValue($fieldHandle, $ids); } - public static function performMerge(Element $element, Field $field, $value): void + public static function performMerge(Element $element, Field $field, $value) { $originalValue = $element->getFieldValue($field->handle); $fieldHandle = $field->handle; diff --git a/src/migrations/Install.php b/src/migrations/Install.php index dd011c0..885d285 100644 --- a/src/migrations/Install.php +++ b/src/migrations/Install.php @@ -33,13 +33,6 @@ public function safeUp() /* * @inheritdoc */ - public function safeDown() - { - $this->driver = Craft::$app->getConfig()->getDb()->driver; - $this->removeTables(); - - return true; - } /** * Creates all necessary tables for this plugin @@ -91,7 +84,6 @@ protected function createTables() return $tablesCreated; } - protected function createIndexes() { $this->createIndex(null, '{{%bulkedit_editcontext}}', ['ownerId'], false); @@ -113,6 +105,14 @@ protected function addForeignKeys() $this->addForeignKey(null, '{{%bulkedit_history}}', ['siteId'], '{{%sites}}', ['id'], 'CASCADE', null); } + public function safeDown() + { + $this->driver = Craft::$app->getConfig()->getDb()->driver; + $this->removeTables(); + + return true; + } + /** * Remove all tables created by this plugin * diff --git a/src/migrations/m190721_201115_add_element_type.php b/src/migrations/m190721_201115_add_element_type.php index 7684578..71aff3b 100644 --- a/src/migrations/m190721_201115_add_element_type.php +++ b/src/migrations/m190721_201115_add_element_type.php @@ -2,7 +2,6 @@ namespace venveo\bulkedit\migrations; -use Craft; use craft\db\Migration; /** diff --git a/src/models/FieldWrapper.php b/src/models/FieldWrapper.php index 53e4a63..1941dd7 100644 --- a/src/models/FieldWrapper.php +++ b/src/models/FieldWrapper.php @@ -5,14 +5,15 @@ use craft\base\Model; use craft\records\User; +use yii\db\ActiveQueryInterface; /** * @property int|null ownerId * @property integer siteId * @property string elementIds - * @property \yii\db\ActiveQueryInterface $site + * @property ActiveQueryInterface $site * @property User $owner - * @property \yii\db\ActiveQueryInterface $historyItems + * @property ActiveQueryInterface $historyItems * @property string fieldIds * @property integer id */ diff --git a/src/queue/jobs/SaveBulkEditJob.php b/src/queue/jobs/SaveBulkEditJob.php index 1f9423b..70f0733 100644 --- a/src/queue/jobs/SaveBulkEditJob.php +++ b/src/queue/jobs/SaveBulkEditJob.php @@ -13,6 +13,7 @@ use Craft; use craft\base\ElementInterface; use craft\queue\BaseJob; +use Throwable; use venveo\bulkedit\Plugin; use venveo\bulkedit\records\EditContext; use venveo\bulkedit\records\History; @@ -38,7 +39,7 @@ class SaveBulkEditJob extends BaseJob /** * Saves bulk edited elements * @param null $queue - * @throws \Throwable + * @throws Throwable */ public function execute($queue = null) { @@ -47,7 +48,8 @@ public function execute($queue = null) try { $currentRow = 0; /** - * @var History $elementHistory */ + * @var History $elementHistory + */ foreach ($elementHistories->each() as $elementHistory) { $elementId = $elementHistory->elementId; /** @var ElementInterface $element */ @@ -68,7 +70,7 @@ public function execute($queue = null) } catch (\Exception $e) { Craft::error('Could not save element in bulk edit job... ' . $e->getMessage(), __METHOD__); throw new Exception('Couldn’t save element ' . $element->id . ' (' . get_class($element) . ')'); - } catch (\Throwable $e) { + } catch (Throwable $e) { throw $e; } if (($currentRow + 1) === (int)$totalSteps) { diff --git a/src/records/EditContext.php b/src/records/EditContext.php index 028af9e..b47a40e 100644 --- a/src/records/EditContext.php +++ b/src/records/EditContext.php @@ -12,9 +12,9 @@ * @property int|null ownerId * @property integer siteId * @property string elementIds - * @property \yii\db\ActiveQueryInterface $site + * @property ActiveQueryInterface $site * @property User $owner - * @property \yii\db\ActiveQueryInterface $historyItems + * @property ActiveQueryInterface $historyItems * @property string fieldIds * @property string elementType * @property integer id diff --git a/src/records/History.php b/src/records/History.php index 914ed9a..d629a75 100644 --- a/src/records/History.php +++ b/src/records/History.php @@ -12,8 +12,8 @@ /** * @property integer fieldId * @property string status - * @property \yii\db\ActiveQueryInterface $element - * @property \yii\db\ActiveQueryInterface $context + * @property ActiveQueryInterface $element + * @property ActiveQueryInterface $context * @property Field $field * @property Site $site * @property integer id diff --git a/src/services/BulkEdit.php b/src/services/BulkEdit.php index f54602c..320ea21 100644 --- a/src/services/BulkEdit.php +++ b/src/services/BulkEdit.php @@ -17,6 +17,10 @@ use craft\base\FieldInterface; use craft\events\RegisterComponentTypesEvent; use craft\records\FieldLayout; +use Exception; +use ReflectionClass; +use ReflectionException; +use Throwable; use venveo\bulkedit\base\AbstractElementTypeProcessor; use venveo\bulkedit\base\AbstractFieldProcessor; use venveo\bulkedit\elements\processors\AssetProcessor; @@ -31,6 +35,8 @@ use venveo\bulkedit\queue\jobs\SaveBulkEditJob; use venveo\bulkedit\records\EditContext; use venveo\bulkedit\records\History; +use yii\db\ActiveQuery; +use yii\db\ActiveQueryInterface; /** * @author Venveo @@ -39,15 +45,15 @@ */ class BulkEdit extends Component { - public const STRATEGY_REPLACE = 'replace'; - public const STRATEGY_MERGE = 'merge'; - public const STRATEGY_SUBTRACT = 'subtract'; - public const STRATEGY_ADD = 'add'; - public const STRATEGY_MULTIPLY = 'multiply'; - public const STRATEGY_DIVIDE = 'divide'; + const STRATEGY_REPLACE = 'replace'; + const STRATEGY_MERGE = 'merge'; + const STRATEGY_SUBTRACT = 'subtract'; + const STRATEGY_ADD = 'add'; + const STRATEGY_MULTIPLY = 'multiply'; + const STRATEGY_DIVIDE = 'divide'; - public const EVENT_REGISTER_ELEMENT_PROCESSORS = 'registerElementProcessors'; - public const EVENT_REGISTER_FIELD_PROCESSORS = 'registerFieldProcessors'; + const EVENT_REGISTER_ELEMENT_PROCESSORS = 'registerElementProcessors'; + const EVENT_REGISTER_FIELD_PROCESSORS = 'registerFieldProcessors'; // Memoized values private static $_ELEMENT_TYPE_PROCESSORS; @@ -59,21 +65,21 @@ class BulkEdit extends Component * @param $elementIds * @param $elementType * @return FieldWrapper[] fields - * @throws \ReflectionException + * @throws ReflectionException */ public function getFieldsForElementIds($elementIds, $elementType): array { // Works for entries $processor = $this->getElementTypeProcessor($elementType); if (!$processor) { - throw new \Exception('Unable to process element type'); + throw new Exception('Unable to process element type'); } $layouts = $processor::getLayoutsFromElementIds($elementIds); $fields = []; /** @var FieldLayout $layout */ foreach ($layouts as $layout) { - $layoutFields = \Craft::$app->fields->getFieldsByLayoutId($layout->id); + $layoutFields = Craft::$app->fields->getFieldsByLayoutId($layout->id); /** @var Field $layoutField */ foreach ($layoutFields as $layoutField) { if (!array_key_exists($layoutField->handle, $fields)) { @@ -89,13 +95,67 @@ public function getFieldsForElementIds($elementIds, $elementType): array return $fields; } + /** + * Retrieves the processor for a type of element. The processor determines how to do things like get the field + * layout. + * @param $elementType + * @return string processor classname + * @throws ReflectionException + */ + public function getElementTypeProcessor($elementType) + { + $processors = $this->getElementTypeProcessors(); + + $processorsKeyedByClass = []; + foreach ($processors as $processor) { + $reflection = new ReflectionClass($processor); + /** @var AbstractElementTypeProcessor $instance */ + $instance = $reflection->newInstanceWithoutConstructor(); + $type = $instance::getType(); + $processorsKeyedByClass[$type] = $processor; + } + + if (array_key_exists($elementType, $processorsKeyedByClass)) { + return $processorsKeyedByClass[$elementType]; + } + return null; + } + + /** + * Gets an array of all element type processors + * @return array + */ + public function getElementTypeProcessors(): array + { + if (self::$_ELEMENT_TYPE_PROCESSORS !== null) { + return self::$_ELEMENT_TYPE_PROCESSORS; + } + + $processors = [ + EntryProcessor::class, + UserProcessor::class, + CategoryProcessor::class, + AssetProcessor::class, + ]; + + if (Craft::$app->plugins->isPluginInstalled('commerce')) { + $processors[] = ProductProcessor::class; + } + + $event = new RegisterComponentTypesEvent(); + $event->types = &$processors; + $this->trigger(self::EVENT_REGISTER_ELEMENT_PROCESSORS, $event); + self::$_ELEMENT_TYPE_PROCESSORS = $processors; + return $processors; + } + /** * Gets all unique elements from incomplete bulk edit jobs * * @param EditContext $context - * @return \yii\db\ActiveQuery + * @return ActiveQuery */ - public function getPendingElementsHistoriesFromContext(EditContext $context): \yii\db\ActiveQuery + public function getPendingElementsHistoriesFromContext(EditContext $context): ActiveQuery { $items = History::find() ->limit(null) @@ -109,9 +169,9 @@ public function getPendingElementsHistoriesFromContext(EditContext $context): \y * Gets all pending bulk edit changes for a particular job * * @param EditContext $context - * @return \yii\db\ActiveQueryInterface + * @return ActiveQueryInterface */ - public function getPendingHistoryFromContext(EditContext $context, $elementId = null): \yii\db\ActiveQueryInterface + public function getPendingHistoryFromContext(EditContext $context, $elementId = null): ActiveQueryInterface { $query = $context->getHistoryItems()->where(['=', 'status', 'pending']); if ($elementId !== null) { @@ -126,10 +186,10 @@ public function getPendingHistoryFromContext(EditContext $context, $elementId = * @param $historyItems * @param Element $element * @return Element - * @throws \Throwable + * @throws Throwable * @throws \yii\base\Exception */ - public function processHistoryItemsForElement($historyItems, Element $element): ?Element + public function processHistoryItemsForElement($historyItems, Element $element) { // We'll process the entire element in a transaction to help avoid problems $transaction = Craft::$app->getDb()->beginTransaction(); @@ -144,7 +204,7 @@ public function processHistoryItemsForElement($historyItems, Element $element): $historyItem->originalValue = \GuzzleHttp\json_encode($originalValue); $historyItem->status = 'completed'; - $field = \Craft::$app->fields->getFieldByHandle($fieldHandle); + $field = Craft::$app->fields->getFieldByHandle($fieldHandle); $processor = $this->getFieldProcessor($field, $historyItem->strategy); $processor::processElementField($element, $field, $historyItem->strategy, $newValue); @@ -152,12 +212,12 @@ public function processHistoryItemsForElement($historyItems, Element $element): Craft::info('Saved history item', __METHOD__); } $element->setScenario(Element::SCENARIO_ESSENTIALS); - \Craft::$app->elements->saveElement($element, false); + Craft::$app->elements->saveElement($element, false); Craft::info('Saved element', __METHOD__); $transaction->commit(); return $element; - } catch (\Exception $e) { + } catch (Exception $e) { $transaction->rollBack(); Craft::error('Transaction rolled back', __METHOD__); throw $e; @@ -165,42 +225,58 @@ public function processHistoryItemsForElement($historyItems, Element $element): } /** - * Gets a general list of all field types supported by the strategies we have - * @return array + * Retrieves the processor for a type of field + * @param FieldInterface $fieldType + * @param $strategy + * @return AbstractFieldProcessor|null field processor class + * @throws ReflectionException */ - public function getSupportedFieldTypes() + public function getFieldProcessor(FieldInterface $fieldType, $strategy = null) { - $fieldProcessors = $this->getFieldProcessors(); - $supportedFields = []; - /** @var AbstractFieldProcessor $fieldProcessor */ - foreach ($fieldProcessors as $fieldProcessor) { - $fields = $fieldProcessor::getSupportedFields(); - $supportedFields = array_merge($supportedFields, $fields); + $processors = $this->getFieldProcessors(); + + foreach ($processors as $processor) { + $reflection = new ReflectionClass($processor); + /** @var AbstractFieldProcessor $instance */ + $instance = $reflection->newInstanceWithoutConstructor(); + + if ($strategy && !in_array($strategy, $instance::getSupportedStrategies(), true)) { + continue; + } + + $fields = $instance::getSupportedFields(); + foreach ($fields as $field) { + if (!$fieldType instanceof $field) { + continue; + } + + return $instance; + } + } - return $supportedFields; } /** - * - * @param FieldInterface $field + * Gets an array of all field processors * @return array */ - public function getProcessorsKeyedByStrategyForField(FieldInterface $field) + public function getFieldProcessors(): array { - $processors = $this->getFieldProcessors(); + if (self::$_FIELD_TYPE_PROCESSORS !== null) { + return self::$_FIELD_TYPE_PROCESSORS; + } + $processors = [ + PlainTextProcessor::class, + RelationFieldProcessor::class, + NumberFieldProcessor::class + ]; - $processorsByStrategy = []; + $event = new RegisterComponentTypesEvent(); + $event->types = &$processors; + $this->trigger(self::EVENT_REGISTER_FIELD_PROCESSORS, $event); - /** @var AbstractFieldProcessor $processor */ - foreach ($processors as $processor) { - if (!$processor::supportsField($field)) { - continue; - } - foreach ($processor::getSupportedStrategies() as $strategy) { - $processorsByStrategy[$strategy][] = $processor; - } - } - return $processorsByStrategy; + self::$_FIELD_TYPE_PROCESSORS = $processors; + return $processors; } public function isFieldSupported(FieldInterface $field, $strategy = null): bool @@ -215,6 +291,22 @@ public function isFieldSupported(FieldInterface $field, $strategy = null): bool return false; } + /** + * Gets a general list of all field types supported by the strategies we have + * @return array + */ + public function getSupportedFieldTypes() + { + $fieldProcessors = $this->getFieldProcessors(); + $supportedFields = []; + /** @var AbstractFieldProcessor $fieldProcessor */ + foreach ($fieldProcessors as $fieldProcessor) { + $fields = $fieldProcessor::getSupportedFields(); + $supportedFields = array_merge($supportedFields, $fields); + } + return $supportedFields; + } + /** * Gets an array of values for supported strategies on field types. This is used by the _fields template * @param FieldInterface $field @@ -253,112 +345,26 @@ public function getSupportedStrategiesForField(FieldInterface $field): array } /** - * Retrieves the processor for a type of element. The processor determines how to do things like get the field - * layout. - * @param $elementType - * @return string processor classname - * @throws \ReflectionException - */ - public function getElementTypeProcessor($elementType): ?string - { - $processors = $this->getElementTypeProcessors(); - - $processorsKeyedByClass = []; - foreach ($processors as $processor) { - $reflection = new \ReflectionClass($processor); - /** @var AbstractElementTypeProcessor $instance */ - $instance = $reflection->newInstanceWithoutConstructor(); - $type = $instance::getType(); - $processorsKeyedByClass[$type] = $processor; - } - - if (array_key_exists($elementType, $processorsKeyedByClass)) { - return $processorsKeyedByClass[$elementType]; - } - return null; - } - - /** - * Gets an array of all element type processors + * + * @param FieldInterface $field * @return array */ - public function getElementTypeProcessors(): array - { - if (self::$_ELEMENT_TYPE_PROCESSORS !== null) { - return self::$_ELEMENT_TYPE_PROCESSORS; - } - - $processors = [ - EntryProcessor::class, - UserProcessor::class, - CategoryProcessor::class, - AssetProcessor::class, - ]; - - if (Craft::$app->plugins->isPluginInstalled('commerce')) { - $processors[] = ProductProcessor::class; - } - - $event = new RegisterComponentTypesEvent(); - $event->types = &$processors; - $this->trigger(self::EVENT_REGISTER_ELEMENT_PROCESSORS, $event); - self::$_ELEMENT_TYPE_PROCESSORS = $processors; - return $processors; - } - - /** - * Retrieves the processor for a type of field - * @param FieldInterface $fieldType - * @param $strategy - * @return AbstractFieldProcessor|null field processor class - * @throws \ReflectionException - */ - public function getFieldProcessor(FieldInterface $fieldType, $strategy = null): ?AbstractFieldProcessor + public function getProcessorsKeyedByStrategyForField(FieldInterface $field) { $processors = $this->getFieldProcessors(); - foreach ($processors as $processor) { - $reflection = new \ReflectionClass($processor); - /** @var AbstractFieldProcessor $instance */ - $instance = $reflection->newInstanceWithoutConstructor(); + $processorsByStrategy = []; - if ($strategy && !in_array($strategy, $instance::getSupportedStrategies(), true)) { + /** @var AbstractFieldProcessor $processor */ + foreach ($processors as $processor) { + if (!$processor::supportsField($field)) { continue; } - - $fields = $instance::getSupportedFields(); - foreach ($fields as $field) { - if (!$fieldType instanceof $field) { - continue; - } - - return $instance; + foreach ($processor::getSupportedStrategies() as $strategy) { + $processorsByStrategy[$strategy][] = $processor; } - - } - } - - /** - * Gets an array of all field processors - * @return array - */ - public function getFieldProcessors(): array - { - if (self::$_FIELD_TYPE_PROCESSORS !== null) { - return self::$_FIELD_TYPE_PROCESSORS; } - $processors = [ - PlainTextProcessor::class, - RelationFieldProcessor::class, - NumberFieldProcessor::class - ]; - - $event = new RegisterComponentTypesEvent(); - $event->types = &$processors; - $this->trigger(self::EVENT_REGISTER_FIELD_PROCESSORS, $event); - - self::$_FIELD_TYPE_PROCESSORS = $processors; - return $processors; + return $processorsByStrategy; } /** @@ -369,20 +375,20 @@ public function getFieldProcessors(): array * @param $fieldIds * @param $keyedFieldValues * @param $fieldStrategies - * @throws \ReflectionException + * @throws ReflectionException * @throws \yii\db\Exception */ - public function saveContext($elementType, $siteId, $elementIds, $fieldIds, $keyedFieldValues, $fieldStrategies): void + public function saveContext($elementType, $siteId, $elementIds, $fieldIds, $keyedFieldValues, $fieldStrategies) { /** @var AbstractElementTypeProcessor $processor */ $processor = $this->getElementTypeProcessor($elementType); if (!$processor::hasPermission($elementIds, Craft::$app->user)) { - throw new \Exception('Missing permissions'); + throw new Exception('Missing permissions'); } $context = new EditContext(); - $context->ownerId = \Craft::$app->getUser()->getIdentity()->id; + $context->ownerId = Craft::$app->getUser()->getIdentity()->id; $context->siteId = $siteId; $context->elementType = $elementType; $context->elementIds = \GuzzleHttp\json_encode($elementIds); @@ -408,12 +414,12 @@ public function saveContext($elementType, $siteId, $elementIds, $fieldIds, $keye } $cols = ['status', 'contextId', 'elementId', 'fieldId', 'siteId', 'originalValue', 'newValue', 'strategy']; - \Craft::$app->db->createCommand()->batchInsert(History::tableName(), $cols, $rows)->execute(); + Craft::$app->db->createCommand()->batchInsert(History::tableName(), $cols, $rows)->execute(); $job = new SaveBulkEditJob([ 'context' => $context ]); - \Craft::$app->getQueue()->push($job); + Craft::$app->getQueue()->push($job); } }