diff --git a/CHANGELOG.md b/CHANGELOG.md index aa351ff..efae40c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,10 +10,14 @@ - Added `EVENT_REGISTER_ELEMENT_PROCESSORS` to allow modules/plugins an opportunity to register an element processor - Added `EVENT_REGISTER_SUPPORTED_FIELDS` to allow modules/plugins an opportunity to register a supported field - Added a progress message to queue job +- Added permissions for each element type +- Edit contexts now ensure the element types match that of the original request ### Changed - Refactored much of the modal form structure to accommodate strategies - Code cleaning & abstraction +- The queue now runs automatically after saving a job +- The queue jobs now batch elements properly ### Removed - Don't save revisions of entries anymore diff --git a/README.md b/README.md index 96fe058..f936518 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,36 @@ -# Bulk Edit plugin for Craft CMS 3.x -This plugin is built to offer extended editing functionality of a set of Craft entries. +# Bulk Edit plugin for Craft CMS 3.2 + +## Overview +The Bulk Edit plugin adds an action to supported element index pages that allows you to edit fields on a large number of +elements at once. Currently, the following element types are supported: +- Entries +- Categories +- Assets +- Users +- Craft Commerce Products + +Additionally, some fields support different strategies for the edit process. At the moment, any field that works with + "relations" (such as Entries, Categories, Assets, etc) supports the following strategies: + - Replace: Replaces all content in the field + - Merge: Merges the selected elements into the relation field + - Subtract: Removes the selected elements from the relation field + +## Instructions +1. Navigate to a supported element index page and select any number of elements +2. Click the gear at the top of the page and select "Bulk Edit" +3. Enable the light-switches next to the fields you're interested in editing and select a strategy +4. Click next and enter the content for the fields +5. Once you click Save, a task will be dispatched to the queue, at which point you will need to refresh the page for +Craft to pick it up. After the queue has finished, you may reload the page and see your changes. + +## Limitation & Issues +* Custom fields and Matrix fields are not currently supported due to issues that arise when a field is rendered without +single entry selected. +* Currently, there isn't a way to edit properties on elements that are not custom fields (for example, title, slug, +post date, etc) +* Validation is not enforced when you're editing these fields, this means you can end up with elements with fields in +potentially erroneous states (for example, removing all content on a required field) +* After the queue finishes running, make sure you refresh the page to see the updates in the element index ## Steps to use: ![Screenshot](resources/img/screenshot1.png) @@ -8,7 +39,7 @@ This plugin is built to offer extended editing functionality of a set of Craft e ## Requirements -This plugin requires Craft CMS 3.0.0 or later. +This plugin requires Craft CMS 3.2.0 or later. ## Installation @@ -24,7 +55,4 @@ To install the plugin, follow these instructions. 3. In the Control Panel, go to Settings → Plugins and click the “Install” button for Bulk Edit. -## Known Issues -* I've had to disabled custom field support as well as Matrix support for the moment. Some refactoring will need to be done to support these types of fields. - Brought to you by [Venveo](https://venveo.com) diff --git a/resources/img/screenshot1.png b/resources/img/screenshot1.png index 4e42cdc..a2f46f1 100644 Binary files a/resources/img/screenshot1.png and b/resources/img/screenshot1.png differ diff --git a/resources/img/screenshot2.png b/resources/img/screenshot2.png index a2f46f1..bcc61e9 100644 Binary files a/resources/img/screenshot2.png and b/resources/img/screenshot2.png differ diff --git a/resources/img/screenshot3.png b/resources/img/screenshot3.png index 0a07c38..71d84f2 100644 Binary files a/resources/img/screenshot3.png and b/resources/img/screenshot3.png differ diff --git a/src/Plugin.php b/src/Plugin.php index 36b112f..ac19986 100644 --- a/src/Plugin.php +++ b/src/Plugin.php @@ -18,6 +18,8 @@ use craft\elements\Entry; use craft\elements\User; use craft\events\RegisterElementActionsEvent; +use craft\events\RegisterUserPermissionsEvent; +use craft\services\UserPermissions; use venveo\bulkedit\elements\actions\BulkEditElementAction; use venveo\bulkedit\services\BulkEdit; use yii\base\Event; @@ -37,43 +39,85 @@ class Plugin extends BasePlugin */ public static $plugin; - public $schemaVersion = '1.0.2'; + 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(); self::$plugin = $this; - Event::on(Entry::class, Element::EVENT_REGISTER_ACTIONS, - function (RegisterElementActionsEvent $event) { - $event->actions[] = BulkEditElementAction::class; + 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') + ]; + $permissions[self::PERMISSION_BULKEDIT_ASSETS] = [ + 'label' => \Craft::t('venveo-bulk-edit', 'Bulk Edit Assets') + ]; + $permissions[self::PERMISSION_BULKEDIT_CATEGORIES] = [ + 'label' => \Craft::t('venveo-bulk-edit', 'Bulk Edit Categories') + ]; + $permissions[self::PERMISSION_BULKEDIT_USERS] = [ + 'label' => \Craft::t('venveo-bulk-edit', 'Bulk Edit Users') + ]; + + if (\Craft::$app->plugins->isPluginInstalled('commerce')) { + $permissions[self::PERMISSION_BULKEDIT_PRODUCTS] = [ + 'label' => \Craft::t('venveo-bulk-edit', 'Bulk Edit Products') + ]; + } + + $event->permissions[\Craft::t('venveo-bulk-edit', 'Bulk Edit')] = $permissions; + }); + + 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; + } + ); } - ); - Event::on(Category::class, Element::EVENT_REGISTER_ACTIONS, - function (RegisterElementActionsEvent $event) { - $event->actions[] = BulkEditElementAction::class; + if (\Craft::$app->user->checkPermission(self::PERMISSION_BULKEDIT_CATEGORIES)) { + Event::on(Category::class, Element::EVENT_REGISTER_ACTIONS, + function (RegisterElementActionsEvent $event) { + $event->actions[] = BulkEditElementAction::class; + } + ); } - ); - Event::on(Asset::class, Element::EVENT_REGISTER_ACTIONS, - function (RegisterElementActionsEvent $event) { - $event->actions[] = BulkEditElementAction::class; + if (\Craft::$app->user->checkPermission(self::PERMISSION_BULKEDIT_ASSETS)) { + Event::on(Asset::class, Element::EVENT_REGISTER_ACTIONS, + function (RegisterElementActionsEvent $event) { + $event->actions[] = BulkEditElementAction::class; + } + ); } - ); - Event::on(User::class, Element::EVENT_REGISTER_ACTIONS, - function (RegisterElementActionsEvent $event) { - $event->actions[] = BulkEditElementAction::class; + if (\Craft::$app->user->checkPermission(self::PERMISSION_BULKEDIT_USERS)) { + Event::on(User::class, Element::EVENT_REGISTER_ACTIONS, + function (RegisterElementActionsEvent $event) { + $event->actions[] = BulkEditElementAction::class; + } + ); } - ); - 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; + 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/dist/js/BulkEditModal.js b/src/assetbundles/bulkeditelementaction/dist/js/BulkEditModal.js index 3247ae5..66c48eb 100644 --- a/src/assetbundles/bulkeditelementaction/dist/js/BulkEditModal.js +++ b/src/assetbundles/bulkeditelementaction/dist/js/BulkEditModal.js @@ -159,6 +159,8 @@ Craft.BulkEditModal = Garnish.Modal.extend( const formValues = this.$container.find('#bulk-edit-values-modal').serializeArray(); Craft.postActionRequest('venveo-bulk-edit/bulk-edit/save-context', formValues, function(response) { this.hide(); + Craft.cp.trackJobProgress(false, true); + Craft.cp.runQueue(); }.bind(this)); }, diff --git a/src/base/ElementTypeProcessorInterface.php b/src/base/ElementTypeProcessorInterface.php index e1a6a0e..fd08b54 100644 --- a/src/base/ElementTypeProcessorInterface.php +++ b/src/base/ElementTypeProcessorInterface.php @@ -1,6 +1,8 @@ getView(); $modalHtml = $view->renderTemplate('venveo-bulk-edit/elementactions/BulkEdit/_fields', [ 'fieldWrappers' => $fields, + 'elementType' => $elementType, 'bulkedit' => $service, 'elementIds' => $elementIds, 'site' => $site @@ -90,6 +91,7 @@ public function actionGetEditScreen() $elementIds = Craft::$app->getRequest()->getRequiredParam('elementIds'); $requestId = Craft::$app->getRequest()->getRequiredParam('requestId'); + $elementType = Craft::$app->getRequest()->getRequiredParam('elementType'); $siteId = Craft::$app->getRequest()->getRequiredParam('siteId'); $fields = Craft::$app->getRequest()->getRequiredParam('fields'); @@ -144,6 +146,7 @@ public function actionGetEditScreen() $modalHtml = $view->renderTemplate('venveo-bulk-edit/elementactions/BulkEdit/_edit', [ 'fields' => $fieldModels, + 'elementType' => $elementType, 'elementIds' => $elementIds, 'fieldData' => $enabledFields, 'site' => $site @@ -171,8 +174,8 @@ public function actionSaveContext() $this->requireAcceptsJson(); $elementIds = Craft::$app->getRequest()->getRequiredParam('elementIds'); + $elementType = Craft::$app->getRequest()->getRequiredParam('elementType'); $siteId = Craft::$app->getRequest()->getRequiredParam('siteId'); -// $fieldIds = array_values(Craft::$app->getRequest()->getRequiredParam('fieldIds')); $fieldMeta = array_values(Craft::$app->getRequest()->getRequiredParam('fieldMeta')); $fieldStrategies = []; @@ -199,42 +202,14 @@ public function actionSaveContext() $elementIds = explode(',', $elementIds); - $context = new EditContext(); - $context->ownerId = \Craft::$app->getUser()->getIdentity()->id; - $context->siteId = $siteId; - $context->elementIds = \GuzzleHttp\json_encode($elementIds); - $context->fieldIds = \GuzzleHttp\json_encode($fieldIds); - $context->save(); - - $rows = []; - foreach ($elementIds as $elementId) { - foreach ($fieldIds as $fieldId) { - $strategy = $fieldStrategies[$fieldId] ?? 'replace'; - - $rows[] = [ - 'pending', - $context->id, - (int)$elementId, - (int)$fieldId, - (int)$siteId, - '[]', - \GuzzleHttp\json_encode($keyedFieldValues[$fieldId]), - $strategy - ]; - } - } - - $cols = ['status', 'contextId', 'elementId', 'fieldId', 'siteId', 'originalValue', 'newValue', 'strategy']; - \Craft::$app->db->createCommand()->batchInsert(History::tableName(), $cols, $rows)->execute(); - - - $job = new SaveBulkEditJob([ - 'context' => $context - ]); - \Craft::$app->getQueue()->push($job); + try { + Plugin::$plugin->bulkEdit->saveContext($elementType, $siteId, $elementIds, $fieldIds, $keyedFieldValues); - return $this->asJson([ - 'success' => true - ]); + return $this->asJson([ + 'success' => true + ]); + } catch (\Exception $e) { + return $this->asErrorJson('Failed to save context'); + } } } diff --git a/src/elements/processors/AssetProcessor.php b/src/elements/processors/AssetProcessor.php index fa7db8d..915138b 100644 --- a/src/elements/processors/AssetProcessor.php +++ b/src/elements/processors/AssetProcessor.php @@ -3,11 +3,11 @@ namespace venveo\bulkedit\elements\processors; use craft\elements\Asset; -use craft\elements\User; use craft\helpers\ArrayHelper; use craft\records\FieldLayout; -use craft\services\Users; +use craft\web\User; use venveo\bulkedit\base\AbstractElementTypeProcessor; +use venveo\bulkedit\Plugin; class AssetProcessor extends AbstractElementTypeProcessor { @@ -41,4 +41,15 @@ public static function getType(): string { return get_class(new Asset); } + + /** + * Return whether a given user has permission to perform bulk edit actions on these elements + * @param $elementIds + * @param $user + * @return bool + */ + public static function hasPermission($elementIds, User $user): bool + { + return $user->checkPermission(Plugin::PERMISSION_BULKEDIT_ASSETS); + } } \ No newline at end of file diff --git a/src/elements/processors/CategoryProcessor.php b/src/elements/processors/CategoryProcessor.php index 2cf9c1d..89a10c9 100644 --- a/src/elements/processors/CategoryProcessor.php +++ b/src/elements/processors/CategoryProcessor.php @@ -3,12 +3,12 @@ namespace venveo\bulkedit\elements\processors; use craft\elements\Category; -use craft\elements\User; use craft\helpers\ArrayHelper; use craft\records\CategoryGroup; use craft\records\FieldLayout; -use craft\services\Users; +use craft\web\User; use venveo\bulkedit\base\AbstractElementTypeProcessor; +use venveo\bulkedit\Plugin; class CategoryProcessor extends AbstractElementTypeProcessor { @@ -50,4 +50,15 @@ public static function getType(): string { return get_class(new Category); } + + /** + * Return whether a given user has permission to perform bulk edit actions on these elements + * @param $elementIds + * @param $user + * @return bool + */ + public static function hasPermission($elementIds, User $user): bool + { + return $user->checkPermission(Plugin::PERMISSION_BULKEDIT_CATEGORIES); + } } \ No newline at end of file diff --git a/src/elements/processors/EntryProcessor.php b/src/elements/processors/EntryProcessor.php index 695cf29..2947690 100644 --- a/src/elements/processors/EntryProcessor.php +++ b/src/elements/processors/EntryProcessor.php @@ -4,7 +4,9 @@ use craft\elements\Entry; use craft\records\FieldLayout; +use craft\web\User; use venveo\bulkedit\base\AbstractElementTypeProcessor; +use venveo\bulkedit\Plugin; class EntryProcessor extends AbstractElementTypeProcessor { @@ -36,4 +38,15 @@ public static function getType(): string { return get_class(new Entry); } + + /** + * Return whether a given user has permission to perform bulk edit actions on these elements + * @param $elementIds + * @param $user + * @return bool + */ + public static function hasPermission($elementIds, User $user): bool + { + return $user->checkPermission(Plugin::PERMISSION_BULKEDIT_ENTRIES); + } } \ No newline at end of file diff --git a/src/elements/processors/ProductProcessor.php b/src/elements/processors/ProductProcessor.php index cb23900..881a1dd 100644 --- a/src/elements/processors/ProductProcessor.php +++ b/src/elements/processors/ProductProcessor.php @@ -4,13 +4,11 @@ use craft\commerce\records\Product; use craft\commerce\records\ProductType; -use craft\elements\Category; -use craft\elements\User; use craft\helpers\ArrayHelper; -use craft\records\CategoryGroup; use craft\records\FieldLayout; -use craft\services\Users; +use craft\web\User; use venveo\bulkedit\base\AbstractElementTypeProcessor; +use venveo\bulkedit\Plugin; class ProductProcessor extends AbstractElementTypeProcessor { @@ -52,4 +50,15 @@ public static function getType(): string { return get_class(new \craft\commerce\elements\Product); } + + /** + * Return whether a given user has permission to perform bulk edit actions on these elements + * @param $elementIds + * @param $user + * @return bool + */ + public static function hasPermission($elementIds, User $user): bool + { + return $user->checkPermission(Plugin::PERMISSION_BULKEDIT_PRODUCTS); + } } \ No newline at end of file diff --git a/src/elements/processors/UserProcessor.php b/src/elements/processors/UserProcessor.php index a6f8508..980ec56 100644 --- a/src/elements/processors/UserProcessor.php +++ b/src/elements/processors/UserProcessor.php @@ -6,6 +6,7 @@ use craft\records\FieldLayout; use craft\services\Users; use venveo\bulkedit\base\AbstractElementTypeProcessor; +use venveo\bulkedit\Plugin; class UserProcessor extends AbstractElementTypeProcessor { @@ -21,8 +22,8 @@ public static function getLayoutsFromElementIds($elementIds): array $fieldLayouts = $projectConfig->get(Users::CONFIG_USERLAYOUT_KEY); $fieldLayoutsUIDs = array_keys($fieldLayouts); $layouts = FieldLayout::find() - ->where(['in', 'uid', $fieldLayoutsUIDs]) - ->all(); + ->where(['in', 'uid', $fieldLayoutsUIDs]) + ->all(); return $layouts; } @@ -34,4 +35,15 @@ public static function getType(): string { return get_class(new User); } + + /** + * Return whether a given user has permission to perform bulk edit actions on these elements + * @param $elementIds + * @param $user + * @return bool + */ + public static function hasPermission($elementIds, \craft\web\User $user): bool + { + return $user->checkPermission(Plugin::PERMISSION_BULKEDIT_USERS); + } } \ No newline at end of file diff --git a/src/migrations/Install.php b/src/migrations/Install.php index 0cda6b9..dd011c0 100644 --- a/src/migrations/Install.php +++ b/src/migrations/Install.php @@ -2,10 +2,7 @@ namespace venveo\bulkedit\migrations; -use venveo\bulkedit\Plugin; - use Craft; -use craft\config\DbConfig; use craft\db\Migration; /** @@ -59,25 +56,26 @@ protected function createTables() $this->createTable( '{{%bulkedit_editcontext}}', [ - 'id' => $this->primaryKey(), + 'id' => $this->primaryKey(), 'dateCreated' => $this->dateTime()->notNull(), 'dateUpdated' => $this->dateTime()->notNull(), - 'uid' => $this->uid(), + 'uid' => $this->uid(), 'siteId' => $this->integer()->notNull(), 'ownerId' => $this->integer()->notNull(), 'elementIds' => $this->string(1024)->notNull(), 'fieldIds' => $this->string(1024)->notNull(), + 'elementType' => $this->string()->notNull(), ] ); $this->createTable( '{{%bulkedit_history}}', [ - 'id' => $this->primaryKey(), + 'id' => $this->primaryKey(), 'dateCreated' => $this->dateTime()->notNull(), 'dateUpdated' => $this->dateTime()->notNull(), 'status' => $this->string()->notNull()->defaultValue('pending'), - 'uid' => $this->uid(), + 'uid' => $this->uid(), 'contextId' => $this->integer()->notNull(), 'elementId' => $this->integer()->notNull(), diff --git a/src/migrations/m181117_192854_increase_data_column_size.php b/src/migrations/m181117_192854_increase_data_column_size.php index cd122c8..422621f 100644 --- a/src/migrations/m181117_192854_increase_data_column_size.php +++ b/src/migrations/m181117_192854_increase_data_column_size.php @@ -2,7 +2,6 @@ namespace venveo\bulkedit\migrations; -use Craft; use craft\db\Migration; /** diff --git a/src/migrations/m181213_193509_move_strategy_field.php b/src/migrations/m181213_193509_move_strategy_field.php index bdbdc30..b81872d 100644 --- a/src/migrations/m181213_193509_move_strategy_field.php +++ b/src/migrations/m181213_193509_move_strategy_field.php @@ -2,7 +2,6 @@ namespace venveo\bulkedit\migrations; -use Craft; use craft\db\Migration; /** @@ -24,7 +23,7 @@ public function safeUp() */ public function safeDown() { - echo "m181213_193509_move_strategy_field cannot be reverted.\n"; - return false; + $this->dropColumn('{{%bulkedit_history}}', 'strategy'); + $this->addColumn('{{%bulkedit_editcontext}}', 'strategy', $this->string(16)->notNull()->defaultValue('replace')); } } diff --git a/src/migrations/m190721_201115_add_element_type.php b/src/migrations/m190721_201115_add_element_type.php new file mode 100644 index 0000000..7684578 --- /dev/null +++ b/src/migrations/m190721_201115_add_element_type.php @@ -0,0 +1,28 @@ +addColumn('{{%bulkedit_editcontext}}', 'elementType', $this->string()->notNull()); + } + + /** + * @inheritdoc + */ + public function safeDown() + { + $this->dropColumn('{{%bulkedit_editcontext}}', 'elementType'); + } +} diff --git a/src/queue/jobs/SaveBulkEditJob.php b/src/queue/jobs/SaveBulkEditJob.php index 790f13c..cd6b15e 100644 --- a/src/queue/jobs/SaveBulkEditJob.php +++ b/src/queue/jobs/SaveBulkEditJob.php @@ -11,9 +11,11 @@ namespace venveo\bulkedit\queue\jobs; use Craft; +use craft\base\ElementInterface; use craft\queue\BaseJob; use venveo\bulkedit\Plugin; use venveo\bulkedit\records\EditContext; +use venveo\bulkedit\records\History; use yii\base\Exception; /** @@ -40,36 +42,47 @@ class SaveBulkEditJob extends BaseJob */ public function execute($queue = null) { - $elementIds = Plugin::$plugin->bulkEdit->getPendingElementIdsFromContext($this->context); - $totalSteps = count($elementIds); + $elementHistories = Plugin::$plugin->bulkEdit->getPendingElementsHistoriesFromContext($this->context); + $totalSteps = $elementHistories->count(); try { - foreach ($elementIds as $key => $elementId) { + $currentRow = 0; + /** + * @var History $elementHistory */ + foreach ($elementHistories->each() as $elementHistory) { + $elementId = $elementHistory->elementId; + /** @var ElementInterface $element */ $element = Craft::$app->getElements()->getElementById($elementId, null, $this->context->siteId); if (!$element) { + Craft::warning('Could not locate an element in a bulk save job: ' . $elementId, __METHOD__); continue; } + + if (get_class($element) !== $this->context->elementType) { + throw new \Exception('Unexpected element type encountered!'); + } + $history = Plugin::$plugin->bulkEdit->getPendingHistoryForElement($this->context, $element->id)->all(); try { Craft::info('Starting processing bulk edit job', __METHOD__); Plugin::$plugin->bulkEdit->processHistoryItemsForElement($history, $element); } catch (\Exception $e) { - Craft::error('Could not save element in bulk edit job... '. $e->getMessage(), __METHOD__); + 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) { throw $e; } - - if (($key + 1) === $totalSteps) { + if (($currentRow + 1) === (int)$totalSteps) { try { $this->context->delete(); } catch (\Exception $e) { throw new Exception('Couldn’t delete context: ' . $e->getMessage()); } } - $this->setProgress($queue, ($key + 1) / $totalSteps, 'Element '. ($key + 1) . ' of '. $totalSteps); + $this->setProgress($queue, ($currentRow + 1) / $totalSteps, 'Element ' . ($currentRow + 1) . ' of ' . $totalSteps); + $currentRow++; } } catch (\Exception $e) { - Craft::error('Failed to save... '. $e->getMessage(), __METHOD__); + Craft::error('Failed to save... ' . $e->getMessage(), __METHOD__); throw $e; } } diff --git a/src/records/EditContext.php b/src/records/EditContext.php index cc3a8cc..028af9e 100644 --- a/src/records/EditContext.php +++ b/src/records/EditContext.php @@ -16,13 +16,11 @@ * @property User $owner * @property \yii\db\ActiveQueryInterface $historyItems * @property string fieldIds + * @property string elementType * @property integer id */ class EditContext extends ActiveRecord { - /* - * @inheritdoc - */ public static function tableName() { return '{{%bulkedit_editcontext}}'; diff --git a/src/records/History.php b/src/records/History.php index e6f9093..914ed9a 100644 --- a/src/records/History.php +++ b/src/records/History.php @@ -33,8 +33,6 @@ public static function tableName() } - - /** * @return ActiveQueryInterface The relational query object. */ diff --git a/src/services/BulkEdit.php b/src/services/BulkEdit.php index f4a9949..d694478 100644 --- a/src/services/BulkEdit.php +++ b/src/services/BulkEdit.php @@ -37,6 +37,7 @@ use venveo\bulkedit\elements\processors\ProductProcessor; use venveo\bulkedit\elements\processors\UserProcessor; use venveo\bulkedit\models\FieldWrapper; +use venveo\bulkedit\queue\jobs\SaveBulkEditJob; use venveo\bulkedit\records\EditContext; use venveo\bulkedit\records\History; @@ -58,8 +59,9 @@ class BulkEdit extends Component * Get all distinct field layouts from a set of elements * * @param $elementIds + * @param $elementType * @return FieldWrapper[] fields - * @throws \Exception + * @throws \ReflectionException */ public function getFieldsForElementIds($elementIds, $elementType) { @@ -103,14 +105,14 @@ public function getBulkEditContextFromId($id): ?EditContext * Gets all unique elements from incomplete bulk edit tasks * * @param EditContext $context - * @return int[] pending element IDs + * @return \yii\db\ActiveQuery */ - public function getPendingElementIdsFromContext(EditContext $context): array + public function getPendingElementsHistoriesFromContext(EditContext $context): \yii\db\ActiveQuery { - $items = array_keys(History::find() + $items = History::find() ->limit(null) ->where(['=', 'contextId', $context->id]) - ->andWhere(['=', 'status', 'pending'])->indexBy('elementId')->all()); + ->andWhere(['=', 'status', 'pending'])->indexBy('elementId'); return $items; } @@ -294,4 +296,59 @@ public function getElementTypeProcessor($elementType) } return null; } + + /** + * Saves an element context + * @param $elementType + * @param $siteId + * @param $elementIds + * @param $fieldIds + * @param $keyedFieldValues + * @throws \yii\db\Exception + * @throws \ReflectionException + */ + public function saveContext($elementType, $siteId, $elementIds, $fieldIds, $keyedFieldValues) + { + /** @var AbstractElementTypeProcessor $processor */ + $processor = $this->getElementTypeProcessor($elementType); + + if (!$processor::hasPermission($elementIds, Craft::$app->user)) { + throw new \Exception('Missing permissions'); + } + + $context = new EditContext(); + $context->ownerId = \Craft::$app->getUser()->getIdentity()->id; + $context->siteId = $siteId; + $context->elementType = $elementType; + $context->elementIds = \GuzzleHttp\json_encode($elementIds); + $context->fieldIds = \GuzzleHttp\json_encode($fieldIds); + $context->save(); + + $rows = []; + foreach ($elementIds as $elementId) { + foreach ($fieldIds as $fieldId) { + $strategy = $fieldStrategies[$fieldId] ?? 'replace'; + + $rows[] = [ + 'pending', + $context->id, + (int)$elementId, + (int)$fieldId, + (int)$siteId, + '[]', + \GuzzleHttp\json_encode($keyedFieldValues[$fieldId]), + $strategy + ]; + } + } + + $cols = ['status', 'contextId', 'elementId', 'fieldId', 'siteId', 'originalValue', 'newValue', 'strategy']; + \Craft::$app->db->createCommand()->batchInsert(History::tableName(), $cols, $rows)->execute(); + + + $job = new SaveBulkEditJob([ + 'context' => $context + ]); + \Craft::$app->getQueue()->push($job); + } } diff --git a/src/templates/elementactions/BulkEdit/_edit.twig b/src/templates/elementactions/BulkEdit/_edit.twig index 6130df3..9e1d374 100644 --- a/src/templates/elementactions/BulkEdit/_edit.twig +++ b/src/templates/elementactions/BulkEdit/_edit.twig @@ -3,6 +3,7 @@