mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
40c98c6047
refactored breadcrumbs to use bootstrap fix border lines on table Layout variables updated for spacing, added toolbar heights Added as part of campaigns as this area needs to use these variables. Added action toolbar for content, swapped values to variables toolbar action spacing Adjustments of breadcrumb variable Move accordion styles to its own component Campaign items, styles for linked items and state badges Small update to class name Class name updates, convert values into variables
519 lines
12 KiB
PHP
519 lines
12 KiB
PHP
<?php
|
|
|
|
/**
|
|
* Campaign section of the CMS
|
|
*
|
|
* @package framework
|
|
* @subpackage admin
|
|
*/
|
|
class CampaignAdmin extends LeftAndMain implements PermissionProvider {
|
|
|
|
private static $allowed_actions = [
|
|
'set',
|
|
'sets',
|
|
'schema',
|
|
'DetailEditForm',
|
|
'readCampaigns',
|
|
'createCampaign',
|
|
'readCampaign',
|
|
'updateCampaign',
|
|
'deleteCampaign',
|
|
'publishCampaign',
|
|
];
|
|
|
|
private static $menu_priority = 11;
|
|
|
|
private static $menu_title = 'Campaigns';
|
|
|
|
private static $url_handlers = [
|
|
'GET sets' => 'readCampaigns',
|
|
'POST set/$ID/publish' => 'publishCampaign',
|
|
'POST set/$ID' => 'createCampaign',
|
|
'GET set/$ID/$Name' => 'readCampaign',
|
|
'PUT set/$ID' => 'updateCampaign',
|
|
'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() {
|
|
$urlSegment = Config::inst()->get($this->class, 'url_segment');
|
|
|
|
return array_merge(parent::getClientConfig(), [
|
|
'forms' => [
|
|
// TODO Use schemaUrl instead
|
|
'editForm' => [
|
|
'schemaUrl' => $this->Link('schema/EditForm')
|
|
]
|
|
],
|
|
'campaignViewRoute' => $urlSegment . '/: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 = <<<JSON
|
|
{
|
|
"id": "EditForm",
|
|
"schema": {
|
|
"name": "EditForm",
|
|
"id": "EditForm",
|
|
"action": "schema",
|
|
"method": "GET",
|
|
"schema_url": "admin\/campaigns\/schema\/EditForm",
|
|
"attributes": {
|
|
"id": "Form_EditForm",
|
|
"action": "admin\/campaigns\/EditForm",
|
|
"method": "POST",
|
|
"enctype": "multipart\/form-data",
|
|
"target": null
|
|
},
|
|
"data": [],
|
|
"fields": [{
|
|
"name": "ID",
|
|
"id": "Form_EditForm_ID",
|
|
"type": "Hidden",
|
|
"component": null,
|
|
"holder_id": null,
|
|
"title": false,
|
|
"source": null,
|
|
"extraClass": "hidden nolabel",
|
|
"description": null,
|
|
"rightTitle": null,
|
|
"leftTitle": null,
|
|
"readOnly": false,
|
|
"disabled": false,
|
|
"customValidationMessage": "",
|
|
"attributes": [],
|
|
"data": []
|
|
}, {
|
|
"name": "ChangeSets",
|
|
"id": "Form_EditForm_ChangeSets",
|
|
"type": "Custom",
|
|
"component": "GridField",
|
|
"holder_id": null,
|
|
"title": "Campaigns",
|
|
"source": null,
|
|
"extraClass": null,
|
|
"description": null,
|
|
"rightTitle": null,
|
|
"leftTitle": null,
|
|
"readOnly": false,
|
|
"disabled": false,
|
|
"customValidationMessage": "",
|
|
"attributes": [],
|
|
"data": {
|
|
"recordType": "ChangeSet",
|
|
"collectionReadEndpoint": {
|
|
"url": "admin\/campaigns\/sets",
|
|
"method": "GET"
|
|
},
|
|
"itemReadEndpoint": {
|
|
"url": "admin\/campaigns\/set\/:id",
|
|
"method": "GET"
|
|
},
|
|
"itemUpdateEndpoint": {
|
|
"url": "admin\/campaigns\/set\/:id",
|
|
"method": "PUT"
|
|
},
|
|
"itemCreateEndpoint": {
|
|
"url": "admin\/campaigns\/set\/:id",
|
|
"method": "POST"
|
|
},
|
|
"itemDeleteEndpoint": {
|
|
"url": "admin\/campaigns\/set\/:id",
|
|
"method": "DELETE"
|
|
},
|
|
"editFormSchemaEndpoint": "admin\/campaigns\/schema\/DetailEditForm",
|
|
"columns": [
|
|
{"name": "Title", "field": "Name"},
|
|
{"name": "Changes", "field": "_embedded.ChangeSetItems.length"},
|
|
{"name": "Description", "field": "Description"}
|
|
]
|
|
}
|
|
}, {
|
|
"name": "SecurityID",
|
|
"id": "Form_EditForm_SecurityID",
|
|
"type": "Hidden",
|
|
"component": null,
|
|
"holder_id": null,
|
|
"title": "Security ID",
|
|
"source": null,
|
|
"extraClass": "hidden",
|
|
"description": null,
|
|
"rightTitle": null,
|
|
"leftTitle": null,
|
|
"readOnly": false,
|
|
"disabled": false,
|
|
"customValidationMessage": "",
|
|
"attributes": [],
|
|
"data": []
|
|
}],
|
|
"actions": []
|
|
}
|
|
}
|
|
JSON;
|
|
|
|
$formName = $request->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');
|
|
$changeSet = ChangeSet::get()->byId($request->param('ID'));
|
|
|
|
switch ($request->param('Name')) {
|
|
case "edit":
|
|
$response->setBody('{"message":"show the edit view"}');
|
|
break;
|
|
case "show":
|
|
$response->setBody(Convert::raw2json($this->getChangeSetResource($changeSet)));
|
|
break;
|
|
default:
|
|
$response->setBody('{"message":"404"}');
|
|
}
|
|
|
|
return $response;
|
|
|
|
} else {
|
|
return $this->index($request);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* REST endpoint to update a campaign.
|
|
*
|
|
* @param SS_HTTPRequest $request
|
|
*
|
|
* @return SS_HTTPResponse
|
|
*/
|
|
public function updateCampaign(SS_HTTPRequest $request) {
|
|
$response = new SS_HTTPResponse();
|
|
$response->addHeader('Content-Type', 'application/json');
|
|
$response->setBody(Convert::raw2json(['campaign' => 'update']));
|
|
|
|
// TODO Implement data update and permission checks
|
|
|
|
return $response;
|
|
}
|
|
|
|
/**
|
|
* 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');
|
|
}
|
|
|
|
/**
|
|
* @todo Use GridFieldDetailForm once it can handle structured data and form schemas
|
|
*
|
|
* @return Form
|
|
*/
|
|
public function getDetailEditForm() {
|
|
return Form::create(
|
|
$this,
|
|
'DetailEditForm',
|
|
ChangeSet::singleton()->getCMSFields(),
|
|
FieldList::create(
|
|
FormAction::create('save', 'Save')
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 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() {
|
|
|
|
}
|
|
|
|
}
|