isters a font collection from the Font Library.
*
* @since 6.5.0
*
* @param string $slug Font collection slug.
* @return bool True if the font collection was unregistered successfully, else false.
*/
function wp_unregister_font_collection( string $slug ) {
return WP_Font_Library::get_instance()->unregister_font_collection( $slug );
}
/**
* Retrieves font uploads directory information.
*
* Same as wp_font_dir() but "light weight" as it doesn't attempt to create the font uploads directory.
* Intended for use in themes, when only 'basedir' and 'baseurl' are needed, generally in all cases
* when not uploading files.
*
* @since 6.5.0
*
* @see wp_font_dir()
*
* @return array See wp_font_dir() for description.
*/
function wp_get_font_dir() {
return wp_font_dir( false );
}
/**
* Returns an array containing the current fonts upload directory's path and URL.
*
* @since 6.5.0
*
* @param bool $create_dir Optional. Whether to check and create the font uploads directory. Default true.
* @return array {
* Array of information about the font upload directory.
*
* @type string $path Base directory and subdirectory or full path to the fonts upload directory.
* @type string $url Base URL and subdirectory or absolute URL to the fonts upload directory.
* @type string $subdir Subdirectory
* @type string $basedir Path without subdir.
* @type string $baseurl URL path without subdir.
* @type string|false $error False or error message.
* }
*/
function wp_font_dir( $create_dir = true ) {
/*
* Allow extenders to manipulate the font directory consistently.
*
* Ensures the upload_dir filter is fired both when calling this function
* directly and when the upload directory is filtered in the Font Face
* REST API endpoint.
*/
add_filter( 'upload_dir', '_wp_filter_font_directory' );
$font_dir = wp_upload_dir( null, $create_dir, false );
remove_filter( 'upload_dir', '_wp_filter_font_directory' );
return $font_dir;
}
/**
* A callback function for use in the {@see 'upload_dir'} filter.
*
* This function is intended for internal use only and should not be used by plugins and themes.
* Use wp_get_font_dir() instead.
*
* @since 6.5.0
* @access private
*
* @param string $font_dir The font directory.
* @return string The modified font directory.
*/
function _wp_filter_font_directory( $font_dir ) {
if ( doing_filter( 'font_dir' ) ) {
// Avoid an infinite loop.
return $font_dir;
}
$font_dir = array(
'path' => untrailingslashit( $font_dir['basedir'] ) . '/fonts',
'url' => untrailingslashit( $font_dir['baseurl'] ) . '/fonts',
'subdir' => '',
'basedir' => untrailingslashit( $font_dir['basedir'] ) . '/fonts',
'baseurl' => untrailingslashit( $font_dir['baseurl'] ) . '/fonts',
'error' => false,
);
/**
* Filters the fonts directory data.
*
* This filter allows developers to modify the fonts directory data.
*
* @since 6.5.0
*
* @param array $font_dir {
* Array of information about the font upload directory.
*
* @type string $path Base directory and subdirectory or full path to the fonts upload directory.
* @type string $url Base URL and subdirectory or absolute URL to the fonts upload directory.
* @type string $subdir Subdirectory
* @type string $basedir Path without subdir.
* @type string $baseurl URL path without subdir.
* @type string|false $error False or error message.
* }
*/
return apply_filters( 'font_dir', $font_dir );
}
/**
* Deletes child font faces when a font family is deleted.
*
* @access private
* @since 6.5.0
*
* @param int $post_id Post ID.
* @param WP_Post $post Post object.
*/
function _wp_after_delete_font_family( $post_id, $post ) {
if ( 'wp_font_family' !== $post->post_type ) {
return;
}
$font_faces = get_children(
array(
'post_parent' => $post_id,
'post_type' => 'wp_font_face',
)
);
foreach ( $font_faces as $font_face ) {
wp_delete_post( $font_face->ID, true );
}
}
/**
* Deletes associated font files when a font face is deleted.
*
* @access private
* @since 6.5.0
*
* @param int $post_id Post ID.
* @param WP_Post $post Post object.
*/
function _wp_before_delete_font_face( $post_id, $post ) {
if ( 'wp_font_face' !== $post->post_type ) {
return;
}
$font_files = get_post_meta( $post_id, '_wp_font_face_file', false );
$font_dir = untrailingslashit( wp_get_font_dir()['basedir'] );
foreach ( $font_files as $font_file ) {
wp_delete_file( $font_dir . '/' . $font_file );
}
}
/**
* Register the default font collections.
*
* @access private
* @since 6.5.0
*/
function _wp_register_default_font_collections() {
wp_register_font_collection(
'google-fonts',
array(
'name' => _x( 'Google Fonts', 'font collection name' ),
'description' => __( 'Install from Google Fonts. Fonts are copied to and served from your site.' ),
'font_families' => 'https://s.w.org/images/fonts/wp-6.7/collections/google-fonts-with-preview.json',
'categories' => array(
array(
'name' => _x( 'Sans Serif', 'font category' ),
'slug' => 'sans-serif',
),
array(
'name' => _x( 'Display', 'font category' ),
'slug' => 'display',
),
array(
'name' => _x( 'Serif', 'font category' ),
'slug' => 'serif',
),
array(
'name' => _x( 'Handwriting', 'font category' ),
'slug' => 'handwriting',
),
array(
'name' => _x( 'Monospace', 'font category' ),
'slug' => 'monospace',
),
),
)
);
}
always resume up at the last pending or unfinished step.
* If the Ads account has already been created, the ID is simply returned.
*
* @return array The newly created (or pre-existing) Ads ID.
* @throws Exception If an error occurs during any step.
*/
public function setup_account(): array {
$state = $this->state->get();
$ads_id = $this->options->get_ads_id();
$account = [ 'id' => $ads_id ];
foreach ( $state as $name => &$step ) {
if ( AdsAccountState::STEP_DONE === $step['status'] ) {
continue;
}
try {
switch ( $name ) {
case 'set_id':
// Just in case, don't create another Ads ID.
if ( ! empty( $ads_id ) ) {
break;
}
$account = $this->container->get( Middleware::class )->create_ads_account();
$step['data']['sub_account'] = true;
$step['data']['created_timestamp'] = time();
break;
case 'billing':
$this->check_billing_status( $account );
break;
case 'conversion_action':
$this->create_conversion_action();
break;
case 'link_merchant':
// Continue to next step if the MC account is not connected yet.
if ( ! $this->options->get_merchant_id() ) {
// Save step as pending and continue the foreach loop with `continue 2`.
$state[ $name ]['status'] = AdsAccountState::STEP_PENDING;
$this->state->update( $state );
continue 2;
}
$this->link_merchant_account();
break;
case 'account_access':
$this->check_ads_account_has_access();
break;
default:
throw new Exception(
/* translators: 1: is a string representing an unknown step name */
sprintf( __( 'Unknown ads account creation step %1$s', 'google-listings-and-ads' ), $name )
);
}
$step['status'] = AdsAccountState::STEP_DONE;
$step['message'] = '';
$this->state->update( $state );
} catch ( Exception $e ) {
$step['status'] = AdsAccountState::STEP_ERROR;
$step['message'] = $e->getMessage();
$this->state->update( $state );
throw $e;
}
}
return $account;
}
/**
* Gets the billing setup status and returns a setup URL if available.
*
* @return array
*/
public function get_billing_status(): array {
$status = $this->container->get( Ads::class )->get_billing_status();
if ( BillingSetupStatus::APPROVED === $status ) {
$this->state->complete_step( 'billing' );
return [ 'status' => $status ];
}
$billing_url = $this->options->get( OptionsInterface::ADS_BILLING_URL );
// Check if user has provided the access and ocid is present.
$connection_status = $this->container->get( Connection::class )->get_status();
$email = $connection_status['email'] ?? '';
$has_access = $this->container->get( Ads::class )->has_access( $email );
$ocid = $this->options->get( OptionsInterface::ADS_ACCOUNT_OCID, null );
// Link directly to the payment page if the customer already has access.
if ( $has_access ) {
$billing_url = add_query_arg(
[
'ocid' => $ocid ?: 0,
],
'https://ads.google.com/aw/signup/payment'
);
}
return [
'status' => $status,
'billing_url' => $billing_url,
];
}
/**
* Check if the Ads account has access.
*
* @throws ExceptionWithResponseData If the account doesn't have access.
*/
private function check_ads_account_has_access() {
$access_status = $this->get_ads_account_has_access();
if ( ! $access_status['has_access'] ) {
throw new ExceptionWithResponseData(
__( 'Account must be accepted before completing setup.', 'google-listings-and-ads' ),
428,
null,
$access_status
);
}
}
/**
* Gets the Ads account access status.
*
* @return array {
* Returns the access status, last completed account setup step,
* and invite link if available.
*
* @type bool $has_access Whether the customer has access to the account.
* @type string $step The last completed setup step for the Ads account.
* @type string $invite_link The URL to the invite link.
* }
*/
public function get_ads_account_has_access() {
$has_access = false;
// Check if an Ads ID is present.
if ( $this->options->get_ads_id() ) {
$connection_status = $this->container->get( Connection::class )->get_status();
$email = $connection_status['email'] ?? '';
}
// If no email, means google account is not connected.
if ( ! empty( $email ) ) {
$has_access = $this->container->get( Ads::class )->has_access( $email );
}
// If we have access, complete the step so that it won't be called next time.
if ( $has_access ) {
$this->state->complete_step( 'account_access' );
}
return [
'has_access' => $has_access,
'step' => $this->state->last_incomplete_step(),
'invite_link' => $this->options->get( OptionsInterface::ADS_BILLING_URL, '' ),
];
}
/**
* Disconnect Ads account
*/
public function disconnect() {
$this->options->delete( OptionsInterface::ADS_ACCOUNT_CURRENCY );
$this->options->delete( OptionsInterface::ADS_ACCOUNT_OCID );
$this->options->delete( OptionsInterface::ADS_ACCOUNT_STATE );
$this->options->delete( OptionsInterface::ADS_BILLING_URL );
$this->options->delete( OptionsInterface::ADS_CONVERSION_ACTION );
$this->options->delete( OptionsInterface::ADS_ID );
$this->options->delete( OptionsInterface::ADS_SETUP_COMPLETED_AT );
$this->options->delete( OptionsInterface::CAMPAIGN_CONVERT_STATUS );
$this->container->get( TransientsInterface::class )->delete( TransientsInterface::ADS_CAMPAIGN_COUNT );
}
/**
* Confirm the billing flow has been completed.
*
* @param array $account Account details.
*
* @throws ExceptionWithResponseData If this step hasn't been completed yet.
*/
private function check_billing_status( array $account ) {
$status = BillingSetupStatus::UNKNOWN;
// Only check billing status if we haven't just created the account.
if ( empty( $account['billing_url'] ) ) {
$status = $this->container->get( Ads::class )->get_billing_status();
}
if ( BillingSetupStatus::APPROVED !== $status ) {
throw new ExceptionWithResponseData(
__( 'Billing setup must be completed.', 'google-listings-and-ads' ),
428,
null,
[
'billing_url' => $this->options->get( OptionsInterface::ADS_BILLING_URL ),
'billing_status' => $status,
]
);
}
}
/**
* Get the callback function for linking a merchant account.
*
* @throws Exception When the ads account hasn't been set yet.
*/
private function link_merchant_account() {
if ( ! $this->options->get_ads_id() ) {
throw new Exception( 'An Ads account must be connected' );
}
$mc_state = $this->container->get( MerchantAccountState::class );
// Create link for Merchant and accept it in Ads.
$this->container->get( Merchant::class )->link_ads_id( $this->options->get_ads_id() );
$this->container->get( Ads::class )->accept_merchant_link( $this->options->get_merchant_id() );
$mc_state->complete_step( 'link_ads' );
}
/**
* Create the generic GLA conversion action and store the details as an option.
*
* @throws Exception If the conversion action can't be created.
*/
private function create_conversion_action(): void {
$action = $this->container->get( AdsConversionAction::class )->create_conversion_action();
$this->options->update( OptionsInterface::ADS_CONVERSION_ACTION, $action );
}
}
Warning: class_implements(): Class Automattic\WooCommerce\GoogleListingsAndAds\Ads\AccountService does not exist and could not be loaded in /htdocs/wp-content/plugins/google-listings-and-ads/src/Internal/DependencyManagement/AbstractServiceProvider.php on line 119
Fatal error: Uncaught TypeError: array_key_exists(): Argument #2 ($array) must be of type array, bool given in /htdocs/wp-content/plugins/google-listings-and-ads/src/Internal/DependencyManagement/AbstractServiceProvider.php:120
Stack trace:
#0 /htdocs/wp-content/plugins/google-listings-and-ads/src/Internal/DependencyManagement/AbstractServiceProvider.php(120): array_key_exists('Automattic\\WooC...', false)
#1 /htdocs/wp-content/plugins/google-listings-and-ads/src/Internal/DependencyManagement/CoreServiceProvider.php(308): Automattic\WooCommerce\GoogleListingsAndAds\Internal\DependencyManagement\AbstractServiceProvider->conditionally_share_with_tags('Automattic\\WooC...', 'Automattic\\WooC...')
#2 /htdocs/wp-content/plugins/google-listings-and-ads/vendor/league/container/src/ServiceProvider/ServiceProviderAggregate.php(102): Automattic\WooCommerce\GoogleListingsAndAds\Internal\DependencyManagement\CoreServiceProvider->register()
#3 /htdocs/wp-content/plugins/google-listings-and-ads/vendor/league/container/src/Container.php(172): Automattic\WooCommerce\GoogleListingsAndAds\Vendor\League\Container\ServiceProvider\ServiceProviderAggregate->register('Automattic\\WooC...')
#4 /htdocs/wp-content/plugins/google-listings-and-ads/src/Container.php(90): Automattic\WooCommerce\GoogleListingsAndAds\Vendor\League\Container\Container->get('Automattic\\WooC...')
#5 /htdocs/wp-content/plugins/google-listings-and-ads/src/Infrastructure/GoogleListingsAndAdsPlugin.php(130): Automattic\WooCommerce\GoogleListingsAndAds\Container->get('Automattic\\WooC...')
#6 /htdocs/wp-content/plugins/google-listings-and-ads/src/Infrastructure/GoogleListingsAndAdsPlugin.php(91): Automattic\WooCommerce\GoogleListingsAndAds\Infrastructure\GoogleListingsAndAdsPlugin->maybe_register_services()
#7 /htdocs/wp-includes/class-wp-hook.php(324): Automattic\WooCommerce\GoogleListingsAndAds\Infrastructure\GoogleListingsAndAdsPlugin->Automattic\WooCommerce\GoogleListingsAndAds\Infrastructure\{closure}('')
#8 /htdocs/wp-includes/class-wp-hook.php(348): WP_Hook->apply_filters(NULL, Array)
#9 /htdocs/wp-includes/plugin.php(517): WP_Hook->do_action(Array)
#10 /htdocs/wp-settings.php(559): do_action('plugins_loaded')
#11 /htdocs/wp-config.php(85): require_once('/htdocs/wp-sett...')
#12 /htdocs/wp-load.php(50): require_once('/htdocs/wp-conf...')
#13 /htdocs/wp-blog-header.php(13): require_once('/htdocs/wp-load...')
#14 /htdocs/index.php(17): require('/htdocs/wp-blog...')
#15 {main}
thrown in /htdocs/wp-content/plugins/google-listings-and-ads/src/Internal/DependencyManagement/AbstractServiceProvider.php on line 120