Skip to content

Commit

Permalink
Merge pull request #2732 from justcarakas/2721-page-duplicate-button
Browse files Browse the repository at this point in the history
Copy page button
  • Loading branch information
carakas committed May 21, 2019
2 parents 0a99e89 + 86bd07b commit 591d493
Show file tree
Hide file tree
Showing 10 changed files with 261 additions and 49 deletions.
4 changes: 3 additions & 1 deletion src/Backend/Core/Engine/DataGrid.php
Expand Up @@ -27,6 +27,7 @@ class DataGrid extends \SpoonDataGrid
'add' => 'fa-plus',
'copy' => 'fa-copy',
'edit' => 'fa-pencil',
'clone' => 'fa-clone',
'import' => 'fa-download',
'export' => 'fa-upload',
'delete' => 'fa-trash-o',
Expand Down Expand Up @@ -111,7 +112,7 @@ public function addColumn(
// known actions that should have a button
if (in_array(
$lowercasedName,
['add', 'edit', 'delete', 'detail', 'details', 'approve', 'mark_as_spam', 'install'],
['add', 'edit', 'delete', 'detail', 'details', 'approve', 'mark_as_spam', 'install', 'clone'],
true
)) {
// rebuild value, it should have special markup
Expand Down Expand Up @@ -154,6 +155,7 @@ public function addColumn(
'install',
'use_revision',
'use_draft',
'clone',
]
)) {
// add special attributes for actions we know
Expand Down
2 changes: 1 addition & 1 deletion src/Backend/Core/Layout/Css/screen.css

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/Backend/Core/Layout/Css/screen.css.map

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions src/Backend/Core/Layout/Sass/components/_pages.scss
Expand Up @@ -299,3 +299,7 @@ label .templateVisual {
margin-top: 1rem;
margin-left: -20px;
}

#copyPageWarning {
margin: 0 0 15px 0;
}
201 changes: 159 additions & 42 deletions src/Backend/Modules/Pages/Actions/Add.php
Expand Up @@ -13,7 +13,10 @@
use Backend\Modules\Pages\Engine\Model as BackendPagesModel;
use Backend\Modules\Search\Engine\Model as BackendSearchModel;
use Backend\Modules\Tags\Engine\Model as BackendTagsModel;
use Backend\Modules\Profiles\Engine\Model as BackendProfilesModel;
use ForkCMS\Utility\Thumbnails;
use SpoonFormHidden;
use Symfony\Component\Filesystem\Filesystem;

/**
* This is the add-action, it will display a form to create a new item
Expand All @@ -34,6 +37,13 @@ class Add extends BackendBaseActionAdd
*/
private $isGod = false;

/**
* Original image from the page we are cloning
*
* @var string|null
*/
private $originalImage;

/**
* The positions
*
Expand Down Expand Up @@ -109,30 +119,45 @@ private function loadForm(): void
// create form
$this->form = new BackendForm('add');

$originalPage = $this->getOriginalPage();

// assign in template
$this->template->assign('defaultTemplateId', $defaultTemplateId);

// create elements
$this->form->addText('title', null, null, 'form-control title', 'form-control danger title');
// assign if profiles module is installed
$this->template->assign('showAuthenticationTab', BackendModel::isModuleInstalled('Profiles'));

$this->form->addText(
'title',
isset($originalPage['title']) ? sprintf(BL::msg('CopiedTitle'), $originalPage['title']) : null,
null,
'form-control title',
'form-control danger title'
);
$this->form->addEditor('html');
$this->form->addHidden('template_id', $defaultTemplateId);
$this->form->addHidden('template_id', $originalPage['template_id'] ?? $defaultTemplateId);
$this->form->addRadiobutton(
'hidden',
[
['label' => BL::lbl('Hidden'), 'value' => 1],
['label' => BL::lbl('Published'), 'value' => 0],
],
0
$originalPage['hidden'] ?? 0
);

// image related fields
$this->form->addImage('image');
$this->form->addImage('image')->setAttribute('data-fork-cms-role', 'image-field');
if ($originalPage !== null) {
$this->form->addCheckbox('remove_image');
$this->originalImage = $originalPage['data']['image'] ?? null;
$this->template->assign('originalImage', $this->originalImage);
}

// just execute if the site is multi-language
if ($this->getContainer()->getParameter('site.multilanguage')) {
// loop active languages
foreach (BL::getActiveLanguages() as $language) {
if ($language != BL::getWorkingLanguage()) {
if ($language !== BL::getWorkingLanguage()) {
$pages = BackendPagesModel::getPagesForDropdown($language);
// add field for each language
$field = $this->form->addDropdown('hreflang_' . $language, $pages)->setDefaultElement('');
Expand All @@ -141,23 +166,62 @@ private function loadForm(): void
}
}

// page auth related fields
// check if profiles module is installed
if (BackendModel::isModuleInstalled('Profiles')) {
// add checkbox for auth_required
$this->form->addCheckbox(
'auth_required',
$originalPage !== null && isset($originalPage['data']['auth_required']) && $originalPage['data']['auth_required']
);

// add checkbox for index page to search
$this->form->addCheckbox(
'remove_from_search_index',
$originalPage !== null && isset($originalPage['data']['remove_from_search_index']) && $originalPage['data']['remove_from_search_index']
);

// get all groups and parse them in key value pair
$groupItems = BackendProfilesModel::getGroups();
if (!empty($groupItems)) {
$groups = [];
foreach ($groupItems as $key => $item) {
$groups[] = ['label' => $item, 'value' => $key];
}
// set checked values
$checkedGroups = [];
if ($originalPage !== null && isset($originalPage['data']['auth_groups'])
&& is_array($originalPage['data']['auth_groups'])) {
foreach ($originalPage['data']['auth_groups'] as $group) {
$checkedGroups[] = $group;
}
}
// add multi checkbox
$this->form->addMultiCheckbox('auth_groups', $groups, $checkedGroups);
}
}

// a god user should be able to adjust the detailed settings for a page easily
if ($this->isGod) {
// init some vars
$items = [
'move' => true,
'children' => true,
'edit' => true,
'delete' => true,
$permissions = [
'move' => ['data-role' => 'allow-move-toggle'],
'children' => ['data-role' => 'allow-children-toggle'],
'edit' => ['data-role' => 'allow-edit-toggle'],
'delete' => ['data-role' => 'allow-delete-toggle'],
];
$checked = [];
$values = [];

foreach ($items as $value => $itemIsChecked) {
$values[] = ['label' => BL::msg(\SpoonFilter::toCamelCase('allow_' . $value)), 'value' => $value];
foreach ($permissions as $permission => $attributes) {
$allowPermission = 'allow_' . $permission;
$values[] = [
'label' => BL::msg(\SpoonFilter::toCamelCase($allowPermission)),
'value' => $permission,
'attributes' => $attributes,
];

if ($itemIsChecked) {
$checked[] = $value;
if ($originalPage === null || (isset($originalPage[$allowPermission]) && $originalPage[$allowPermission])) {
$checked[] = $permission;
}
}

Expand Down Expand Up @@ -210,7 +274,7 @@ private function loadForm(): void
$html = $this->getRequest()->request->get('block_html_' . $i);

// extra-type is HTML
if ($block['extra_id'] === null || $block['extra_type'] == 'usertemplate') {
if ($block['extra_id'] === null || $block['extra_type'] === 'usertemplate') {
if ($this->getRequest()->request->get('block_extra_type_' . $i) === 'usertemplate') {
$block['extra_id'] = $this->getRequest()->request->get('block_extra_id_' . $i);
$_POST['block_extra_data_' . $i] = htmlspecialchars($_POST['block_extra_data_' . $i]);
Expand All @@ -219,17 +283,14 @@ private function loadForm(): void
$block['extra_id'] = null;
}
$block['html'] = $html;
} else {
// type of block
if (isset($this->extras[$block['extra_id']]['type']) && $this->extras[$block['extra_id']]['type'] == 'block') {
// set error
if ($hasBlock) {
$this->form->addError(BL::err('CantAdd2Blocks'));
}

// reset var
$hasBlock = true;
} elseif (isset($this->extras[$block['extra_id']]['type']) && $this->extras[$block['extra_id']]['type'] === 'block') {
// set error
if ($hasBlock) {
$this->form->addError(BL::err('CantAdd2Blocks'));
}

// reset var
$hasBlock = true;
}

// set data
Expand Down Expand Up @@ -281,6 +342,13 @@ private function loadForm(): void
}

// redirect
$redirectValue = 'none';
if ($originalPage !== null && isset($originalPage['data']['internal_redirect']['page_id'])) {
$redirectValue = 'internal';
}
if ($originalPage !== null && isset($originalPage['data']['external_redirect']['url'])) {
$redirectValue = 'external';
}
$redirectValues = [
['value' => 'none', 'label' => \SpoonFilter::ucfirst(BL::lbl('None'))],
[
Expand All @@ -294,27 +362,39 @@ private function loadForm(): void
'variables' => ['isExternal' => true],
],
];
$this->form->addRadiobutton('redirect', $redirectValues, 'none');
$this->form->addDropdown('internal_redirect', BackendPagesModel::getPagesForDropdown());
$this->form->addText('external_redirect', null, null, null, null, true);
$this->form->addRadiobutton('redirect', $redirectValues, $redirectValue);
$this->form->addDropdown(
'internal_redirect',
BackendPagesModel::getPagesForDropdown(),
($redirectValue === 'internal') ? $originalPage['data']['internal_redirect']['page_id'] : null
);
$this->form->addText(
'external_redirect',
($redirectValue === 'external') ? urldecode($originalPage['data']['external_redirect']['url']) : null,
null,
null,
null,
true
);

// page info
$this->form->addCheckbox('navigation_title_overwrite');
$this->form->addText('navigation_title');
$this->form->addCheckbox('navigation_title_overwrite', $originalPage['navigation_title_overwrite'] ?? null);
$this->form->addText('navigation_title', $originalPage['navigation_title'] ?? null);

if ($this->showTags()) {
// tags
$this->form->addText(
'tags',
null,
$originalPage === null ? null : BackendTagsModel::getTags($this->url->getModule(), $originalPage['id']),
null,
'form-control js-tags-input',
'form-control danger js-tags-input'
)->setAttribute('aria-describedby', 'tags-info');
}

// a specific action
$this->form->addCheckbox('is_action', false);
$isAction = $originalPage !== null && isset($originalPage['data']['is_action']) && $originalPage['data']['is_action'];
$this->form->addCheckbox('is_action', $isAction);

// extra
$blockTypes = BackendPagesModel::getTypes();
Expand All @@ -325,7 +405,7 @@ private function loadForm(): void

// set callback for generating an unique URL
$this->meta->setUrlCallback(
'Backend\Modules\Pages\Engine\Model',
BackendPagesModel::class,
'getUrl',
[0, $this->getRequest()->query->getInt('parent'), false]
);
Expand Down Expand Up @@ -383,18 +463,18 @@ private function validateForm(): void

// validate redirect
$redirectValue = $this->form->getField('redirect')->getValue();
if ($redirectValue == 'internal') {
if ($redirectValue === 'internal') {
$this->form->getField('internal_redirect')->isFilled(
BL::err('FieldIsRequired')
);
}
if ($redirectValue == 'external') {
if ($redirectValue === 'external') {
$this->form->getField('external_redirect')->isURL(BL::err('InvalidURL'));
}

// set callback for generating an unique URL
$this->meta->setUrlCallback(
'Backend\Modules\Pages\Engine\Model',
BackendPagesModel::class,
'getUrl',
[0, $this->getRequest()->query->getInt('parent'), $this->form->getField('is_action')->getChecked()]
);
Expand Down Expand Up @@ -544,7 +624,7 @@ private function validateForm(): void
BackendPagesModel::buildCache(BL::getWorkingLanguage());

// active
if ($page['status'] == 'active') {
if ($page['status'] === 'active') {
// init var
$text = '';

Expand Down Expand Up @@ -576,7 +656,7 @@ private function validateForm(): void
$page['title']
) . '&highlight=row-' . $page['id']
);
} elseif ($page['status'] == 'draft') {
} elseif ($page['status'] === 'draft') {
// everything is saved, so redirect to the edit action
$this->redirect(
BackendModel::createUrlForAction(
Expand All @@ -592,17 +672,37 @@ private function validateForm(): void

private function getImage(bool $allowImage): ?string
{
if (!$allowImage || !$this->form->getField('image')->isFilled()) {
if (!$allowImage
|| (!$this->form->getField('image')->isFilled() && $this->originalImage === null)
|| ($this->originalImage !== null && $this->form->getField('remove_image')->isChecked())) {
return null;
}

$imagePath = FRONTEND_FILES_PATH . '/Pages/images';
$imageFilename = $this->meta->getUrl() . '_' . time() . '.' . $this->form->getField('image')->getExtension();

if ($this->originalImage !== null && !$this->form->getField('image')->isFilled()) {
$originalImagePath = $imagePath . '/source/' . $this->originalImage;
$imageFilename = $this->getImageFilenameForExtension(pathinfo($originalImagePath, PATHINFO_EXTENSION));
$newImagePath = $imagePath . '/source/' . $imageFilename;

// make sure we have a separate image for the copy in case the original image gets removed
(new Filesystem())->copy($originalImagePath, $newImagePath);
$this->get(Thumbnails::class)->generate($imagePath, $newImagePath);

return $imageFilename;
}

$imageFilename = $this->getImageFilenameForExtension($this->form->getField('image')->getExtension());
$this->form->getField('image')->generateThumbnails($imagePath, $imageFilename);

return $imageFilename;
}

private function getImageFilenameForExtension(string $extension): string
{
return $this->meta->getUrl() . '_' . time() . '.' . $extension;
}

/**
* Check if the user has the right to see/edit tags
*
Expand All @@ -622,4 +722,21 @@ public function getValue($allowHTML = null)
}
};
}

private function getOriginalPage(): ?array
{
$id = $this->getRequest()->query->getInt('copy');

// check if the page exists
if ($id === 0 || !BackendPagesModel::exists($id)) {
return null;
}

$this->template->assign('showCopyWarning', true);

$originalPage = BackendPagesModel::get($id);
$this->blocksContent = BackendPagesModel::getBlocks($id, $originalPage['revision_id']);

return $originalPage;
}
}

0 comments on commit 591d493

Please sign in to comment.