Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Attributes: Add support for site options #3112

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion blocks/api/serializer.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,9 @@ export function getCommentAttributes( allAttributes, schema ) {
}

// Ignore values sources from content and post meta
if ( attributeSchema.source || attributeSchema.meta ) {
if ( attributeSchema.source ||
attributeSchema.meta ||
attributeSchema.option ) {
return result;
}

Expand Down
1 change: 1 addition & 0 deletions blocks/library/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ import './text-columns';
import './verse';
import './video';
import './audio';
import './site-description';
83 changes: 83 additions & 0 deletions blocks/library/site-description/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/**
* WordPress dependencies
*/
import { Placeholder, Spinner } from '@wordpress/components';
import { __ } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import { registerBlockType } from '../../api';
import InspectorControls from '../../inspector-controls';
import BlockDescription from '../../block-description';
import BlockControls from '../../block-controls';

registerBlockType( 'core/site-description', {
title: __( 'Site Description' ),

icon: 'list-view',

category: 'widgets',

attributes: {
description: {
type: 'string',
option: 'description',
},
shouldRenderDescription: {
type: 'boolean',
default: false,
},
},

keywords: [ __( 'site tagline' ) ],

edit( { attributes, setAttributes, focus } ) {
const {
description,
shouldRenderDescription,
} = attributes;

if ( description === undefined ) {
return (
<Placeholder
icon="admin-post"
label={ __( 'Site Description' ) }
>
<Spinner />
</Placeholder>
);
}

return [
focus && <BlockControls key="controls" />,
focus && (
<InspectorControls key="inspector">
<BlockDescription>
<p>{ __( 'Shows your site\'s description' ) }</p>
</BlockDescription>
<InspectorControls.ToggleControl
label={ __( 'Render description' ) }
checked={ shouldRenderDescription }
onChange={ () => setAttributes( {
shouldRenderDescription: ! shouldRenderDescription,
} ) }
/>
</InspectorControls>
),
<input key="site-title"
className="wp-block-site-description"
onChange={ ( event ) => setAttributes( {
description: event.target.value,
} ) }
value={ description } />,
];
},

save( { attributes } ) {
const { description, shouldRenderDescription } = attributes;
return shouldRenderDescription
? <div>{ description }</div>
: null;
},
} );
46 changes: 46 additions & 0 deletions blocks/library/site-description/index.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php
/**
* Server-side rendering of the `core/site-description` block.
*
* @package gutenberg
*/

/**
* Renders the `core/site-description` block on server.
*
* @param array $attributes The block attributes.
*
* @return string Returns HTML for the site description block.
*/
function gutenberg_render_block_core_site_description( $attributes ) {
if ( ! $attributes[ 'shouldRenderDescription' ] ) {
return '';
}

$class = "wp-block-site-description";
$description = get_option( 'blogdescription' );
$block_content = sprintf(
'<div class="%1$s">%2$s</div>',
esc_attr( $class ),
esc_html( $description )
);

return $block_content;
}

register_block_type( 'core/site-description', array(
'attributes' => array(
/* option attribute
'description' => array(
'type' => 'string',
'option' => 'description',
),
*/
'shouldRenderDescription' => array(
'type' => 'boolean',
'default' => false,
),
),

'render_callback' => 'gutenberg_render_block_core_site_description',
) );
13 changes: 12 additions & 1 deletion components/higher-order/with-api-data/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { Component } from 'element';
/**
* Internal dependencies
*/
import request from './request';
import request, { encodeParams } from './request';
import { getRoute } from './routes';

export default ( mapPropsToData ) => ( WrappedComponent ) => {
Expand Down Expand Up @@ -166,6 +166,8 @@ export default ( mapPropsToData ) => ( WrappedComponent ) => {
return result;
}

console.log( 'route', path, route );

result[ propName ] = route.methods.reduce( ( stateValue, method ) => {
// Add request initiater into data props
const requestKey = this.getRequestKey( method );
Expand All @@ -183,6 +185,15 @@ export default ( mapPropsToData ) => ( WrappedComponent ) => {
// Track path for future map skipping
stateValue.path = path;

// TODO move this somewhere else and/or document
if ( method === 'PUT' ) {
stateValue.saveWith = ( data ) =>
this.request(
propName,
method,
path + encodeParams( data ) );
}

return stateValue;
}, {} );

Expand Down
11 changes: 9 additions & 2 deletions components/higher-order/with-api-data/request.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* External dependencies
*/
import memoize from 'memize';
import { mapKeys } from 'lodash';
import { isEmpty, mapKeys, toPairs } from 'lodash';

export const getStablePath = memoize( ( path ) => {
const [ base, query ] = path.split( '?' );
Expand All @@ -29,13 +29,20 @@ export const getStablePath = memoize( ( path ) => {
// 'a=5&b=1&c=2'
} );

export const encodeParams = ( data ) =>
isEmpty( data )
? ''
: '?' + toPairs( data )
.map( ( [ key, value ] ) => `${ key }=${ encodeURI( value ) }` )
.join( '& ' );

/**
* Response cache of path to response (object of data, headers arrays).
* Optionally populated from window global for preloading.
*
* @type {Object}
*/
export const cache = mapKeys(
export const cache = window._wpAPICache = mapKeys(
window._wpAPIDataPreload,
( value, key ) => getStablePath( key )
);
Expand Down
30 changes: 28 additions & 2 deletions editor/effects.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* External dependencies
*/
import { BEGIN, COMMIT, REVERT } from 'redux-optimist';
import { get, uniqueId } from 'lodash';
import { get, noop, uniqueId } from 'lodash';

/**
* WordPress dependencies
Expand Down Expand Up @@ -36,6 +36,11 @@ import {
isEditedPostNew,
isEditedPostSaveable,
} from './selectors';
import {
fetchSiteOptions,
getOptionUpdatedMessage,
saveSiteOptions,
} from './site-options';

const SAVE_POST_NOTICE_ID = 'SAVE_POST_NOTICE_ID';
const TRASH_POST_NOTICE_ID = 'TRASH_POST_NOTICE_ID';
Expand Down Expand Up @@ -86,7 +91,7 @@ export default {
},
REQUEST_POST_UPDATE_SUCCESS( action, store ) {
const { previousPost, post } = action;
const { dispatch } = store;
const { dispatch, getState } = store;

const publishStatus = [ 'publish', 'private', 'future' ];
const isPublished = publishStatus.indexOf( previousPost.status ) !== -1;
Expand All @@ -113,6 +118,19 @@ export default {
) );
}

const siteOptions = getState().siteOptions;
saveSiteOptions( siteOptions ).then( ( newOptions ) => {
Object.keys( newOptions )
.map( getOptionUpdatedMessage )
.filter( Boolean )
.map( ( label ) => createSuccessNotice(
<p>
<span>{ label }</span>
</p>
) )
.forEach( dispatch );
} );

if ( get( window.history.state, 'id' ) !== post.id ) {
window.history.replaceState(
{ id: post.id },
Expand Down Expand Up @@ -273,4 +291,12 @@ export default {

return effects;
},
RESET_SITE_OPTIONS( _, { dispatch } ) {
fetchSiteOptions().then( ( siteOptions ) => {
dispatch( {
type: 'UPDATE_SITE_OPTIONS',
siteOptions,
} );
} ).catch( noop );
},
};
16 changes: 16 additions & 0 deletions editor/modes/visual-editor/block.js
Original file line number Diff line number Diff line change
Expand Up @@ -148,12 +148,24 @@ class VisualEditorBlock extends Component {
return result;
}, {} );

const optionAttributes = reduce( attributes, ( result, value, key ) => {
if ( type && has( type, [ 'attributes', key, 'option' ] ) ) {
result[ type.attributes[ key ].option ] = value;
}

return result;
}, {} );

if ( size( metaAttributes ) ) {
this.props.onMetaChange( {
...this.props.meta,
...metaAttributes,
} );
}

if ( size( optionAttributes ) ) {
this.props.onOptionsChange( optionAttributes );
}
}

maybeHover() {
Expand Down Expand Up @@ -463,5 +475,9 @@ export default connect(
onMetaChange( meta ) {
dispatch( editPost( { meta } ) );
},

onOptionsChange( siteOptions ) {
dispatch( { type: 'UPDATE_SITE_OPTIONS', siteOptions } );
},
} )
)( VisualEditorBlock );
2 changes: 2 additions & 0 deletions editor/modes/visual-editor/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import WritingFlow from '../../writing-flow';
import TableOfContents from '../../table-of-contents';
import { getBlockUids, getMultiSelectedBlockUids } from '../../selectors';
import { clearSelectedBlock, multiSelect, redo, undo, removeBlocks } from '../../actions';
import { QuerySiteOptions } from '../../site-options';

class VisualEditor extends Component {
constructor() {
Expand Down Expand Up @@ -101,6 +102,7 @@ class VisualEditor extends Component {
backspace: this.deleteSelectedBlocks,
del: this.deleteSelectedBlocks,
} } />
<QuerySiteOptions />
<WritingFlow>
<PostTitle />
<VisualEditorBlockList ref={ this.bindBlocksContainer } />
Expand Down
10 changes: 10 additions & 0 deletions editor/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,15 @@ export function notices( state = {}, action ) {
return state;
}

export function siteOptions( state = {}, action ) {
switch ( action.type ) {
case 'UPDATE_SITE_OPTIONS':
return { ...state, ...action.siteOptions };
}

return state;
}

export default optimist( combineReducers( {
editor,
currentPost,
Expand All @@ -535,4 +544,5 @@ export default optimist( combineReducers( {
panel,
saving,
notices,
siteOptions,
} ) );
17 changes: 16 additions & 1 deletion editor/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -397,13 +397,23 @@ export const getBlock = createSelector(
return result;
}, {} );

if ( ! Object.keys( metaAttributes ).length ) {
const optionAttributes = reduce( type.attributes, ( result, value, key ) => {
if ( value && ( 'option' in value ) ) {
result[ key ] = getSiteOption( state, value.option );
}

return result;
}, {} );

if ( ! Object.keys( metaAttributes ).length &&
! Object.keys( optionAttributes ).length ) {
return block;
}

return {
...block,
attributes: {
...optionAttributes,
...block.attributes,
...metaAttributes,
},
Expand All @@ -413,6 +423,7 @@ export const getBlock = createSelector(
get( state, [ 'editor', 'blocksByUid', uid ] ),
get( state, 'editor.edits.meta' ),
get( state, 'currentPost.meta' ),
get( state, 'siteOptions' ),
]
);

Expand All @@ -422,6 +433,10 @@ function getPostMeta( state, key ) {
: get( state, [ 'currentPost', 'meta', key ] );
}

function getSiteOption( state, key ) {
return get( state, [ 'siteOptions', key ] );
}

/**
* Returns all block objects for the current post being edited as an array in
* the order they appear in the post.
Expand Down
Loading