Skip to content

Commit

Permalink
NEW adds 'Duplicate' action to elements
Browse files Browse the repository at this point in the history
- Duplicates element.
- Adds ' (Copy)' to the Title.
- Reorder duplicate to position after the cloned element.
  • Loading branch information
jcarter committed Jan 9, 2020
1 parent 51da698 commit de7d25f
Show file tree
Hide file tree
Showing 14 changed files with 171 additions and 3 deletions.
1 change: 1 addition & 0 deletions _config/graphql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ SilverStripe\GraphQL\Manager:
ObjectType: DNADesign\Elemental\GraphQL\Types\ObjectType
mutations:
sortBlock: DNADesign\Elemental\GraphQL\SortBlockMutationCreator
duplicateBlock: DNADesign\Elemental\GraphQL\DuplicateElementMutation
addElementToArea: DNADesign\Elemental\GraphQL\AddElementToAreaMutation
scaffolding:
types:
Expand Down
2 changes: 1 addition & 1 deletion client/dist/js/bundle.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions client/lang/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ if (typeof(ss) === 'undefined' || typeof(ss.i18n) === 'undefined') {
"ElementArchiveAction.CONFIRM_DELETE": "Are you sure you want to send this block to the archive?",
"ElementArchiveAction.CONFIRM_DELETE_AND_UNPUBLISH": "Warning: This block will be unpublished before being sent to the archive. Are you sure you want to proceed?",
"ElementArchiveAction.ARCHIVE": "Archive",
"ElementArchiveAction.DUPLICATE": "Duplicate",
"ElementHeader.NOTITLE": "Untitled {type} block",
"ElementPublishAction.SUCCESS_NOTIFICATION": "Published '{title}' successfully",
"ElementPublishAction.ERROR_NOTIFICATION": "Error publishing '{title}'",
Expand Down
3 changes: 2 additions & 1 deletion client/lang/fr.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ if (typeof(ss) === 'undefined' || typeof(ss.i18n) === 'undefined') {
ss.i18n.addDictionary('fr', {
"ElementArchiveAction.CONFIRM_DELETE": "Êtes-vous sûr de vouloir envoyer ce bloc aux archives ?",
"ElementArchiveAction.CONFIRM_DELETE_AND_UNPUBLISH": "Attention: Ce bloc sera dépublié avant d'être envoyé aux archives. Êtes-vous sûr de vouloir continuer ?",
"ElementArchiveAction.ARCHIVE": "Archive",
"ElementArchiveAction.ARCHIVE": "Archiver",
"ElementArchiveAction.DUPLICATE": "Dupliquer",
"ElementHeader.NOTITLE": "Bloc {type} sans titre",
"ElementPublishAction.SUCCESS_NOTIFICATION": "'{title}' publié avec succès",
"ElementPublishAction.ERROR_NOTIFICATION": "Erreur à la publication de '{title}'",
Expand Down
1 change: 1 addition & 0 deletions client/lang/nl.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ if (typeof(ss) === 'undefined' || typeof(ss.i18n) === 'undefined') {
"ElementArchiveAction.CONFIRM_DELETE": "Weet je zeker dat je dit blok wil archiveren?",
"ElementArchiveAction.CONFIRM_DELETE_AND_UNPUBLISH": "Waarschuwing: Dit blok wordt teruggezet naar draft voordat het gearchiveerd word. Wilt u doorgaan?",
"ElementArchiveAction.ARCHIVE": "Archiveren",
"ElementArchiveAction.DUPLICATE": "Duplicaat",
"ElementHeader.NOTITLE": "Onbenoemd {type} blok",
"ElementPublishAction.SUCCESS_NOTIFICATION": "'{title}' succesvol gepubliceerd",
"ElementPublishAction.ERROR_NOTIFICATION": "Fout bij het publiceren van '{title}'",
Expand Down
1 change: 1 addition & 0 deletions client/lang/src/cs.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"ElementArchiveAction.CONFIRM_DELETE": "Jste si jistí, že chcete blok odeslat do archivu?",
"ElementArchiveAction.CONFIRM_DELETE_AND_UNPUBLISH": "Varování: Tento blok bude před odesláním do archivu označen jako nezveřejněný. Opravdu chcete pokračovat?",
"ElementArchiveAction.ARCHIVE": "Archiv",
"ElementArchiveAction.DUPLICATE": "Duplikát",
"ElementHeader.NOTITLE": "Nepojmenovaný {type} blok",
"ElementPublishAction.SUCCESS_NOTIFICATION": "Úspěšně zveřejněno '{title}'",
"ElementPublishAction.ERROR_NOTIFICATION": "Chyba při zveřejnění '{title}'",
Expand Down
1 change: 1 addition & 0 deletions client/lang/src/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"ElementArchiveAction.CONFIRM_DELETE": "Sind Sie sicher, dass Sie diesen Block archivieren wollen?",
"ElementArchiveAction.CONFIRM_DELETE_AND_UNPUBLISH": "Warnung: Dieser Block wird von der veröffentlichten Seite entfernt, bevor er archiviert wird. Wollen Sie fortfahren?",
"ElementArchiveAction.ARCHIVE": "Archiv",
"ElementArchiveAction.DUPLICATE": "Duplikat",
"ElementHeader.NOTITLE": "{type} block ohne Titel",
"ElementPublishAction.SUCCESS_NOTIFICATION": "'{title}' erfolgreich veröffentlicht",
"ElementPublishAction.ERROR_NOTIFICATION": "Fehler beim Veröffentlichen von '{title}'",
Expand Down
1 change: 1 addition & 0 deletions client/lang/src/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"ElementArchiveAction.CONFIRM_DELETE": "Are you sure you want to send this block to the archive?",
"ElementArchiveAction.CONFIRM_DELETE_AND_UNPUBLISH": "Warning: This block will be unpublished before being sent to the archive. Are you sure you want to proceed?",
"ElementArchiveAction.ARCHIVE": "Archive",
"ElementArchiveAction.DUPLICATE": "Duplicate",
"ElementHeader.NOTITLE": "Untitled {type} block",
"ElementPublishAction.SUCCESS_NOTIFICATION": "Published '{title}' successfully",
"ElementPublishAction.ERROR_NOTIFICATION": "Error publishing '{title}'",
Expand Down
3 changes: 2 additions & 1 deletion client/lang/src/fr.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{
"ElementArchiveAction.CONFIRM_DELETE": "Êtes-vous sûr de vouloir envoyer ce bloc aux archives ?",
"ElementArchiveAction.CONFIRM_DELETE_AND_UNPUBLISH": "Attention: Ce bloc sera dépublié avant d'être envoyé aux archives. Êtes-vous sûr de vouloir continuer ?",
"ElementArchiveAction.ARCHIVE": "Archive",
"ElementArchiveAction.ARCHIVE": "Archiver",
"ElementArchiveAction.DUPLICATE": "Dupliquer",
"ElementHeader.NOTITLE": "Bloc {type} sans titre",
"ElementPublishAction.SUCCESS_NOTIFICATION": "'{title}' publié avec succès",
"ElementPublishAction.ERROR_NOTIFICATION": "Erreur à la publication de '{title}'",
Expand Down
1 change: 1 addition & 0 deletions client/lang/src/nl.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"ElementArchiveAction.CONFIRM_DELETE": "Weet je zeker dat je dit blok wil archiveren?",
"ElementArchiveAction.CONFIRM_DELETE_AND_UNPUBLISH": "Waarschuwing: Dit blok wordt teruggezet naar draft voordat het gearchiveerd word. Wilt u doorgaan?",
"ElementArchiveAction.ARCHIVE": "Archiveren",
"ElementArchiveAction.DUPLICATE": "Duplicaat",
"ElementHeader.NOTITLE": "Onbenoemd {type} blok",
"ElementPublishAction.SUCCESS_NOTIFICATION": "'{title}' succesvol gepubliceerd",
"ElementPublishAction.ERROR_NOTIFICATION": "Fout bij het publiceren van '{title}'",
Expand Down
2 changes: 2 additions & 0 deletions client/src/boot/registerTransforms.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import revertToBlockVersionMutation from 'state/history/revertToBlockVersionMuta
import readBlocksForAreaQuery from 'state/editor/readBlocksForAreaQuery';
import addElementToArea from 'state/editor/addElementMutation';
import ArchiveAction from 'components/ElementActions/ArchiveAction';
import DuplicateAction from 'components/ElementActions/DuplicateAction';
import PublishAction from 'components/ElementActions/PublishAction';
import SaveAction from 'components/ElementActions/SaveAction';
import UnpublishAction from 'components/ElementActions/UnpublishAction';
Expand Down Expand Up @@ -78,5 +79,6 @@ export default () => {
updater.component('ElementActions', PublishAction, 'ElementActionsWithPublish');
updater.component('ElementActions', UnpublishAction, 'ElementActionsWithUnpublish');
updater.component('ElementActions', ArchiveAction, 'ElementActionsWithArchive');
updater.component('ElementActions', DuplicateAction, 'ElementActionsWithDuplicate');
});
};
43 changes: 43 additions & 0 deletions client/src/components/ElementActions/DuplicateAction.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/* global window */
import React from 'react';
import { compose } from 'redux';
import AbstractAction from 'components/ElementActions/AbstractAction';
import duplicateBlockMutation from 'state/editor/duplicateBlockMutation';
import i18n from 'i18n';

/**
* Adds the elemental menu action to duplicate a block
*/
const DuplicateAction = (MenuComponent) => (props) => {
const handleClick = (event) => {
event.stopPropagation();

const { element: { ID: id }, actions: { handleDuplicateBlock } } = props;

if (handleDuplicateBlock) {
handleDuplicateBlock(id).then(() => {
const preview = window.jQuery('.cms-preview');
preview.entwine('ss.preview')._loadUrl(preview.find('iframe').attr('src'));
});
}
};

const newProps = {
title: i18n._t('ElementArchiveAction.DUPLICATE', 'Duplicate'),
className: 'element-editor__actions-duplicate',
onClick: handleClick,
toggle: props.toggle,
};

return (
<MenuComponent {...props}>
{props.children}

<AbstractAction {...newProps} />
</MenuComponent>
);
};

export { DuplicateAction as Component };

export default compose(duplicateBlockMutation, DuplicateAction);
38 changes: 38 additions & 0 deletions client/src/state/editor/duplicateBlockMutation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { graphql } from 'react-apollo';
import gql from 'graphql-tag';
import { config as readBlocksConfig, query as readBlocksQuery } from './readBlocksForAreaQuery';

// GraphQL query for duplicating a specific block
const mutation = gql`
mutation DuplicateBlock($blockId: ID!) {
duplicateBlock(ID: $blockId) {
ID
}
}
`;

const config = {
props: ({ mutate, ownProps: { actions } }) => {
const handleDuplicateBlock = (blockId) => mutate({
variables: { blockId },
});

return {
actions: {
...actions,
handleDuplicateBlock,
},
};
},
options: ({ areaId }) => ({
// Refetch versions after mutation is completed
refetchQueries: [{
query: readBlocksQuery,
variables: readBlocksConfig.options({ areaId }).variables
}]
}),
};

export { mutation, config };

export default graphql(mutation, config);
76 changes: 76 additions & 0 deletions src/GraphQL/DuplicateElementMutation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php

namespace DNADesign\Elemental\GraphQL;

use DNADesign\Elemental\Models\BaseElement;
use DNADesign\Elemental\Models\ElementalArea;
use DNADesign\Elemental\Services\ReorderElements;
use GraphQL\Type\Definition\ResolveInfo;
use GraphQL\Type\Definition\Type;
use InvalidArgumentException;
use Exception;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\GraphQL\MutationCreator;
use SilverStripe\GraphQL\OperationResolver;
use SilverStripe\GraphQL\Scaffolding\StaticSchema;

class DuplicateElementMutation extends MutationCreator implements OperationResolver
{
public function attributes()
{
return [
'name' => 'duplicateBlock',
'description' => 'Duplicate an Element in this ElementalArea'
];
}

public function type()
{
return $this->manager->getType(StaticSchema::inst()->typeNameForDataObject(BaseElement::class));
}

public function args()
{
return [
'ID' => ['type' => Type::nonNull(Type::id())],
];
}

public function resolve($object, array $args, $context, ResolveInfo $info)
{
// load element to clone
$elementID = $args['ID'];
$element = BaseElement::get_by_id($elementID);
if (!$element) {
throw new InvalidArgumentException("Invalid BaseElementID: $elementID");
}

// check can edit the elemental area
$areaID = $element->ParentID;
$area = ElementalArea::get_by_id($areaID);
if (!$area) {
throw new InvalidArgumentException("Invalid ParentID on BaseElement: $elementID");
}
if (!$area->canEdit($context['currentUser'])) {
throw new InvalidArgumentException(
"The current user has insufficient permission to edit ElementalArea: $areaID"
);
}

try {
// clone element
$clone = $element->duplicate(false);
$clone->Title .= ' (Copy)';
$clone->Sort = 0; // must be zeroed for reorder to work
$area->Elements()->add($clone);

// reorder
$reorderer = Injector::inst()->create(ReorderElements::class, $clone);
$reorderer->reorder($elementID);

return $clone;
} catch (Exception $e) {
throw new Exception("Something went wrong when duplicating element: $elementID");
}
}
}

0 comments on commit de7d25f

Please sign in to comment.