first commit
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
const { flow } = window.lodash;
|
||||
|
||||
/**
|
||||
* WordPress dependencies
|
||||
*/
|
||||
const { __ } = wp.i18n;
|
||||
const { Dropdown, Button } = wp.components;
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import './style.scss';
|
||||
import ImportForm from '../import-form';
|
||||
|
||||
function ImportDropdown( { onUpload } ) {
|
||||
return (
|
||||
<Dropdown
|
||||
position="bottom right"
|
||||
contentClassName="list-layout-blocks-import-dropdown__content"
|
||||
renderToggle={ ( { isOpen, onToggle } ) => (
|
||||
<Button
|
||||
type="button"
|
||||
aria-expanded={ isOpen }
|
||||
onClick={ onToggle }
|
||||
isPrimary
|
||||
>
|
||||
{ __( 'Import from JSON', 'canvas' ) }
|
||||
</Button>
|
||||
) }
|
||||
renderContent={ ( { onClose } ) => (
|
||||
<ImportForm onUpload={ flow( onClose, onUpload ) } />
|
||||
) }
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default ImportDropdown;
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* WordPress dependencies
|
||||
*/
|
||||
const { Component } = wp.element;
|
||||
const { withInstanceId } = wp.compose;
|
||||
const { __ } = wp.i18n;
|
||||
const { Button, Notice } = wp.components;
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import './style.scss';
|
||||
import importLayoutBlock from '../../utils/import';
|
||||
|
||||
class ImportForm extends Component {
|
||||
constructor() {
|
||||
super( ...arguments );
|
||||
this.state = {
|
||||
isLoading: false,
|
||||
error: null,
|
||||
file: null,
|
||||
};
|
||||
|
||||
this.isStillMounted = true;
|
||||
this.onChangeFile = this.onChangeFile.bind( this );
|
||||
this.onSubmit = this.onSubmit.bind( this );
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.isStillMounted = false;
|
||||
}
|
||||
|
||||
onChangeFile( event ) {
|
||||
this.setState( { file: event.target.files[ 0 ] } );
|
||||
}
|
||||
|
||||
onSubmit( event ) {
|
||||
event.preventDefault();
|
||||
const { file } = this.state;
|
||||
const { onUpload } = this.props;
|
||||
if ( ! file ) {
|
||||
return;
|
||||
}
|
||||
this.setState( { isLoading: true } );
|
||||
importLayoutBlock( file )
|
||||
.then( ( layoutBlock ) => {
|
||||
if ( ! this.isStillMounted ) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState( { isLoading: false } );
|
||||
onUpload( layoutBlock );
|
||||
} )
|
||||
.catch( ( error ) => {
|
||||
if ( ! this.isStillMounted ) {
|
||||
return;
|
||||
}
|
||||
|
||||
let uiMessage;
|
||||
switch ( error.message ) {
|
||||
case 'Invalid JSON file':
|
||||
uiMessage = __( 'Invalid JSON file', 'canvas' );
|
||||
break;
|
||||
case 'Invalid Layout Block JSON file':
|
||||
uiMessage = __( 'Invalid Layout Block JSON file', 'canvas' );
|
||||
break;
|
||||
default:
|
||||
uiMessage = __( 'Unknown error', 'canvas' );
|
||||
}
|
||||
|
||||
this.setState( { isLoading: false, error: uiMessage } );
|
||||
} );
|
||||
}
|
||||
|
||||
render() {
|
||||
const { instanceId } = this.props;
|
||||
const { file, isLoading, error } = this.state;
|
||||
const inputId = 'list-layout-blocks-import-form-' + instanceId;
|
||||
return (
|
||||
<form
|
||||
className="list-layout-blocks-import-form"
|
||||
onSubmit={ this.onSubmit }
|
||||
>
|
||||
{ error && (
|
||||
<Notice status="error">
|
||||
{ error }
|
||||
</Notice>
|
||||
) }
|
||||
<label
|
||||
htmlFor={ inputId }
|
||||
className="list-layout-blocks-import-form__label"
|
||||
>
|
||||
{ __( 'File', 'canvas' ) }
|
||||
</label>
|
||||
<input
|
||||
id={ inputId }
|
||||
type="file"
|
||||
onChange={ this.onChangeFile }
|
||||
/>
|
||||
<Button
|
||||
type="submit"
|
||||
isBusy={ isLoading }
|
||||
disabled={ ! file || isLoading }
|
||||
isDefault
|
||||
className="list-layout-blocks-import-form__button"
|
||||
>
|
||||
{ __( 'Import', 'canvas' ) }
|
||||
</Button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withInstanceId( ImportForm );
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* WordPress dependencies
|
||||
*/
|
||||
const {
|
||||
jQuery: $,
|
||||
} = window;
|
||||
|
||||
const { render } = wp.element;
|
||||
|
||||
const { __ } = wp.i18n;
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import exportLayoutBlock from './utils/export';
|
||||
import ImportDropdown from './components/import-dropdown';
|
||||
|
||||
$( document ).on( 'DOMContentLoaded', () => {
|
||||
// Setup Export Links
|
||||
$( '.type-canvas_layout.status-publish > .column-title > .row-actions' ).each( function() {
|
||||
const $this = $( this );
|
||||
const $parent = $this.closest( '.type-canvas_layout.status-publish' );
|
||||
let id = '';
|
||||
|
||||
if ( $parent.attr('id') ) {
|
||||
id = $parent.attr('id').replace(/^post-/g, '');
|
||||
}
|
||||
|
||||
$this.append( `
|
||||
<span class="export">
|
||||
|
|
||||
<button type="button" class="wp-list-layout-blocks__export button-link" data-id="${ id }">${ __( 'Export as JSON', 'canvas' ) }</button>
|
||||
</span>
|
||||
` );
|
||||
} );
|
||||
$( document ).on( 'click', '.wp-list-layout-blocks__export', ( e ) => {
|
||||
e.preventDefault();
|
||||
exportLayoutBlock( e.target.dataset.id );
|
||||
} );
|
||||
|
||||
// Setup Import Form
|
||||
const button = document.querySelector( '.page-title-action' );
|
||||
if ( ! button ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const showNotice = () => {
|
||||
const notice = document.createElement( 'div' );
|
||||
notice.className = 'notice notice-success is-dismissible';
|
||||
notice.innerHTML = `<p>${ __( 'Layout block imported successfully!', 'canvas' ) }</p>`;
|
||||
|
||||
const headerEnd = document.querySelector( '.wp-header-end' );
|
||||
if ( ! headerEnd ) {
|
||||
return;
|
||||
}
|
||||
headerEnd.parentNode.insertBefore( notice, headerEnd );
|
||||
};
|
||||
|
||||
const container = document.createElement( 'div' );
|
||||
container.className = 'list-layout-blocks__container';
|
||||
button.parentNode.insertBefore( container, button );
|
||||
render( <ImportDropdown onUpload={ showNotice } />, container );
|
||||
} );
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
const {
|
||||
kebabCase,
|
||||
} = window.lodash;
|
||||
|
||||
/**
|
||||
* WordPress dependencies
|
||||
*/
|
||||
const {
|
||||
apiFetch,
|
||||
} = wp;
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { download } from './file';
|
||||
|
||||
/**
|
||||
* Export a layout block as a JSON file.
|
||||
*
|
||||
* @param {number} id
|
||||
*/
|
||||
async function exportLayoutBlock( id ) {
|
||||
const postType = await apiFetch( { path: `/wp/v2/types/canvas_layout` } );
|
||||
const post = await apiFetch( { path: `/wp/v2/${ postType.rest_base }/${ id }?context=edit` } );
|
||||
const title = post.title.raw;
|
||||
const content = post.content.raw;
|
||||
const fileContent = JSON.stringify( {
|
||||
__file: 'canvas_layout',
|
||||
title,
|
||||
content,
|
||||
}, null, 2 );
|
||||
const fileName = kebabCase( title ) + '.json';
|
||||
|
||||
download( fileName, fileContent, 'application/json' );
|
||||
}
|
||||
|
||||
export default exportLayoutBlock;
|
||||
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Downloads a file.
|
||||
*
|
||||
* @param {string} fileName File Name.
|
||||
* @param {string} content File Content.
|
||||
* @param {string} contentType File mime type.
|
||||
*/
|
||||
export function download( fileName, content, contentType ) {
|
||||
const file = new window.Blob( [ content ], { type: contentType } );
|
||||
|
||||
// IE11 can't use the click to download technique
|
||||
// we use a specific IE11 technique instead.
|
||||
if ( window.navigator.msSaveOrOpenBlob ) {
|
||||
window.navigator.msSaveOrOpenBlob( file, fileName );
|
||||
} else {
|
||||
const a = document.createElement( 'a' );
|
||||
a.href = URL.createObjectURL( file );
|
||||
a.download = fileName;
|
||||
|
||||
a.style.display = 'none';
|
||||
document.body.appendChild( a );
|
||||
a.click();
|
||||
document.body.removeChild( a );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the textual content of the given file.
|
||||
*
|
||||
* @param {File} file File.
|
||||
* @return {Promise<string>} Content of the file.
|
||||
*/
|
||||
export function readTextFile( file ) {
|
||||
const reader = new window.FileReader();
|
||||
return new Promise( ( resolve ) => {
|
||||
reader.onload = function() {
|
||||
resolve( reader.result );
|
||||
};
|
||||
reader.readAsText( file );
|
||||
} );
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
const {
|
||||
isString,
|
||||
} = window.lodash;
|
||||
|
||||
/**
|
||||
* WordPress dependencies
|
||||
*/
|
||||
const {
|
||||
apiFetch,
|
||||
} = wp;
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { readTextFile } from './file';
|
||||
|
||||
/**
|
||||
* Import a layout block from a JSON file.
|
||||
*
|
||||
* @param {File} file File.
|
||||
* @return {Promise} Promise returning the imported layout block.
|
||||
*/
|
||||
async function importLayoutBlock( file ) {
|
||||
const fileContent = await readTextFile( file );
|
||||
let parsedContent;
|
||||
try {
|
||||
parsedContent = JSON.parse( fileContent );
|
||||
} catch ( e ) {
|
||||
throw new Error( 'Invalid JSON file' );
|
||||
}
|
||||
if (
|
||||
parsedContent.__file !== 'canvas_layout' ||
|
||||
! parsedContent.title ||
|
||||
! parsedContent.content ||
|
||||
! isString( parsedContent.title ) ||
|
||||
! isString( parsedContent.content )
|
||||
) {
|
||||
throw new Error( 'Invalid Canvas Layout JSON file' );
|
||||
}
|
||||
const postType = await apiFetch( { path: `/wp/v2/types/canvas_layout` } );
|
||||
const layoutBlock = await apiFetch( {
|
||||
path: `/wp/v2/${ postType.rest_base }`,
|
||||
data: {
|
||||
title: parsedContent.title,
|
||||
content: parsedContent.content,
|
||||
status: 'publish',
|
||||
},
|
||||
method: 'POST',
|
||||
} );
|
||||
|
||||
return layoutBlock;
|
||||
}
|
||||
|
||||
export default importLayoutBlock;
|
||||
Reference in New Issue
Block a user