Source of file AddToCampaignHandler.php
Size: 16,511 Bytes - Last Modified: 2021-12-23T10:28:10+00:00
/var/www/docs.ssmods.com/process/src/src/AddToCampaignHandler.php
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481 | <?php namespace SilverStripe\CampaignAdmin; use SilverStripe\Control\Controller; use SilverStripe\Control\Director; use SilverStripe\Control\HTTPResponse_Exception; use SilverStripe\Control\HTTPResponse; use SilverStripe\Core\ClassInfo; use SilverStripe\Core\Injector\Injectable; use SilverStripe\Dev\Deprecation; use SilverStripe\Forms\CheckboxField; use SilverStripe\Forms\DropdownField; use SilverStripe\Forms\HiddenField; use SilverStripe\Forms\LiteralField; use SilverStripe\Forms\FieldList; use SilverStripe\Forms\Form; use SilverStripe\Forms\TextField; use SilverStripe\ORM\ArrayList; use SilverStripe\ORM\DataObject; use SilverStripe\ORM\FieldType\DBHTMLText; use SilverStripe\ORM\ValidationException; use SilverStripe\ORM\ValidationResult; use SilverStripe\Versioned\ChangeSet; use SilverStripe\Versioned\ChangeSetItem; use SilverStripe\Versioned\Versioned; use SilverStripe\Core\Convert; /** * Class AddToCampaignHandler - handle the AddToCampaign action. * * This is a class designed to be delegated to by a Form action handler method in the EditForm of a LeftAndMain * child class. * * Add To Campaign can be seen as an item action like "publish" or "rollback", but unlike those actions * it needs one additional piece of information to execute, the ChangeSet ID. * * So this handler does one of two things to respond to the action request, depending on whether the ChangeSet ID * was included in the submitted data * - If it was, perform the Add To Campaign action (as per any other action) * - If it wasn't, return a form to get the ChangeSet ID and then repeat this action submission * * To use, you'd add an action to your LeftAndMain subclass, like this: * * function addtocampaign($data, $form) { * $handler = AddToCampaignHandler::create($form, $data); * return $handler->handle(); * } * * and add an AddToCampaignHandler_FormAction to the EditForm, possibly through getCMSActions */ class AddToCampaignHandler { use Injectable; /** * Parent controller for this form * * @var Controller */ protected $controller; /** * The submitted form data * * @var array */ protected $data; /** * Form name to use * * @var string */ protected $name; /** * AddToCampaignHandler constructor. * * @param Controller $controller Controller for this form * @param array|DataObject $data The data submitted as part of that form * @param string $name Form name */ public function __construct($controller = null, $data = [], $name = 'AddToCampaignForm') { $this->controller = $controller; if ($data instanceof DataObject) { $data = $data->toMap(); } $this->data = $data; $this->name = $name; } /** * Perform the action. Either returns a Form or performs the action, as per the class doc * * @return DBHTMLText|HTTPResponse */ public function handle() { Deprecation::notice('5.0', 'handle() will be removed. Use addToCampaign or Form directly'); $object = $this->getObject($this->data['ID'], $this->data['ClassName']); if (empty($this->data['Campaign'])) { return $this->Form($object)->forTemplate(); } else { return $this->addToCampaign($object, $this->data['Campaign']); } } /** * Get what ChangeSets are available for an item to be added to by this user * * @return ArrayList|ChangeSet[] */ protected function getAvailableChangeSets() { return ChangeSet::get() ->filter([ 'State' => ChangeSet::STATE_OPEN, 'IsInferred' => 0 ]) ->filterByCallback(function ($item) { /** @var ChangeSet $item */ return $item->canView(); }); } /** * Get changesets that a given object is already in * * @param DataObject * @return ArrayList[ChangeSet] */ protected function getInChangeSets($object) { $inChangeSetIDs = array_unique(ChangeSetItem::get_for_object($object)->column('ChangeSetID')); if ($inChangeSetIDs > 0) { $changeSets = $this->getAvailableChangeSets()->filter([ 'ID' => $inChangeSetIDs, 'State' => ChangeSet::STATE_OPEN ]); } else { $changeSets = new ArrayList(); } return $changeSets; } /** * Safely get a DataObject from a client-supplied ID and ClassName, checking: argument * validity; existence; and canView permissions. * * @param int $id The ID of the DataObject * @param string $class The Class of the DataObject * @return DataObject The referenced DataObject * @throws HTTPResponse_Exception */ protected function getObject($id, $class) { $id = (int)$id; $class = ClassInfo::class_name($class); if (!$class || !is_subclass_of($class, DataObject::class) || !DataObject::has_extension($class, Versioned::class) ) { $this->controller->httpError(400, _t( __CLASS__ . '.ErrorGeneral', 'We apologise, but there was an error' )); return null; } $object = DataObject::get($class)->byID($id); if (!$object) { $this->controller->httpError(404, _t( __CLASS__ . '.ErrorNotFound', 'That {Type} couldn\'t be found', '', ['Type' => $class] )); return null; } if (!$object->canView()) { $this->controller->httpError(403, _t( __CLASS__ . '.ErrorItemPermissionDenied', 'It seems you don\'t have the necessary permissions to add {ObjectTitle} to a campaign', '', ['ObjectTitle' => $object->Title] )); return null; } return $object; } /** * Builds a Form that mirrors the parent editForm, but with an extra field to collect the ChangeSet ID * * @param DataObject $object The object we're going to be adding to whichever ChangeSet is chosen * @return Form */ public function Form($object) { $inChangeSets = $this->getInChangeSets($object); $inChangeSetIDs = $inChangeSets->column('ID'); // Get changesets that can be added to $candidateChangeSets = $this->getAvailableChangeSets(); if ($inChangeSetIDs) { $candidateChangeSets = $candidateChangeSets->exclude('ID', $inChangeSetIDs); } $canCreate = ChangeSet::singleton()->canCreate(); $message = $this->getFormAlert($inChangeSets, $candidateChangeSets, $canCreate); $fields = new FieldList(array_filter([ $message ? LiteralField::create("AlertMessages", $message) : null, HiddenField::create('ID', null, $object->ID), HiddenField::create('ClassName', null, $object->baseClass()) ])); // Add fields based on available options $showSelect = $candidateChangeSets->count() > 0; if ($showSelect) { $campaignDropdown = DropdownField::create( 'Campaign', _t(__CLASS__ . '.AddToCampaignAvailableLabel', 'Available campaigns'), $candidateChangeSets ) ->setEmptyString(_t(__CLASS__ . '.AddToCampaignFormFieldLabel', 'Select a Campaign')) ->addExtraClass('noborder') ->addExtraClass('no-chosen'); $fields->push($campaignDropdown); // Show visibilty toggle of other create field if ($canCreate) { $addCampaignSelect = CheckboxField::create('AddNewSelect', _t( __CLASS__ . '.ADD_TO_A_NEW_CAMPAIGN', 'Add to a new campaign' )) ->setAttribute('data-shows', 'NewTitle') ->setSchemaData(['data' => ['shows' => 'NewTitle']]); $fields->push($addCampaignSelect); } } if ($canCreate) { $placeholder = _t(__CLASS__ . '.CREATE_NEW_PLACEHOLDER', 'Enter campaign name'); $createBox = TextField::create( 'NewTitle', _t(__CLASS__ . '.CREATE_NEW', 'Create a new campaign') ) ->setAttribute('placeholder', $placeholder) ->setSchemaData(['attributes' => ['placeholder' => $placeholder]]); $fields->push($createBox); } $actions = FieldList::create(); if ($canCreate || $showSelect) { $actions->push( AddToCampaignHandler_FormAction::create() ->setTitle(_t(__CLASS__ . '.AddToCampaignAddAction', 'Add')) ->addExtraClass('add-to-campaign__action') ); } $form = Form::create( $this->controller, $this->name, $fields, $actions, AddToCampaignValidator::create() ); $form->setHTMLID('Form_EditForm_AddToCampaign'); $form->addExtraClass('form--no-dividers add-to-campaign__form'); return $form; } /** * Performs the actual action of adding the object to the ChangeSet, once the ChangeSet ID is known * * @param DataObject $object The object to add to the ChangeSet * @param array|int $data Post data for this campaign form, or the ID of the campaign to add to * @return HTTPResponse * @throws ValidationException */ public function addToCampaign($object, $data) { // Extract $campaignID from $data $campaignID = $this->getOrCreateCampaign($data); /** @var ChangeSet $changeSet */ $changeSet = ChangeSet::get()->byID($campaignID); if (!$changeSet) { throw new ValidationException(_t( __CLASS__ . '.ErrorNotFound', 'That {Type} couldn\'t be found', ['Type' => 'Campaign'] )); } if (!$changeSet->canEdit()) { throw new ValidationException(_t( __CLASS__ . '.ErrorCampaignPermissionDenied', 'It seems you don\'t have the necessary permissions to add {ObjectTitle} to {CampaignTitle}', [ 'ObjectTitle' => $object->Title, 'CampaignTitle' => $changeSet->Title ] )); } $changeSet->addObject($object); $request = $this->controller->getRequest(); $message = _t( __CLASS__ . '.Success', 'Successfully added <strong>{ObjectTitle}</strong> to <strong>{CampaignTitle}</strong>', [ 'ObjectTitle' => Convert::raw2xml($object->Title), 'CampaignTitle' => Convert::raw2xml($changeSet->Title) ] ); if ($request->getHeader('X-Formschema-Request')) { return $message; } elseif (Director::is_ajax()) { $response = new HTTPResponse($message, 200); $response->addHeader('Content-Type', 'text/html; charset=utf-8'); return $response; } else { return $this->controller->redirectBack(); } } /** * Get descriptive alert to display at the top of the form * * @param ArrayList $inChangeSets List of changesets this item exists in * @param ArrayList $candidateChangeSets List of changesets this item could be added to * @param bool $canCreate * @return string */ protected function getFormAlert($inChangeSets, $candidateChangeSets, $canCreate) { // In a subset of changesets if ($inChangeSets->count() > 0 && $candidateChangeSets->count() > 0) { return sprintf( '<div class="alert alert-info"><strong>%s</strong><br/>%s</div>', _t( __CLASS__ . '.AddToCampaignInChangsetLabel', 'Heads up, this item is already in campaign(s):' ), Convert::raw2xml(implode(', ', $inChangeSets->column('Name'))) ); } // In all changesets if ($inChangeSets->count() > 0) { return sprintf( '<div class="alert alert-info"><strong>%s</strong><br/>%s</div>', _t( __CLASS__ . '.AddToCampaignInChangsetLabelAll', 'Heads up, this item is already in ALL campaign(s):' ), Convert::raw2xml(implode(', ', $inChangeSets->column('Name'))) ); } // Create only if ($candidateChangeSets->count() === 0 && $canCreate) { return sprintf( '<div class="alert alert-info">%s</div>', _t( __CLASS__ . '.NO_CAMPAIGNS', "You currently don't have any campaigns. " . "You can edit campaign details later in the Campaigns section." ) ); } // Can't select or create if ($candidateChangeSets->count() === 0 && !$canCreate) { return sprintf( '<div class="alert alert-warning">%s</div>', _t( __CLASS__ . '.NO_CREATE', "Oh no! You currently don't have any campaigns created. " . "Your current login does not have privileges to create campaigns. " . "Campaigns can only be created by users with Campaigns section rights." ) ); } return null; } /** * Find or build campaign from posted data * * @param array|int $data * @return int * @throws ValidationException */ protected function getOrCreateCampaign($data) { // Create new campaign if selected if (is_array($data) && !empty($data['AddNewSelect']) // Explicitly click "Add to a new campaign" || (is_array($data) && !isset($data['Campaign']) && isset($data['NewTitle'])) // This is the only option ) { // Permission if (!ChangeSet::singleton()->canCreate()) { throw $this->validationResult( _t(__CLASS__ . '.CREATE_DENIED', 'You do not have permission to create campaigns') ); } // Check title is valid $title = $data['NewTitle']; if (empty($title)) { throw $this->validationResult( _t(__CLASS__ . '.MISSING_TITLE', 'Campaign name is required'), 'NewTitle' ); } // Prevent duplicates $hasExistingName = Changeset::get() ->filter('Name:nocase', $title) ->count() > 0; if ($hasExistingName) { throw $this->validationResult( _t( 'SilverStripe\\CampaignAdmin\\CampaignAdmin.ERROR_DUPLICATE_NAME', 'Name "{Name}" already exists', ['Name' => $title] ), 'NewTitle' ); } // Create and return $campaign = ChangeSet::create(); $campaign->Name = $title; $campaign->write(); return $campaign->ID; } // Get selected campaign ID $campaignID = null; if (is_array($data) && !empty($data['Campaign'])) { $campaignID = $data['Campaign']; } elseif (is_numeric($data)) { $campaignID = (int)$data; } if (empty($campaignID)) { throw $this->validationResult(_t(__CLASS__ . '.NONE_SELECTED', 'No campaign selected')); } return $campaignID; } /** * Raise validation error * * @param string $message * @param string $field * @return ValidationException */ protected function validationResult($message, $field = null) { $error = ValidationResult::create() ->addFieldError($field, $message); return new ValidationException($error); } } |