'readCampaigns', 'POST set/$ID/publish' => 'publishCampaign', 'POST set/$ID' => 'createCampaign', 'GET set/$ID/$Name' => 'readCampaign', 'DELETE set/$ID' => 'deleteCampaign', ]; private static $url_segment = 'campaigns'; /** * Size of thumbnail width * * @config * @var int */ private static $thumbnail_width = 64; /** * Size of thumbnail height * * @config * @var int */ private static $thumbnail_height = 64; public function getClientConfig() { return array_merge(parent::getClientConfig(), [ 'forms' => [ // TODO Use schemaUrl instead 'EditForm' => [ 'schemaUrl' => $this->Link('schema/EditForm') ], 'DetailEditForm' => [ 'schemaUrl' => $this->Link('schema/DetailEditForm') ] ], 'campaignViewRoute' => $this->Link() . ':type?/:id?/:view?', 'itemListViewEndpoint' => $this->Link() . 'set/:id/show', 'publishEndpoint' => [ 'url' => $this->Link() . 'set/:id/publish', 'method' => 'post' ] ]); } public function schema($request) { // TODO Hardcoding schema until we can get GridField to generate a schema dynamically $json = <<param('ID'); if($formName == 'EditForm') { $response = $this->getResponse(); $response->addHeader('Content-Type', 'application/json'); $response->setBody($json); return $response; } else { return parent::schema($request); } } /** * REST endpoint to create a campaign. * * @param SS_HTTPRequest $request * * @return SS_HTTPResponse */ public function createCampaign(SS_HTTPRequest $request) { $response = new SS_HTTPResponse(); $response->addHeader('Content-Type', 'application/json'); $response->setBody(Convert::raw2json(['campaign' => 'create'])); // TODO Implement permission check and data creation return $response; } /** * REST endpoint to get a list of campaigns. * * @param SS_HTTPRequest $request * * @return SS_HTTPResponse */ public function readCampaigns(SS_HTTPRequest $request) { $response = new SS_HTTPResponse(); $response->addHeader('Content-Type', 'application/json'); $hal = $this->getListResource(); $response->setBody(Convert::array2json($hal)); return $response; } /** * Get list contained as a hal wrapper * * @return array */ protected function getListResource() { $items = $this->getListItems(); $count = $items->count(); $hal = [ 'count' => $count, 'total' => $count, '_links' => [ 'self' => [ 'href' => $this->Link('items') ] ], '_embedded' => ['ChangeSets' => []] ]; foreach($items as $item) { /** @var ChangeSet $item */ $resource = $this->getChangeSetResource($item); $hal['_embedded']['ChangeSets'][] = $resource; } return $hal; } /** * Build item resource from a changeset * * @param ChangeSet $changeSet * @return array */ protected function getChangeSetResource(ChangeSet $changeSet) { $hal = [ '_links' => [ 'self' => [ 'href' => $this->SetLink($changeSet->ID) ] ], 'ID' => $changeSet->ID, 'Name' => $changeSet->Name, 'Description' => $changeSet->getDescription(), 'Created' => $changeSet->Created, 'LastEdited' => $changeSet->LastEdited, 'State' => $changeSet->State, 'canEdit' => $changeSet->canEdit(), 'canPublish' => $changeSet->canPublish(), '_embedded' => ['ChangeSetItems' => []] ]; foreach($changeSet->Changes() as $changeSetItem) { if(!$changeSetItem) { continue; } /** @var ChangesetItem $changeSetItem */ $resource = $this->getChangeSetItemResource($changeSetItem); $hal['_embedded']['ChangeSetItems'][] = $resource; } return $hal; } /** * Build item resource from a changesetitem * * @param ChangeSetItem $changeSetItem * @return array */ protected function getChangeSetItemResource(ChangeSetItem $changeSetItem) { $baseClass = ClassInfo::baseDataClass($changeSetItem->ObjectClass); $baseSingleton = DataObject::singleton($baseClass); $thumbnailWidth = (int)$this->config()->thumbnail_width; $thumbnailHeight = (int)$this->config()->thumbnail_height; $hal = [ '_links' => [ 'self' => [ 'href' => $this->ItemLink($changeSetItem->ID) ] ], 'ID' => $changeSetItem->ID, 'Created' => $changeSetItem->Created, 'LastEdited' => $changeSetItem->LastEdited, 'Title' => $changeSetItem->getTitle(), 'ChangeType' => $changeSetItem->getChangeType(), 'Added' => $changeSetItem->Added, 'ObjectClass' => $changeSetItem->ObjectClass, 'ObjectID' => $changeSetItem->ObjectID, 'BaseClass' => $baseClass, 'Singular' => $baseSingleton->i18n_singular_name(), 'Plural' => $baseSingleton->i18n_plural_name(), 'Thumbnail' => $changeSetItem->ThumbnailURL($thumbnailWidth, $thumbnailHeight), ]; // Depending on whether the object was added implicitly or explicitly, set // other related objects. if($changeSetItem->Added === ChangeSetItem::IMPLICITLY) { $referencedItems = $changeSetItem->ReferencedBy(); $referencedBy = []; foreach($referencedItems as $referencedItem) { $referencedBy[] = [ 'href' => $this->SetLink($referencedItem->ID) ]; } if($referencedBy) { $hal['_links']['referenced_by'] = $referencedBy; } } return $hal; } /** * Gets viewable list of campaigns * * @return SS_List */ protected function getListItems() { return ChangeSet::get() ->filter('State', ChangeSet::STATE_OPEN) ->filterByCallback(function($item) { return ($item->canView()); }); } /** * REST endpoint to get a campaign. * * @param SS_HTTPRequest $request * * @return SS_HTTPResponse */ public function readCampaign(SS_HTTPRequest $request) { $response = new SS_HTTPResponse(); if ($request->getHeader('Accept') == 'text/json') { $response->addHeader('Content-Type', 'application/json'); if ($request->param('Name')) { $changeSet = ChangeSet::get()->byId($request->param('ID')); $response->setBody(Convert::raw2json($this->getChangeSetResource($changeSet))); } else { $response->setBody('{"message":"Resource not found"}'); } return $response; } else { return $this->index($request); } } /** * REST endpoint to delete a campaign. * * @param SS_HTTPRequest $request * * @return SS_HTTPResponse */ public function deleteCampaign(SS_HTTPRequest $request) { $id = $request->param('ID'); if (!$id || !is_numeric($id)) { return (new SS_HTTPResponse(json_encode(['status' => 'error']), 400)) ->addHeader('Content-Type', 'application/json'); } $record = ChangeSet::get()->byID($id); if(!$record) { return (new SS_HTTPResponse(json_encode(['status' => 'error']), 404)) ->addHeader('Content-Type', 'application/json'); } if(!$record->canDelete()) { return (new SS_HTTPResponse(json_encode(['status' => 'error']), 401)) ->addHeader('Content-Type', 'application/json'); } $record->delete(); return (new SS_HTTPResponse('', 204)); } /** * REST endpoint to publish a {@link ChangeSet} and all of its items. * * @param SS_HTTPRequest $request * * @return SS_HTTPResponse */ public function publishCampaign(SS_HTTPRequest $request) { // Protect against CSRF on destructive action if(!SecurityToken::inst()->checkRequest($request)) { return (new SS_HTTPResponse(json_encode(['status' => 'error']), 400)) ->addHeader('Content-Type', 'application/json'); } $id = $request->param('ID'); if(!$id || !is_numeric($id)) { return (new SS_HTTPResponse(json_encode(['status' => 'error']), 400)) ->addHeader('Content-Type', 'application/json'); } $record = ChangeSet::get()->byID($id); if(!$record) { return (new SS_HTTPResponse(json_encode(['status' => 'error']), 404)) ->addHeader('Content-Type', 'application/json'); } if(!$record->canPublish()) { return (new SS_HTTPResponse(json_encode(['status' => 'error']), 401)) ->addHeader('Content-Type', 'application/json'); } try { $record->publish(); } catch(LogicException $e) { return (new SS_HTTPResponse(json_encode(['status' => 'error', 'message' => $e->getMessage()]), 401)) ->addHeader('Content-Type', 'application/json'); } return (new SS_HTTPResponse( Convert::raw2json($this->getChangeSetResource($record)), 200 ))->addHeader('Content-Type', 'application/json'); } /** * Url handler for edit form * * @param SS_HTTPRequest $request * @return Form */ public function DetailEditForm($request) { // Get ID either from posted back value, or url parameter $id = $request->param('ID') ?: $request->postVar('ID'); return $this->getDetailEditForm($id); } /** * @todo Use GridFieldDetailForm once it can handle structured data and form schemas * * @param int $id * @return Form */ public function getDetailEditForm($id) { // Get record-specific fields $record = null; if($id) { $record = ChangeSet::get()->byId($id); } if(!$record) { $record = ChangeSet::singleton(); } $fields = $record->getCMSFields(); // Add standard fields $fields->push(HiddenField::create('ID')); $form = Form::create( $this, 'DetailEditForm', $fields, FieldList::create( FormAction::create('save', 'Save') ) ); // Configure form to respond to validation errors with form schema // if requested via react. $form->setValidationResponseCallback(function() use ($form) { return $this->getSchemaResponse($form); }); return $form; } /** * Gets user-visible url to edit a specific {@see ChangeSet} * * @param $itemID * @return string */ public function SetLink($itemID) { return Controller::join_links( $this->Link('set'), $itemID ); } /** * Gets user-visible url to edit a specific {@see ChangeSetItem} * * @param int $itemID * @return string */ public function ItemLink($itemID) { return Controller::join_links( $this->Link('item'), $itemID ); } /** * */ public function FindReferencedChanges() { } }