Version/1.0 master merge (https://github.com/woocommerce/woocommerce-admin/pull/3797)
* Try: Moving Customers to main Woo Menu (https://github.com/woocommerce/woocommerce-admin/pull/3632) * Only add onboarding settings on wc-admin pages when task list should be shown. (https://github.com/woocommerce/woocommerce-admin/pull/3722) * Use cron for unsnoozing admin notes (https://github.com/woocommerce/woocommerce-admin/pull/3662) * Use wp-cron for admin note snoozing. * Remove "unsnooze" scheduled action. * Use correct version. * Avoid using deprecated method for unscheduling actions. * Onboarding: Fix toggle tracking events (https://github.com/woocommerce/woocommerce-admin/pull/3645) * Fix errant wcadmin prefix on event name * Track the onboarding toggle on the option in case enable_onboarding isn't used * Move toggle actions to separate function * Move onboarding actions * Move onboarding filters * Move help tab updates to add_toggle_actions * Only run onboarding actions when enabled * Onboarding: Add tracks events when profiler steps are completed (https://github.com/woocommerce/woocommerce-admin/pull/3726) * Add tracks for store profiler step completion * Record event when profiler is completed * Ensure continue setup loads the onboarding profiler (https://github.com/woocommerce/woocommerce-admin/pull/3646) * 'All that include' option removed when input field is empty (https://github.com/woocommerce/woocommerce-admin/pull/3700) * 'All that include' option removed when input field is empty Added a control to check that when the input field 'Search by customer name' is empty, the 'All that include' option is not appearing. * Const name improved The constant name hasValues was changed to optionsHaveValues (more descriptive) * Fix select text alignment (https://github.com/woocommerce/woocommerce-admin/pull/3723) * Stock panel indicator - cache and use lookup tables. (https://github.com/woocommerce/woocommerce-admin/pull/3729) * Stock panel indicator - cache and use lookup tables. * Revise query, clear transient on product update. * Fix error, ht Josh. * Checklist: Remove sideloaded images to reduce build size, take 2 (https://github.com/woocommerce/woocommerce-admin/pull/3731) * Remove homepage template images. * Use other-small on all industries, adjust text color. * Remove background dim and opacity set to 0 * Fix/3631 (https://github.com/woocommerce/woocommerce-admin/pull/3730) * Added CBD as an industry type CBD was added as an industry type in API * Industries options modified Modified the industries options. Now we are able to choose if we will use an input or not in the option. * API control changed for industries. API control changed for industries. Now it accepts the data type we need. * Added input in Industries list for the option "Other" Added an input for the option "Other" in the industries list * Added suggested changes in review comments. * Added data preparation for recordEvent * Changed variable to snake_case The variable "industriesWithDetail" was changed to "industries_with_detail" (snake_case) * Onboarding: Create homepage without redirect (https://github.com/woocommerce/woocommerce-admin/pull/3727) * Add link to edit homepage instead of redirect * Add busy state to homepage creation button * Publish homepage on create via API * Update homepage notice to show on first post update * Update homepage creation notice per design * Record event on customize homepage * Set homepage to frontpage on creation * Add deactivation note for feature plugin (https://github.com/woocommerce/woocommerce-admin/pull/3687) * Add version deactivation note * Add the note to deactivate if the version is older than the current WC version * Deactivate wc admin feature plugin on action click * Add notes version hooks * change the Package class namespace to exclude from standalone autoloader * add use statement for FeaturePlugin * add note explaining namespace * use wc-admin-deactivate-plugin note name * Rename file and class to WC_Admin_Notes_Deactivate_Plugin Co-authored-by: Ron Rennick <ron@ronandandrea.com> Co-authored-by: Paul Sealock <psealock@gmail.com> * Add Travis tests on GH for release branch (https://github.com/woocommerce/woocommerce-admin/pull/3751) * Add Travis tests on GH for release branch * fix linter errors * ActivityPanels.php -> use public static functions * Remove free text Search option when no query exists (https://github.com/woocommerce/woocommerce-admin/pull/3755) * Revert changes in woocommerce/woocommerce-admin#3700 * Don't add free text search if no query exists * Add tests for Search without query * Add test for showing free text search option * Fix image sideloading for store industries. (https://github.com/woocommerce/woocommerce-admin/pull/3743) * Fix image sideloading for store industries. Data format changed in https://github.com/woocommerce/woocommerce-admin/pull/3730 * Fix industry image sideload in cases where the count is less than requested. * Be backwards compatible with the old industry data format. * Added event props to identify stores with WCS and Jetpack installed (https://github.com/woocommerce/woocommerce-admin/pull/3750) * Added event props to identify stores with WCS and Jetpack installed Also, added Jeckpack connected status * Improved variable name * Simplified method Simplified method. "intersection" check was removed * Tests errors repeared The method "clear_low_out_of_stock_count_transient" now is static. * OBW: fix sideloading image test error (https://github.com/woocommerce/woocommerce-admin/pull/3762) * Release 0.26.0 changes (https://github.com/woocommerce/woocommerce-admin/pull/3753) * add deactivation hook to Package.php (https://github.com/woocommerce/woocommerce-admin/pull/3770) * Add active version functions (https://github.com/woocommerce/woocommerce-admin/pull/3772) * add active version functions to Package.php Co-authored-by: Joshua T Flowers <joshuatf@gmail.com> * 0.26.1 changes (https://github.com/woocommerce/woocommerce-admin/pull/3773) * Customers Report: fix missing report param in search (https://github.com/woocommerce/woocommerce-admin/pull/3778) * Product titles include encoded entities (https://github.com/woocommerce/woocommerce-admin/pull/3765) * Stripped HTML from product titles and decoded before displaying them Stripped html from product titles and entities are decoded before displaying them * Stripped HTML from product titles and decoded in Stock report Stripped html from product titles and entities are decoded before displaying them. Now in Stock report * Added support for HTML tags and encoded entities on product titles Added support for HTML tags and encoded entities on filtered product list, dropdown menus and tag names. Also, strip_tags() function was replaced with wp_strip_all_tags() instead. * strip_tags() function was replaced with wp_strip_all_tags() instead. * Added control for a variable Added control for "item->data" before applying wp_strip_all_tags method. * pre-commit changes * Test text corrected * fix linting issues * fix mis-merged changes * Update jsdoc Co-Authored-By: Paul Sealock <psealock@gmail.com> Co-authored-by: Timmy Crawford <timmyc@users.noreply.github.com> Co-authored-by: Jeff Stieler <jeff.m.stieler@gmail.com> Co-authored-by: Joshua T Flowers <joshuatf@gmail.com> Co-authored-by: Fernando <ultimoround@gmail.com> Co-authored-by: edmundcwm <edmundcwm@gmail.com> Co-authored-by: Paul Sealock <psealock@gmail.com>
|
@ -49,6 +49,7 @@ script:
|
|||
branches:
|
||||
only:
|
||||
- master
|
||||
- version/1.0
|
||||
|
||||
before_deploy:
|
||||
# Remove our unneeded symlink.
|
||||
|
|
|
@ -94,6 +94,18 @@ export const getReports = () => {
|
|||
return applyFilters( REPORTS_FILTER, reports );
|
||||
};
|
||||
|
||||
/**
|
||||
* The Customers Report will not have the `report` param supplied by the router/
|
||||
* because it no longer exists under the path `/analytics/:report`. Use `props.path`/
|
||||
* instead to determine if the Customers Report is being rendered.
|
||||
*
|
||||
* @param {Object} props - component props
|
||||
* @return {string} - report parameter
|
||||
*/
|
||||
const getReportParam = ( { params, path } ) => {
|
||||
return params.report || path.replace( /^\/+/, '' );
|
||||
};
|
||||
|
||||
class Report extends Component {
|
||||
constructor() {
|
||||
super( ...arguments );
|
||||
|
@ -117,13 +129,13 @@ class Report extends Component {
|
|||
return null;
|
||||
}
|
||||
|
||||
const { params, path, isError } = this.props;
|
||||
const { isError } = this.props;
|
||||
|
||||
if ( isError ) {
|
||||
return <ReportError isError />;
|
||||
}
|
||||
|
||||
const reportParam = params.report || path.replace( /^\/+/, '' );
|
||||
const reportParam = getReportParam( this.props );
|
||||
|
||||
const report = find( getReports(), { report: reportParam } );
|
||||
if ( ! report ) {
|
||||
|
@ -148,7 +160,7 @@ export default compose(
|
|||
return {};
|
||||
}
|
||||
|
||||
const { report } = props.params;
|
||||
const report = getReportParam( props );
|
||||
const searchWords = getSearchWords( query );
|
||||
// Single Category view in Categories Report uses the products endpoint, so search must also.
|
||||
const mappedReport =
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
import { __, _n, _x, sprintf } from '@wordpress/i18n';
|
||||
import { Component } from '@wordpress/element';
|
||||
import { compose } from '@wordpress/compose';
|
||||
import { decodeEntities } from '@wordpress/html-entities';
|
||||
import { map } from 'lodash';
|
||||
|
||||
/**
|
||||
|
@ -117,12 +118,13 @@ class ProductsReportTable extends Component {
|
|||
category_ids: categoryIds,
|
||||
low_stock_amount: lowStockAmount,
|
||||
manage_stock: extendedInfoManageStock,
|
||||
name,
|
||||
sku,
|
||||
stock_status: extendedInfoStockStatus,
|
||||
stock_quantity: stockQuantity,
|
||||
variations = [],
|
||||
} = extendedInfo;
|
||||
|
||||
const name = decodeEntities( extendedInfo.name );
|
||||
const ordersLink = getNewPath(
|
||||
persistedQuery,
|
||||
'/analytics/orders',
|
||||
|
@ -144,7 +146,7 @@ class ProductsReportTable extends Component {
|
|||
const productCategories =
|
||||
( categoryIds &&
|
||||
categoryIds
|
||||
.map( categoryId => categories.get( categoryId ) )
|
||||
.map( ( categoryId ) => categories.get( categoryId ) )
|
||||
.filter( Boolean ) ) ||
|
||||
[];
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
*/
|
||||
import { __, _n, _x } from '@wordpress/i18n';
|
||||
import { Component } from '@wordpress/element';
|
||||
import { decodeEntities } from '@wordpress/html-entities';
|
||||
|
||||
/**
|
||||
* WooCommerce dependencies
|
||||
|
@ -65,7 +66,6 @@ export default class StockReportTable extends Component {
|
|||
const {
|
||||
id,
|
||||
manage_stock: manageStock,
|
||||
name,
|
||||
parent_id: parentId,
|
||||
sku,
|
||||
stock_quantity: stockQuantity,
|
||||
|
@ -73,6 +73,8 @@ export default class StockReportTable extends Component {
|
|||
low_stock_amount: lowStockAmount,
|
||||
} = product;
|
||||
|
||||
const name = decodeEntities( product.name );
|
||||
|
||||
const productDetailLink = getNewPath(
|
||||
persistedQuery,
|
||||
'/analytics/products',
|
||||
|
|
|
@ -5,7 +5,7 @@ import { __ } from '@wordpress/i18n';
|
|||
import { Component, Fragment } from '@wordpress/element';
|
||||
import { Button, CheckboxControl } from '@wordpress/components';
|
||||
import { compose } from '@wordpress/compose';
|
||||
import { filter, get, includes } from 'lodash';
|
||||
import { filter, get, find, findIndex } from 'lodash';
|
||||
import { withDispatch } from '@wordpress/data';
|
||||
|
||||
/**
|
||||
|
@ -16,7 +16,7 @@ import { getSetting } from '@woocommerce/wc-admin-settings';
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { H, Card } from '@woocommerce/components';
|
||||
import { H, Card, TextControl } from '@woocommerce/components';
|
||||
import withSelect from 'wc-api/with-select';
|
||||
import { recordEvent } from 'lib/tracks';
|
||||
|
||||
|
@ -30,9 +30,11 @@ class Industry extends Component {
|
|||
this.state = {
|
||||
error: null,
|
||||
selected: profileItems.industry || [],
|
||||
textInputListContent: {},
|
||||
};
|
||||
this.onContinue = this.onContinue.bind( this );
|
||||
this.onChange = this.onChange.bind( this );
|
||||
this.onIndustryChange = this.onIndustryChange.bind( this );
|
||||
this.onDetailChange = this.onDetailChange.bind( this );
|
||||
}
|
||||
|
||||
async onContinue() {
|
||||
|
@ -47,9 +49,16 @@ class Industry extends Component {
|
|||
isError,
|
||||
updateProfileItems,
|
||||
} = this.props;
|
||||
const selectedIndustriesList = this.state.selected.map(
|
||||
( industry ) => industry.slug
|
||||
);
|
||||
const industriesWithDetail = filter( this.state.selected, ( value ) => {
|
||||
return typeof value.detail !== 'undefined';
|
||||
} );
|
||||
|
||||
recordEvent( 'storeprofiler_store_industry_continue', {
|
||||
store_industry: this.state.selected,
|
||||
store_industry: selectedIndustriesList,
|
||||
industries_with_detail: industriesWithDetail,
|
||||
} );
|
||||
await updateProfileItems( { industry: this.state.selected } );
|
||||
|
||||
|
@ -73,19 +82,26 @@ class Industry extends Component {
|
|||
this.setState( { error } );
|
||||
}
|
||||
|
||||
onChange( slug ) {
|
||||
onIndustryChange( slug ) {
|
||||
this.setState(
|
||||
( state ) => {
|
||||
if ( includes( state.selected, slug ) ) {
|
||||
const newSelected = state.selected;
|
||||
const selectedIndustry = find( newSelected, { slug } );
|
||||
if ( selectedIndustry ) {
|
||||
const newTextInputListContent = state.textInputListContent;
|
||||
newTextInputListContent[ slug ] = selectedIndustry.detail;
|
||||
return {
|
||||
selected:
|
||||
filter( state.selected, ( value ) => {
|
||||
return value !== slug;
|
||||
return value.slug !== slug;
|
||||
} ) || [],
|
||||
textInputListContent: newTextInputListContent,
|
||||
};
|
||||
}
|
||||
const newSelected = state.selected;
|
||||
newSelected.push( slug );
|
||||
newSelected.push( {
|
||||
slug,
|
||||
detail: state.textInputListContent[ slug ],
|
||||
} );
|
||||
return {
|
||||
selected: newSelected,
|
||||
};
|
||||
|
@ -94,9 +110,23 @@ class Industry extends Component {
|
|||
);
|
||||
}
|
||||
|
||||
onDetailChange( value, slug ) {
|
||||
this.setState( ( state ) => {
|
||||
const newSelected = state.selected;
|
||||
const newTextInputListContent = state.textInputListContent;
|
||||
const industryIndex = findIndex( newSelected, { slug } );
|
||||
newSelected[ industryIndex ].detail = value;
|
||||
newTextInputListContent[ slug ] = value;
|
||||
return {
|
||||
selected: newSelected,
|
||||
textInputListContent: newTextInputListContent,
|
||||
};
|
||||
} );
|
||||
}
|
||||
|
||||
render() {
|
||||
const { industries } = onboarding;
|
||||
const { error, selected } = this.state;
|
||||
const { error, selected, textInputListContent } = this.state;
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
|
@ -112,14 +142,45 @@ class Industry extends Component {
|
|||
<Card>
|
||||
<div className="woocommerce-profile-wizard__checkbox-group">
|
||||
{ Object.keys( industries ).map( ( slug ) => {
|
||||
const selectedIndustry = find( selected, { slug } );
|
||||
|
||||
return (
|
||||
<CheckboxControl
|
||||
key={ slug }
|
||||
label={ industries[ slug ] }
|
||||
onChange={ () => this.onChange( slug ) }
|
||||
checked={ selected.includes( slug ) }
|
||||
className="woocommerce-profile-wizard__checkbox"
|
||||
/>
|
||||
<div key={ `div-${ slug }` }>
|
||||
<CheckboxControl
|
||||
key={ `checkbox-control-${ slug }` }
|
||||
label={ industries[ slug ].label }
|
||||
onChange={ () =>
|
||||
this.onIndustryChange( slug )
|
||||
}
|
||||
checked={ selectedIndustry || false }
|
||||
className="woocommerce-profile-wizard__checkbox"
|
||||
/>
|
||||
{ industries[ slug ].use_description &&
|
||||
selectedIndustry && (
|
||||
<TextControl
|
||||
key={ `text-control-${ selectedIndustry.slug }` }
|
||||
label={
|
||||
industries[
|
||||
selectedIndustry.slug
|
||||
].description_label
|
||||
}
|
||||
value={
|
||||
selectedIndustry.detail ||
|
||||
textInputListContent[
|
||||
slug
|
||||
] ||
|
||||
''
|
||||
}
|
||||
onChange={ ( value ) =>
|
||||
this.onDetailChange(
|
||||
value,
|
||||
selectedIndustry.slug
|
||||
)
|
||||
}
|
||||
className="woocommerce-profile-wizard__text"
|
||||
/>
|
||||
) }
|
||||
</div>
|
||||
);
|
||||
} ) }
|
||||
{ error && (
|
||||
|
|
|
@ -240,16 +240,6 @@
|
|||
padding: $gap-small $gap;
|
||||
min-height: 56px;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: $gap-large * 2 + $gap;
|
||||
width: calc(100% - #{$gap-large * 2 + $gap});
|
||||
height: 1px;
|
||||
background-color: $studio-gray-5;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.components-base-control {
|
||||
position: relative;
|
||||
}
|
||||
|
@ -293,6 +283,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
.woocommerce-profile-wizard__text {
|
||||
margin: 0 16px 10px;
|
||||
}
|
||||
|
||||
svg.dashicon.components-checkbox-control__checked {
|
||||
left: 1px;
|
||||
top: -1px;
|
||||
|
|
|
@ -14,6 +14,7 @@ import { withDispatch } from '@wordpress/data';
|
|||
*/
|
||||
import { Card, List, MenuItem, EllipsisMenu } from '@woocommerce/components';
|
||||
import { updateQueryString } from '@woocommerce/navigation';
|
||||
import { getSetting } from '@woocommerce/wc-admin-settings';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
|
@ -73,8 +74,25 @@ class TaskDashboard extends Component {
|
|||
} ).filter( ( task ) => task.visible );
|
||||
}
|
||||
|
||||
getPluginsInformation() {
|
||||
const { isJetpackConnected } = this.props;
|
||||
const { activePlugins, installedPlugins } = getSetting(
|
||||
'onboarding',
|
||||
{}
|
||||
);
|
||||
return {
|
||||
wcs_installed: installedPlugins.includes( 'woocommerce-services' ),
|
||||
wcs_active: activePlugins.includes( 'woocommerce-services' ),
|
||||
jetpack_installed: installedPlugins.includes( 'jetpack' ),
|
||||
jetpack_active: activePlugins.includes( 'jetpack' ),
|
||||
jetpack_connected: isJetpackConnected,
|
||||
};
|
||||
}
|
||||
|
||||
recordTaskView() {
|
||||
const { task } = this.props.query;
|
||||
// eslint-disable-next-line @wordpress/no-unused-vars-before-return
|
||||
const pluginsInformation = this.getPluginsInformation();
|
||||
|
||||
if ( ! task ) {
|
||||
return;
|
||||
|
@ -82,6 +100,7 @@ class TaskDashboard extends Component {
|
|||
|
||||
recordEvent( 'task_view', {
|
||||
task_name: task,
|
||||
...pluginsInformation,
|
||||
} );
|
||||
}
|
||||
|
||||
|
@ -320,7 +339,9 @@ class TaskDashboard extends Component {
|
|||
|
||||
export default compose(
|
||||
withSelect( ( select ) => {
|
||||
const { getProfileItems, getOptions } = select( 'wc-api' );
|
||||
const { getProfileItems, getOptions, isJetpackConnected } = select(
|
||||
'wc-api'
|
||||
);
|
||||
const profileItems = getProfileItems();
|
||||
|
||||
const options = getOptions( [
|
||||
|
@ -346,6 +367,7 @@ export default compose(
|
|||
profileItems,
|
||||
promptShown,
|
||||
taskListPayments,
|
||||
isJetpackConnected: isJetpackConnected(),
|
||||
};
|
||||
} ),
|
||||
withDispatch( ( dispatch ) => {
|
||||
|
|
|
@ -24,9 +24,9 @@ import { getSetting, setSetting } from '@woocommerce/wc-admin-settings';
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { queueRecordEvent, recordEvent } from 'lib/tracks';
|
||||
import { WC_ADMIN_NAMESPACE } from 'wc-api/constants';
|
||||
import withSelect from 'wc-api/with-select';
|
||||
import { recordEvent } from 'lib/tracks';
|
||||
|
||||
class Appearance extends Component {
|
||||
constructor( props ) {
|
||||
|
@ -192,13 +192,25 @@ class Appearance extends Component {
|
|||
path: '/wc-admin/onboarding/tasks/create_homepage',
|
||||
method: 'POST',
|
||||
} )
|
||||
.then( ( response ) => {
|
||||
createNotice( response.status, response.message );
|
||||
.then( response => {
|
||||
createNotice( response.status, response.message, {
|
||||
actions: response.edit_post_link
|
||||
? [
|
||||
{
|
||||
label: __( 'Customize', 'woocommerce-admin' ),
|
||||
onClick: () => {
|
||||
queueRecordEvent( 'tasklist_appearance_customize_homepage', {} );
|
||||
window.location = `${
|
||||
response.edit_post_link
|
||||
}&wc_onboarding_active_task=homepage`;
|
||||
},
|
||||
},
|
||||
]
|
||||
: null,
|
||||
} );
|
||||
|
||||
this.setState( { isPending: false } );
|
||||
if ( response.edit_post_link ) {
|
||||
window.location = `${ response.edit_post_link }&wc_onboarding_active_task=homepage`;
|
||||
}
|
||||
this.completeStep();
|
||||
} )
|
||||
.catch( ( error ) => {
|
||||
createNotice( 'error', error.message );
|
||||
|
@ -284,7 +296,7 @@ class Appearance extends Component {
|
|||
),
|
||||
content: (
|
||||
<Fragment>
|
||||
<Button isPrimary onClick={ this.createHomepage }>
|
||||
<Button isPrimary isBusy={ isPending } onClick={ this.createHomepage }>
|
||||
{ __( 'Create homepage', 'woocommerce-admin' ) }
|
||||
</Button>
|
||||
<Button
|
||||
|
|
|
@ -336,7 +336,7 @@ class ActivityPanel extends Component {
|
|||
export default withSelect( ( select ) => {
|
||||
const hasUnreadNotes = getUnreadNotes( select );
|
||||
const hasUnreadOrders = getUnreadOrders( select );
|
||||
const hasUnreadStock = getUnreadStock( select );
|
||||
const hasUnreadStock = getUnreadStock();
|
||||
const hasUnapprovedReviews = getUnapprovedReviews( select );
|
||||
|
||||
return {
|
||||
|
|
|
@ -112,21 +112,6 @@ export function getUnapprovedReviews( select ) {
|
|||
return false;
|
||||
}
|
||||
|
||||
export function getUnreadStock( select ) {
|
||||
const { getItems, getItemsError, getItemsTotalCount } = select( 'wc-api' );
|
||||
const productsQuery = {
|
||||
page: 1,
|
||||
per_page: 1,
|
||||
low_in_stock: true,
|
||||
status: 'publish',
|
||||
};
|
||||
getItems( 'products', productsQuery );
|
||||
const isError = Boolean( getItemsError( 'products', productsQuery ) );
|
||||
|
||||
if ( isError ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const lowInStockCount = getItemsTotalCount( 'products', productsQuery );
|
||||
return lowInStockCount > 0;
|
||||
export function getUnreadStock() {
|
||||
return getSetting( 'hasLowStock', false );
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
*/
|
||||
import { dispatch } from '@wordpress/data';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import apiFetch from '@wordpress/api-fetch';
|
||||
import domReady from '@wordpress/dom-ready';
|
||||
|
||||
/**
|
||||
|
@ -22,7 +21,11 @@ import { queueRecordEvent } from 'lib/tracks';
|
|||
* @return {Promise} Promise for overlay existence.
|
||||
*/
|
||||
const saveStarted = () => {
|
||||
if ( document.querySelector( '.editor-post-publish-button' ) === null ) {
|
||||
if (
|
||||
! document
|
||||
.querySelector( '.editor-post-publish-button' )
|
||||
.classList.contains( 'is-busy' )
|
||||
) {
|
||||
const promise = new Promise( ( resolve ) => {
|
||||
window.requestAnimationFrame( resolve );
|
||||
} );
|
||||
|
@ -39,7 +42,9 @@ const saveStarted = () => {
|
|||
*/
|
||||
const saveCompleted = () => {
|
||||
if (
|
||||
document.querySelector( '.post-publish-panel__postpublish' ) === null
|
||||
document
|
||||
.querySelector( '.editor-post-publish-button' )
|
||||
.classList.contains( 'is-busy' )
|
||||
) {
|
||||
const promise = new Promise( ( resolve ) => {
|
||||
window.requestAnimationFrame( resolve );
|
||||
|
@ -62,21 +67,11 @@ const onboardingHomepageNotice = () => {
|
|||
saveButton.classList.add( 'is-clicked' );
|
||||
|
||||
saveCompleted().then( () => {
|
||||
const postId = document.querySelector( '#post_ID' ).value;
|
||||
const notificationType =
|
||||
document.querySelector( '.components-snackbar__content' ) !== null
|
||||
? 'snackbar'
|
||||
: 'default';
|
||||
|
||||
apiFetch( {
|
||||
path: '/wc-admin/options',
|
||||
method: 'POST',
|
||||
data: {
|
||||
show_on_front: 'page',
|
||||
page_on_front: postId,
|
||||
},
|
||||
} );
|
||||
|
||||
dispatch( 'core/notices' ).removeNotice( 'SAVE_POST_NOTICE_ID' );
|
||||
dispatch( 'core/notices' ).createSuccessNotice(
|
||||
__(
|
||||
|
@ -107,21 +102,12 @@ const onboardingHomepageNotice = () => {
|
|||
|
||||
domReady( () => {
|
||||
const publishButton = document.querySelector(
|
||||
'.editor-post-publish-panel__toggle'
|
||||
'.editor-post-publish-button'
|
||||
);
|
||||
if ( publishButton ) {
|
||||
publishButton.addEventListener( 'click', function() {
|
||||
saveStarted().then( () => {
|
||||
const confirmButton = document.querySelector(
|
||||
'.editor-post-publish-button'
|
||||
);
|
||||
if ( confirmButton ) {
|
||||
confirmButton.addEventListener(
|
||||
'click',
|
||||
onboardingHomepageNotice
|
||||
);
|
||||
}
|
||||
} );
|
||||
} );
|
||||
publishButton.addEventListener(
|
||||
'click',
|
||||
saveStarted().then( () => onboardingHomepageNotice() )
|
||||
);
|
||||
}
|
||||
} );
|
||||
|
|
Before Width: | Height: | Size: 721 KiB |
Before Width: | Height: | Size: 533 KiB |
Before Width: | Height: | Size: 796 KiB |
Before Width: | Height: | Size: 1.3 MiB |
Before Width: | Height: | Size: 468 KiB |
Before Width: | Height: | Size: 1.0 MiB |
After Width: | Height: | Size: 78 KiB |
Before Width: | Height: | Size: 390 KiB |
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@woocommerce/admin-library",
|
||||
"version": "0.25.1",
|
||||
"version": "0.26.1",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@woocommerce/admin-library",
|
||||
"version": "0.25.1",
|
||||
"version": "0.26.1",
|
||||
"homepage": "https://woocommerce.github.io/woocommerce-admin/",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* External dependencies
|
||||
*/
|
||||
import { Button } from '@wordpress/components';
|
||||
import { decodeEntities } from '@wordpress/html-entities';
|
||||
import PropTypes from 'prop-types';
|
||||
import classnames from 'classnames';
|
||||
|
||||
|
@ -26,7 +27,7 @@ const DropdownButton = ( props ) => {
|
|||
>
|
||||
<div className="woocommerce-dropdown-button__labels">
|
||||
{ labels.map( ( label, i ) => (
|
||||
<span key={ i }>{ label }</span>
|
||||
<span key={ i }>{ decodeEntities( label ) }</span>
|
||||
) ) }
|
||||
</div>
|
||||
</Button>
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { decodeEntities } from '@wordpress/html-entities';
|
||||
|
||||
/**
|
||||
* @typedef {Object} Completer
|
||||
|
@ -24,14 +25,14 @@ export function computeSuggestionMatch( suggestion, query ) {
|
|||
.indexOf( query.toLocaleLowerCase() );
|
||||
|
||||
return {
|
||||
suggestionBeforeMatch: suggestion.substring( 0, indexOfMatch ),
|
||||
suggestionMatch: suggestion.substring(
|
||||
suggestionBeforeMatch: decodeEntities( suggestion.substring( 0, indexOfMatch ) ),
|
||||
suggestionMatch: decodeEntities( suggestion.substring(
|
||||
indexOfMatch,
|
||||
indexOfMatch + query.length
|
||||
),
|
||||
suggestionAfterMatch: suggestion.substring(
|
||||
) ),
|
||||
suggestionAfterMatch: decodeEntities( suggestion.substring(
|
||||
indexOfMatch + query.length
|
||||
),
|
||||
) ),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ import {
|
|||
* A search box which autocompletes results while typing, allowing for the user to select an existing object
|
||||
* (product, order, customer, etc). Currently only products are supported.
|
||||
*/
|
||||
class Search extends Component {
|
||||
export class Search extends Component {
|
||||
constructor( props ) {
|
||||
super( props );
|
||||
this.state = {
|
||||
|
@ -116,6 +116,10 @@ class Search extends Component {
|
|||
appendFreeTextSearch( options, query ) {
|
||||
const { allowFreeTextSearch } = this.props;
|
||||
|
||||
if ( ! query || ! query.length ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if ( ! allowFreeTextSearch ) {
|
||||
return options;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { Search } from '../index';
|
||||
import { computeSuggestionMatch } from '../autocompleters/utils';
|
||||
|
||||
describe( 'Search', () => {
|
||||
it( 'shows the free text search option', () => {
|
||||
const search = shallow(
|
||||
<Search type="products" allowFreeTextSearch />
|
||||
);
|
||||
const options = search
|
||||
.instance()
|
||||
.appendFreeTextSearch( [], 'Product Query' );
|
||||
|
||||
expect( options.length ).toBe( 1 );
|
||||
} );
|
||||
|
||||
it( "doesn't show options with an empty search", () => {
|
||||
const search = shallow(
|
||||
<Search type="products" allowFreeTextSearch />
|
||||
);
|
||||
const options = search.instance().appendFreeTextSearch( [], '' );
|
||||
|
||||
expect( options.length ).toBe( 0 );
|
||||
} );
|
||||
|
||||
it( 'returns an object with decoded text', () => {
|
||||
const decodedText = computeSuggestionMatch(
|
||||
'A test & a test',
|
||||
'test'
|
||||
);
|
||||
const expected =
|
||||
'{"suggestionBeforeMatch":"A ","suggestionMatch":"test","suggestionAfterMatch":" & a test"}';
|
||||
expect( JSON.stringify( decodedText ) ).toBe( expected );
|
||||
} );
|
||||
} );
|
|
@ -154,10 +154,6 @@ class List extends Component {
|
|||
selectedIndex,
|
||||
staticList,
|
||||
} = this.props;
|
||||
const optionsHaveValues = options ? options[ 0 ].value.id !== '' : false;
|
||||
if ( ! optionsHaveValues ) {
|
||||
return null;
|
||||
}
|
||||
const listboxClasses = classnames(
|
||||
'woocommerce-select-control__listbox',
|
||||
{
|
||||
|
|
|
@ -5,6 +5,7 @@ import { __, sprintf } from '@wordpress/i18n';
|
|||
import { Fragment } from '@wordpress/element';
|
||||
import classnames from 'classnames';
|
||||
import { Button, Dashicon, IconButton, Popover } from '@wordpress/components';
|
||||
import { decodeEntities } from '@wordpress/html-entities';
|
||||
import PropTypes from 'prop-types';
|
||||
import { withState, withInstanceId } from '@wordpress/compose';
|
||||
|
||||
|
@ -31,6 +32,7 @@ const Tag = ( {
|
|||
// @todo Maybe this should be a loading indicator?
|
||||
return null;
|
||||
}
|
||||
label = decodeEntities( label );
|
||||
const classes = classnames( 'woocommerce-tag', className, {
|
||||
'has-remove': !! remove,
|
||||
} );
|
||||
|
|
|
@ -71,6 +71,23 @@ Release and roadmap notes are available on the [WooCommerce Developers Blog](htt
|
|||
|
||||
== Changelog ==
|
||||
|
||||
= 0.26.0 2020-02-21 =
|
||||
|
||||
- Fix: Warning in product data store when tax amount is non-numeric. #3656
|
||||
- Fix: Enable onboarding in production. #3680
|
||||
- Enhancement: Move Customers report to WooCommerce Menu #3632
|
||||
- Performance: Remove slow physical products query from non setup checklist pages #3722
|
||||
- Tweak: use cron instead of Action Scheduler for unsnoozing notes. #3662
|
||||
- Dev: Add tracks events when profiler steps are completed #3726
|
||||
- Dev: Ensure continue setup loads the onboarding profiler #3646
|
||||
- Fix: Added new control in /packages/components/src/select-control/list.js #3700
|
||||
- Fix: Alignment of select text #3723 👏 @edmundcwm
|
||||
- Performance: Make Stock Panel indicator more performant. #3729
|
||||
- Performance: Remove sideloaded images to save on build size #3731
|
||||
- Fix: Create Onboarding homepage without redirect #3727
|
||||
- Add: Deactivation note for feature plugin #3687
|
||||
- Dev: Travis tests on Github for release branch #3751
|
||||
=======
|
||||
= 0.25.1 2020-02-07 =
|
||||
|
||||
- Dev: Enable onboarding #3651 (Onboarding)
|
||||
|
|
|
@ -240,11 +240,9 @@ class OnboardingProfile extends \WC_REST_Data_Controller {
|
|||
'description' => __( 'Industry.', 'woocommerce-admin' ),
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
'sanitize_callback' => 'wp_parse_slug_list',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
'items' => array(
|
||||
'enum' => array_keys( Onboarding::get_allowed_industries() ),
|
||||
'type' => 'string',
|
||||
'type' => 'json',
|
||||
),
|
||||
),
|
||||
'product_types' => array(
|
||||
|
|
|
@ -187,13 +187,13 @@ class OnboardingTasks extends \WC_REST_Data_Controller {
|
|||
private static function get_homepage_cover_block( $image ) {
|
||||
$shop_url = get_permalink( wc_get_page_id( 'shop' ) );
|
||||
if ( ! empty( $image['url'] ) && ! empty( $image['id'] ) ) {
|
||||
return '<!-- wp:cover {"url":"' . esc_url( $image['url'] ) . '","id":' . intval( $image['id'] ) . '} -->
|
||||
<div class="wp-block-cover has-background-dim" style="background-image:url(' . esc_url( $image['url'] ) . ')"><div class="wp-block-cover__inner-container"><!-- wp:paragraph {"align":"center","placeholder":"' . __( 'Write title…', 'woocommerce-admin' ) . '","fontSize":"large"} -->
|
||||
return '<!-- wp:cover {"url":"' . esc_url( $image['url'] ) . '","id":' . intval( $image['id'] ) . ',"dimRatio":0} -->
|
||||
<div class="wp-block-cover" style="background-image:url(' . esc_url( $image['url'] ) . ')"><div class="wp-block-cover__inner-container"><!-- wp:paragraph {"align":"center","placeholder":"' . __( 'Write title…', 'woocommerce-admin' ) . '","textColor":"white","fontSize":"large"} -->
|
||||
<p class="has-text-align-center has-large-font-size">' . __( 'Welcome to the store', 'woocommerce-admin' ) . '</p>
|
||||
<!-- /wp:paragraph -->
|
||||
|
||||
<!-- wp:paragraph {"align":"center"} -->
|
||||
<p class="has-text-align-center">' . __( 'Write a short welcome message here', 'woocommerce-admin' ) . '</p>
|
||||
<!-- wp:paragraph {"align":"center","textColor":"white"} -->
|
||||
<p class="has-text-color has-text-align-center">' . __( 'Write a short welcome message here', 'woocommerce-admin' ) . '</p>
|
||||
<!-- /wp:paragraph -->
|
||||
|
||||
<!-- wp:button {"align":"center"} -->
|
||||
|
@ -202,13 +202,13 @@ class OnboardingTasks extends \WC_REST_Data_Controller {
|
|||
<!-- /wp:cover -->';
|
||||
}
|
||||
|
||||
return '<!-- wp:cover {"overlayColor":"very-dark-gray"} -->
|
||||
<div class="wp-block-cover has-very-dark-gray-background-color has-background-dim"><div class="wp-block-cover__inner-container"><!-- wp:paragraph {"align":"center","placeholder":"' . __( 'Write title…', 'woocommerce-admin' ) . '","fontSize":"large"} -->
|
||||
<p class="has-text-align-center has-large-font-size">' . __( 'Welcome to the store', 'woocommerce-admin' ) . '</p>
|
||||
return '<!-- wp:cover {"dimRatio":0} -->
|
||||
<div class="wp-block-cover"><div class="wp-block-cover__inner-container"><!-- wp:paragraph {"align":"center","placeholder":"' . __( 'Write title…', 'woocommerce-admin' ) . '","textColor":"white","fontSize":"large"} -->
|
||||
<p class="has-text-color has-text-align-center has-large-font-size">' . __( 'Welcome to the store', 'woocommerce-admin' ) . '</p>
|
||||
<!-- /wp:paragraph -->
|
||||
|
||||
<!-- wp:paragraph {"align":"center"} -->
|
||||
<p class="has-text-align-center">' . __( 'Write a short welcome message here', 'woocommerce-admin' ) . '</p>
|
||||
<!-- wp:paragraph {"align":"center","textColor":"white"} -->
|
||||
<p class="has-text-color has-text-align-center">' . __( 'Write a short welcome message here', 'woocommerce-admin' ) . '</p>
|
||||
<!-- /wp:paragraph -->
|
||||
|
||||
<!-- wp:button {"align":"center"} -->
|
||||
|
@ -319,12 +319,7 @@ class OnboardingTasks extends \WC_REST_Data_Controller {
|
|||
$industry_images = array();
|
||||
$industries = Onboarding::get_allowed_industries();
|
||||
foreach ( $industries as $industry_slug => $label ) {
|
||||
$file_path = WC_ADMIN_ABSPATH . 'images/onboarding/' . $industry_slug . '.jpg';
|
||||
if ( 'other' === $industry_slug || ! file_exists( $file_path ) ) {
|
||||
$industry_images[ $industry_slug ] = apply_filters( 'woocommerce_admin_onboarding_industry_image', plugins_url( 'images/onboarding/other.jpg', WC_ADMIN_PLUGIN_FILE ), $industry_slug );
|
||||
continue;
|
||||
}
|
||||
$industry_images[ $industry_slug ] = apply_filters( 'woocommerce_admin_onboarding_industry_image', plugins_url( 'images/onboarding/' . $industry_slug . '.jpg', WC_ADMIN_PLUGIN_FILE ), $industry_slug );
|
||||
$industry_images[ $industry_slug ] = apply_filters( 'woocommerce_admin_onboarding_industry_image', plugins_url( 'images/onboarding/other-small.jpg', WC_ADMIN_PLUGIN_FILE ), $industry_slug );
|
||||
}
|
||||
return $industry_images;
|
||||
}
|
||||
|
@ -347,14 +342,25 @@ class OnboardingTasks extends \WC_REST_Data_Controller {
|
|||
|
||||
if ( ! empty( $profile['industry'] ) ) {
|
||||
foreach ( $profile['industry'] as $selected_industry ) {
|
||||
$images_to_sideload[] = ! empty( $available_images[ $selected_industry ] ) ? $available_images[ $selected_industry ] : $available_images['other'];
|
||||
if ( is_string( $selected_industry ) ) {
|
||||
$industry_slug = $selected_industry;
|
||||
} elseif ( is_array( $selected_industry ) && ! empty( $selected_industry['slug'] ) ) {
|
||||
$industry_slug = $selected_industry['slug'];
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
// Capture the first industry for use in our minimum images logic.
|
||||
$first_industry = isset( $first_industry ) ? $first_industry : $industry_slug;
|
||||
$images_to_sideload[] = ! empty( $available_images[ $industry_slug ] ) ? $available_images[ $industry_slug ] : $available_images['other'];
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure we have at least {$number_of_images} images.
|
||||
if ( count( $images_to_sideload ) < $number_of_images ) {
|
||||
for ( $i = count( $images_to_sideload ); $i < $number_of_images; $i++ ) {
|
||||
$images_to_sideload[] = ! empty( $profile['industry'] ) && ! empty( $available_images[ $profile['industry'][0] ] ) ? $available_images[ $profile['industry'][0] ] : $available_images['other'];
|
||||
// Fill up missing image slots with the first selected industry, or other.
|
||||
$industry = isset( $first_industry ) ? $first_industry : 'other';
|
||||
$images_to_sideload[] = empty( $available_images[ $industry ] ) ? $available_images['other'] : $available_images[ $industry ];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -402,7 +408,7 @@ class OnboardingTasks extends \WC_REST_Data_Controller {
|
|||
array(
|
||||
'post_title' => __( 'Homepage', 'woocommerce-admin' ),
|
||||
'post_type' => 'page',
|
||||
'post_status' => 'draft',
|
||||
'post_status' => 'publish',
|
||||
'post_content' => '', // Template content is updated below, so images can be attached to the post.
|
||||
)
|
||||
);
|
||||
|
@ -417,11 +423,13 @@ class OnboardingTasks extends \WC_REST_Data_Controller {
|
|||
)
|
||||
);
|
||||
|
||||
update_option( 'show_on_front', 'page' );
|
||||
update_option( 'page_on_front', $post_id );
|
||||
update_option( 'woocommerce_onboarding_homepage_post_id', $post_id );
|
||||
|
||||
return array(
|
||||
'status' => 'success',
|
||||
'message' => __( 'Homepage created successfully.', 'woocommerce-admin' ),
|
||||
'message' => __( 'Homepage created.', 'woocommerce-admin' ),
|
||||
'post_id' => $post_id,
|
||||
'edit_post_link' => htmlspecialchars_decode( get_edit_post_link( $post_id ) ),
|
||||
);
|
||||
|
|
|
@ -127,6 +127,7 @@ class Products extends \WC_REST_Products_Controller {
|
|||
if ( $request->get_param( 'low_in_stock' ) && is_numeric( $object_data['low_stock_amount'] ) ) {
|
||||
$data->data['low_stock_amount'] = $object_data['low_stock_amount'];
|
||||
}
|
||||
$data->data['name'] = wp_strip_all_tags( $data->data['name'] );
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
|
|
@ -70,7 +70,10 @@ class Controller extends \WC_REST_Reports_Controller implements ExportableInterf
|
|||
$data = array();
|
||||
|
||||
foreach ( $products_data->data as $product_data ) {
|
||||
$item = $this->prepare_item_for_response( $product_data, $request );
|
||||
$item = $this->prepare_item_for_response( $product_data, $request );
|
||||
if ( isset( $item->data['extended_info']['name'] ) ) {
|
||||
$item->data['extended_info']['name'] = wp_strip_all_tags( $item->data['extended_info']['name'] );
|
||||
}
|
||||
$data[] = $this->prepare_response_for_collection( $item );
|
||||
}
|
||||
|
||||
|
|
|
@ -290,7 +290,7 @@ class Controller extends \WC_REST_Reports_Controller implements ExportableInterf
|
|||
$data = array(
|
||||
'id' => $product->get_id(),
|
||||
'parent_id' => $product->get_parent_id(),
|
||||
'name' => $product->get_name(),
|
||||
'name' => wp_strip_all_tags( $product->get_name() ),
|
||||
'sku' => $product->get_sku(),
|
||||
'stock_status' => $product->get_stock_status(),
|
||||
'stock_quantity' => (float) $product->get_stock_quantity(),
|
||||
|
|
|
@ -148,7 +148,7 @@ class FeaturePlugin {
|
|||
$this->define( 'WC_ADMIN_PLUGIN_FILE', WC_ADMIN_ABSPATH . 'woocommerce-admin.php' );
|
||||
// WARNING: Do not directly edit this version number constant.
|
||||
// It is updated as part of the prebuild process from the package.json value.
|
||||
$this->define( 'WC_ADMIN_VERSION_NUMBER', '0.25.1' );
|
||||
$this->define( 'WC_ADMIN_VERSION_NUMBER', '0.26.1' );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -22,6 +22,11 @@ class ActivityPanels {
|
|||
*/
|
||||
protected static $instance = null;
|
||||
|
||||
/**
|
||||
* Low Stock Transient Name.
|
||||
*/
|
||||
const LOW_STOCK_TRANSIENT_NAME = 'woocommerce_admin_low_out_of_stock_count';
|
||||
|
||||
/**
|
||||
* Get class instance.
|
||||
*/
|
||||
|
@ -42,6 +47,7 @@ class ActivityPanels {
|
|||
// New settings injection.
|
||||
add_filter( 'woocommerce_shared_settings', array( $this, 'component_settings' ), 20 );
|
||||
add_action( 'woocommerce_updated', array( $this, 'woocommerce_updated_note' ) );
|
||||
add_action( 'woocommerce_update_product', array( __CLASS__, 'clear_low_out_of_stock_count_transient' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -60,13 +66,55 @@ class ActivityPanels {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if there are out of stock or low stock products.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function has_low_stock_products() {
|
||||
global $wpdb;
|
||||
|
||||
// Bail early if store does not manage stock, or Woo version < 3.6 needs lookup tables.
|
||||
if (
|
||||
'yes' !== get_option( 'woocommerce_manage_stock' ) ||
|
||||
version_compare( get_option( 'woocommerce_db_version', null ), '3.6', '<' )
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$low_stock_out_of_stock_count = get_transient( self::LOW_STOCK_TRANSIENT_NAME );
|
||||
|
||||
if ( false === $low_stock_out_of_stock_count ) {
|
||||
$low_stock_out_of_stock_count = (int) $wpdb->get_var(
|
||||
"SELECT COUNT( product_id )
|
||||
FROM {$wpdb->wc_product_meta_lookup} AS lookup
|
||||
INNER JOIN {$wpdb->posts} as posts ON lookup.product_id = posts.ID
|
||||
WHERE stock_status IN ( 'onbackorder', 'outofstock' )
|
||||
AND posts.post_status = 'publish'"
|
||||
);
|
||||
set_transient( self::LOW_STOCK_TRANSIENT_NAME, $low_stock_out_of_stock_count, HOUR_IN_SECONDS );
|
||||
}
|
||||
return $low_stock_out_of_stock_count > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears transient for out of stock indicator
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function clear_low_out_of_stock_count_transient() {
|
||||
delete_transient( self::LOW_STOCK_TRANSIENT_NAME );
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add alert count to the component settings.
|
||||
*
|
||||
* @param array $settings Component settings.
|
||||
*/
|
||||
public function component_settings( $settings ) {
|
||||
$settings['alertCount'] = WC_Admin_Notes::get_notes_count( array( 'error', 'update' ), array( 'unactioned' ) );
|
||||
$settings['alertCount'] = WC_Admin_Notes::get_notes_count( array( 'error', 'update' ), array( 'unactioned' ) );
|
||||
$settings['hasLowStock'] = $this->has_low_stock_products();
|
||||
return $settings;
|
||||
}
|
||||
|
||||
|
|
|
@ -182,16 +182,50 @@ class Onboarding {
|
|||
* @return array
|
||||
*/
|
||||
public static function get_allowed_industries() {
|
||||
/* With "use_description" we turn the description input on. With "description_label" we set the input label */
|
||||
return apply_filters(
|
||||
'woocommerce_admin_onboarding_industries',
|
||||
array(
|
||||
'fashion-apparel-accessories' => __( 'Fashion, apparel, and accessories', 'woocommerce-admin' ),
|
||||
'health-beauty' => __( 'Health and beauty', 'woocommerce-admin' ),
|
||||
'art-music-photography' => __( 'Art, music, and photography', 'woocommerce-admin' ),
|
||||
'electronics-computers' => __( 'Electronics and computers', 'woocommerce-admin' ),
|
||||
'food-drink' => __( 'Food and drink', 'woocommerce-admin' ),
|
||||
'home-furniture-garden' => __( 'Home, furniture, and garden', 'woocommerce-admin' ),
|
||||
'other' => __( 'Other', 'woocommerce-admin' ),
|
||||
'fashion-apparel-accessories' => array(
|
||||
'label' => __( 'Fashion, apparel, and accessories', 'woocommerce-admin' ),
|
||||
'use_description' => false,
|
||||
'description_label' => '',
|
||||
),
|
||||
'health-beauty' => array(
|
||||
'label' => __( 'Health and beauty', 'woocommerce-admin' ),
|
||||
'use_description' => false,
|
||||
'description_label' => '',
|
||||
),
|
||||
'art-music-photography' => array(
|
||||
'label' => __( 'Art, music, and photography', 'woocommerce-admin' ),
|
||||
'use_description' => false,
|
||||
'description_label' => '',
|
||||
),
|
||||
'electronics-computers' => array(
|
||||
'label' => __( 'Electronics and computers', 'woocommerce-admin' ),
|
||||
'use_description' => false,
|
||||
'description_label' => '',
|
||||
),
|
||||
'food-drink' => array(
|
||||
'label' => __( 'Food and drink', 'woocommerce-admin' ),
|
||||
'use_description' => false,
|
||||
'description_label' => '',
|
||||
),
|
||||
'home-furniture-garden' => array(
|
||||
'label' => __( 'Home, furniture, and garden', 'woocommerce-admin' ),
|
||||
'use_description' => false,
|
||||
'description_label' => '',
|
||||
),
|
||||
'cbd-other-hemp-derived-products' => array(
|
||||
'label' => __( 'CBD and other hemp-derived products', 'woocommerce-admin' ),
|
||||
'use_description' => false,
|
||||
'description_label' => '',
|
||||
),
|
||||
'other' => array(
|
||||
'label' => __( 'Other', 'woocommerce-admin' ),
|
||||
'use_description' => true,
|
||||
'description_label' => 'Description',
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
<?php
|
||||
/**
|
||||
* WooCommerce Admin: Update version reminder note.
|
||||
*
|
||||
* Creates a note to nudge users to use the newer version when two are installed.
|
||||
*
|
||||
* @package WooCommerce Admin
|
||||
*/
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Notes;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* WC_Admin_Notes_Deactivate_Plugin.
|
||||
*/
|
||||
class WC_Admin_Notes_Deactivate_Plugin {
|
||||
const NOTE_NAME = 'wc-admin-deactivate-plugin';
|
||||
|
||||
/**
|
||||
* Attach hooks.
|
||||
*/
|
||||
public function __construct() {
|
||||
add_action( 'init', array( $this, 'deactivate_feature_plugin' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the note to deactivate the older version.
|
||||
*/
|
||||
public static function add_note() {
|
||||
$data_store = \WC_Data_Store::load( 'admin-note' );
|
||||
$note_ids = $data_store->get_notes_with_name( self::NOTE_NAME );
|
||||
if ( ! empty( $note_ids ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$note = new WC_Admin_Note();
|
||||
$note->set_title( __( 'Deactivate old WooCommerce Admin version', 'woocommerce-admin' ) );
|
||||
$note->set_content( __( 'Your current version of WooCommerce Admin is outdated and a newer version is included with WooCommerce. We recommend deactivating the plugin and using the stable version included with WooCommerce.', 'woocommerce-admin' ) );
|
||||
$note->set_type( WC_Admin_Note::E_WC_ADMIN_NOTE_UPDATE );
|
||||
$note->set_icon( 'warning' );
|
||||
$note->set_name( self::NOTE_NAME );
|
||||
$note->set_content_data( (object) array() );
|
||||
$note->set_source( 'woocommerce-admin' );
|
||||
$note->add_action(
|
||||
'deactivate-feature-plugin',
|
||||
__( 'Deactivate', 'woocommerce-admin' ),
|
||||
wc_admin_url( '&action=deactivate-feature-plugin' ),
|
||||
'unactioned',
|
||||
true
|
||||
);
|
||||
$note->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the note if the version is higher than the included.
|
||||
*/
|
||||
public static function delete_note() {
|
||||
WC_Admin_Notes::delete_notes_with_name( self::NOTE_NAME );
|
||||
}
|
||||
|
||||
/**
|
||||
* Deactivate feature plugin.
|
||||
*/
|
||||
public function deactivate_feature_plugin() {
|
||||
/* phpcs:disable WordPress.Security.NonceVerification */
|
||||
if (
|
||||
! isset( $_GET['page'] ) ||
|
||||
'wc-admin' !== $_GET['page'] ||
|
||||
! isset( $_GET['action'] ) ||
|
||||
'deactivate-feature-plugin' !== $_GET['action'] ||
|
||||
! defined( 'WC_ADMIN_PLUGIN_FILE' )
|
||||
) {
|
||||
return;
|
||||
}
|
||||
/* phpcs:enable */
|
||||
|
||||
$deactivate_url = admin_url( 'plugins.php?action=deactivate&plugin=' . rawurlencode( WC_ADMIN_PLUGIN_FILE ) . '&plugin_status=all&paged=1&_wpnonce=' . wp_create_nonce( 'deactivate-plugin_' . WC_ADMIN_PLUGIN_FILE ) );
|
||||
wp_safe_redirect( $deactivate_url );
|
||||
exit;
|
||||
}
|
||||
}
|
|
@ -5,10 +5,17 @@
|
|||
* @package Automattic/WooCommerce/WCAdmin
|
||||
*/
|
||||
|
||||
namespace Automattic\WooCommerce\Admin;
|
||||
/**
|
||||
* This namespace isn't compatible with the PSR-4
|
||||
* which ensures that the copy in the standalone plugin will not be autoloaded.
|
||||
*/
|
||||
namespace Automattic\WooCommerce\Admin\Composer;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
use Automattic\WooCommerce\Admin\Notes\WC_Admin_Notes_Deactivate_Plugin;
|
||||
use Automattic\WooCommerce\Admin\FeaturePlugin;
|
||||
|
||||
/**
|
||||
* Main package class.
|
||||
*/
|
||||
|
@ -19,7 +26,14 @@ class Package {
|
|||
*
|
||||
* @var string
|
||||
*/
|
||||
const VERSION = '0.25.1';
|
||||
const VERSION = '0.26.1';
|
||||
|
||||
/**
|
||||
* Package active.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private static $package_active = false;
|
||||
|
||||
/**
|
||||
* Init the package.
|
||||
|
@ -39,9 +53,20 @@ class Package {
|
|||
|
||||
// Avoid double initialization when the feature plugin is in use.
|
||||
if ( defined( 'WC_ADMIN_VERSION_NUMBER' ) ) {
|
||||
$update_version = new WC_Admin_Notes_Deactivate_Plugin();
|
||||
if ( version_compare( WC_ADMIN_VERSION_NUMBER, self::VERSION, '<' ) ) {
|
||||
$update_version::add_note();
|
||||
} else {
|
||||
$update_version::delete_note();
|
||||
}
|
||||
|
||||
// Register a deactivation hook for the feature plugin.
|
||||
register_deactivation_hook( WC_ADMIN_PLUGIN_FILE, array( __CLASS__, 'on_deactivation' ) );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
self::$package_active = true;
|
||||
FeaturePlugin::instance()->init();
|
||||
}
|
||||
|
||||
|
@ -54,6 +79,24 @@ class Package {
|
|||
return self::VERSION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the active version of WC Admin.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_active_version() {
|
||||
return self::$package_active ? self::VERSION : WC_ADMIN_VERSION_NUMBER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the package is active.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_package_active() {
|
||||
return self::$package_active;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the path to the package.
|
||||
*
|
||||
|
@ -62,4 +105,12 @@ class Package {
|
|||
public static function get_path() {
|
||||
return dirname( __DIR__ );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add deactivation hook for versions of the plugin that don't have the deactivation note.
|
||||
*/
|
||||
public static function on_deactivation() {
|
||||
$update_version = new WC_Admin_Notes_Deactivate_Plugin();
|
||||
$update_version::delete_note();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* Author URI: https://woocommerce.com/
|
||||
* Text Domain: woocommerce-admin
|
||||
* Domain Path: /languages
|
||||
* Version: 0.25.1
|
||||
* Version: 0.26.1
|
||||
* Requires at least: 5.3.0
|
||||
* Requires PHP: 5.6.20
|
||||
*
|
||||
|
|