forked from silverstripe/silverstripe-elemental
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
00840f6
commit 906d6d5
Showing
8 changed files
with
5,147 additions
and
7 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
/* global window */ | ||
import React, { useState } from 'react'; | ||
import { loadComponent } from 'lib/Injector'; | ||
import { compose } from 'redux'; | ||
import AbstractAction from 'components/ElementActions/AbstractAction'; | ||
import moveBlockMutation from 'state/editor/moveBlockMutation'; | ||
import { getConfig } from 'state/editor/elementConfig'; | ||
import i18n from 'i18n'; | ||
|
||
/** | ||
* Adds the elemental menu action to move a block of any state | ||
*/ | ||
const MoveAction = (MenuComponent) => (props) => { | ||
const FormBuilderModal = loadComponent('FormBuilderModal'); | ||
const [modalOpen, setModalOpen] = useState(false); | ||
// const { element: { id }, isPublished, actions: { handleMoveBlock } } = props; | ||
const { element: { id } } = props; | ||
|
||
const handleClick = (event) => { | ||
event.stopPropagation(); | ||
|
||
setModalOpen(true); | ||
}; | ||
|
||
const closeModal = () => { | ||
// TODO: refetch the elemental list when the modal is closed | ||
setModalOpen(false); | ||
}; | ||
|
||
// Allow user to move to another page if they have create permissions | ||
const disabled = props.element.canCreate !== undefined && !props.element.canCreate; | ||
const label = i18n._t('ElementMoveAction.MOVE', 'Move'); | ||
const title = disabled | ||
? i18n._t('ElementMoveAction.MOVE_PERMISSION_DENY', 'Move, insufficient permissions') | ||
: label; | ||
const newProps = { | ||
label, | ||
title, | ||
disabled, | ||
className: 'element-editor__actions-move', | ||
onClick: handleClick, | ||
toggle: props.toggle, // todo: what is this? | ||
}; | ||
|
||
const modalSchemaUrl = `${getConfig().form.elementForm.moveModalSchemaUrl}/${id}`; | ||
|
||
// Todo: Render modal once per area rather than once per block | ||
return ( | ||
<MenuComponent {...props}> | ||
{props.children} | ||
<AbstractAction {...newProps} /> | ||
<FormBuilderModal | ||
title="Move block to" | ||
identifier="Elemental.MoveBlockTo" | ||
isOpen={modalOpen} | ||
onClosed={closeModal} | ||
schemaUrl={modalSchemaUrl} | ||
bodyClassName="modal__dialog" | ||
responseClassBad="modal__response modal__response--error" | ||
responseClassGood="modal__response modal__response--good" | ||
/> | ||
</MenuComponent> | ||
); | ||
}; | ||
|
||
export { MoveAction as Component }; | ||
|
||
export default compose(moveBlockMutation, MoveAction); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import { graphql } from 'react-apollo'; | ||
import gql from 'graphql-tag'; | ||
import { config as readBlocksConfig, query as readBlocksQuery } from './readBlocksForAreaQuery'; | ||
|
||
// GraphQL query for moving a specific block | ||
const mutation = gql` | ||
mutation MoveBlock($blockId:ID!) { | ||
moveBlock( | ||
id: $blockId | ||
) { | ||
id | ||
} | ||
} | ||
`; | ||
|
||
const config = { | ||
props: ({ mutate, ownProps: { actions } }) => { | ||
const handleMoveBlock = (blockId, fromPageId, toPageId) => mutate({ | ||
variables: { | ||
blockId, | ||
fromPageId, | ||
toPageId, | ||
}, | ||
}); | ||
|
||
return { | ||
actions: { | ||
...actions, | ||
handleMoveBlock, | ||
}, | ||
}; | ||
}, | ||
options: ({ areaId }) => ({ | ||
// Refetch versions after mutation is completed | ||
refetchQueries: [{ | ||
query: readBlocksQuery, | ||
variables: readBlocksConfig.options({ areaId }).variables | ||
}] | ||
}), | ||
}; | ||
|
||
export { mutation, config }; | ||
|
||
export default graphql(mutation, config); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
<?php | ||
|
||
namespace DNADesign\Elemental\Forms; | ||
|
||
use DNADesign\Elemental\Extensions\ElementalPageExtension; | ||
use SilverStripe\CMS\Model\SiteTree; | ||
use SilverStripe\Core\Injector\Injectable; | ||
use SilverStripe\Forms\FieldList; | ||
use SilverStripe\Forms\Form; | ||
use SilverStripe\Forms\FormAction; | ||
use SilverStripe\Forms\HiddenField; | ||
use SilverStripe\Forms\TreeDropdownField; | ||
use SilverStripe\ORM\ValidationException; | ||
use SilverStripe\ORM\ValidationResult; | ||
|
||
class MoveElementHandler | ||
{ | ||
use Injectable; | ||
|
||
/** | ||
* Parent controller for this form | ||
* | ||
* @var Controller | ||
*/ | ||
protected $controller; | ||
|
||
public function __construct($controller = null) | ||
{ | ||
$this->controller = $controller; | ||
} | ||
|
||
public function Form($elementID) | ||
{ | ||
$fields = FieldList::create([ | ||
HiddenField::create( | ||
'ElementID', | ||
null, | ||
$elementID | ||
), | ||
$pageField = TreeDropdownField::create( | ||
'PageID', | ||
'Select a page', | ||
SiteTree::class | ||
) | ||
]); | ||
$actions = FieldList::create([ | ||
FormAction::create('moveelement', 'Move') | ||
->addExtraClass('btn btn-primary') | ||
]); | ||
|
||
$pageField->setDisableFunction(function ($page) { | ||
return !$page->hasExtension(ElementalPageExtension::class); | ||
}); | ||
|
||
$form = Form::create( | ||
$this->controller, | ||
sprintf('MoveElementForm_%s', $elementID), | ||
$fields, | ||
$actions | ||
); | ||
|
||
// Todo: make this dynamic | ||
$form->setFormAction('admin/elemental-area/MoveElementForm/'); | ||
$form->addExtraClass('form--no-dividers'); | ||
|
||
return $form; | ||
} | ||
|
||
public function moveElement($element, $formData) | ||
{ | ||
$page = SiteTree::get()->byId($formData['PageID']); | ||
|
||
// if ElementalAreaNotFound | ||
if (!$page->ElementalArea()->exists()) { | ||
throw $this->validationResult(_t( | ||
__CLASS__ . '.ElementalAreaNotFound', | ||
'Could not find an elemental area on <strong>{PageName}</strong> to move <strong>{BlockName}</strong> to', | ||
[ | ||
'PageName' => $page->Title, | ||
'BlockName' => $element->Title | ||
] | ||
)); | ||
} | ||
|
||
if (!$page->canEdit() || !$element->canEdit()) { | ||
throw $this->validationResult(_t( | ||
__CLASS__ . '.InsufficientPermissions', | ||
'Can not move <strong>{PageName}</strong> to <strong>{BlockName}</strong> due to insufficient permissions', | ||
[ | ||
'PageName' => $page->Title, | ||
'BlockName' => $element->Title | ||
] | ||
)); | ||
} | ||
|
||
// TODO: Error handling | ||
// TODO: pages with multiple element areas | ||
// TODO: How does this work with sort? | ||
$page->ElementalArea()->Elements()->add($element->ID); | ||
|
||
$request = $this->controller->getRequest(); | ||
$message = _t( | ||
__CLASS__ . '.Success', | ||
'Successfully moved <a href="{BlockEditLink}">{BlockName}</a> to <a href="{PageEditLink}">{PageName}</a>.', | ||
[ | ||
'BlockName' => $element->Title, | ||
'BlockEditLink' => $element->MovedElementCMSLink(true, $element->ID), | ||
'PageName' => $page->Title, | ||
'PageEditLink' => $page->CMSEditLink(), | ||
] | ||
); | ||
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(); | ||
} | ||
} | ||
|
||
/** | ||
* 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); | ||
} | ||
} |
Oops, something went wrong.