/home/lnzliplg/www/aioseo-local-business.tar
aioseo-local-business.php000064400000006572151727277620011507 0ustar00<?php
/**
 * Plugin Name: AIOSEO - Local Business
 * Plugin URI:  https://aioseo.com
 * Description: Adds Local Business support to All in One SEO.
 * Author:      All in One SEO Team
 * Author URI:  https://aioseo.com
 * Version:     1.3.3
 * Text Domain: aioseo-local-business
 * Domain Path: languages
 *
 * All in One SEO is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * any later version.
 *
 * All in One SEO is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with All in One SEO. If not, see <https://www.gnu.org/licenses/>.
 *
 * @since     1.0.0
 * @author    All in One SEO
 * @package   AIOSEO\Extend\LocalBusiness
 * @license   GPL-2.0+
 * @copyright Copyright (c) 2020, All in One SEO
 */

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

// Plugin constants.
define( 'AIOSEO_LOCAL_BUSINESS_FILE', __FILE__ );
define( 'AIOSEO_LOCAL_BUSINESS_DIR', __DIR__ );
define( 'AIOSEO_LOCAL_BUSINESS_PATH', plugin_dir_path( AIOSEO_LOCAL_BUSINESS_FILE ) );
define( 'AIOSEO_LOCAL_BUSINESS_URL', plugin_dir_url( AIOSEO_LOCAL_BUSINESS_FILE ) );

// Require our translation downloader.
require_once __DIR__ . '/extend/translations.php';

add_action( 'init', 'aioseo_local_business_translations' );
function aioseo_local_business_translations() {
	$translations = new AIOSEOTranslations(
		'plugin',
		'aioseo-local-business',
		'https://aioseo.com/aioseo-plugin/aioseo-local-business/packages.json'
	);
	$translations->init();

	// @NOTE: The slugs here need to stay as aioseo-addon.
	$addonTranslations = new AIOSEOTranslations(
		'plugin',
		'aioseo-addon',
		'https://aioseo.com/aioseo-plugin/aioseo-addon/packages.json'
	);
	$addonTranslations->init();
}

// Require our plugin compatibility checker.
require_once __DIR__ . '/extend/init.php';

// Check if this plugin should be disabled.
if ( aioseoAddonIsDisabled( 'aioseo-local-business' ) ) {
	return;
}

// Plugin compatibility checks.
new AIOSEOExtend( 'AIOSEO - Local Business', 'aioseo_local_business_load', AIOSEO_LOCAL_BUSINESS_FILE, '4.6.4' );

/**
 * Function to load the addon.
 *
 * @since 1.0.0
 *
 * @return void
 */
function aioseo_local_business_load() {
	$levels = aioseo()->addons->getAddonLevels( 'aioseo-local-business' );
	$extend = new AIOSEOExtend( 'AIOSEO - Local Business', '', AIOSEO_LOCAL_BUSINESS_FILE, '4.6.4', $levels );

	$addon = aioseo()->addons->getAddon( 'aioseo-local-business' );
	if ( ! $addon->hasMinimumVersion ) {
		$extend->requiresUpdate();

		return;
	}

	if ( ! aioseo()->pro ) {
		$extend->requiresPro();

		return;
	}

	// We don't want to return if the plan is only expired.
	if ( aioseo()->license->isExpired() ) {
		$extend->requiresUnexpiredLicense();
		$extend->disableNotices = true;
	}

	if ( aioseo()->license->isInvalid() || aioseo()->license->isDisabled() ) {
		$extend->requiresActiveLicense();

		return;
	}

	if ( ! aioseo()->license->isAddonAllowed( 'aioseo-local-business' ) ) {
		$extend->requiresPlanLevel();

		return;
	}

	require_once __DIR__ . '/app/LocalBusiness.php';

	aioseoLocalBusiness();
}extend/init.php000064400000034433151727277630007537 0ustar00<?php
// phpcs:disable Generic.Arrays.DisallowLongArraySyntax.Found
// If the class exists already don't redeclare.
if ( ! class_exists( 'AIOSEOExtend' ) ) {
	/**
	 * This class checks for compatibility for this plugin to load.
	 *
	 * @since 1.0.0
	 */
	class AIOSEOExtend {
		/**
		 * The addon name.
		 *
		 * @since 1.0.0
		 *
		 * @var string
		 */
		private $name = '';

		/**
		 * The function name.
		 *
		 * @since 1.0.0
		 *
		 * @var string
		 */
		private $function = '';

		/**
		 * The filename.
		 *
		 * @since 1.0.0
		 *
		 * @var string
		 */
		private $file = '';

		/**
		 * The minimum version.
		 *
		 * @since 1.0.0
		 *
		 * @var string
		 */
		private $minimumVersion = '';

		/**
		 * The required plan levels.
		 *
		 * @since 1.0.0
		 *
		 * @var array
		 */
		private $levels = array();

		/**
		 * Whether to disable other notices.
		 *
		 * @since 1.0.0
		 *
		 * @var bool
		 */
		public $disableNotices = false;

		/**
		 * Holds the active AIOSEO Pro version.
		 *
		 * @since 1.0.0
		 *
		 * @var string
		 */
		public static $aioseoVersion = '';

		/**
		 * Holds the addon notices.
		 *
		 * @since 1.0.0
		 *
		 * @var string
		 */
		public static $addonNotices = array();

		/**
		 * The construct function.
		 *
		 * @since 1.0.0
		 *
		 * @param string $name           The name of the addon.
		 * @param string $function       The name of the function to call once we pass compatibility checks.
		 * @param string $file           The addon file to deactivate if checks fail.
		 * @param string $minimumVersion The minimum version of our plugin we can activate against.
		 * @param array  $levels         The levels that this addon support.
		 */
		public function __construct( $name, $function, $file, $minimumVersion, $levels = array() ) {
			$this->name           = $name;
			$this->function       = $function;
			$this->file           = $file;
			$this->minimumVersion = $minimumVersion;
			$this->levels         = $levels;

			add_action( 'plugins_loaded', array( $this, 'init' ) );
		}

		/**
		 * Check addon requirements.
		 * We do it on `plugins_loaded` hook. If earlier the core constants are still not defined.
		 *
		 * @since 1.0.0
		 *
		 * @return void
		 */
		public function init() {
			if ( version_compare( PHP_VERSION, '7.0', '<' ) ) {
				$this->failPhp();

				return;
			}

			// Since the version numbers may vary, we only want to compare the first 3 numbers.
			self::$aioseoVersion = defined( 'AIOSEO_VERSION' ) ? current( explode( '-', AIOSEO_VERSION ) ) : '';

			if ( ! has_action( 'admin_notices', array( __CLASS__, 'adminNotices' ) ) ) {
				add_action( 'admin_notices', array( __CLASS__, 'adminNotices' ) );
			}

			if (
				empty( self::$aioseoVersion ) ||
				version_compare( self::$aioseoVersion, $this->minimumVersion, '<' )
			) {
				$this->requiresPro();
				add_filter( 'auto_update_plugin', array( $this, 'disableAutoUpdate' ), 10, 2 );
				add_filter( 'plugin_auto_update_setting_html', array( $this, 'modifyAutoupdaterSettingHtml' ), 11, 2 );

				return;
			}

			add_action( 'aioseo_loaded', $this->function, 10 );
		}

		/**
		 * Prints out all notices.
		 *
		 * @since 1.0.0
		 *
		 * @return void
		 */
		public static function adminNotices() {
			// Double check we're actually in the admin before outputting anything.
			if ( ! is_admin() ) {
				return;
			}

			self::showAddonsNotice( 'FailPhpVersion', sprintf(
				// Translators: 1 - Opening link tag, 2 - Closing link tag.
				esc_html__( 'Your site is running an outdated version of PHP that is no longer supported and is not compatible with the following addons (%1$sRead more%2$s for additional information):', 'aioseo-addon' ), // phpcs:ignore Generic.Files.LineLength.MaxExceeded
				'<a href="https://aioseo.com/docs/supported-php-version/" target="_blank" rel="noopener noreferrer">',
				'</a>'
			) );

			self::showAddonsNotice( 'FailUpdate', sprintf(
				// Translators: 1 - "All in One SEO 4.0.0".
				esc_html__( 'The following addons cannot be used, because they require an update to work with %1$s:', 'aioseo-addon' ),
				'<strong>All in One SEO Pro ' . wp_kses_post( self::$aioseoVersion ) . '</strong>' // We need to put the name here in since the plugin is most likely not active.
			) );

			self::showAddonsNotice( 'FailActiveLicense', sprintf(
				// Translators: 1 - "All in One SEO", 2 - Opening HTML link tag, 3 - Closing HTML link tag.
				esc_html__( 'The following addons cannot be used, because they require an active license for %1$s. Your license is missing or has expired. To verify your subscription, please %2$svisit our website%3$s.', 'aioseo-addon' ), // phpcs:ignore Generic.Files.LineLength.MaxExceeded
				esc_html( defined( 'AIOSEO_PLUGIN_NAME' ) ? AIOSEO_PLUGIN_NAME : 'All in One SEO' ),
				'<a target="_blank" href="' . self::getUtmUrl( 'account/', 'FailActiveLicense', 'fail-valid-license' ) . '">', // phpcs:ignore WordPress.Security.EscapeOutput, Generic.Files.LineLength.MaxExceeded
				'</a>'
			) );

			self::showMinimumVersionAddonsNotice( 'FailProVersion', sprintf(
				// Translators: 1 - "All in One SEO 4.0.0".
				esc_html__( 'The following addons cannot be used, because they require %1$s or later to work:', 'aioseo-addon' ),
				'<strong>All in One SEO Pro [minimumVersion]</strong>'
			) );

			self::showAddonsNotice( 'FailPlanExpired', sprintf(
				// Translators: 1 - Opening HTML link tag, 2 - Closing HTML link tag.
				esc_html__( 'The following addons cannot be used, because your plan has expired. To renew your subscription, please %1$svisit our website%2$s.', 'aioseo-addon' ),
				'<a target="_blank" href="' . self::getUtmUrl( 'account/subscriptions/', 'FailPlanExpired', 'fail-plan-expired' ) . '">', // phpcs:ignore WordPress.Security.EscapeOutput, Generic.Files.LineLength.MaxExceeded
				'</a>'
			) );

			$level = '';
			if ( self::$aioseoVersion ) {
				$level = aioseo()->internalOptions->internal->has( 'license' ) && aioseo()->internalOptions->internal->license->level
					? aioseo()->internalOptions->internal->license->level
					: $level;
				if ( ! $level ) {
					if ( aioseo()->core->cache->get( 'failed_update' ) ) {
						return;
					}
					$level = esc_html__( 'Unlicensed', 'aioseo-addon' );
				}
			}

			self::showAddonsNotice( 'FailPlanLevel', sprintf(
				// Translators: 1 - The current plan name, 2 - Opening HTML link tag, 3 - Closing HTML link tag.
				esc_html__( 'The following addons cannot be used, because your plan level %1$s does not include access to these addons. To upgrade your subscription, please %2$svisit our website%3$s.', 'aioseo-addon' ), // phpcs:ignore Generic.Files.LineLength.MaxExceeded
				'<strong>(' . ( ! empty( $level ) ? wp_kses_post( ucfirst( $level ) ) : '' ) . ')</strong>',
				'<a target="_blank" href="' . self::getUtmUrl( 'pro-upgrade/', 'FailPlanLevel', 'fail-plan-level' ) . '">', // phpcs:ignore WordPress.Security.EscapeOutput, Generic.Files.LineLength.MaxExceeded
				'</a>'
			) );
		}

		/**
		 * Get the current notice.
		 *
		 * @since 1.0.0
		 *
		 * @param  string $noticeKey The notice key.
		 * @return string            The current notice.
		 */
		public static function getCurrentNotice( $noticeKey ) {
			$noticeKey = 'aioseoAddon' . $noticeKey;
			if ( empty( self::$addonNotices[ $noticeKey ] ) ) {
				return '';
			}

			return current( self::$addonNotices[ $noticeKey ] );
		}

		/**
		 * Get the URL for the current notice.
		 *
		 * @since 1.0.0
		 *
		 * @param  string $uri       The URL to append to the marketing site.
		 * @param  string $noticeKey The notice key.
		 * @param  string $content   The UTM content parameter.
		 * @return string            The current notice.
		 */
		public static function getUtmUrl( $uri, $noticeKey, $content ) {
			$url = 'https://aioseo.com/' . $uri;

			// Generate the new arguments.
			$args = [
				'utm_source'   => 'WordPress',
				'utm_campaign' => 'addon',
				'utm_medium'   => self::getCurrentNotice( $noticeKey ),
				'utm_content'  => $content
			];

			// Return the new URL.
			$url = add_query_arg( $args, $url );

			return esc_url( $url );
		}

		/**
		 * Adds a generic notice for an addon.
		 *
		 * @since 1.0.0
		 *
		 * @param  string $noticeKey The notice key.
		 * @param  string $addonName The addon name.
		 * @return void
		 */
		public function addAddonNotice( $noticeKey, $addonName ) {
			if ( $this->disableNotices ) {
				return;
			}

			$noticeKey = 'aioseoAddon' . $noticeKey;
			if ( empty( self::$addonNotices[ $noticeKey ] ) ) {
				self::$addonNotices[ $noticeKey ] = array();
			}

			self::$addonNotices[ $noticeKey ][] = [
				'addonName'      => $addonName,
				'minimumVersion' => $this->minimumVersion
			];
		}

		/**
		 * HTML for the addon notice.
		 *
		 * @since 1.0.0
		 *
		 * @param  string $noticeKey The notice key.
		 * @param  string $message   The message to output.
		 * @return void
		 */
		public static function showAddonsNotice( $noticeKey, $message ) {
			$noticeKey = 'aioseoAddon' . $noticeKey;
			if ( empty( self::$addonNotices[ $noticeKey ] ) ) {
				return;
			}

			echo '<div class="notice notice-error"><p>';
			echo wp_kses_post( $message );
			echo '</p>';

			$addonsList = wp_list_pluck( self::$addonNotices[ $noticeKey ], 'addonName' );
			$addonsList = implode( '</strong></li><li><strong>', $addonsList );
			echo '<ul><li><strong>' . wp_kses_post( $addonsList ) . '</strong></li></ul>';

			echo '</div>';

			if ( isset( $_GET['activate'] ) ) { // phpcs:ignore HM.Security.NonceVerification.Recommended
				unset( $_GET['activate'] );
			}
		}

		/**
		 * HTML for the addon notice.
		 *
		 * @since 1.0.0
		 *
		 * @param  string $noticeKey The notice key.
		 * @param  string $message   The message to output.
		 * @return void
		 */
		public static function showMinimumVersionAddonsNotice( $noticeKey, $message ) {
			$noticeKey = 'aioseoAddon' . $noticeKey;
			if ( empty( self::$addonNotices[ $noticeKey ] ) ) {
				return;
			}

			$versions = array_unique( wp_list_pluck( self::$addonNotices[ $noticeKey ], 'minimumVersion' ) );
			sort( $versions );

			foreach ( $versions as $version ) {
				$versionMessage = str_replace( '[minimumVersion]', $version, $message );
				$addonsList     = wp_filter_object_list( self::$addonNotices[ $noticeKey ], [ 'minimumVersion' => $version ], 'and', 'addonName' );

				echo '<div class="notice notice-error"><p>';
				echo wp_kses_post( $versionMessage );
				echo '</p>';

				$addonsList = implode( '</strong></li><li><strong>', $addonsList );
				echo '<ul><li><strong>' . wp_kses_post( $addonsList ) . '</strong></li></ul>';

				echo '</div>';
			}

			if ( isset( $_GET['activate'] ) ) { // phpcs:ignore HM.Security.NonceVerification.Recommended
				unset( $_GET['activate'] );
			}
		}

		/**
		 * Throws a notice if PHP version is too low.
		 *
		 * @since 1.0.0
		 *
		 * @return void
		 */
		public function failPhp() {
			$this->addAddonNotice( 'FailPhpVersion', $this->name );
		}

		/**
		 * A secondary function to call if Pro is not active.
		 *
		 * @since 1.0.0
		 *
		 * @return void
		 */
		public function requiresPro() {
			$this->addAddonNotice( 'FailProVersion', $this->name );
		}

		/**
		 * A secondary function to call if Pro is not active.
		 *
		 * @since 1.0.0
		 *
		 * @return void
		 */
		public function requiresUpdate() {
			$this->addAddonNotice( 'FailUpdate', $this->name );
		}

		/**
		 * A secondary function to call if an active license is not found.
		 *
		 * @since 1.0.0
		 *
		 * @return void
		 */
		public function requiresActiveLicense() {
			$this->addAddonNotice( 'FailActiveLicense', $this->name );
		}

		/**
		 * A secondary function to call if an expired license is found.
		 *
		 * @since 1.0.0
		 *
		 * @return void
		 */
		public function requiresUnexpiredLicense() {
			$this->addAddonNotice( 'FailPlanExpired', $this->name );
		}

		/**
		 * A secondary function to call if an active license is not found.
		 *
		 * @since 1.0.0
		 *
		 * @return void
		 */
		public function requiresPlanLevel() {
			$this->addAddonNotice( 'FailPlanLevel', $this->name );
		}

		/**
		 * Disable auto-update.
		 *
		 * @since 1.0.0
		 *
		 * @param  bool   $update Flag to update the plugin or not.
		 * @param  object $item   Update data about a specific plugin.
		 * @return bool           Whether or not to auto update.
		 */
		public function disableAutoUpdate( $update, $item ) {
			// If this is multisite and is not on the main site, return early.
			if ( is_multisite() && ! is_main_site() ) {
				return $update;
			}

			if (
				! empty( $item->plugin ) &&
				plugin_basename( $this->file ) === $item->plugin
			) {
				return false;
			}

			return $update;
		}

		/**
		 * Display AIOSEO Pro CTA on Plugins -> autoupdater setting column
		 *
		 * @since 1.0.0
		 *
		 * @param  string $html
		 * @param  string $pluginFile
		 * @return string             The HTML.
		 */
		public function modifyAutoupdaterSettingHtml( $html, $pluginFile ) {
			if ( plugin_basename( $this->file ) === $pluginFile &&
				// If main plugin (free) happens to be enabled and already takes care of this, then bail
				! apply_filters( "aioseo_is_autoupdate_setting_html_filtered_$pluginFile", false )
			) {
				$html = sprintf(
					'<a href="%s" target="_blank">%s</a>',
					"https://aioseo.com/docs/how-to-upgrade-from-all-in-one-seo-lite-to-pro/?utm_source=liteplugin&utm_medium=plugins-autoupdate&utm_campaign=upgrade-to-autoupdate&utm_content={$this->name}", // phpcs:ignore Generic.Files.LineLength.MaxExceeded
					// Translators: 1 - "AIOSEO Pro"
					sprintf( esc_html__( 'Enable the %1$s plugin to manage auto-updates', 'aioseo-addon' ), 'AIOSEO Pro' )
				);
			}

			return $html;
		}
	}
}

if ( ! function_exists( 'aioseoAddonIsDisabled' ) ) {
	/**
	 * Disable the current addon if triggered externally.
	 *
	 * @since 1.0.0
	 *
	 * @param  string $slug The addon slug.
	 * @return bool         True if the addon should be disabled.
	 */
	function aioseoAddonIsDisabled( $slug ) {
		if ( ! defined( 'AIOSEO_DEV_VERSION' ) && ! isset( $_REQUEST['aioseo-dev'] ) ) { // phpcs:ignore HM.Security.NonceVerification.Recommended
			return false;
		}

		if ( ! isset( $_REQUEST['aioseo-disable-addon'] ) ) { // phpcs:ignore HM.Security.NonceVerification.Recommended
			return false;
		}

		$request = wp_unslash( $_REQUEST['aioseo-disable-addon'] ); // phpcs:ignore HM.Security
		$request = explode( ',', $request );
		foreach ( $request as $r ) {
			if ( 0 !== strpos( $r, 'aioseo' ) ) {
				$r = 'aioseo-' . $r;
			}

			if ( $slug === $r ) {
				return true;
			}
		}

		return false;
	}
}extend/translations.php000064400000015272151727277630011315 0ustar00<?php
// phpcs:disable Generic.Arrays.DisallowLongArraySyntax.Found
// If the class exists already don't redeclare.
if ( ! class_exists( 'AIOSEOTranslations' ) ) {
	/**
	 * This class pulls in translations for the current addon.
	 *
	 * @since 1.0.0
	 */
	class AIOSEOTranslations {
		/**
		 * The project type.
		 *
		 * @since 1.0.0
		 *
		 * @var string
		 */
		private $type = '';

		/**
		 * The project dir slug.
		 *
		 * @since 1.0.0
		 *
		 * @var string
		 */
		private $slug = '';

		/**
		 * The GlotPress API URL.
		 *
		 * @since 1.0.0
		 *
		 * @var string
		 */
		private $apiUrl = '';

		/**
		 * Installed translations.
		 *
		 * @since 1.0.0
		 *
		 * @var array
		 */
		private static $installedTranslations = array();

		/**
		 * Available languages.
		 *
		 * @since 1.0.0
		 *
		 * @var array
		 */
		private static $availableLanguages = array();

		/**
		 * Class constructor.
		 *
		 * @param string $type   Project type. Either plugin or theme.
		 * @param string $slug   Project directory slug.
		 * @param string $apiUrl Full GlotPress API URL for the project.
		 */
		public function __construct( $type, $slug, $apiUrl ) {
			$this->type   = $type;
			$this->slug   = $slug;
			$this->apiUrl = $apiUrl;
		}

		/**
		 * Adds a new project to load translations for.
		 *
		 * @since 1.0.0
		 *
		 * @return void
		 */
		public function init() {
			if ( ! has_action( 'init', [ $this, 'registerCleanTranslationsCache' ] ) ) {
				add_action( 'init', [ $this, 'registerCleanTranslationsCache' ], 9999 );
			}

			// Short-circuits translations API requests for private projects.
			add_filter(
				'translations_api',
				function ( $result, $requestedType, $args ) {
					if ( $this->type . 's' === $requestedType && $this->slug === $args['slug'] ) {
						return $this->getTranslations( $this->type, $args['slug'], $this->apiUrl );
					}

					return $result;
				},
				10,
				3
			);

			// Filters the translations transients to include the private plugin or theme. @see wp_get_translation_updates().
			add_filter(
				'site_transient_update_' . $this->type . 's',
				function ( $value ) {
					if ( ! $value ) {
						$value = new \stdClass();
					}

					if ( ! is_object( $value ) ) {
						// If the value isn't an object at this point, bail in order to prevent errors.
						return $value;
					}

					if ( ! isset( $value->translations ) || ! is_array( $value->translations ) ) {
						$value->translations = [];
					}

					$translations = $this->getTranslations( $this->type, $this->slug, $this->apiUrl );

					if ( ! isset( $translations[ $this->slug ]['translations'] ) ) {
						return $value;
					}

					if ( empty( self::$installedTranslations ) ) {
						self::$installedTranslations = wp_get_installed_translations( $this->type . 's' );
					}

					if ( empty( self::$availableLanguages ) ) {
						self::$availableLanguages = get_available_languages();
					}

					foreach ( (array) $translations[ $this->slug ]['translations'] as $translation ) {
						if ( in_array( $translation['language'], self::$availableLanguages, true ) ) {
							if ( isset( self::$installedTranslations[ $this->slug ][ $translation['language'] ] ) && $translation['updated'] ) {
								$local  = new DateTime( self::$installedTranslations[ $this->slug ][ $translation['language'] ]['PO-Revision-Date'] );
								$remote = new DateTime( $translation['updated'] );

								if ( $local >= $remote ) {
									continue;
								}
							}

							$translation['type'] = $this->type;
							$translation['slug'] = $this->slug;

							$value->translations[] = $translation;
						}
					}

					return $value;
				}
			);
		}

		/**
		 * Registers actions for clearing translation caches.
		 *
		 * @since 1.0.0
		 *
		 * @return void
		 */
		public function registerCleanTranslationsCache() {
			$clearPluginTranslations = function() {
				$this->cleanTranslationsCache( 'plugin' );
			};

			$clearThemeTranslations = function() {
				$this->cleanTranslationsCache( 'theme' );
			};

			add_action( 'set_site_transient_update_plugins', $clearPluginTranslations );
			add_action( 'delete_site_transient_update_plugins', $clearPluginTranslations );

			add_action( 'set_site_transient_update_themes', $clearThemeTranslations );
			add_action( 'delete_site_transient_update_themes', $clearThemeTranslations );
		}

		/**
		 * Clears existing translation cache for a given type.
		 *
		 * @since 1.0.0
		 *
		 * @param  string $type Project type. Either plugin or theme.
		 * @return void
		 */
		public function cleanTranslationsCache( $type ) {
			$transientKey = '_aioseo_translations_' . $this->slug . '_' . $type;

			if ( is_multisite() ) {
				switch_to_blog( get_network()->site_id );
			}

			$translations = get_site_transient( $transientKey );

			if ( is_multisite() ) {
				restore_current_blog();
			}

			if ( ! is_array( $translations ) ) {
				return;
			}

			// Don't delete the cache if the transient gets changed multiple times
			// during a single request. Set cache lifetime to maximum 15 seconds.
			$cacheLifespan  = 15;
			$timeNotChanged = isset( $translations['_last_checked'] ) && ( time() - $translations['_last_checked'] ) > $cacheLifespan;

			if ( ! $timeNotChanged ) {
				return;
			}

			if ( is_multisite() ) {
				switch_to_blog( get_network()->site_id );
			}

			delete_site_transient( $transientKey );

			if ( is_multisite() ) {
				restore_current_blog();
			}
		}

		/**
		 * Gets the translations for a given project.
		 *
		 * @since 1.0.0
		 *
		 * @param  string $type Project type. Either plugin or theme.
		 * @param  string $slug Project directory slug.
		 * @param  string $url  Full GlotPress API URL for the project.
		 * @return array        Translation data.
		 */
		public function getTranslations( $type, $slug, $url ) {
			$transientKey = '_aioseo_translations_' . $slug . '_' . $type;

			if ( is_multisite() ) {
				switch_to_blog( get_network()->site_id );
			}

			$translations = get_site_transient( $transientKey );

			if ( is_multisite() ) {
				restore_current_blog();
			}

			if ( false !== $translations && is_array( $translations ) ) {
				return $translations;
			}

			if ( ! is_array( $translations ) ) {
				$translations = [];
			}

			if ( isset( $translations[ $slug ] ) && is_array( $translations[ $slug ] ) ) {
				return $translations[ $slug ];
			}

			$result = json_decode( wp_remote_retrieve_body( wp_remote_get( $url, [ 'timeout' => 2 ] ) ), true );
			if ( ! is_array( $result ) ) {
				$result = [];
			}

			$translations[ $slug ]         = $result;
			$translations['_last_checked'] = time();

			if ( is_multisite() ) {
				switch_to_blog( get_network()->site_id );
			}

			set_site_transient( $transientKey, $translations );

			if ( is_multisite() ) {
				restore_current_blog();
			}

			return $result;
		}
	}
}dist/js/map.DRPxR061.js000064400000002305151727277630010444 0ustar00document.addEventListener(window.aioseoMapOptions.mapLoadEvent,function(e){if(e.detail===null||!e.detail||!e.detail.element||!e.detail.mapOptions)return;const n=document.querySelector(e.detail.element);if(!n||n.className.match(/map-loaded/))return;if(e.detail.placeId){const a=document.createElement("iframe");a.style.width=e.detail.instance.width,a.style.height=e.detail.instance.height,a.style.border="0",a.frameborder="0",a.src="https://www.google.com/maps/embed/v1/place?key="+window.aioseoMapOptions.apiKey+"&q=place_id:"+e.detail.placeId,n.replaceChildren(a),n.className+=" map-loaded";return}new google.maps.plugins.loader.Loader({apiKey:window.aioseoMapOptions.apiKey,libraries:["places"]}).load().then(a=>{const t=new a.maps.Map(n,e.detail.mapOptions),i=new a.maps.Marker({map:t,position:e.detail.mapOptions.center,icon:e.detail.customMarker?{url:e.detail.customMarker,origin:new a.maps.Point(0,0),anchor:new a.maps.Point(17,34),scaledSize:new a.maps.Size(25,25)}:null});if(e.detail.infoWindowContent){const o=new a.maps.InfoWindow({content:e.detail.infoWindowContent});o.open(t,i),i.addListener("click",()=>{o.open({anchor:i,map:t,shouldFocus:!1})})}n.className+=" map-loaded"}).catch(a=>{console.error(a)})});
dist/js/js-api-loader.liRm3N-P.js000064400000054703151727277630012442 0ustar00/* @googlemaps/js-api-loader 1.12.1 */
this.google=this.google||{},this.google.maps=this.google.maps||{},this.google.maps.plugins=this.google.maps.plugins||{},this.google.maps.plugins.loader=function(t){"use strict";function e(t,e){for(var n=0;n<e.length;n++){var r=e[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(t,r.key,r)}}var n="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};function r(t,e){return t(e={exports:{}},e.exports),e.exports}var o=function(t){return t&&t.Math==Math&&t},i=o("object"==typeof globalThis&&globalThis)||o("object"==typeof window&&window)||o("object"==typeof self&&self)||o("object"==typeof n&&n)||function(){return this}()||Function("return this")(),c=function(t){try{return!!t()}catch(t){return!0}},a=!c((function(){return 7!=Object.defineProperty({},1,{get:function(){return 7}})[1]})),u={}.propertyIsEnumerable,s=Object.getOwnPropertyDescriptor,f={f:s&&!u.call({1:2},1)?function(t){var e=s(this,t);return!!e&&e.enumerable}:u},l=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}},h={}.toString,p=function(t){return h.call(t).slice(8,-1)},d="".split,v=c((function(){return!Object("z").propertyIsEnumerable(0)}))?function(t){return"String"==p(t)?d.call(t,""):Object(t)}:Object,g=function(t){if(null==t)throw TypeError("Can't call method on "+t);return t},y=function(t){return v(g(t))},m=function(t){return"object"==typeof t?null!==t:"function"==typeof t},b=function(t,e){if(!m(t))return t;var n,r;if(e&&"function"==typeof(n=t.toString)&&!m(r=n.call(t)))return r;if("function"==typeof(n=t.valueOf)&&!m(r=n.call(t)))return r;if(!e&&"function"==typeof(n=t.toString)&&!m(r=n.call(t)))return r;throw TypeError("Can't convert object to primitive value")},w=function(t){return Object(g(t))},S={}.hasOwnProperty,j=Object.hasOwn||function(t,e){return S.call(w(t),e)},O=i.document,E=m(O)&&m(O.createElement),k=function(t){return E?O.createElement(t):{}},T=!a&&!c((function(){return 7!=Object.defineProperty(k("div"),"a",{get:function(){return 7}}).a})),L=Object.getOwnPropertyDescriptor,P={f:a?L:function(t,e){if(t=y(t),e=b(e,!0),T)try{return L(t,e)}catch(t){}if(j(t,e))return l(!f.f.call(t,e),t[e])}},M=function(t){if(!m(t))throw TypeError(String(t)+" is not an object");return t},x=Object.defineProperty,C={f:a?x:function(t,e,n){if(M(t),e=b(e,!0),M(n),T)try{return x(t,e,n)}catch(t){}if("get"in n||"set"in n)throw TypeError("Accessors not supported");return"value"in n&&(t[e]=n.value),t}},_=a?function(t,e,n){return C.f(t,e,l(1,n))}:function(t,e,n){return t[e]=n,t},A=function(t,e){try{_(i,t,e)}catch(n){i[t]=e}return e},I="__core-js_shared__",N=i[I]||A(I,{}),R=Function.toString;"function"!=typeof N.inspectSource&&(N.inspectSource=function(t){return R.call(t)});var D,F,G,K,V=N.inspectSource,B=i.WeakMap,z="function"==typeof B&&/native code/.test(V(B)),U=r((function(t){(t.exports=function(t,e){return N[t]||(N[t]=void 0!==e?e:{})})("versions",[]).push({version:"3.14.0",mode:"global",copyright:"© 2021 Denis Pushkarev (zloirock.ru)"})})),H=0,q=Math.random(),W=function(t){return"Symbol("+String(void 0===t?"":t)+")_"+(++H+q).toString(36)},J=U("keys"),Y={},Q="Object already initialized",X=i.WeakMap;if(z||N.state){var Z=N.state||(N.state=new X),$=Z.get,tt=Z.has,et=Z.set;D=function(t,e){if(tt.call(Z,t))throw new TypeError(Q);return e.facade=t,et.call(Z,t,e),e},F=function(t){return $.call(Z,t)||{}},G=function(t){return tt.call(Z,t)}}else{var nt=J[K="state"]||(J[K]=W(K));Y[nt]=!0,D=function(t,e){if(j(t,nt))throw new TypeError(Q);return e.facade=t,_(t,nt,e),e},F=function(t){return j(t,nt)?t[nt]:{}},G=function(t){return j(t,nt)}}var rt,ot,it={set:D,get:F,has:G,enforce:function(t){return G(t)?F(t):D(t,{})},getterFor:function(t){return function(e){var n;if(!m(e)||(n=F(e)).type!==t)throw TypeError("Incompatible receiver, "+t+" required");return n}}},ct=r((function(t){var e=it.get,n=it.enforce,r=String(String).split("String");(t.exports=function(t,e,o,c){var a,u=!!c&&!!c.unsafe,s=!!c&&!!c.enumerable,f=!!c&&!!c.noTargetGet;"function"==typeof o&&("string"!=typeof e||j(o,"name")||_(o,"name",e),(a=n(o)).source||(a.source=r.join("string"==typeof e?e:""))),t!==i?(u?!f&&t[e]&&(s=!0):delete t[e],s?t[e]=o:_(t,e,o)):s?t[e]=o:A(e,o)})(Function.prototype,"toString",(function(){return"function"==typeof this&&e(this).source||V(this)}))})),at=i,ut=function(t){return"function"==typeof t?t:void 0},st=function(t,e){return arguments.length<2?ut(at[t])||ut(i[t]):at[t]&&at[t][e]||i[t]&&i[t][e]},ft=Math.ceil,lt=Math.floor,ht=function(t){return isNaN(t=+t)?0:(t>0?lt:ft)(t)},pt=Math.min,dt=function(t){return t>0?pt(ht(t),9007199254740991):0},vt=Math.max,gt=Math.min,yt=function(t){return function(e,n,r){var o,i=y(e),c=dt(i.length),a=function(t,e){var n=ht(t);return n<0?vt(n+e,0):gt(n,e)}(r,c);if(t&&n!=n){for(;c>a;)if((o=i[a++])!=o)return!0}else for(;c>a;a++)if((t||a in i)&&i[a]===n)return t||a||0;return!t&&-1}},mt={includes:yt(!0),indexOf:yt(!1)}.indexOf,bt=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"].concat("length","prototype"),wt={f:Object.getOwnPropertyNames||function(t){return function(t,e){var n,r=y(t),o=0,i=[];for(n in r)!j(Y,n)&&j(r,n)&&i.push(n);for(;e.length>o;)j(r,n=e[o++])&&(~mt(i,n)||i.push(n));return i}(t,bt)}},St={f:Object.getOwnPropertySymbols},jt=st("Reflect","ownKeys")||function(t){var e=wt.f(M(t)),n=St.f;return n?e.concat(n(t)):e},Ot=function(t,e){for(var n=jt(e),r=C.f,o=P.f,i=0;i<n.length;i++){var c=n[i];j(t,c)||r(t,c,o(e,c))}},Et=/#|\.prototype\./,kt=function(t,e){var n=Lt[Tt(t)];return n==Mt||n!=Pt&&("function"==typeof e?c(e):!!e)},Tt=kt.normalize=function(t){return String(t).replace(Et,".").toLowerCase()},Lt=kt.data={},Pt=kt.NATIVE="N",Mt=kt.POLYFILL="P",xt=kt,Ct=P.f,_t=function(t,e){var n,r,o,c,a,u=t.target,s=t.global,f=t.stat;if(n=s?i:f?i[u]||A(u,{}):(i[u]||{}).prototype)for(r in e){if(c=e[r],o=t.noTargetGet?(a=Ct(n,r))&&a.value:n[r],!xt(s?r:u+(f?".":"#")+r,t.forced)&&void 0!==o){if(typeof c==typeof o)continue;Ot(c,o)}(t.sham||o&&o.sham)&&_(c,"sham",!0),ct(n,r,c,t)}},At=Array.isArray||function(t){return"Array"==p(t)},It=function(t,e,n){var r=b(e);r in t?C.f(t,r,l(0,n)):t[r]=n},Nt=st("navigator","userAgent")||"",Rt=i.process,Dt=Rt&&Rt.versions,Ft=Dt&&Dt.v8;Ft?ot=(rt=Ft.split("."))[0]<4?1:rt[0]+rt[1]:Nt&&(!(rt=Nt.match(/Edge\/(\d+)/))||rt[1]>=74)&&(rt=Nt.match(/Chrome\/(\d+)/))&&(ot=rt[1]);var Gt,Kt=ot&&+ot,Vt=!!Object.getOwnPropertySymbols&&!c((function(){var t=Symbol();return!String(t)||!(Object(t)instanceof Symbol)||!Symbol.sham&&Kt&&Kt<41})),Bt=Vt&&!Symbol.sham&&"symbol"==typeof Symbol.iterator,zt=U("wks"),Ut=i.Symbol,Ht=Bt?Ut:Ut&&Ut.withoutSetter||W,qt=function(t){return j(zt,t)&&(Vt||"string"==typeof zt[t])||(Vt&&j(Ut,t)?zt[t]=Ut[t]:zt[t]=Ht("Symbol."+t)),zt[t]},Wt=qt("species"),Jt=function(t,e){var n;return At(t)&&("function"!=typeof(n=t.constructor)||n!==Array&&!At(n.prototype)?m(n)&&null===(n=n[Wt])&&(n=void 0):n=void 0),new(void 0===n?Array:n)(0===e?0:e)},Yt=qt("species"),Qt=qt("isConcatSpreadable"),Xt=9007199254740991,Zt="Maximum allowed index exceeded",$t=Kt>=51||!c((function(){var t=[];return t[Qt]=!1,t.concat()[0]!==t})),te=(Gt="concat",Kt>=51||!c((function(){var t=[];return(t.constructor={})[Yt]=function(){return{foo:1}},1!==t[Gt](Boolean).foo}))),ee=function(t){if(!m(t))return!1;var e=t[Qt];return void 0!==e?!!e:At(t)};_t({target:"Array",proto:!0,forced:!$t||!te},{concat:function(t){var e,n,r,o,i,c=w(this),a=Jt(c,0),u=0;for(e=-1,r=arguments.length;e<r;e++)if(ee(i=-1===e?c:arguments[e])){if(u+(o=dt(i.length))>Xt)throw TypeError(Zt);for(n=0;n<o;n++,u++)n in i&&It(a,u,i[n])}else{if(u>=Xt)throw TypeError(Zt);It(a,u++,i)}return a.length=u,a}});var ne=function(t,e){var n=[][t];return!!n&&c((function(){n.call(null,e||function(){throw 1},1)}))},re=[].join,oe=v!=Object,ie=ne("join",",");_t({target:"Array",proto:!0,forced:oe||!ie},{join:function(t){return re.call(y(this),void 0===t?",":t)}});var ce={};ce[qt("toStringTag")]="z";var ae="[object z]"===String(ce),ue=qt("toStringTag"),se="Arguments"==p(function(){return arguments}()),fe=ae?p:function(t){var e,n,r;return void 0===t?"Undefined":null===t?"Null":"string"==typeof(n=function(t,e){try{return t[e]}catch(t){}}(e=Object(t),ue))?n:se?p(e):"Object"==(r=p(e))&&"function"==typeof e.callee?"Arguments":r},le=ae?{}.toString:function(){return"[object "+fe(this)+"]"};ae||ct(Object.prototype,"toString",le,{unsafe:!0});var he=i.Promise,pe=Object.setPrototypeOf||("__proto__"in{}?function(){var t,e=!1,n={};try{(t=Object.getOwnPropertyDescriptor(Object.prototype,"__proto__").set).call(n,[]),e=n instanceof Array}catch(t){}return function(n,r){return M(n),function(t){if(!m(t)&&null!==t)throw TypeError("Can't set "+String(t)+" as a prototype")}(r),e?t.call(n,r):n.__proto__=r,n}}():void 0),de=C.f,ve=qt("toStringTag"),ge=qt("species"),ye=function(t){if("function"!=typeof t)throw TypeError(String(t)+" is not a function");return t},me={},be=qt("iterator"),we=Array.prototype,Se=function(t,e,n){if(ye(t),void 0===e)return t;switch(n){case 0:return function(){return t.call(e)};case 1:return function(n){return t.call(e,n)};case 2:return function(n,r){return t.call(e,n,r)};case 3:return function(n,r,o){return t.call(e,n,r,o)}}return function(){return t.apply(e,arguments)}},je=qt("iterator"),Oe=function(t){var e=t.return;if(void 0!==e)return M(e.call(t)).value},Ee=function(t,e){this.stopped=t,this.result=e},ke=function(t,e,n){var r,o,i,c,a,u,s,f,l=n&&n.that,h=!(!n||!n.AS_ENTRIES),p=!(!n||!n.IS_ITERATOR),d=!(!n||!n.INTERRUPTED),v=Se(e,l,1+h+d),g=function(t){return r&&Oe(r),new Ee(!0,t)},y=function(t){return h?(M(t),d?v(t[0],t[1],g):v(t[0],t[1])):d?v(t,g):v(t)};if(p)r=t;else{if("function"!=typeof(o=function(t){if(null!=t)return t[je]||t["@@iterator"]||me[fe(t)]}(t)))throw TypeError("Target is not iterable");if(void 0!==(f=o)&&(me.Array===f||we[be]===f)){for(i=0,c=dt(t.length);c>i;i++)if((a=y(t[i]))&&a instanceof Ee)return a;return new Ee(!1)}r=o.call(t)}for(u=r.next;!(s=u.call(r)).done;){try{a=y(s.value)}catch(t){throw Oe(r),t}if("object"==typeof a&&a&&a instanceof Ee)return a}return new Ee(!1)},Te=qt("iterator"),Le=!1;try{var Pe=0,Me={next:function(){return{done:!!Pe++}},return:function(){Le=!0}};Me[Te]=function(){return this},Array.from(Me,(function(){throw 2}))}catch(t){}var xe,Ce,_e,Ae=qt("species"),Ie=st("document","documentElement"),Ne=/(?:iphone|ipod|ipad).*applewebkit/i.test(Nt),Re="process"==p(i.process),De=i.location,Fe=i.setImmediate,Ge=i.clearImmediate,Ke=i.process,Ve=i.MessageChannel,Be=i.Dispatch,ze=0,Ue={},He="onreadystatechange",qe=function(t){if(Ue.hasOwnProperty(t)){var e=Ue[t];delete Ue[t],e()}},We=function(t){return function(){qe(t)}},Je=function(t){qe(t.data)},Ye=function(t){i.postMessage(t+"",De.protocol+"//"+De.host)};Fe&&Ge||(Fe=function(t){for(var e=[],n=1;arguments.length>n;)e.push(arguments[n++]);return Ue[++ze]=function(){("function"==typeof t?t:Function(t)).apply(void 0,e)},xe(ze),ze},Ge=function(t){delete Ue[t]},Re?xe=function(t){Ke.nextTick(We(t))}:Be&&Be.now?xe=function(t){Be.now(We(t))}:Ve&&!Ne?(_e=(Ce=new Ve).port2,Ce.port1.onmessage=Je,xe=Se(_e.postMessage,_e,1)):i.addEventListener&&"function"==typeof postMessage&&!i.importScripts&&De&&"file:"!==De.protocol&&!c(Ye)?(xe=Ye,i.addEventListener("message",Je,!1)):xe=He in k("script")?function(t){Ie.appendChild(k("script")).onreadystatechange=function(){Ie.removeChild(this),qe(t)}}:function(t){setTimeout(We(t),0)});var Qe,Xe,Ze,$e,tn,en,nn,rn,on={set:Fe,clear:Ge},cn=/web0s(?!.*chrome)/i.test(Nt),an=P.f,un=on.set,sn=i.MutationObserver||i.WebKitMutationObserver,fn=i.document,ln=i.process,hn=i.Promise,pn=an(i,"queueMicrotask"),dn=pn&&pn.value;dn||(Qe=function(){var t,e;for(Re&&(t=ln.domain)&&t.exit();Xe;){e=Xe.fn,Xe=Xe.next;try{e()}catch(t){throw Xe?$e():Ze=void 0,t}}Ze=void 0,t&&t.enter()},Ne||Re||cn||!sn||!fn?hn&&hn.resolve?((nn=hn.resolve(void 0)).constructor=hn,rn=nn.then,$e=function(){rn.call(nn,Qe)}):$e=Re?function(){ln.nextTick(Qe)}:function(){un.call(i,Qe)}:(tn=!0,en=fn.createTextNode(""),new sn(Qe).observe(en,{characterData:!0}),$e=function(){en.data=tn=!tn}));var vn,gn,yn,mn,bn,wn,Sn,jn=dn||function(t){var e={fn:t,next:void 0};Ze&&(Ze.next=e),Xe||(Xe=e,$e()),Ze=e},On=function(t){var e,n;this.promise=new t((function(t,r){if(void 0!==e||void 0!==n)throw TypeError("Bad Promise constructor");e=t,n=r})),this.resolve=ye(e),this.reject=ye(n)},En={f:function(t){return new On(t)}},kn=function(t){try{return{error:!1,value:t()}}catch(t){return{error:!0,value:t}}},Tn="object"==typeof window,Ln=on.set,Pn=qt("species"),Mn="Promise",xn=it.get,Cn=it.set,_n=it.getterFor(Mn),An=he&&he.prototype,In=he,Nn=An,Rn=i.TypeError,Dn=i.document,Fn=i.process,Gn=En.f,Kn=Gn,Vn=!!(Dn&&Dn.createEvent&&i.dispatchEvent),Bn="function"==typeof PromiseRejectionEvent,zn="unhandledrejection",Un=!1,Hn=xt(Mn,(function(){var t=V(In)!==String(In);if(!t&&66===Kt)return!0;if(Kt>=51&&/native code/.test(In))return!1;var e=new In((function(t){t(1)})),n=function(t){t((function(){}),(function(){}))};return(e.constructor={})[Pn]=n,!(Un=e.then((function(){}))instanceof n)||!t&&Tn&&!Bn})),qn=Hn||!function(t,e){if(!e&&!Le)return!1;var n=!1;try{var r={};r[Te]=function(){return{next:function(){return{done:n=!0}}}},t(r)}catch(t){}return n}((function(t){In.all(t).catch((function(){}))})),Wn=function(t){var e;return!(!m(t)||"function"!=typeof(e=t.then))&&e},Jn=function(t,e){if(!t.notified){t.notified=!0;var n=t.reactions;jn((function(){for(var r=t.value,o=1==t.state,i=0;n.length>i;){var c,a,u,s=n[i++],f=o?s.ok:s.fail,l=s.resolve,h=s.reject,p=s.domain;try{f?(o||(2===t.rejection&&Zn(t),t.rejection=1),!0===f?c=r:(p&&p.enter(),c=f(r),p&&(p.exit(),u=!0)),c===s.promise?h(Rn("Promise-chain cycle")):(a=Wn(c))?a.call(c,l,h):l(c)):h(r)}catch(t){p&&!u&&p.exit(),h(t)}}t.reactions=[],t.notified=!1,e&&!t.rejection&&Qn(t)}))}},Yn=function(t,e,n){var r,o;Vn?((r=Dn.createEvent("Event")).promise=e,r.reason=n,r.initEvent(t,!1,!0),i.dispatchEvent(r)):r={promise:e,reason:n},!Bn&&(o=i["on"+t])?o(r):t===zn&&function(t,e){var n=i.console;n&&n.error&&(1===arguments.length?n.error(t):n.error(t,e))}("Unhandled promise rejection",n)},Qn=function(t){Ln.call(i,(function(){var e,n=t.facade,r=t.value;if(Xn(t)&&(e=kn((function(){Re?Fn.emit("unhandledRejection",r,n):Yn(zn,n,r)})),t.rejection=Re||Xn(t)?2:1,e.error))throw e.value}))},Xn=function(t){return 1!==t.rejection&&!t.parent},Zn=function(t){Ln.call(i,(function(){var e=t.facade;Re?Fn.emit("rejectionHandled",e):Yn("rejectionhandled",e,t.value)}))},$n=function(t,e,n){return function(r){t(e,r,n)}},tr=function(t,e,n){t.done||(t.done=!0,n&&(t=n),t.value=e,t.state=2,Jn(t,!0))},er=function(t,e,n){if(!t.done){t.done=!0,n&&(t=n);try{if(t.facade===e)throw Rn("Promise can't be resolved itself");var r=Wn(e);r?jn((function(){var n={done:!1};try{r.call(e,$n(er,n,t),$n(tr,n,t))}catch(e){tr(n,e,t)}})):(t.value=e,t.state=1,Jn(t,!1))}catch(e){tr({done:!1},e,t)}}};if(Hn&&(Nn=(In=function(t){!function(t,e,n){if(!(t instanceof e))throw TypeError("Incorrect "+(n?n+" ":"")+"invocation")}(this,In,Mn),ye(t),vn.call(this);var e=xn(this);try{t($n(er,e),$n(tr,e))}catch(t){tr(e,t)}}).prototype,(vn=function(t){Cn(this,{type:Mn,done:!1,notified:!1,parent:!1,reactions:[],rejection:!1,state:0,value:void 0})}).prototype=function(t,e,n){for(var r in e)ct(t,r,e[r],n);return t}(Nn,{then:function(t,e){var n,r,o,i=_n(this),c=Gn((n=In,void 0===(o=M(this).constructor)||null==(r=M(o)[Ae])?n:ye(r)));return c.ok="function"!=typeof t||t,c.fail="function"==typeof e&&e,c.domain=Re?Fn.domain:void 0,i.parent=!0,i.reactions.push(c),0!=i.state&&Jn(i,!1),c.promise},catch:function(t){return this.then(void 0,t)}}),gn=function(){var t=new vn,e=xn(t);this.promise=t,this.resolve=$n(er,e),this.reject=$n(tr,e)},En.f=Gn=function(t){return t===In||t===yn?new gn(t):Kn(t)},"function"==typeof he&&An!==Object.prototype)){mn=An.then,Un||(ct(An,"then",(function(t,e){var n=this;return new In((function(t,e){mn.call(n,t,e)})).then(t,e)}),{unsafe:!0}),ct(An,"catch",Nn.catch,{unsafe:!0}));try{delete An.constructor}catch(t){}pe&&pe(An,Nn)}_t({global:!0,wrap:!0,forced:Hn},{Promise:In}),wn=Mn,Sn=!1,(bn=In)&&!j(bn=Sn?bn:bn.prototype,ve)&&de(bn,ve,{configurable:!0,value:wn}),function(t){var e=st(t),n=C.f;a&&e&&!e[ge]&&n(e,ge,{configurable:!0,get:function(){return this}})}(Mn),yn=st(Mn),_t({target:Mn,stat:!0,forced:Hn},{reject:function(t){var e=Gn(this);return e.reject.call(void 0,t),e.promise}}),_t({target:Mn,stat:!0,forced:Hn},{resolve:function(t){return function(t,e){if(M(t),m(e)&&e.constructor===t)return e;var n=En.f(t);return(0,n.resolve)(e),n.promise}(this,t)}}),_t({target:Mn,stat:!0,forced:qn},{all:function(t){var e=this,n=Gn(e),r=n.resolve,o=n.reject,i=kn((function(){var n=ye(e.resolve),i=[],c=0,a=1;ke(t,(function(t){var u=c++,s=!1;i.push(void 0),a++,n.call(e,t).then((function(t){s||(s=!0,i[u]=t,--a||r(i))}),o)})),--a||r(i)}));return i.error&&o(i.value),n.promise},race:function(t){var e=this,n=Gn(e),r=n.reject,o=kn((function(){var o=ye(e.resolve);ke(t,(function(t){o.call(e,t).then(n.resolve,r)}))}));return o.error&&r(o.value),n.promise}});var nr=[].push,rr=function(t){var e=1==t,n=2==t,r=3==t,o=4==t,i=6==t,c=7==t,a=5==t||i;return function(u,s,f,l){for(var h,p,d=w(u),g=v(d),y=Se(s,f,3),m=dt(g.length),b=0,S=l||Jt,j=e?S(u,m):n||c?S(u,0):void 0;m>b;b++)if((a||b in g)&&(p=y(h=g[b],b,d),t))if(e)j[b]=p;else if(p)switch(t){case 3:return!0;case 5:return h;case 6:return b;case 2:nr.call(j,h)}else switch(t){case 4:return!1;case 7:nr.call(j,h)}return i?-1:r||o?o:j}},or={forEach:rr(0),map:rr(1),filter:rr(2),some:rr(3),every:rr(4),find:rr(5),findIndex:rr(6),filterOut:rr(7)}.forEach,ir=ne("forEach")?[].forEach:function(t){return or(this,t,arguments.length>1?arguments[1]:void 0)};for(var cr in{CSSRuleList:0,CSSStyleDeclaration:0,CSSValueList:0,ClientRectList:0,DOMRectList:0,DOMStringList:0,DOMTokenList:1,DataTransferItemList:0,FileList:0,HTMLAllCollection:0,HTMLCollection:0,HTMLFormElement:0,HTMLSelectElement:0,MediaList:0,MimeTypeArray:0,NamedNodeMap:0,NodeList:1,PaintRequestList:0,Plugin:0,PluginArray:0,SVGLengthList:0,SVGNumberList:0,SVGPathSegList:0,SVGPointList:0,SVGStringList:0,SVGTransformList:0,SourceBufferList:0,StyleSheetList:0,TextTrackCueList:0,TextTrackList:0,TouchList:0}){var ar=i[cr],ur=ar&&ar.prototype;if(ur&&ur.forEach!==ir)try{_(ur,"forEach",ir)}catch(t){ur.forEach=ir}}var sr="__googleMapsScriptId",fr=function(){function t(e){var n=e.apiKey,r=e.channel,o=e.client,i=e.id,c=void 0===i?sr:i,a=e.libraries,u=void 0===a?[]:a,s=e.language,f=e.region,l=e.version,h=e.mapIds,p=e.nonce,d=e.retries,v=void 0===d?3:d,g=e.url,y=void 0===g?"https://maps.googleapis.com/maps/api/js":g;if(function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,t),this.CALLBACK="__googleMapsCallback",this.callbacks=[],this.done=!1,this.loading=!1,this.errors=[],this.version=l,this.apiKey=n,this.channel=r,this.client=o,this.id=c||sr,this.libraries=u,this.language=s,this.region=f,this.mapIds=h,this.nonce=p,this.retries=v,this.url=y,t.instance){if(!function t(e,n){if(e===n)return!0;if(e&&n&&"object"==typeof e&&"object"==typeof n){if(e.constructor!==n.constructor)return!1;var r,o,i;if(Array.isArray(e)){if((r=e.length)!=n.length)return!1;for(o=r;0!=o--;)if(!t(e[o],n[o]))return!1;return!0}if(e.constructor===RegExp)return e.source===n.source&&e.flags===n.flags;if(e.valueOf!==Object.prototype.valueOf)return e.valueOf()===n.valueOf();if(e.toString!==Object.prototype.toString)return e.toString()===n.toString();if((r=(i=Object.keys(e)).length)!==Object.keys(n).length)return!1;for(o=r;0!=o--;)if(!Object.prototype.hasOwnProperty.call(n,i[o]))return!1;for(o=r;0!=o--;){var c=i[o];if(!t(e[c],n[c]))return!1}return!0}return e!=e&&n!=n}(this.options,t.instance.options))throw new Error("Loader must not be called again with different options. ".concat(JSON.stringify(this.options)," !== ").concat(JSON.stringify(t.instance.options)));return t.instance}t.instance=this}var n,r,o;return n=t,(r=[{key:"options",get:function(){return{version:this.version,apiKey:this.apiKey,channel:this.channel,client:this.client,id:this.id,libraries:this.libraries,language:this.language,region:this.region,mapIds:this.mapIds,nonce:this.nonce,url:this.url}}},{key:"failed",get:function(){return this.done&&!this.loading&&this.errors.length>=this.retries+1}},{key:"createUrl",value:function(){var t=this.url;return t+="?callback=".concat(this.CALLBACK),this.apiKey&&(t+="&key=".concat(this.apiKey)),this.channel&&(t+="&channel=".concat(this.channel)),this.client&&(t+="&client=".concat(this.client)),this.libraries.length>0&&(t+="&libraries=".concat(this.libraries.join(","))),this.language&&(t+="&language=".concat(this.language)),this.region&&(t+="&region=".concat(this.region)),this.version&&(t+="&v=".concat(this.version)),this.mapIds&&(t+="&map_ids=".concat(this.mapIds.join(","))),t}},{key:"load",value:function(){return this.loadPromise()}},{key:"loadPromise",value:function(){var t=this;return new Promise((function(e,n){t.loadCallback((function(t){t?n(t):e(window.google)}))}))}},{key:"loadCallback",value:function(t){this.callbacks.push(t),this.execute()}},{key:"setScript",value:function(){if(document.getElementById(this.id))this.callback();else{var t=this.createUrl(),e=document.createElement("script");e.id=this.id,e.type="text/javascript",e.src=t,e.onerror=this.loadErrorCallback.bind(this),e.defer=!0,e.async=!0,this.nonce&&(e.nonce=this.nonce),document.head.appendChild(e)}}},{key:"deleteScript",value:function(){var t=document.getElementById(this.id);t&&t.remove()}},{key:"reset",value:function(){this.deleteScript(),this.done=!1,this.loading=!1,this.errors=[],this.onerrorEvent=null}},{key:"resetIfRetryingFailed",value:function(){this.failed&&this.reset()}},{key:"loadErrorCallback",value:function(t){var e=this;if(this.errors.push(t),this.errors.length<=this.retries){var n=this.errors.length*Math.pow(2,this.errors.length);console.log("Failed to load Google Maps script, retrying in ".concat(n," ms.")),setTimeout((function(){e.deleteScript(),e.setScript()}),n)}else this.onerrorEvent=t,this.callback()}},{key:"setCallback",value:function(){window.__googleMapsCallback=this.callback.bind(this)}},{key:"callback",value:function(){var t=this;this.done=!0,this.loading=!1,this.callbacks.forEach((function(e){e(t.onerrorEvent)})),this.callbacks=[]}},{key:"execute",value:function(){if(this.resetIfRetryingFailed(),this.done)this.callback();else{if(window.google&&window.google.maps&&window.google.maps.version)return console.warn("Google Maps already loaded outside @googlemaps/js-api-loader.This may result in undesirable behavior as options and script parameters may not match."),void this.callback();this.loading||(this.loading=!0,this.setCallback(),this.setScript())}}}])&&e(n.prototype,r),o&&e(n,o),t}();return t.DEFAULT_ID=sr,t.Loader=fr,Object.defineProperty(t,"__esModule",{value:!0}),t}({});dist/manifest.php000064400000001516151727277630010052 0ustar00<?php
/* THIS IS A GENERATED FILE. DO NOT EDIT DIRECTLY. */
$manifestJson = '{
  "src/assets/scss/business-info.scss": {
    "file": "css/business-info.DL1L3NWa.css",
    "src": "src/assets/scss/business-info.scss",
    "isEntry": true
  },
  "src/assets/scss/location-map.scss": {
    "file": "css/location-map.DZ9K1ruc.css",
    "src": "src/assets/scss/location-map.scss",
    "isEntry": true
  },
  "src/assets/scss/opening-hours.scss": {
    "file": "css/opening-hours.Bdv_HHb0.css",
    "src": "src/assets/scss/opening-hours.scss",
    "isEntry": true
  },
  "src/js-api-loader.js": {
    "file": "js/js-api-loader.liRm3N-P.js",
    "name": "js-api-loader",
    "src": "src/js-api-loader.js",
    "isEntry": true
  },
  "src/map.js": {
    "file": "js/map.DRPxR061.js",
    "name": "map",
    "src": "src/map.js",
    "isEntry": true
  }
}';dist/css/location-map.DZ9K1ruc.css000064400000000335151727277630012731 0ustar00.aioseo-location-map .d-flex{display:flex}.aioseo-location-map .col{flex:1}.aioseo-location-map .col-auto{flex:0 0 auto}.aioseo-location-map .aioseo-local-map .marker-info-window .name{font-weight:700;padding-bottom:3px}
dist/css/opening-hours.Bdv_HHb0.css000064400000001220151727277630013135 0ustar00.aioseo-opening-hours .d-flex{display:flex}.aioseo-opening-hours .col{flex:1}.aioseo-opening-hours .col-auto{flex:0 0 auto}.aioseo-opening-hours .aioseo-opening-hours-icon{display:inline-block}[dir=ltr] .aioseo-opening-hours .aioseo-opening-hours-icon{margin-right:17px}[dir=rtl] .aioseo-opening-hours .aioseo-opening-hours-icon{margin-left:17px}.aioseo-opening-hours .aioseo-opening-hours-table{border:transparent;border-collapse:collapse;border-spacing:0;margin:10px -.5em;max-width:100%;overflow:hidden;width:100%}.aioseo-opening-hours .aioseo-opening-hours-table th,.aioseo-opening-hours .aioseo-opening-hours-table td{border:transparent;padding:.5em}
dist/css/business-info.DL1L3NWa.css000064400000001305151727277640013002 0ustar00.aioseo-location-box,.aioseo-location-box .d-flex{display:flex}.aioseo-location-box>[class*=col]{padding:0 15px 20px}.aioseo-location-box .col{flex:1}.aioseo-location-box .col-auto{flex:0 0 auto}.aioseo-location-box .label{opacity:.5}[dir=ltr] .aioseo-location-box .icon{padding-right:17px}[dir=rtl] .aioseo-location-box .icon{padding-left:17px}.aioseo-location-box .aioseo-name-details{padding-bottom:10px}.aioseo-location-box .aioseo-address-details{padding-bottom:26px}.aioseo-location-box .aioseo-contact-details>div{padding-bottom:20px}.aioseo-location-box.hide-label .label,.aioseo-location-box.hide-icon .icon{display:none}.aioseo-local-business-info-widget .aioseo-location-box{flex-direction:column}
app/Admin/Location.php000064400000017051151727277640010663 0ustar00<?php
namespace AIOSEO\Plugin\Addon\LocalBusiness\Admin;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * The Location class.
 *
 * @since 1.1.0
 */
class Location {
	/**
	 * Class constructor.
	 *
	 * @since 1.1.0
	 */
	public function __construct() {
		add_action( 'init', [ $this, 'register' ] );
	}

	/**
	 * Returns the post type name.
	 *
	 * @since 1.1.0
	 *
	 * @return string The post name.
	 */
	public function getName() {
		return apply_filters( 'aioseo_local_business_post_type_name', 'aioseo-location' );
	}

	/**
	 * Returns the current slug for the post type.
	 *
	 * @since 1.1.0
	 *
	 * @return string The post rewrite slug.
	 */
	public function getSlug() {
		$useCustomSlug    = aioseo()->options->localBusiness->locations->general->useCustomSlug;
		$customSlugOption = aioseo()->options->localBusiness->locations->general->customSlug ?: '';

		$customSlug = ( true === $useCustomSlug && 0 < strlen( $customSlugOption ) )
			? $customSlugOption
			: $this->getDefaultSlug();

		$customSlug = apply_filters( 'aioseo_local_business_post_type_slug', $customSlug );

		return $customSlug;
	}

	/**
	 * Returns the default slug for the post type.
	 *
	 * @since 1.1.0
	 *
	 * @return string The default rewrite slug.
	 */
	public function getDefaultSlug() {
		return 'location';
	}

	/**
	 * Returns the single label for the post type.
	 *
	 * @since 1.1.0
	 *
	 * @return string The post single label.
	 */
	public function getSingleLabel() {
		$singleLabelOption = aioseo()->options->localBusiness->locations->general->singleLabel ?: '';

		$singleLabel = ( 0 < strlen( $singleLabelOption ) ) ? $singleLabelOption : __( 'Location', 'aioseo-local-business' );
		$singleLabel = apply_filters( 'aioseo_local_business_post_type_single_label', $singleLabel );

		return $singleLabel;
	}

	/**
	 * Returns the plural label for the post type.
	 *
	 * @since 1.1.0
	 *
	 * @return string The post plural label.
	 */
	public function getPluralLabel() {
		$pluralLabelOption = aioseo()->options->localBusiness->locations->general->pluralLabel ?: '';

		$pluralLabel = ( 0 < strlen( $pluralLabelOption ) ) ? $pluralLabelOption : __( 'Locations', 'aioseo-local-business' );
		$pluralLabel = apply_filters( 'aioseo_local_business_post_type_plural_label', $pluralLabel );

		return $pluralLabel;
	}

	/**
	 * Returns current permalink structure for this post type.
	 *
	 * @since 1.1.0
	 *
	 * @return string The loaded permastruct or a default if not enabled yet.
	 */
	public function getPermaStructure() {
		return aioseoLocalBusiness()->helpers->getPermaStructure( $this->getName(), $this->getSlug() );
	}

	/**
	 * Returns edit link for this post type.
	 *
	 * @since 1.1.0
	 *
	 * @return false|string The main post type edit link.
	 */
	public function getEditLink() {
		return admin_url( 'edit.php?post_type=' . $this->getName() );
	}

	/**
	 * Returns this post type capabilites.
	 *
	 * @since 1.1.0
	 *
	 * @return array An array of mapped capabilities.
	 */
	public function getCapabilities() {
		return [
			'edit_post'          => 'edit_aioseo_location',
			'edit_posts'         => 'edit_aioseo_locations',
			'edit_others_posts'  => 'edit_other_aioseo_locations',
			'publish_posts'      => 'publish_aioseo_locations',
			'read_post'          => 'read_aioseo_location',
			'read_private_posts' => 'read_private_aioseo_locations',
			'delete_post'        => 'delete_aioseo_location'
		];
	}

	/**
	 * Register the post type.
	 *
	 * @since 1.1.0
	 *
	 * @return void
	 */
	public function register() {
		if ( ! aioseo()->options->localBusiness->locations->general->multiple ) {
			return;
		}

		$labels = [
			'name'                  => $this->getPluralLabel(),
			'singular_name'         => $this->getSingleLabel(),
			// Translators: 1 - Singular post type name.
			'archives'              => sprintf( _x( '%1$s Archives', 'Post Type', 'aioseo-local-business' ), $this->getSingleLabel() ),
			// Translators: 1 - Singular post type name.
			'attributes'            => sprintf( _x( '%1$s Attributes', 'Post Type', 'aioseo-local-business' ), $this->getSingleLabel() ),
			// Translators: 1 - Singular post type name.
			'parent_item_colon'     => sprintf( _x( 'Parent %1$s:', 'Post Type', 'aioseo-local-business' ), $this->getSingleLabel() ),
			// Translators: 1 - Plural post type name.
			'all_items'             => sprintf( _x( 'All %1$s', 'Post Type', 'aioseo-local-business' ), $this->getPluralLabel() ),
			// Translators: 1 - Singular post type name.
			'add_new'          => sprintf( _x( 'Add New %1$s', 'Post Type', 'aioseo-local-business' ), $this->getSingleLabel() ),
			// Translators: 1 - Singular post type name.
			'add_new_item'          => sprintf( _x( 'Add New %1$s', 'Post Type', 'aioseo-local-business' ), $this->getSingleLabel() ),
			// Translators: 1 - Singular post type name.
			'new_item'              => sprintf( _x( 'New %1$s', 'Post Type', 'aioseo-local-business' ), $this->getSingleLabel() ),
			// Translators: 1 - Singular post type name.
			'edit_item'             => sprintf( _x( 'Edit %1$s', 'Post Type', 'aioseo-local-business' ), $this->getSingleLabel() ),
			// Translators: 1 - Singular post type name.
			'update_item'           => sprintf( _x( 'Update %1$s', 'Post Type', 'aioseo-local-business' ), $this->getSingleLabel() ),
			// Translators: 1 - Singular or plural post type name.
			'view_item'             => sprintf( _x( 'View %1$s', 'Post Type', 'aioseo-local-business' ), $this->getSingleLabel() ),
			// Translators: 1 - Singular or plural post type name.
			'view_items'            => sprintf( _x( 'View %1$s', 'Post Type', 'aioseo-local-business' ), $this->getPluralLabel() ),
			// Translators: 1 - Singular post type name.
			'search_items'          => sprintf( _x( 'Search %1$s', 'Post Type', 'aioseo-local-business' ), $this->getSingleLabel() ),
			// Translators: 1 - Singular post type name.
			'insert_into_item'      => sprintf( _x( 'Insert into %1$s', 'Post Type', 'aioseo-local-business' ), $this->getSingleLabel() ),
			// Translators: 1 - Singular post type name.
			'uploaded_to_this_item' => sprintf( _x( 'Uploaded to this %1$s', 'Post Type', 'aioseo-local-business' ), $this->getSingleLabel() ),
			// Translators: 1 - Plural post type name.
			'items_list'            => sprintf( _x( '%1$s list', 'Post Type', 'aioseo-local-business' ), $this->getPluralLabel() ),
			// Translators: 1 - Plural post type name.
			'items_list_navigation' => sprintf( _x( '%1$s list navigation', 'Post Type', 'aioseo-local-business' ), $this->getPluralLabel() ),
			// Translators: 1 - Plural post type name.
			'filter_items_list'     => sprintf( _x( 'Filter %1$s list', 'Post Type', 'aioseo-local-business' ), $this->getPluralLabel() ),
		];

		$postType = [
			'labels'       => $labels,
			'description'  => sprintf(
				// Translators: 1 - The plugin name ("All in One SEO").
				__( '%1$s uses these locations to generate Local Business schema markup.', 'aioseo-local-business' ),
				AIOSEO_PLUGIN_NAME
			),
			'public'       => true,
			'rewrite'      => [
				'slug' => $this->getSlug(),
			],
			'pages'        => false,
			'menu_icon'    => 'dashicons-location',
			'show_in_rest' => true,
			'capabilities' => $this->getCapabilities(),
			'map_meta_cap' => true,
			'supports'     => [ 'title', 'editor', 'thumbnail' ]
		];

		$postType = apply_filters( 'aioseo_local_business_post_type', $postType );

		if ( ! in_array( $this->getName(), get_post_types(), true ) ) {
			aioseo()->options->flushRewriteRules();
		}

		register_post_type(
			$this->getName(),
			$postType
		);

		register_rest_field( $this->getName(), 'maps', [
			'get_callback' => [ aioseoLocalBusiness()->maps, 'restMapInfo' ]
		] );
	}
}app/Admin/Admin.php000064400000002542151727277640010142 0ustar00<?php
namespace AIOSEO\Plugin\Addon\LocalBusiness\Admin;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

use AIOSEO\Plugin\Common\Admin as CommonAdmin;

/**
 * The Admin class.
 *
 * @since 1.1.0
 */
class Admin extends CommonAdmin\Admin {
	/**
	 * Class constructor.
	 *
	 * @since 1.0.0
	 */
	public function __construct() {
		if ( is_admin() ) {
			add_action( 'add_meta_boxes', [ $this, 'addMetabox' ] );
		}
	}

	/**
	 * Adds a meta box to the page/posts screens.
	 *
	 * @since 1.1.0
	 *
	 * @return void
	 */
	public function addMetabox() {
		if ( ! aioseo()->access->hasCapability( 'aioseo_page_local_seo_settings' ) ) {
			return;
		}

		// Translators: 1 - The plugin short name ("AIOSEO").
		$aioseoMetaboxTitle = sprintf( esc_html__( '%1$s Local Business', 'aioseo-local-business' ), AIOSEO_PLUGIN_SHORT_NAME );

		add_meta_box(
			'aioseo-local-settings',
			$aioseoMetaboxTitle,
			[ $this, 'renderMetabox' ],
			[ aioseoLocalBusiness()->postType->getName() ],
			'normal',
			'high'
		);
	}

	/**
	 * Render the on-page settings metabox with the Vue App wrapper.
	 *
	 * @since 1.1.0
	 *
	 * @return void
	 */
	public function renderMetabox() {
		aioseo()->postSettings->postSettingsHiddenField();
		?>
		<div id="aioseo-location-settings-metabox">
			<?php aioseo()->templates->getTemplate( 'parts/loader.php' ); ?>
		</div>
		<?php
	}
}app/Admin/LocationCategory.php000064400000012343151727277640012360 0ustar00<?php
namespace AIOSEO\Plugin\Addon\LocalBusiness\Admin;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * The Location class.
 *
 * @since 1.1.0
 */
class LocationCategory {
	/**
	 * Location constructor.
	 *
	 * @since 1.1.0
	 */
	public function __construct() {
		add_action( 'init', [ $this, 'register' ] );
	}

	/**
	 * Returns the post type name.
	 *
	 * @since 1.1.0
	 *
	 * @return string The taxonomy name.
	 */
	public function getName() {
		return apply_filters( 'aioseo_local_business_taxonomy_name', 'aioseo-location-category' );
	}

	/**
	 * Returns the current slug for the taxonomy.
	 *
	 * @since 1.1.0
	 *
	 * @return string The taxonomy slug.
	 */
	public function getSlug() {
		$useCustomSlug    = aioseo()->options->localBusiness->locations->general->useCustomCategorySlug;
		$customSlugOption = aioseo()->options->localBusiness->locations->general->customCategorySlug ?: '';

		$customSlug = ( true === $useCustomSlug && 0 < strlen( $customSlugOption ) )
			? $customSlugOption
			: $this->getDefaultSlug();

		$customSlug = apply_filters( 'aioseo_local_business_taxonomy_slug', $customSlug );

		return $customSlug;
	}

	/**
	 * Returns the default slug for the taxonomy.
	 *
	 * @since 1.1.0
	 *
	 * @return string The taxonomy default slug.
	 */
	public function getDefaultSlug() {
		return 'locations-category';
	}

	/**
	 * Returns current permalink structure for this taxonomy.
	 *
	 * @since 1.1.0
	 *
	 * @return string The loaded permalink structure or a default if not enabled yet.
	 */
	public function getPermaStructure() {
		return aioseoLocalBusiness()->helpers->getPermaStructure( $this->getName(), $this->getSlug() );
	}

	/**
	 * Returns the single label for the taxonomy.
	 *
	 * @since 1.1.0
	 *
	 * @return string The taxonomy label.
	 */
	public function getSingleLabel() {
		return __( 'Location Category', 'aioseo-local-business' );
	}

	/**
	 * Returns the plural label for taxonomy.
	 *
	 * @since 1.1.0
	 *
	 * @return string The taxonomy plural label.
	 */
	public function getPluralLabel() {
		return __( 'Location Categories', 'aioseo-local-business' );
	}

	/**
	 * Returns the post type capabilites.
	 *
	 * @since 1.1.0
	 *
	 * @return array An array of mapped capabilities.
	 */
	public function getCapabilities() {
		return [
			'manage_terms' => 'manage_location_categories',
			'edit_terms'   => 'edit_location_categories',
			'delete_terms' => 'delete_location_categories',
			'assign_terms' => 'assign_location_categories'
		];
	}

	/**
	 * Registers the taxonomy.
	 *
	 * @since 1.1.0
	 *
	 * @return void
	 */
	public function register() {
		if ( ! aioseo()->options->localBusiness->locations->general->multiple ) {
			return;
		}

		$labels = [
			'name'                  => $this->getPluralLabel(),
			'singular_name'         => $this->getSingleLabel(),
			// Translators: 1 - Plural post type name.
			'all_items'             => sprintf( _x( 'All %1$s', 'Taxonomy', 'aioseo-local-business' ), $this->getPluralLabel() ),
			// Translators: 1 - Singular post type name.
			'parent_item'           => sprintf( _x( 'Parent %1$s', 'Taxonomy', 'aioseo-local-business' ), $this->getSingleLabel() ),
			// Translators: 1 - Singular post type name.
			'parent_item_colon'     => sprintf( _x( 'Parent %1$s:', 'Taxonomy', 'aioseo-local-business' ), $this->getSingleLabel() ),
			// Translators: 1 - Singular post type name.
			'add_new_item'          => sprintf( _x( 'Add New %1$s', 'Taxonomy', 'aioseo-local-business' ), $this->getSingleLabel() ),
			// Translators: 1 - Singular post type name.
			'edit_item'             => sprintf( _x( 'Edit %1$s', 'Taxonomy', 'aioseo-local-business' ), $this->getSingleLabel() ),
			// Translators: 1 - Singular post type name.
			'update_item'           => sprintf( _x( 'Update %1$s', 'Taxonomy', 'aioseo-local-business' ), $this->getSingleLabel() ),
			// Translators: 1 - Singular or plural post type name.
			'view_item'             => sprintf( _x( 'View %1$s', 'Taxonomy', 'aioseo-local-business' ), $this->getSingleLabel() ),
			// Translators: 1 - Singular or plural post type name.
			'view_items'            => sprintf( _x( 'View %1$s', 'Taxonomy', 'aioseo-local-business' ), $this->getPluralLabel() ),
			// Translators: 1 - Plural post type name.
			'search_items'          => sprintf( _x( 'Search %1$s', 'Taxonomy', 'aioseo-local-business' ), $this->getPluralLabel() ),
			// Translators: 1 - Plural post type name.
			'no_terms'              => sprintf( _x( 'No %1$s', 'Taxonomy', 'aioseo-local-business' ), $this->getPluralLabel() ),
			// Translators: 1 - Plural post type name.
			'items_list_navigation' => sprintf( _x( '%1$s list navigation', 'Taxonomy', 'aioseo-local-business' ), $this->getPluralLabel() ),
			// Translators: 1 - Plural post type name.
			'filter_items_list'     => sprintf( _x( 'Filter %1$s list', 'Taxonomy', 'aioseo-local-business' ), $this->getPluralLabel() )
		];

		$taxonomy = [
			'labels'       => $labels,
			'public'       => true,
			'rewrite'      => [
				'slug' => $this->getSlug(),
			],
			'show_in_rest' => true,
			'capabilities' => $this->getCapabilities(),
			'hierarchical' => true
		];

		$taxonomy = apply_filters( 'aioseo_local_business_taxonomy', $taxonomy );

		register_taxonomy( $this->getName(), aioseoLocalBusiness()->postType->getName(), $taxonomy );
	}
}app/Admin/Usage.php000064400000000670151727277640010156 0ustar00<?php
namespace AIOSEO\Plugin\Addon\LocalBusiness\Admin;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Description
 *
 * @since 1.2.10
 */
class Usage {
	/**
	 * Retrieves the data to send in the usage tracking.
	 *
	 * @since 1.2.10
	 *
	 * @return array An array of data to send.
	 */
	public function getData() {
		return [
			'internalOptions' => aioseoLocalBusiness()->internalOptions->all()
		];
	}
}app/Widgets/Map.php000064400000015107151727277650010207 0ustar00<?php
namespace AIOSEO\Plugin\Addon\LocalBusiness\Widgets;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * AIOSEO Business Info widget.
 *
 * @since 1.1.3
 */
class Map extends \WP_Widget {
	/**
	 * The default attributes.
	 *
	 * @since 1.2.12
	 *
	 * @var array
	 */
	private $defaults = [];

	/**
	 * Class constructor.
	 *
	 * @since 1.1.3
	 */
	public function __construct() {
		// Widget defaults.
		$this->defaults = [
			'title'      => '',
			'locationId' => '',
			'showLabel'  => true,
			'showIcon'   => true,
			'width'      => '100%',
			'height'     => '450px',
			'label'      => __( 'Our location:', 'aioseo-local-business' )
		];

		// Widget Slug.
		$widgetSlug = 'aioseo-local-map-widget';

		// Widget basics.
		$widgetOps = [
			'classname'   => $widgetSlug,
			'description' => esc_html__( 'Display a location map.', 'aioseo-local-business' ),
		];

		// Widget controls.
		$controlOps = [
			'id_base' => $widgetSlug,
		];

		// Load widget.
		parent::__construct( $widgetSlug, esc_html__( 'AIOSEO Local - Map', 'aioseo-local-business' ), $widgetOps, $controlOps );
	}

	/**
	 * Widget callback.
	 *
	 * @since 1.1.3
	 *
	 * @param  array $args     Widget args.
	 * @param  array $instance The widget instance options.
	 * @return void
	 */
	public function widget( $args, $instance ) {
		// Merge with defaults.
		$instance = wp_parse_args( (array) $instance, $this->defaults );

		echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped

		// Title.
		if ( ! empty( $instance['title'] ) ) {
			echo $args['before_title'] . apply_filters( 'widget_title', $instance['title'], $instance, $this->id_base ) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped,Generic.Files.LineLength.MaxExceeded
		}

		// Location info.
		aioseoLocalBusiness()->locations->outputLocationMap( $instance['locationId'], $instance );

		echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
	}

	/**
	 * Widget option update.
	 *
	 * @since 1.1.3
	 *
	 * @param  array $newInstance New instance options.
	 * @param  array $oldInstance Old instance options.
	 * @return array              Processed new instance options.
	 */
	public function update( $newInstance, $oldInstance ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
		$newInstance['title']     = wp_strip_all_tags( $newInstance['title'] );
		$newInstance['showLabel'] = isset( $newInstance['showLabel'] ) ? '1' : false;
		$newInstance['showIcon']  = isset( $newInstance['showIcon'] ) ? '1' : false;

		return $newInstance;
	}

	/**
	 * Widget options form.
	 *
	 * @since 1.1.3
	 *
	 * @param  array $instance The widget instance options.
	 * @return void
	 */
	public function form( $instance ) {
		// Merge with defaults.
		$instance = wp_parse_args( (array) $instance, $this->defaults );
		?>
		<p>
			<label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>">
				<?php echo esc_html( __( 'Title:', 'aioseo-local-business' ) ); ?>
			</label>
			<input
					type="text"
					id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"
					name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>"
					value="<?php echo esc_attr( $instance['title'] ); ?>"
					class="widefat"
			/>
		</p>
		<?php if ( aioseo()->options->localBusiness->locations->general->multiple ) { ?>
			<p>
				<label for="<?php echo esc_attr( $this->get_field_id( 'locationId' ) ); ?>">
					<?php esc_html_e( 'Location:', 'aioseo-local-business' ); ?>
				</label>
				<select class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'locationId' ) ); ?>"
						name="<?php echo esc_attr( $this->get_field_name( 'locationId' ) ); ?>">
					<?php
					$locations = aioseoLocalBusiness()->locations->getLocations( [
						'order_by' => 'post_title',
						'order'    => 'asc'
					] );
					if ( ! empty( $locations ) ) {
						echo '<option value="" selected disabled>' . esc_html__( 'Select your location', 'aioseo-local-business' ) . '</option>';
						foreach ( $locations as $location ) {
							echo '<option value="' . esc_attr( $location->ID ) . '" ' . selected( $instance['locationId'], $location->ID, false ) . '>' . esc_html( $location->post_title ) . '</option>'; // phpcs:ignore Generic.Files.LineLength.MaxExceeded
						}
					} else {
						echo '<option value="">' . esc_html__( 'No locations available', 'aioseo-local-business' ) . '</option>';
					}
					?>
				</select>
			</p>
		<?php } ?>
		<p>
			<input type="checkbox" id="<?php echo esc_attr( $this->get_field_id( 'showLabel' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'showLabel' ) ); ?>" <?php checked( '1', $instance['showLabel'] ); ?>> <?php // phpcs:ignore Generic.Files.LineLength.MaxExceeded ?>
			<label for="<?php echo esc_attr( $this->get_field_id( 'showLabel' ) ); ?>">
				<?php esc_html_e( 'Show label', 'aioseo-local-business' ); ?>
			</label>
		</p>
		<p>
			<input type="checkbox" id="<?php echo esc_attr( $this->get_field_id( 'showIcon' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'showIcon' ) ); ?>" <?php checked( '1', $instance['showIcon'] ); ?>> <?php // phpcs:ignore Generic.Files.LineLength.MaxExceeded ?>
			<label for="<?php echo esc_attr( $this->get_field_id( 'showIcon' ) ); ?>">
				<?php esc_html_e( 'Show icon', 'aioseo-local-business' ); ?>
			</label>
		</p>
		<p>
			<label for="<?php echo esc_attr( $this->get_field_id( 'width' ) ); ?>">
				<?php echo esc_html( __( 'Width', 'aioseo-local-business' ) ); ?>:
			</label>
			<input
					type="text"
					id="<?php echo esc_attr( $this->get_field_id( 'width' ) ); ?>"
					name="<?php echo esc_attr( $this->get_field_name( 'width' ) ); ?>"
					value="<?php echo esc_attr( $instance['width'] ); ?>"
					class="widefat"
			/>
		</p>
		<p>
			<label for="<?php echo esc_attr( $this->get_field_id( 'height' ) ); ?>">
				<?php echo esc_html( __( 'Height', 'aioseo-local-business' ) ); ?>:
			</label>
			<input
					type="text"
					id="<?php echo esc_attr( $this->get_field_id( 'height' ) ); ?>"
					name="<?php echo esc_attr( $this->get_field_name( 'height' ) ); ?>"
					value="<?php echo esc_attr( $instance['height'] ); ?>"
					class="widefat"
			/>
		</p>
		<p>
			<label for="<?php echo esc_attr( $this->get_field_id( 'label' ) ); ?>">
				<?php esc_html_e( 'Label', 'aioseo-local-business' ); ?>
			</label>
			<input
					type="text"
					id="<?php echo esc_attr( $this->get_field_id( 'label' ) ); ?>"
					name="<?php echo esc_attr( $this->get_field_name( 'label' ) ); ?>"
					value="<?php echo esc_attr( $instance['label'] ); ?>"
					class="widefat"
			/>
		</p>
		<?php
	}
}app/Widgets/Locations.php000064400000010717151727277650011427 0ustar00<?php
namespace AIOSEO\Plugin\Addon\LocalBusiness\Widgets;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Class Locations.
 *
 * @since 1.1.0
 */
class Locations extends \WP_Widget {
	/**
	 * The default attributes.
	 *
	 * @since 1.2.12
	 *
	 * @var array
	 */
	private $defaults = [];

	/**
	 * Class constructor.
	 *
	 * @since 1.1.0
	 */
	public function __construct() {
		// Widget defaults.
		$this->defaults = [
			'title'      => '',
			'categoryId' => ''
		];

		// Widget Slug.
		$widgetSlug = 'aioseo-local-category-widget';

		// Widget basics.
		$widgetOps = [
			'classname'   => $widgetSlug,
			'description' => esc_html__( 'Display a list of locations by category.', 'aioseo-local-business' ),
		];

		// Widget controls.
		$controlOps = [
			'id_base' => $widgetSlug,
		];

		// Load widget.
		parent::__construct( $widgetSlug, esc_html__( 'AIOSEO Local - Locations by Category', 'aioseo-local-business' ), $widgetOps, $controlOps );
	}

	/**
	 * Widget callback.
	 *
	 * @since 1.1.0
	 *
	 * @param  array $args     Widget args.
	 * @param  array $instance The widget instance options.
	 * @return void
	 */
	public function widget( $args, $instance ) {
		if ( aioseo()->options->localBusiness->locations->general->multiple ) {

			if ( empty( $instance['categoryId'] ) || is_wp_error( get_term( $instance['categoryId'], aioseoLocalBusiness()->taxonomy->getName() ) ) ) {
				return;
			}

			// Merge with defaults.
			$instance = wp_parse_args( (array) $instance, $this->defaults );

			echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped

			// Title.
			if ( ! empty( $instance['title'] ) ) {
				echo $args['before_title'] . apply_filters( 'widget_title', $instance['title'], $instance, $this->id_base ) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped,Generic.Files.LineLength.MaxExceeded
			}

			// Location info.
			if ( ! empty( $instance['categoryId'] ) ) {
				aioseoLocalBusiness()->locations->outputLocationCategory( absint( $instance['categoryId'] ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
			}

			echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
		}
	}

	/**
	 * Widget option update.
	 *
	 * @since 1.1.0
	 *
	 * @param  array $newInstance New instance options.
	 * @param  array $oldInstance Old instance options.
	 * @return array              Processed new instance options.
	 */
	public function update( $newInstance, $oldInstance ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
		$newInstance['title']      = wp_strip_all_tags( $newInstance['title'] );
		$newInstance['categoryId'] = ! empty( $newInstance['categoryId'] ) ? (int) $newInstance['categoryId'] : 0;

		return $newInstance;
	}

	/**
	 * Widget options form.
	 *
	 * @since 1.1.0
	 *
	 * @param  array $instance The widget instance options.
	 * @return void
	 */
	public function form( $instance ) {
		// Merge with defaults.
		$instance = wp_parse_args( (array) $instance, $this->defaults );
		?>
		<p>
			<label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>">
				<?php echo esc_html( __( 'Title:', 'aioseo-local-business' ) ); ?>
			</label>
			<input
					type="text"
					id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"
					name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>"
					value="<?php echo esc_attr( $instance['title'] ); ?>"
					class="widefat"
			/>
		</p>
		<p>
			<label for="<?php echo esc_attr( $this->get_field_id( 'categoryId' ) ); ?>">
				<?php echo esc_html__( 'Category:', 'aioseo-local-business' ); ?>
			</label>
			<select class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'categoryId' ) ); ?>"
					name="<?php echo esc_attr( $this->get_field_name( 'categoryId' ) ); ?>">
				<?php
				$categories = aioseoLocalBusiness()->locations->getLocationCategories();
				if ( ! empty( $categories ) ) {
					echo '<option value="" selected disabled>' . esc_html__( 'Select a category', 'aioseo-local-business' ) . '</option>';
					foreach ( $categories as $category ) {
						echo '<option value="' . esc_attr( $category->term_id ) . '" ' . selected( $instance['categoryId'], $category->term_id, false ) . '>';
						echo esc_html( $category->name );
						echo '</option>';
					}
				} else {
					echo '<option value="">' . esc_html__( 'No categories available', 'aioseo-local-business' ) . '</option>';
				}
				?>
			</select>
		</p>
		<?php
	}
}app/Widgets/OpeningHours.php000064400000025164151727277650012116 0ustar00<?php
namespace AIOSEO\Plugin\Addon\LocalBusiness\Widgets;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Class OpeningHours.
 *
 * @since 1.1.0
 */
class OpeningHours extends \WP_Widget {
	/**
	 * The default attributes.
	 *
	 * @since 1.2.12
	 *
	 * @var array
	 */
	private $defaults = [];

	/**
	 * Class constructor.
	 *
	 * @since 1.1.0
	 */
	public function __construct() {
		// Widget defaults.
		$this->defaults = [
			'title'         => '',
			'locationId'    => '',
			'showTitle'     => true,
			'showIcons'     => true,
			'showMonday'    => true,
			'showTuesday'   => true,
			'showWednesday' => true,
			'showThursday'  => true,
			'showFriday'    => true,
			'showSaturday'  => true,
			'showSunday'    => true,
			'label'         => __( 'Our Opening Hours:', 'aioseo-local-business' ),
			'after'         => '',
		];

		// Widget Slug.
		$widgetSlug = 'aioseo-local-opening-hours-widget';

		// Widget basics.
		$widgetOps = [
			'classname'   => $widgetSlug,
			'description' => esc_html__( 'Display opening hours of a location.', 'aioseo-local-business' ),
		];

		// Widget controls.
		$controlOps = [
			'id_base' => $widgetSlug,
		];

		// Load widget.
		parent::__construct( $widgetSlug, esc_html__( 'AIOSEO Local - Opening Hours', 'aioseo-local-business' ), $widgetOps, $controlOps );
	}

	/**
	 * Widget callback.
	 *
	 * @since 1.1.0
	 *
	 * @param  array $args     Widget args.
	 * @param  array $instance The widget instance options.
	 * @return void
	 */
	public function widget( $args, $instance ) {
		// Merge with defaults.
		$instance = wp_parse_args( (array) $instance, $this->defaults );

		echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped

		// Title.
		if ( ! empty( $instance['title'] ) ) {
			echo $args['before_title'] . apply_filters( 'widget_title', $instance['title'], $instance, $this->id_base ) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped,Generic.Files.LineLength.MaxExceeded
		}

		// Location info.
		aioseoLocalBusiness()->locations->outputOpeningHours( $instance['locationId'], $instance ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped

		// After.
		if ( ! empty( $instance['after'] ) ) {
			echo $instance['after']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
		}

		echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
	}

	/**
	 * Widget option update.
	 *
	 * @since 1.1.0
	 *
	 * @param  array $newInstance New instance options.
	 * @param  array $oldInstance Old instance options.
	 * @return array              Processed new instance options.
	 */
	public function update( $newInstance, $oldInstance ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
		// $newInstance['locationId']    = ! empty( $newInstance['locationId'] ) ? (int) $newInstance['locationId'] : 0;
		$newInstance['title']         = wp_kses( $newInstance['title'], 'post' );
		$newInstance['showTitle']     = ! empty( $newInstance['showTitle'] ) ? '1' : false;
		$newInstance['showIcons']     = ! empty( $newInstance['showIcons'] ) ? '1' : false;
		$newInstance['showMonday']    = ! empty( $newInstance['showMonday'] ) ? '1' : false;
		$newInstance['showTuesday']   = ! empty( $newInstance['showTuesday'] ) ? '1' : false;
		$newInstance['showWednesday'] = ! empty( $newInstance['showWednesday'] ) ? '1' : false;
		$newInstance['showThursday']  = ! empty( $newInstance['showThursday'] ) ? '1' : false;
		$newInstance['showFriday']    = ! empty( $newInstance['showFriday'] ) ? '1' : false;
		$newInstance['showSaturday']  = ! empty( $newInstance['showSaturday'] ) ? '1' : false;
		$newInstance['showSunday']    = ! empty( $newInstance['showSunday'] ) ? '1' : false;
		$newInstance['after']         = wp_kses( $newInstance['after'], 'post' );

		return $newInstance;
	}

	/**
	 * Widget options form.
	 *
	 * @since 1.1.0
	 *
	 * @param  array $instance The widget instance options.
	 * @return void
	 */
	public function form( $instance ) {
		// Merge with defaults.
		$instance = wp_parse_args( (array) $instance, $this->defaults );
		?>
		<p>
			<label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>">
				<?php echo esc_html( __( 'Title:', 'aioseo-local-business' ) ); ?>
			</label>
			<input
					type="text"
					id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"
					name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>"
					value="<?php echo esc_attr( $instance['title'] ); ?>"
					class="widefat"
			/>
		</p>
		<?php if ( aioseo()->options->localBusiness->locations->general->multiple ) { ?>
			<p>
				<label for="<?php echo esc_attr( $this->get_field_id( 'locationId' ) ); ?>">
					<?php echo esc_html__( 'Location:', 'aioseo-local-business' ); ?>
				</label>
				<select class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'locationId' ) ); ?>"
						name="<?php echo esc_attr( $this->get_field_name( 'locationId' ) ); ?>">
					<?php
					$locations = aioseoLocalBusiness()->locations->getLocations( [
						'order_by' => 'post_title',
						'order'    => 'asc'
					] );
					if ( ! empty( $locations ) ) {
						echo '<option value="" selected disabled>' . esc_html__( 'Select your location', 'aioseo-local-business' ) . '</option>';
						foreach ( $locations as $location ) {
							echo '<option value="' . esc_attr( $location->ID ) . '" ' . selected( $instance['locationId'], $location->ID, false ) . '>' . esc_html( $location->post_title ) . '</option>'; // phpcs:ignore Generic.Files.LineLength.MaxExceeded
						}
					} else {
						echo '<option value="">' . esc_html__( 'No locations available', 'aioseo-local-business' ) . '</option>';
					}
					?>
				</select>
			</p>
		<?php } ?>
		<p>
			<input type="checkbox" id="<?php echo esc_attr( $this->get_field_id( 'showTitle' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'showTitle' ) ); ?>" <?php checked( '1', $instance['showTitle'] ); ?>> <?php // phpcs:ignore Generic.Files.LineLength.MaxExceeded ?>
			<label for="<?php echo esc_attr( $this->get_field_id( 'showTitle' ) ); ?>">
				<?php esc_html_e( 'Show title', 'aioseo-local-business' ); ?>
			</label>
		</p>
		<p>
			<input type="checkbox" id="<?php echo esc_attr( $this->get_field_id( 'showIcons' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'showIcons' ) ); ?>" <?php checked( '1', $instance['showIcons'] ); ?>> <?php // phpcs:ignore Generic.Files.LineLength.MaxExceeded ?>
			<label for="<?php echo esc_attr( $this->get_field_id( 'showIcons' ) ); ?>">
				<?php esc_html_e( 'Show icons', 'aioseo-local-business' ); ?>
			</label>
		</p>
		<p>
			<strong><?php esc_html_e( 'Opening Hours', 'aioseo-local-business' ); ?></strong>
		</p>
		<p>
			<input type="checkbox" id="<?php echo esc_attr( $this->get_field_id( 'showMonday' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'showMonday' ) ); ?>" <?php checked( '1', $instance['showMonday'] ); ?>> <?php // phpcs:ignore Generic.Files.LineLength.MaxExceeded ?>
			<label for="<?php echo esc_attr( $this->get_field_id( 'showMonday' ) ); ?>">
				<?php esc_html_e( 'Monday', 'aioseo-local-business' ); ?>
			</label>
		</p>
		<p>
			<input type="checkbox" id="<?php echo esc_attr( $this->get_field_id( 'showTuesday' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'showTuesday' ) ); ?>" <?php checked( '1', $instance['showTuesday'] ); ?>> <?php // phpcs:ignore Generic.Files.LineLength.MaxExceeded ?>
			<label for="<?php echo esc_attr( $this->get_field_id( 'showTuesday' ) ); ?>">
				<?php esc_html_e( 'Tuesday', 'aioseo-local-business' ); ?>
			</label>
		</p>
		<p>
			<input type="checkbox" id="<?php echo esc_attr( $this->get_field_id( 'showWednesday' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'showWednesday' ) ); ?>" <?php checked( '1', $instance['showWednesday'] ); ?>> <?php // phpcs:ignore Generic.Files.LineLength.MaxExceeded ?>
			<label for="<?php echo esc_attr( $this->get_field_id( 'showWednesday' ) ); ?>">
				<?php esc_html_e( 'Wednesday', 'aioseo-local-business' ); ?>
			</label>
		</p>
		<p>
			<input type="checkbox" id="<?php echo esc_attr( $this->get_field_id( 'showThursday' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'showThursday' ) ); ?>" <?php checked( '1', $instance['showThursday'] ); ?>> <?php // phpcs:ignore Generic.Files.LineLength.MaxExceeded ?>
			<label for="<?php echo esc_attr( $this->get_field_id( 'showThursday' ) ); ?>">
				<?php esc_html_e( 'Thursday', 'aioseo-local-business' ); ?>
			</label>
		</p>
		<p>
			<input type="checkbox" id="<?php echo esc_attr( $this->get_field_id( 'showFriday' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'showFriday' ) ); ?>" <?php checked( '1', $instance['showFriday'] ); ?>> <?php // phpcs:ignore Generic.Files.LineLength.MaxExceeded ?>
			<label for="<?php echo esc_attr( $this->get_field_id( 'showFriday' ) ); ?>">
				<?php esc_html_e( 'Friday', 'aioseo-local-business' ); ?>
			</label>
		</p>
		<p>
			<input type="checkbox" id="<?php echo esc_attr( $this->get_field_id( 'showSaturday' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'showSaturday' ) ); ?>" <?php checked( '1', $instance['showSaturday'] ); ?>> <?php // phpcs:ignore Generic.Files.LineLength.MaxExceeded ?>
			<label for="<?php echo esc_attr( $this->get_field_id( 'showSaturday' ) ); ?>">
				<?php esc_html_e( 'Saturday', 'aioseo-local-business' ); ?>
			</label>
		</p>
		<p>
			<input type="checkbox" id="<?php echo esc_attr( $this->get_field_id( 'showSunday' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'showSunday' ) ); ?>" <?php checked( '1', $instance['showSunday'] ); ?>> <?php // phpcs:ignore Generic.Files.LineLength.MaxExceeded ?>
			<label for="<?php echo esc_attr( $this->get_field_id( 'showSunday' ) ); ?>">
				<?php esc_html_e( 'Sunday', 'aioseo-local-business' ); ?>
			</label>
		</p>
		<p>
			<label for="<?php echo esc_attr( $this->get_field_id( 'label' ) ); ?>">
				<?php esc_html_e( 'Label', 'aioseo-local-business' ); ?>
			</label>
			<input
					type="text"
					id="<?php echo esc_attr( $this->get_field_id( 'label' ) ); ?>"
					name="<?php echo esc_attr( $this->get_field_name( 'label' ) ); ?>"
					value="<?php echo esc_attr( $instance['label'] ); ?>"
					class="widefat"
			/>
		</p>
		<p>
			<label for="<?php echo esc_attr( $this->get_field_id( 'after' ) ); ?>">
				<?php echo esc_html__( 'After widget text:', 'aioseo-local-business' ); ?>
			</label><br />
			<textarea id="<?php echo esc_attr( $this->get_field_id( 'after' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'after' ) ); ?>" class="widefat"><?php echo esc_attr( $instance['after'] ); ?></textarea> <?php // phpcs:ignore Generic.Files.LineLength.MaxExceeded ?>
		</p>
		<?php
	}
}app/Widgets/BusinessInfo.php000064400000027772151727277650012114 0ustar00<?php
namespace AIOSEO\Plugin\Addon\LocalBusiness\Widgets;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * AIOSEO Business Info widget.
 *
 * @since 1.1.0
 */
class BusinessInfo extends \WP_Widget {
	/**
	 * The default attributes.
	 *
	 * @since 1.2.12
	 *
	 * @var array
	 */
	private $defaults = [];

	/**
	 * Class constructor.
	 *
	 * @since 1.1.0
	 */
	public function __construct() {
		// Widget defaults.
		$this->defaults = [
			'title'           => '',
			'locationId'      => '',
			'showLabels'      => true,
			'showIcons'       => true,
			'showName'        => true,
			'showAddress'     => true,
			'showPhone'       => true,
			'showFax'         => true,
			'showCountryCode' => true,
			'showEmail'       => true,
			'showVat'         => true,
			'showTax'         => true,
			'addressLabel'    => __( 'Address:', 'aioseo-local-business' ),
			'vatIdLabel'      => __( 'VAT ID:', 'aioseo-local-business' ),
			'taxIdLabel'      => __( 'Tax ID:', 'aioseo-local-business' ),
			'phoneLabel'      => __( 'Phone:', 'aioseo-local-business' ),
			'faxLabel'        => __( 'Fax:', 'aioseo-local-business' ),
			'emailLabel'      => __( 'Email:', 'aioseo-local-business' ),
			'after'           => '',
		];

		// Widget Slug.
		$widgetSlug = 'aioseo-local-business-info-widget';

		// Widget basics.
		$widgetOps = [
			'classname'   => $widgetSlug,
			'description' => esc_html__( 'Display a location information.', 'aioseo-local-business' ),
		];

		// Widget controls.
		$controlOps = [
			'id_base' => $widgetSlug,
		];

		// Load widget.
		parent::__construct( $widgetSlug, esc_html__( 'AIOSEO Local - Business Info', 'aioseo-local-business' ), $widgetOps, $controlOps );
	}

	/**
	 * Widget callback.
	 *
	 * @since 1.1.0
	 *
	 * @param  array $args     Widget args.
	 * @param  array $instance The widget instance options.
	 * @return void
	 */
	public function widget( $args, $instance ) {
		// Merge with defaults.
		$instance = wp_parse_args( (array) $instance, $this->defaults );

		echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped

		// Title.
		if ( ! empty( $instance['title'] ) ) {
			echo $args['before_title'] . apply_filters( 'widget_title', $instance['title'], $instance, $this->id_base ) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped,Generic.Files.LineLength.MaxExceeded
		}

		// Location info.
		aioseoLocalBusiness()->locations->outputBusinessInfo( $instance['locationId'], $instance );

		// After.
		if ( ! empty( $instance['after'] ) ) {
			echo $instance['after']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
		}

		echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
	}

	/**
	 * Widget option update.
	 *
	 * @since 1.1.0
	 *
	 * @param  array $newInstance New instance options.
	 * @param  array $oldInstance Old instance options.
	 * @return array              Processed new instance options.
	 */
	public function update( $newInstance, $oldInstance ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
		// $newInstance['locationId']  = ! empty( $newInstance['locationId'] ) ? (int) $newInstance['locationId'] : 0;
		$newInstance['title']           = wp_strip_all_tags( $newInstance['title'] );
		$newInstance['showLabels']      = ! empty( $newInstance['showLabels'] ) ? '1' : false;
		$newInstance['showIcons']       = ! empty( $newInstance['showIcons'] ) ? '1' : false;
		$newInstance['showName']        = ! empty( $newInstance['showName'] ) ? '1' : false;
		$newInstance['showAddress']     = ! empty( $newInstance['showAddress'] ) ? '1' : false;
		$newInstance['showPhone']       = ! empty( $newInstance['showPhone'] ) ? '1' : false;
		$newInstance['showFax']         = ! empty( $newInstance['showFax'] ) ? '1' : false;
		$newInstance['showCountryCode'] = ! empty( $newInstance['showCountryCode'] ) ? '1' : false;
		$newInstance['showEmail']       = ! empty( $newInstance['showEmail'] ) ? '1' : false;
		$newInstance['showVat']         = ! empty( $newInstance['showVat'] ) ? '1' : false;
		$newInstance['showTax']         = ! empty( $newInstance['showTax'] ) ? '1' : false;
		$newInstance['after']           = wp_strip_all_tags( $newInstance['after'] );

		return $newInstance;
	}

	/**
	 * Widget options form.
	 *
	 * @since 1.1.0
	 *
	 * @param  array $instance The widget instance options.
	 * @return void
	 */
	public function form( $instance ) {
		// Merge with defaults.
		$instance = wp_parse_args( (array) $instance, $this->defaults );
		?>
		<p>
			<label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>">
				<?php echo esc_html( __( 'Title:', 'aioseo-local-business' ) ); ?>
			</label>
			<input
					type="text"
					id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"
					name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>"
					value="<?php echo esc_attr( $instance['title'] ); ?>"
					class="widefat"
			/>
		</p>
		<?php if ( aioseo()->options->localBusiness->locations->general->multiple ) { ?>
			<p>
				<label for="<?php echo esc_attr( $this->get_field_id( 'locationId' ) ); ?>">
					<?php esc_html_e( 'Location:', 'aioseo-local-business' ); ?>
				</label>
				<select class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'locationId' ) ); ?>"
						name="<?php echo esc_attr( $this->get_field_name( 'locationId' ) ); ?>">
					<?php
					$locations = aioseoLocalBusiness()->locations->getLocations( [
						'order_by' => 'post_title',
						'order'    => 'asc'
					] );
					if ( ! empty( $locations ) ) {
						echo '<option value="" selected disabled>' . esc_html__( 'Select your location', 'aioseo-local-business' ) . '</option>';
						foreach ( $locations as $location ) {
							echo '<option value="' . esc_attr( $location->ID ) . '" ' . selected( $instance['locationId'], $location->ID, false ) . '>' . esc_html( $location->post_title ) . '</option>'; // phpcs:ignore Generic.Files.LineLength.MaxExceeded
						}
					} else {
						echo '<option value="">' . esc_html__( 'No locations available', 'aioseo-local-business' ) . '</option>';
					}
					?>
				</select>
			</p>
		<?php } ?>
		<p>
			<input type="checkbox" id="<?php echo esc_attr( $this->get_field_id( 'showLabels' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'showLabels' ) ); ?>" <?php checked( '1', $instance['showLabels'] ); ?>> <?php // phpcs:ignore Generic.Files.LineLength.MaxExceeded ?>
			<label for="<?php echo esc_attr( $this->get_field_id( 'showLabels' ) ); ?>">
				<?php esc_html_e( 'Show labels', 'aioseo-local-business' ); ?>
			</label>
		</p>
		<p>
			<input type="checkbox" id="<?php echo esc_attr( $this->get_field_id( 'showIcons' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'showIcons' ) ); ?>" <?php checked( '1', $instance['showIcons'] ); ?>> <?php // phpcs:ignore Generic.Files.LineLength.MaxExceeded ?>
			<label for="<?php echo esc_attr( $this->get_field_id( 'showIcons' ) ); ?>">
				<?php esc_html_e( 'Show icons', 'aioseo-local-business' ); ?>
			</label>
		</p>
		<p>
			<strong><?php esc_html_e( 'Business Info:', 'aioseo-local-business' ); ?></strong>
		</p>
		<p>
			<input type="checkbox" id="<?php echo esc_attr( $this->get_field_id( 'showName' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'showName' ) ); ?>" <?php checked( '1', $instance['showName'] ); ?>> <?php // phpcs:ignore Generic.Files.LineLength.MaxExceeded ?>
			<label for="<?php echo esc_attr( $this->get_field_id( 'showName' ) ); ?>">
				<?php esc_html_e( 'Business name', 'aioseo-local-business' ); ?>
			</label>
		</p>
		<p>
			<input type="checkbox" id="<?php echo esc_attr( $this->get_field_id( 'showAddress' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'showAddress' ) ); ?>" <?php checked( '1', $instance['showAddress'] ); ?>> <?php // phpcs:ignore Generic.Files.LineLength.MaxExceeded ?>
			<label for="<?php echo esc_attr( $this->get_field_id( 'showAddress' ) ); ?>">
				<?php esc_html_e( 'Address', 'aioseo-local-business' ); ?>
			</label>
		</p>
		<p>
			<input type="checkbox" id="<?php echo esc_attr( $this->get_field_id( 'showPhone' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'showPhone' ) ); ?>" <?php checked( '1', $instance['showPhone'] ); ?>> <?php // phpcs:ignore Generic.Files.LineLength.MaxExceeded ?>
			<label for="<?php echo esc_attr( $this->get_field_id( 'showPhone' ) ); ?>">
				<?php esc_html_e( 'Phone Number', 'aioseo-local-business' ); ?>
			</label>
		</p>
		<p>
			<input type="checkbox" id="<?php echo esc_attr( $this->get_field_id( 'showFax' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'showFax' ) ); ?>" <?php checked( '1', $instance['showFax'] ); ?>> <?php // phpcs:ignore Generic.Files.LineLength.MaxExceeded ?>
			<label for="<?php echo esc_attr( $this->get_field_id( 'showFax' ) ); ?>">
				<?php esc_html_e( 'Fax Number', 'aioseo-local-business' ); ?>
			</label>
		</p>
		<p>
			<input type="checkbox" id="<?php echo esc_attr( $this->get_field_id( 'showCountryCode' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'showCountryCode' ) ); ?>" <?php checked( '1', $instance['showCountryCode'] ); ?>> <?php // phpcs:ignore Generic.Files.LineLength.MaxExceeded ?>
			<label for="<?php echo esc_attr( $this->get_field_id( 'showCountryCode' ) ); ?>">
				<?php esc_html_e( 'Show Country Code', 'aioseo-local-business' ); ?>
			</label>
		</p>
		<p>
			<input type="checkbox" id="<?php echo esc_attr( $this->get_field_id( 'showEmail' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'showEmail' ) ); ?>" <?php checked( '1', $instance['showEmail'] ); ?>> <?php // phpcs:ignore Generic.Files.LineLength.MaxExceeded ?>
			<label for="<?php echo esc_attr( $this->get_field_id( 'showEmail' ) ); ?>">
				<?php esc_html_e( 'Email address', 'aioseo-local-business' ); ?>
			</label>
		</p>
		<p>
			<input type="checkbox" id="<?php echo esc_attr( $this->get_field_id( 'showVat' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'showVat' ) ); ?>" <?php checked( '1', $instance['showVat'] ); ?>> <?php // phpcs:ignore Generic.Files.LineLength.MaxExceeded ?>
			<label for="<?php echo esc_attr( $this->get_field_id( 'showVat' ) ); ?>">
				<?php esc_html_e( 'Show VAT ID', 'aioseo-local-business' ); ?>
			</label>
		</p>
		<p>
			<input type="checkbox" id="<?php echo esc_attr( $this->get_field_id( 'showTax' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'showTax' ) ); ?>" <?php checked( '1', $instance['showTax'] ); ?>> <?php // phpcs:ignore Generic.Files.LineLength.MaxExceeded ?>
			<label for="<?php echo esc_attr( $this->get_field_id( 'showTax' ) ); ?>">
				<?php esc_html_e( 'Show Tax ID', 'aioseo-local-business' ); ?>
			</label>
		</p>
		<p><strong><?php echo esc_html( __( 'Labels:', 'aioseo-local-business' ) ); ?></strong></p>
		<p class="labels">
			<?php
			$labels = [
				'addressLabel' => __( 'Address', 'aioseo-local-business' ),
				'vatIdLabel'   => __( 'Vat ID', 'aioseo-local-business' ),
				'taxIdLabel'   => __( 'Tax ID', 'aioseo-local-business' ),
				'phoneLabel'   => __( 'Phone', 'aioseo-local-business' ),
				'faxLabel'     => __( 'Fax', 'aioseo-local-business' ),
				'emailLabel'   => __( 'Email', 'aioseo-local-business' )
			]
			?>
			<?php foreach ( $labels as $labelKey => $label ) { ?>
				<label for="<?php echo esc_attr( $this->get_field_id( $labelKey ) ); ?>">
					<?php echo esc_html( $label ); ?>
				</label>
				<input
						type="text"
						id="<?php echo esc_attr( $this->get_field_id( $labelKey ) ); ?>"
						name="<?php echo esc_attr( $this->get_field_name( $labelKey ) ); ?>"
						value="<?php echo esc_attr( $instance[ $labelKey ] ); ?>"
						class="widefat"
				/>
			<?php } ?>
		</p>
		<p>
			<label for="<?php echo esc_attr( $this->get_field_id( 'after' ) ); ?>">
				<?php esc_html_e( 'After widget text:', 'aioseo-local-business' ); ?>
			</label><br />
			<textarea id="<?php echo esc_attr( $this->get_field_id( 'after' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'after' ) ); ?>" class="widefat"><?php echo esc_attr( $instance['after'] ); ?></textarea> <?php // phpcs:ignore Generic.Files.LineLength.MaxExceeded ?>
		</p>
		<?php
	}
}app/Widgets/Widgets.php000064400000001415151727277660011076 0ustar00<?php
namespace AIOSEO\Plugin\Addon\LocalBusiness\Widgets;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Class Widgets.
 *
 * @since 1.1.0
 */
class Widgets {
	/**
	 * Widgets constructor.
	 *
	 * @since 1.1.0
	 */
	public function __construct() {
		add_action( 'widgets_init', [ $this, 'registerWidgets' ] );
	}

	/**
	 * Register AIOSEO plugin widgets.
	 *
	 * @since 1.1.0
	 *
	 * @return void
	 */
	public function registerWidgets() {
		register_widget( 'AIOSEO\Plugin\Addon\LocalBusiness\Widgets\BusinessInfo' );
		register_widget( 'AIOSEO\Plugin\Addon\LocalBusiness\Widgets\Locations' );
		register_widget( 'AIOSEO\Plugin\Addon\LocalBusiness\Widgets\OpeningHours' );
		register_widget( 'AIOSEO\Plugin\Addon\LocalBusiness\Widgets\Map' );
	}
}app/Schema/Graphs/KgOrganization.php000064400000002355151727277660013440 0ustar00<?php
namespace AIOSEO\Plugin\Addon\LocalBusiness\Schema\Graphs;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Knowledge Graph Organization graph class.
 *
 * @since 1.3.3
 */
class KgOrganization extends Base {
	/**
	 *The data for the global options.
	 *
	 * @since 1.3.3
	 *
	 * @var object|null
	 */
	protected $dataObject = null;

	/**
	 * Class constructor.
	 *
	 * @since 1.3.3
	 */
	public function __construct() {
		$this->dataObject = aioseoLocalBusiness()->helpers->getLocalBusinessOptions();
	}

	/**
	 * Returns the graph data.
	 *
	 * @since 1.3.3
	 *
	 * @return void
	 */
	public function get() {}

	/**
	 * Returns the graph data.
	 *
	 * @since 1.3.3
	 *
	 * @return array $data The graph data.
	 */
	public function getAdditionalGraphData( $postId, $data ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
		if ( aioseo()->options->localBusiness->locations->general->multiple ) {
			return $data;
		}

		$additionalData = [
			'address' => $this->address()
		];

		if ( empty( $data['email'] ) ) {
			$additionalData['email'] = $this->dataObject->locations->business->contact->email;
		}

		$additionalData += $this->ids();

		return array_merge( $data, $additionalData );
	}
}app/Schema/Graphs/LocalBusiness.php000064400000015045151727277660013260 0ustar00<?php
namespace AIOSEO\Plugin\Addon\LocalBusiness\Schema\Graphs;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * LocalBusiness graph class.
 *
 * @since 1.0.0
 */
class LocalBusiness extends Base {
	/**
	 * The Local SEO post ID.
	 *
	 * @since 1.1.0
	 *
	 * @var integer
	 */
	private $postId = null;

	/**
	 *The data for the CPT or the global options.
	 *
	 * @since 1.1.0
	 *
	 * @var object|null
	 */
	protected $dataObject = null;

	/**
	 * Class constructor.
	 *
	 * @since 1.1.0
	 */
	public function __construct() {
		$this->dataObject = aioseoLocalBusiness()->helpers->getLocalBusinessOptions();

		$postId = get_the_ID();
		if ( $postId && get_post_type( $postId ) === aioseoLocalBusiness()->postType->getName() ) {
			$this->postId     = $postId;
			$this->dataObject = aioseoLocalBusiness()->locations->getLocation( $postId );
		}
	}

	/**
	 * Returns the graph data.
	 *
	 * @since 1.0.0
	 *
	 * @return array The graph data.
	 */
	public function get() {
		if ( empty( $this->dataObject ) ) {
			return [];
		}

		$data = [
			'@type'                     => $this->businessType(),
			'@id'                       => trailingslashit( home_url() ) . '#localbusiness',
			'name'                      => $this->name(),
			'brand'                     => [
				'@id' => trailingslashit( home_url() ) . '#organization'
			],
			'url'                       => $this->postId ? get_permalink( $this->postId ) : trailingslashit( home_url() ),
			'image'                     => $this->businessImage(),
			'logo'                      => $this->postId ? get_permalink( $this->postId ) . '#logo' : trailingslashit( home_url() ) . '#logo',
			'address'                   => $this->address(),
			'email'                     => $this->dataObject->locations->business->contact->email,
			'telephone'                 => $this->phoneNumber(),
			'faxNumber'                 => $this->dataObject->locations->business->contact->fax,
			'priceRange'                => $this->dataObject->locations->business->payment->priceRange,
			'currenciesAccepted'        => $this->acceptedCurrencies(),
			'paymentAccepted'           => $this->dataObject->locations->business->payment->methods,
			'areaServed'                => $this->dataObject->locations->business->areaServed,
			'openingHoursSpecification' => $this->openingHours()
		];

		if ( $data['address'] ) {
			$data['location'] = [
				'@id' => trailingslashit( home_url() ) . '#postaladdress'
			];
		}

		$data += $this->ids();

		return $data;
	}

	/**
	 * Returns the business type.
	 *
	 * @since 1.0.0
	 *
	 * @return string $businessType The business type.
	 */
	private function businessType() {
		$businessType = $this->dataObject->locations->business->businessType;
		if ( is_object( $businessType ) ) {
			$businessType = $businessType->value;
		}

		return ! empty( $businessType ) ? $businessType : 'LocalBusiness';
	}

	/**
	 * Returns the name.
	 *
	 * @since 1.0.0
	 *
	 * @return string $name The name.
	 */
	private function name() {
		$name = $this->dataObject->locations->business->name;
		if ( $name ) {
			return $name;
		}

		// Default a single location to its post title.
		if ( $this->postId ) {
			$name = get_the_title( $this->postId );
			if ( $name ) {
				return $name;
			}
		}

		$name = aioseo()->tags->replaceTags( aioseo()->options->searchAppearance->global->schema->organizationName );
		if ( $name ) {
			return $name;
		}

		return aioseo()->helpers->decodeHtmlEntities( get_bloginfo( 'name' ) );
	}

	/**
	 * Returns the image.
	 *
	 * @since 1.0.0
	 *
	 * @return string $image The image.
	 */
	private function businessImage() {
		$image = $this->dataObject->locations->business->image;
		if ( $image ) {
			return $image;
		}

		// Default a single location to it's post title.
		if ( $this->postId ) {
			$image = get_the_post_thumbnail_url( $this->postId, 'full' );
			if ( $image ) {
				return $image;
			}
		}

		// Fallback to Organization Logo.
		$image = aioseo()->options->searchAppearance->global->schema->organizationLogo;
		if ( $image ) {
			return $image;
		}

		// Fallback to Site Logo.
		return aioseo()->helpers->getSiteLogoUrl();
	}

	/**
	 * Returns the phone number.
	 *
	 * @since 1.0.0
	 *
	 * @return string The phone number.
	 */
	private function phoneNumber() {
		$phoneNumber = $this->dataObject->locations->business->contact->phone;
		if ( $phoneNumber ) {
			return $phoneNumber;
		}
		// Fallback to Organization Phone.
		$phoneNumber = aioseo()->options->searchAppearance->global->schema->phone;
		if ( $phoneNumber ) {
			return $phoneNumber;
		}

		return false;
	}

	/**
	 * Returns the accepted currencies.
	 *
	 * @since 1.0.0
	 *
	 * @return string The accepted currencies as a string, separated by commas.
	 */
	private function acceptedCurrencies() {
		$currencies    = $this->dataObject->locations->business->payment->currenciesAccepted;
		$rawCurrencies = ! empty( $currencies ) ? json_decode( $currencies ) : $currencies;
		if ( ! $rawCurrencies ) {
			return '';
		}

		$currencies = [];
		foreach ( $rawCurrencies as $currency ) {
			$currencies[] = $currency->value;
		}

		return ! empty( $currencies ) ? implode( ', ', $currencies ) : '';
	}

	/**
	 * Returns the opening hours.
	 *
	 * @since 1.0.0
	 *
	 * @return array The opening hours.
	 */
	private function openingHours() {
		$openingHoursObject = $this->dataObject->openingHours;
		if ( isset( $openingHoursObject->useDefaults ) && true === $openingHoursObject->useDefaults ) {
			$openingHoursObject = aioseoLocalBusiness()->helpers->getLocalBusinessOptions()->openingHours;
		}

		if ( ! $openingHoursObject->show ) {
			return [];
		}

		if ( $openingHoursObject->alwaysOpen ) {
			return [
				'@type'     => 'OpeningHoursSpecification',
				'dayOfWeek' => [
					'https://schema.org/Monday',
					'https://schema.org/Tuesday',
					'https://schema.org/Wednesday',
					'https://schema.org/Thursday',
					'https://schema.org/Friday',
					'https://schema.org/Saturday',
					'https://schema.org/Sunday'
				],
				'opens'     => '00:00',
				'closes'    => '23:59'
			];
		}

		$days = [];
		foreach ( $openingHoursObject->days as $day => $values ) {
			if ( $values->closed ) {
				continue;
			}
			if ( $values->open24h ) {
				$days[] = [
					'@type'     => 'OpeningHoursSpecification',
					'dayOfWeek' => [
						'https://schema.org/' . ucfirst( $day )
					],
					'opens'     => '00:00',
					'closes'    => '23:59'
				];
				continue;
			}
			$days[] = [
				'@type'     => 'OpeningHoursSpecification',
				'dayOfWeek' => [
					'https://schema.org/' . ucfirst( $day )
				],
				'opens'     => $values->openTime,
				'closes'    => $values->closeTime
			];
		}

		return $days;
	}
}app/Schema/Graphs/Base.php000064400000004757151727277660011374 0ustar00<?php
namespace AIOSEO\Plugin\Addon\LocalBusiness\Schema\Graphs;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

use AIOSEO\Plugin\Common\Schema\Graphs as CommonGraphs;

/**
 * Base graph class.
 *
 * @since 1.3.3
 */
abstract class Base extends CommonGraphs\Graph {
	/**
	 *The data for the CPT or the global options.
	 *
	 * @since 1.3.3
	 *
	 * @var object|null
	 */
	protected $dataObject = null;

	/**
	 * Returns the address.
	 *
	 * @since   1.0.0
	 * @version 1.3.3 Moved to abstract class.
	 *
	 * @return array The address.
	 */
	protected function address() {
		if (
			! $this->dataObject->locations->business->address->streetLine1 &&
			! $this->dataObject->locations->business->address->streetLine2
		) {
			return [];
		}

		return [
			'@id'             => trailingslashit( home_url() ) . '#postaladdress',
			'@type'           => 'PostalAddress',
			'streetAddress'   => sprintf(
				'%1$s, %2$s',
				$this->dataObject->locations->business->address->streetLine1,
				$this->dataObject->locations->business->address->streetLine2
			),
			'postalCode'      => $this->dataObject->locations->business->address->zipCode,
			'addressLocality' => $this->dataObject->locations->business->address->city,
			'addressRegion'   => $this->dataObject->locations->business->address->state,
			'addressCountry'  => $this->dataObject->locations->business->address->country
		];
	}

	/**
	 * Returns the IDs.
	 *
	 * @since 1.3.3
	 *
	 * @return array The ids.
	 */
	protected function ids() {
		$ids = [
			'taxID'                => $this->dataObject->locations->business->ids->tax ?? '',
			'vatID'                => $this->dataObject->locations->business->ids->vat ?? '',
			'iso6523Code'          => $this->dataObject->locations->business->ids->iso6523 ?? '',
			'leiCode'              => $this->dataObject->locations->business->ids->lei ?? '',
			'duns'                 => $this->dataObject->locations->business->ids->duns ?? '',
			'naics'                => $this->dataObject->locations->business->ids->naics ?? '',
			'globalLocationNumber' => $this->dataObject->locations->business->ids->gs1 ?? ''
		];

		if ( empty( $ids['iso6523Code'] ) && ! empty( $ids['duns'] ) ) {
			$ids['iso6523Code'] = '0060:' . $ids['duns'];
		}

		if ( empty( $ids['iso6523Code'] ) && ! empty( $ids['globalLocationNumber'] ) ) {
			$ids['iso6523Code'] = '0088:' . $ids['globalLocationNumber'];
		}

		if ( empty( $ids['iso6523Code'] ) && ! empty( $ids['leiCode'] ) ) {
			$ids['iso6523Code'] = '0199:' . $ids['leiCode'];
		}

		return array_filter( $ids );
	}
}app/Schema/Schema.php000064400000002051151727277660010457 0ustar00<?php
namespace AIOSEO\Plugin\Addon\LocalBusiness\Schema;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Builds our schema.
 *
 * @since 1.0.0
 */
class Schema {
	/**
	 * Determines which graphs need to be output.
	 *
	 * @since   1.1.0
	 * @version 1.3.0
	 *
	 * @return array A list of graphs that need to be output.
	 */
	public function determineGraphsAndContext() {
		if ( ! aioseo()->options->localBusiness->locations->general->multiple ) {
			return is_front_page() || ( aioseo()->helpers->isStaticHomepage() && aioseo()->schema->generatingValidatorOutput ) ? [ 'LocalBusiness' ] : [];
		}

		return is_singular( aioseoLocalBusiness()->postType->getName() ) ? [ 'LocalBusiness' ] : [];
	}

	/**
	 * Returns data for the given graph if it's contained within this addon.
	 *
	 * @since 1.0.0
	 *
	 * @return array The graph data.
	 */
	public function get( $graphName ) {
		$namespace = __NAMESPACE__ . "\Graphs\\$graphName";
		if ( ! class_exists( $namespace ) ) {
			return [];
		}

		return ( new $namespace() )->get();
	}
}app/Utils/Cache.php000064400000000600151727277660010160 0ustar00<?php
namespace AIOSEO\Plugin\Addon\LocalBusiness\Utils;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Main class for redirects cache.
 *
 * @since 1.3.0
 */
class Cache extends \AIOSEO\Plugin\Common\Utils\Cache {
	/**
	 * The redirect addon cache prefix.
	 *
	 * @since 1.3.0
	 *
	 * @var string
	 */
	protected $prefix = 'aioseo_local_business_';
}app/Utils/InternalOptions.php000064400000003352151727277670012315 0ustar00<?php
namespace AIOSEO\Plugin\Addon\LocalBusiness\Utils;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

use AIOSEO\Plugin\Common\Traits;

/**
 * Class that holds all internal options for AIOSEO.
 *
 * @since 1.1.0.2
 */
class InternalOptions {
	use Traits\Options;

	/**
	 * All the default options.
	 *
	 * @since 1.1.0.2
	 *
	 * @var array
	 */
	protected $defaults = [
		// phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
		'internal' => [
			'lastActiveVersion' => [ 'type' => 'string', 'default' => '0.0' ]
		]
		// phpcs:enable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
	];

	/**
	 * Holds a list of all the possible deprecated options.
	 *
	 * @since 1.2.12
	 *
	 * @var array
	 */
	protected $allDeprecatedOptions = [];

	/**
	 * The Construct method.
	 *
	 * @since 1.1.0.2
	 *
	 * @param string $optionsName An array of options.
	 */
	public function __construct( $optionsName = 'aioseo_local_seo_options_internal' ) {
		$this->optionsName = $optionsName;

		$this->init();

		add_action( 'shutdown', [ $this, 'save' ] );
	}

	/**
	 * Initializes the options.
	 *
	 * @since 1.1.0.2
	 *
	 * @return void
	 */
	protected function init() {
		// Options from the DB.
		$dbOptions = json_decode( get_option( $this->optionsName ), true );
		if ( empty( $dbOptions ) ) {
			$dbOptions = [];
		}

		// Refactor options.
		$this->defaultsMerged = array_replace_recursive( $this->defaults, $this->defaultsMerged );

		$options = array_replace_recursive(
			$this->defaultsMerged,
			$this->addValueToValuesArray( $this->defaultsMerged, $dbOptions )
		);

		aioseo()->core->optionsCache->setOptions( $this->optionsName, apply_filters( 'aioseo_get_local_seo_options_internal', $options ) );
	}
}app/Utils/Templates.php000064400000001300151727277670011112 0ustar00<?php
namespace AIOSEO\Plugin\Addon\LocalBusiness\Utils;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

use AIOSEO\Plugin\Common\Utils\Templates as CommonTemplates;

/**
 * Class Templates
 *
 * @since 1.1.0
 */
class Templates extends CommonTemplates {
	/**
	 * This plugin absolute path.
	 *
	 * @since 1.1.0
	 *
	 * @var string
	 */
	protected $pluginPath = AIOSEO_LOCAL_BUSINESS_PATH;

	/**
	 * Paths were our template files are located.
	 *
	 * @since 1.1.0
	 *
	 * @var string Array of paths.
	 */
	protected $paths = [
		'app/Views'
	];

	/**
	 * Subpath for theme usage.
	 *
	 * @since 1.1.0
	 *
	 * @var string
	 */
	protected $themeTemplateSubpath = 'localBusiness';
}app/Utils/Access.php000064400000003017151727277670010364 0ustar00<?php
namespace AIOSEO\Plugin\Addon\LocalBusiness\Utils;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * The Access class.
 *
 * @since 1.1.0
 */
class Access {
	/**
	 * The Access class instance from the main plugin.
	 *
	 * @since 1.1.0
	 *
	 * @var \AIOSEO\Plugin\Pro\Utils\Access
	 */
	public $access;

	/**
	 * Class constructor.
	 *
	 * @since 1.1.0
	 */
	public function __construct() {
		$this->access = aioseo()->access;
	}

	/**
	 * Add needed capabilities to run Local Business.
	 *
	 * @since 1.1.0
	 *
	 * @return void
	 */
	public function addCapabilities() {
		foreach ( $this->access->getRoles() as $wpRole => $role ) {
			$roleObject = get_role( $wpRole );
			if ( ! is_object( $roleObject ) ) {
				continue;
			}

			if ( ! $this->access->hasCapability( 'aioseo_page_local_seo_settings', $role ) ) {
				foreach ( aioseoLocalBusiness()->postType->getCapabilities() as $postCapability ) {
					$roleObject->remove_cap( $postCapability );
				}
				foreach ( aioseoLocalBusiness()->taxonomy->getCapabilities() as $taxCapability ) {
					$roleObject->remove_cap( $taxCapability );
				}
			}

			if ( $this->access->isAdmin( $role ) || $this->access->hasCapability( 'aioseo_page_local_seo_settings', $role ) ) {
				foreach ( aioseoLocalBusiness()->postType->getCapabilities() as $postCapability ) {
					$roleObject->add_cap( $postCapability );
				}
				foreach ( aioseoLocalBusiness()->taxonomy->getCapabilities() as $taxCapability ) {
					$roleObject->add_cap( $taxCapability );
				}
			}
		}
	}
}app/Utils/Helpers.php000064400000013577151727277670010601 0ustar00<?php
namespace AIOSEO\Plugin\Addon\LocalBusiness\Utils;

use AIOSEO\Plugin\Addon\LocalBusiness\Models\Post;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * The Helpers class.
 *
 * @since 1.1.0
 */
class Helpers {
	/**
	 * Returns the global Local SEO options as an object.
	 *
	 * @since 1.1.0
	 *
	 * @return object Global Local SEO options as an object.
	 */
	public function getLocalBusinessOptions() {
		return json_decode( wp_json_encode( aioseo()->options->localBusiness->all(), JSON_FORCE_OBJECT ) );
	}

	/**
	 * Returns current permalink structure for this post type.
	 *
	 * @since 1.1.0
	 *
	 * @param  string $postTypeName The post type name.
	 * @param  string $postTypeSlug The post type slug.
	 * @return string               The loaded permastruct or a default if not enabled yet.
	 */
	public function getPermaStructure( $postTypeName, $postTypeSlug ) {
		global $wp_rewrite;

		$structure = $wp_rewrite->get_extra_permastruct( $postTypeName );

		if ( ! $structure ) {
			$structure = '/' . $postTypeSlug . '/%' . $postTypeName . '%/';
		}

		// Account for trailing slashes.
		return user_trailingslashit( $structure );
	}

	/**
	 * Gets the data for vue.
	 *
	 * @since 1.1.0
	 *
	 * @param  array  $data       The parent data array to modify.
	 * @param  string $page       The current page.
	 * @return array              An array of data.
	 */
	public function getVueData( $data = [], $page = null ) {
		$data['localBusiness'] = [
			'postTypeName'        => aioseoLocalBusiness()->postType->getName(),
			'postTypeEditLink'    => aioseoLocalBusiness()->postType->getEditLink(),
			'postTypeDefaultSlug' => aioseoLocalBusiness()->postType->getDefaultSlug(),
			'postTypeSingleLabel' => aioseoLocalBusiness()->postType->getSingleLabel(),
			'postTypePluralLabel' => aioseoLocalBusiness()->postType->getPluralLabel(),
			'taxonomyName'        => aioseoLocalBusiness()->taxonomy->getName(),
			'taxonomyDefaultSlug' => aioseoLocalBusiness()->taxonomy->getDefaultSlug(),
			'taxonomySingleLabel' => aioseoLocalBusiness()->taxonomy->getSingleLabel(),
			'taxonomyPluralLabel' => aioseoLocalBusiness()->taxonomy->getPluralLabel(),
			'mapLoadEvent'        => aioseoLocalBusiness()->maps->mapLoadEvent,
			'mapDefaults'         => [
				'center' => [
					'lat' => aioseo()->options->localBusiness->maps->mapOptions->center->getDefault( 'lat' ),
					'lng' => aioseo()->options->localBusiness->maps->mapOptions->center->getDefault( 'lng' )
				],
				'zoom'   => aioseo()->options->localBusiness->maps->mapOptions->getDefault( 'zoom' )
			],
			'importers'           => aioseoLocalBusiness()->import->plugins()
		];

		if ( ! empty( $data['currentPost'] ) && 'post' === $data['currentPost']['context'] && aioseoLocalBusiness()->postType->getName() === $data['currentPost']['postType'] ) {
			$locationData                     = aioseoLocalBusiness()->locations->getLocation( $data['currentPost']['id'] );
			$data['currentPost']['local_seo'] = Post::parseLocalSeoOptions( $locationData );

			$data['currentPost']['local_seo']['maps']['geocodeAddress'] = $this->addressToGeocode( $data['currentPost']['local_seo']['locations']['business']['address'] );

			$locationCategories                           = wp_get_object_terms(
				$data['currentPost']['id'],
				aioseoLocalBusiness()->taxonomy->getName(),
				[ 'fields' => 'ids' ]
			);
			$data['currentPost']['localBusinessCategory'] = current( $locationCategories ) ?: [];

			// Disable TruSEO.
			$data['options']['advanced']['truSeo'] = false;
		}

		if ( aioseo()->helpers->isScreenBase( 'edit' ) && aioseo()->helpers->isScreenPostType( aioseoLocalBusiness()->postType->getName() ) ) {
			// Disable TruSEO.
			$data['options']['advanced']['truSeo'] = false;
		}

		if ( 'local-seo' === $page ) {
			$cptStructure = aioseoLocalBusiness()->postType->getPermaStructure();
			if ( ! empty( $cptStructure ) and stripos( $cptStructure, aioseoLocalBusiness()->postType->getSlug() ) !== false ) {
				$cptStructure = preg_split( '~\/(' . aioseoLocalBusiness()->postType->getSlug() . ')\/~', $cptStructure, - 1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY );
				foreach ( $cptStructure as &$cptStructureItem ) {
					if ( aioseoLocalBusiness()->postType->getSlug() === $cptStructureItem ) {
						$cptStructureItem = '{slug}';
					} else {
						$cptStructureItem = '/' . $cptStructureItem;
					}
				}
			}

			$taxStructure = aioseoLocalBusiness()->taxonomy->getPermaStructure();
			if ( ! empty( $taxStructure ) and stripos( $taxStructure, aioseoLocalBusiness()->taxonomy->getSlug() ) !== false ) {
				$taxStructure = preg_split( '~\/(' . aioseoLocalBusiness()->taxonomy->getSlug() . ')\/~', $taxStructure, - 1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY );
				foreach ( $taxStructure as &$taxStructureItem ) {
					if ( aioseoLocalBusiness()->taxonomy->getSlug() === $taxStructureItem ) {
						$taxStructureItem = '{slug}';
					} else {
						$taxStructureItem = '/' . $taxStructureItem;
					}
				}
			}

			$data['localBusiness']['postTypePermalinkStructure'] = $cptStructure;
			$data['localBusiness']['taxonomyPermalinkStructure'] = $taxStructure;

			// getDataObject() pulls from the options state, which is why we use that as the first level here.
			$data['options']['localBusiness']['maps']['geocodeAddress'] = $this->addressToGeocode( aioseo()->options->localBusiness->locations->business->address->all() );

			$data['localBusiness']['enhancedSearchTest'] = aioseoLocalBusiness()->search->testSearch();
		}

		return $data;
	}

	/**
	 * Returns a format address for Google's geocoding.
	 *
	 * @since 1.2.1
	 *
	 * @param  array  $address The address array.
	 * @return string          The formatted address.
	 */
	private function addressToGeocode( $address ) {
		$address = [
			$address['streetLine1'],
			$address['streetLine2'],
			$address['city'],
			$address['state'],
			$address['country'],
			$address['zipCode']
		];

		return implode( ',', array_filter( $address ) );
	}
}app/Utils/Tags.php000064400000011364151727277700010057 0ustar00<?php
namespace AIOSEO\Plugin\Addon\LocalBusiness\Utils;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Replaces tags with their respective values.
 *
 * @since 1.1.0
 */
class Tags {
	/**
	 * The tag values that we support.
	 *
	 * @since 1.1.0
	 *
	 * @var array
	 */
	private $tags = [];

	/**
	 * The contexts to separate tags.
	 *
	 * @since 1.1.0
	 *
	 * @var array
	 */
	private $context = [
		'addressFormat' => [
			'streetLineOne',
			'streetLineTwo',
			'zipCode',
			'city',
			'state',
			'country',
			'newLine'
		]
	];

	/**
	 * The address data object.
	 *
	 * @since 1.1.0
	 *
	 * @var object
	 */
	private $address;

	/**
	 * Class constructor.
	 *
	 * @since 1.1.0
	 */
	public function __construct() {
		add_action( 'init', [ $this, 'init' ] );
	}

	/**
	 * Init the tags later to allow filters to be registered and run.
	 *
	 * @since 1.1.0
	 *
	 * @return void
	 */
	public function init() {
		aioseo()->tags->addContext( $this->context );

		$this->tags = [
			[
				'id'          => 'streetLineOne',
				'name'        => __( 'Address Line 1', 'aioseo-local-business' ),
				'description' => __( 'Your Address Line 1', 'aioseo-local-business' ),
				'context'     => [ 'addressFormat' ]
			],
			[
				'id'          => 'streetLineTwo',
				'name'        => __( 'Address Line 2', 'aioseo-local-business' ),
				'description' => __( 'Your Address Line 2', 'aioseo-local-business' ),
				'context'     => [ 'addressFormat' ]
			],
			[
				'id'          => 'zipCode',
				'name'        => __( 'Zip Code', 'aioseo-local-business' ),
				'description' => __( 'Your Zip Code', 'aioseo-local-business' ),
				'context'     => [ 'addressFormat' ]
			],
			[
				'id'          => 'city',
				'name'        => __( 'City', 'aioseo-local-business' ),
				'description' => __( 'Your City', 'aioseo-local-business' ),
				'context'     => [ 'addressFormat' ]
			],
			[
				'id'          => 'state',
				'name'        => __( 'State', 'aioseo-local-business' ),
				'description' => __( 'Your State', 'aioseo-local-business' ),
				'context'     => [ 'addressFormat' ]
			],
			[
				'id'          => 'country',
				'name'        => __( 'Country', 'aioseo-local-business' ),
				'description' => __( 'Your Country', 'aioseo-local-business' ),
				'context'     => [ 'addressFormat' ]
			]
		];

		$this->tags = apply_filters( 'aioseo_local_business_address_tags', $this->tags );

		aioseo()->tags->addTags( $this->tags );
	}

	/**
	 * Replace the tags in the string provided.
	 *
	 * @since 1.1.0
	 *
	 * @param  string $string        The string with tags.
	 * @param  int    $id            The page or post ID.
	 * @param  object $addressObject A data object to run the tags against.
	 * @return string                The string with tags replaced.
	 */
	public function replaceTags( $string, $id, $addressObject = null ) {
		if ( ! $string || ! preg_match( '/#/', $string ) ) {
			return $string;
		}

		if ( $addressObject ) {
			$this->address = $addressObject;
		} else {
			if ( 'global' === $id ) {
				$this->address = aioseoLocalBusiness()->helpers->getLocalBusinessOptions();
			} else {
				$this->address = aioseoLocalBusiness()->locations->getLocation( $id );
			}

			if ( empty( $this->address->locations->business->address ) ) {
				return $string;
			}

			$this->address = $this->address->locations->business->address;
		}

		$lines = preg_split( '/\r\n|\n|\r/', $string );

		foreach ( $lines as $lineKey => &$line ) {
			foreach ( $this->tags as $tag ) {
				$tagId   = aioseo()->tags->denotationChar . $tag['id'];
				$pattern = "/$tagId(?![a-zA-Z0-9_])/im";
				if ( preg_match( $pattern, $line ) ) {
					$line = trim( preg_replace( $pattern, $this->getTagValue( $tag, $id ), $line ) );
					if ( empty( $line ) ) {
						unset( $lines[ $lineKey ] );
					}
				}
			}
		}

		$string = implode( PHP_EOL, $lines );

		return aioseo()->tags->replaceTags( $string, $id );
	}

	/**
	 * Get the value of the tag to replace.
	 *
	 * @since 1.1.0
	 *
	 * @param  array  $tag The tag to look for.
	 * @param  int    $id  The page or post ID.
	 * @return string      The value of the tag.
	 */
	public function getTagValue( $tag, $id ) {
		$tagValue = '';

		if ( ! empty( $this->address->{$tag['id']} ) ) {
			$tagValue = $this->address->{$tag['id']};
			if ( 'country' === $tag['id'] ) {
				$tagValue = aioseo()->helpers->getCountryName( $this->address->{$tag['id']} );
			}
		}

		if ( empty( $tagValue ) ) {
			switch ( $tag['id'] ) {
				case 'streetLineOne':
					$tagValue = $this->address->streetLine1;
					break;
				case 'streetLineTwo':
					$tagValue = $this->address->streetLine2;
					break;
				default:
					$tagValue = aioseo()->tags->getTagValue( $tag, $id );
					break;
			}
		}

		return apply_filters( 'aioseo_local_business_address_tag_value', $tagValue, $tag, $id );
	}
}app/Import/Notifications.php000064400000015673151727277700012153 0ustar00<?php
namespace AIOSEO\Plugin\Addon\LocalBusiness\Import;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

use AIOSEO\Plugin\Common\Models;

class Notifications {
	/**
	 * The notifications.
	 *
	 * @since 1.3.0
	 *
	 * @var array
	 */
	private static $notifications = [];

	/**
	 * Class constructor.
	 *
	 * @since 1.3.0
	 */
	public function __construct() {
		add_action( 'aioseo_local_seo_imported', [ $this, 'pushNotifications' ] );
	}

	/**
	 * Adds a notification.
	 *
	 * @since 1.3.0
	 *
	 * @param  array $args The notification arguments from Models\Notification.
	 * @return void
	 */
	public function addNotification( $args ) {
		$notificationName = 'local-business-' . md5( $args['title'] );
		if ( ! empty( self::$notifications[ $notificationName ] ) ) {
			return;
		}

		self::$notifications[ $notificationName ] = array_merge( [
			'slug'              => uniqid(),
			'notification_name' => $notificationName,
			'type'              => 'warning',
			'level'             => [ 'all' ],
			'start'             => gmdate( 'Y-m-d H:i:s' )
		], $args );
	}

	/**
	 * Adds a notification for the user to re-enter the business type.
	 *
	 * @since 1.3.0
	 *
	 * @param  string $businessType The business type.
	 * @return void
	 */
	public function businessTypeNotSupported( $businessType ) {
		$this->addNotification( [
			'title'          => __( 'Re-Enter Business Type in Local Business', 'aioseo-local-business' ),
			'content'        => sprintf(
			// Translators: 1 - The country.
				__( 'For technical reasons, we were unable to migrate the business type you entered for your Local Business schema markup.
				Please enter it (%1$s) again by using the dropdown menu.', 'aioseo-local-business' ),
				"<strong>$businessType</strong>"
			),
			'button1_label'  => __( 'Fix Now', 'aioseo-local-business' ),
			'button1_action' => 'http://route#aioseo-local-seo&aioseo-scroll=info-business-type&aioseo-highlight=info-business-type:locations',
			'button2_label'  => __( 'Remind Me Later', 'aioseo-local-business' ),
			'button2_action' => 'http://action#notification/import-local-business-type-reminder'
		] );
	}

	/**
	 * Adds a notification for the user to re-enter the country.
	 *
	 * @since 1.3.0
	 *
	 * @param  string $country The country.
	 * @return void
	 */
	public function countryNotSupported( $country ) {
		$this->addNotification( [
			'title'          => __( 'Re-Enter Country in Local Business', 'aioseo-local-business' ),
			'content'        => sprintf(
			// Translators: 1 - The country.
				__( 'For technical reasons, we were unable to migrate the country you entered for your Local Business schema markup.
				Please enter it (%1$s) again by using the dropdown menu.', 'aioseo-local-business' ),
				"<strong>$country</strong>"
			),
			'button1_label'  => __( 'Fix Now', 'aioseo-local-business' ),
			'button1_action' => 'http://route#aioseo-local-seo&aioseo-scroll=info-business-address-row&aioseo-highlight=aioseo-local-business-business-country:business-info',
			'button2_label'  => __( 'Remind Me Later', 'aioseo-local-business' ),
			'button2_action' => 'http://action#notification/import-local-business-country-reminder'
		] );
	}

	/**
	 * Adds a notification for the user to re-enter the phone number.
	 *
	 * @since 1.3.0
	 *
	 * @param  string $phoneNumber The phone number.
	 * @return void
	 */
	public function phoneNumberNotSupported( $phoneNumber ) {
		$this->addNotification( [
			'title'          => __( 'Invalid Phone Number for Local SEO', 'aioseo-local-business' ),
			'content'        => sprintf(
			// Translators: 1 - The phone number.
				__( 'The phone number that you previously entered for your Local Business schema markup is invalid.
					As it needs to be internationally formatted, please enter it (%1$s) again with the country code, e.g. +1 (555) 555-1234.', 'aioseo-local-business' ),
				"<strong>$phoneNumber</strong>"
			),
			'button1_label'  => __( 'Fix Now', 'aioseo-local-business' ),
			'button1_action' => 'http://route#aioseo-local-seo&aioseo-scroll=info-business-contact-row&aioseo-highlight=aioseo-local-business-phone-number:business-info',
			'button2_label'  => __( 'Remind Me Later', 'aioseo-local-business' ),
			'button2_action' => 'http://action#notification/import-local-business-number-reminder'
		] );
	}

	/**
	 * Adds a notification for the user to re-enter the fax number.
	 *
	 * @since 1.3.0
	 *
	 * @param  string $faxNumber The fax number.
	 * @return void
	 */
	public function faxNumberNotSupported( $faxNumber ) {
		$this->addNotification( [
			'title'          => __( 'Invalid Fax Number for Local SEO', 'aioseo-local-business' ),
			'content'        => sprintf(
			// Translators: 1 - The fax number.
				__( 'The fax number that you previously entered for your Local Business schema markup is invalid.
					As it needs to be internationally formatted, please enter it (%1$s) again with the country code, e.g. +1 (555) 555-1234.', 'aioseo-local-business' ),
				"<strong>$faxNumber</strong>"
			),
			'button1_label'  => __( 'Fix Now', 'aioseo-local-business' ),
			'button1_action' => 'http://route#aioseo-local-seo&aioseo-scroll=info-business-contact-row&aioseo-highlight=aioseo-local-business-fax-number:business-info',
			'button2_label'  => __( 'Remind Me Later', 'aioseo-local-business' ),
			'button2_action' => 'http://action#notification/import-local-business-fax-reminder'
		] );
	}

	/**
	 * Adds a notification for the user to re-enter the currencies.
	 *
	 * @since 1.3.0
	 *
	 * @param  array $currencies The currencies.
	 * @return void
	 */
	public function currenciesNotSupported( $currencies = [] ) {
		$currenciesList = '<ul>';
		foreach ( $currencies as $currency ) {
			$currenciesList .= '<li>' . esc_html( $currency ) . '<li>';
		}
		$currenciesList .= '</ul>';

		$this->addNotification( [
			'title'          => __( 'Invalid Currencies for Local SEO', 'aioseo-local-business' ),
			'content'        => sprintf(
			// Translators: 1 - The phone number.
				__( 'One or more currencies that you previously entered for your Local Business schema markup are invalid.
					Please select these again using our dropdown menu.</br>%1$s', 'aioseo-local-business' ),
				"<strong>$currenciesList</strong>"
			),
			'button1_label'  => __( 'Fix Now', 'aioseo-local-business' ),
			'button1_action' => 'http://route#aioseo-local-seo&aioseo-scroll=info-payment-info-row&aioseo-highlight=aioseo-local-business-currencies-accepted:business-info',
			'button2_label'  => __( 'Remind Me Later', 'aioseo-local-business' ),
			'button2_action' => 'http://action#notification/import-local-business-currencies-reminder'
		] );
	}

	/**
	 * Pushes notifications to AIOSEO notifications.
	 *
	 * @since 1.3.0
	 *
	 * @return void
	 */
	public function pushNotifications() {
		foreach ( self::$notifications as $notification ) {
			$exists = Models\Notification::getNotificationByName( $notification['notification_name'] );
			if ( $exists->exists() ) {
				return;
			}

			Models\Notification::addNotification( $notification );
		}

		self::$notifications = [];
	}
}app/Import/Helpers.php000064400000040716151727277700010740 0ustar00<?php
namespace AIOSEO\Plugin\Addon\LocalBusiness\Import;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Migrates the Local Business settings from other plugins.
 *
 * @since   4.0.0
 * @version 1.3.0 Moved from Pro.
 */
class Helpers extends \AIOSEO\Plugin\Common\ImportExport\Helpers {
	/**
	 * The error notifications class.
	 *
	 * @since 1.3.0
	 *
	 * @var Notifications
	 */
	protected $notifications = null;

	/**
	 * Constructor.
	 *
	 * @since 1.3.0
	 */
	public function __construct() {
		$this->notifications = new Notifications();
	}

	/**
	 * Migrates the Local Business type.
	 *
	 * @since   4.0.0
	 * @version 1.3.0 Moved from Pro.
	 *
	 * @param  string $businessType The business type.
	 * @return bool                Whether the business type was imported.
	 */
	protected function importLocalBusinessType( $businessType ) {
		if ( ! $this->validateLocalBusinessType( $businessType ) ) {
			return false;
		}

		aioseo()->options->localBusiness->locations->business->businessType = $businessType;

		return true;
	}

	/**
	 * Validates the Local Business type.
	 *
	 * @since 1.3.0
	 *
	 * @param  string $businessType The business type.
	 * @return bool                 Whether the business type is valid.
	 */
	protected function validateLocalBusinessType( $businessType ) {
		if ( ! $businessType ) {
			return false;
		}

		if ( ! in_array( $businessType, $this->getLocalBusinessTypes(), true ) ) {
			$this->notifications->businessTypeNotSupported( $businessType );

			return false;
		}

		return true;
	}

	/**
	 * Migrates the Local Business country.
	 *
	 * @since   4.0.0
	 * @version 1.3.0 Moved from Pro.
	 *
	 * @param  string $country The country.
	 * @return void
	 */
	protected function importLocalBusinessCountry( $country ) {
		if ( empty( $country ) ) {
			return;
		}

		$found   = false;
		$country = strtoupper( $country );
		foreach ( \AIOSEO\Plugin\Pro\Migration\LocalBusiness::getSupportedCountries() as $countryCode => $countryName ) {
			if ( $countryCode === $country || strtoupper( $countryName ) === $country ) {
				$found   = true;
				$country = $countryCode;
				break;
			}
		}

		if ( ! $found ) {
			$this->notifications->countryNotSupported( $country );

			return;
		}

		aioseo()->options->localBusiness->locations->business->address->country = $country;
	}

	/**
	 * Imports the Local Business phone number.
	 *
	 * @since   4.0.0
	 * @version 1.3.0 Moved from Pro.
	 *
	 * @param  string $phoneNumber The phone number.
	 * @return void
	 */
	protected function importLocalBusinessPhoneNumber( $phoneNumber ) {
		if ( empty( $phoneNumber ) ) {
			return;
		}

		// Format the phone number.
		$phoneNumber = preg_replace( '/[^0-9+]/', '', $phoneNumber );

		if ( ! preg_match( '#^\+\d+#', $phoneNumber ) ) {
			$this->notifications->phoneNumberNotSupported( $phoneNumber );

			return;
		}

		aioseo()->options->localBusiness->locations->business->contact->phone = $phoneNumber;
	}

	/**
	 * Imports the Local Business fax number.
	 *
	 * @since   4.0.0
	 * @version 1.3.0 Moved from Pro.
	 *
	 * @return void
	 */
	protected function importLocalBusinessFaxNumber( $faxNumber ) {
		if ( empty( $faxNumber ) ) {
			return;
		}

		// Format the fax number.
		$faxNumber = preg_replace( '/[^0-9+]/', '', $faxNumber );

		if ( ! preg_match( '#^\+\d+#', $faxNumber ) ) {
			$this->notifications->faxNumberNotSupported( $faxNumber );

			return;
		}

		aioseo()->options->localBusiness->locations->business->contact->fax = $faxNumber;
	}

	/**
	 * Imports the Local Business accepted currencies.
	 *
	 * @since   4.0.0
	 * @version 1.3.0 Moved from Pro.
	 *
	 * @param  array $currencies The currencies.
	 * @return void
	 */
	protected function importCurrencies( $currencies ) {
		$currencies = $this->formatCurrencies( $currencies );

		if ( empty( $currencies ) ) {
			return;
		}

		aioseo()->options->localBusiness->locations->business->payment->currenciesAccepted = wp_json_encode( $currencies['supported'] );

		if ( count( $currencies['unsupported'] ) ) {
			$this->notifications->currenciesNotSupported( $currencies['unsupported'] );
		}
	}

	/**
	 * Formats Yoast currencies to AIOSEO currencies.
	 *
	 * @since 1.3.0
	 *
	 * @param  array $currencies The currencies array.
	 * @return array             The formatted currencies.
	 */
	protected function formatCurrencies( $currencies ) {
		$supported = [];
		if ( empty( $currencies ) ) {
			return $supported;
		}

		$dropdownCurrencies = json_decode( $this->getLocalBusinessCurrencies(), true );
		$unsupported        = [];
		foreach ( $currencies as $currency ) {
			$currency = strtoupper( trim( $currency ) );
			$found    = false;
			foreach ( $dropdownCurrencies as $dropdownCurrency ) {
				if ( $currency === $dropdownCurrency['value'] ) {
					$supported[] = $dropdownCurrency;
					$found       = true;
					break;
				}
			}

			if ( ! $found ) {
				$unsupported[] = $currency;
			}
		}

		return [
			'supported'   => $supported,
			'unsupported' => $unsupported
		];
	}

	/**
	 * Returns our supported Local Business types.
	 *
	 * @since   4.0.0
	 * @version 1.3.0 Moved from Pro.
	 *
	 * @return array The list of supported business types.
	 */
	protected function getLocalBusinessTypes() {
		return [
			'LocalBusiness',
			'AnimalShelter',
			'ArchiveOrganization',
			'AutomotiveBusiness',
			'ChildCare',
			'Dentist',
			'DryCleaningOrLaundry',
			'EmergencyService',
			'EmploymentAgency',
			'EntertainmentBusiness',
			'FinancialService',
			'FoodEstablishment',
			'GovernmentOffice',
			'HealthAndBeautyBusiness',
			'HomeAndConstructionBusiness',
			'InternetCafe',
			'LegalService',
			'Library',
			'LodgingBusiness',
			'MedicalBusiness',
			'RadioStation',
			'RealEstateAgent',
			'RecyclingCenter',
			'SelfStorage',
			'ShoppingCenter',
			'SportsActivityLocation',
			'Store',
			'TelevisionStation',
			'TouristInformationCenter',
			'TravelAgency'
		];
	}

	/**
	 * Returns our supported Local Business currencies.
	 *
	 * @since   4.0.0
	 * @version 1.3.0 Moved from Pro.
	 *
	 * @return array The list of supported currencies in JSON.
	 */
	protected function getLocalBusinessCurrencies() {
		return '[{ "symbol": "$", "label": "US Dollar", "value": "USD" },
		{ "symbol": "CA$", "label": "Canadian Dollar", "value": "CAD" },
		{ "symbol": "€", "label": "Euro", "value": "EUR" },
		{ "symbol": "Ƀ", "label": "Bitcoin", "value": "BTC" },
		{ "symbol": "AED", "label": "United Arab Emirates Dirham", "value": "AED" },
		{ "symbol": "Af", "label": "Afghan Afghani", "value": "AFN" },
		{ "symbol": "ALL", "label": "Albanian Lek", "value": "ALL" },
		{ "symbol": "AMD", "label": "Armenian Dram", "value": "AMD" },
		{ "symbol": "AR$", "label": "Argentine Peso", "value": "ARS" },
		{ "symbol": "AU$", "label": "Australian Dollar", "value": "AUD" },
		{ "symbol": "man.", "label": "Azerbaijani Manat", "value": "AZN" },
		{ "symbol": "KM", "label": "Bosnia-Herzegovina Convertible Mark", "value": "BAM" },
		{ "symbol": "Tk", "label": "Bangladeshi Taka", "value": "BDT" },
		{ "symbol": "BGN", "label": "Bulgarian Lev", "value": "BGN" },
		{ "symbol": "BD", "label": "Bahraini Dinar", "value": "BHD" },
		{ "symbol": "FBu", "label": "Burundian Franc", "value": "BIF" },
		{ "symbol": "BN$", "label": "Brunei Dollar", "value": "BND" },
		{ "symbol": "Bs", "label": "Bolivian Boliviano", "value": "BOB" },
		{ "symbol": "R$", "label": "Brazilian Real", "value": "BRL" },
		{ "symbol": "BWP", "label": "Botswanan Pula", "value": "BWP" },
		{ "symbol": "Br", "label": "Belarusian Ruble", "value": "BYN" },
		{ "symbol": "BZ$", "label": "Belize Dollar", "value": "BZD" },
		{ "symbol": "CDF", "label": "Congolese Franc", "value": "CDF" },
		{ "symbol": "CHF", "label": "Swiss Franc", "value": "CHF" },
		{ "symbol": "CL$", "label": "Chilean Peso", "value": "CLP" },
		{ "symbol": "CN¥", "label": "Chinese Yuan", "value": "CNY" },
		{ "symbol": "CO$", "label": "Colombian Peso", "value": "COP" },
		{ "symbol": "₡", "label": "Costa Rican Colón", "value": "CRC" },
		{ "symbol": "CV$", "label": "Cape Verdean Escudo", "value": "CVE" },
		{ "symbol": "Kč", "label": "Czech Republic Koruna", "value": "CZK" },
		{ "symbol": "Fdj", "label": "Djiboutian Franc", "value": "DJF" },
		{ "symbol": "Dkr", "label": "Danish Krone", "value": "DKK" },
		{ "symbol": "RD$", "label": "Dominican Peso", "value": "DOP" },
		{ "symbol": "DA", "label": "Algerian Dinar", "value": "DZD" },
		{ "symbol": "Ekr", "label": "Estonian Kroon", "value": "EEK" },
		{ "symbol": "EGP", "label": "Egyptian Pound", "value": "EGP" },
		{ "symbol": "Nfk", "label": "Eritrean Nakfa", "value": "ERN" },
		{ "symbol": "Br", "label": "Ethiopian Birr", "value": "ETB" },
		{ "symbol": "£", "label": "British Pound Sterling", "value": "GBP" },
		{ "symbol": "GEL", "label": "Georgian Lari", "value": "GEL" },
		{ "symbol": "GH₵", "label": "Ghanaian Cedi", "value": "GHS" },
		{ "symbol": "FG", "label": "Guinean Franc", "value": "GNF" },
		{ "symbol": "GTQ", "label": "Guatemalan Quetzal", "value": "GTQ" },
		{ "symbol": "HK$", "label": "Hong Kong Dollar", "value": "HKD" },
		{ "symbol": "HNL", "label": "Honduran Lempira", "value": "HNL" },
		{ "symbol": "kn", "label": "Croatian Kuna", "value": "HRK" },
		{ "symbol": "Ft", "label": "Hungarian Forint", "value": "HUF" },
		{ "symbol": "Rp", "label": "Indonesian Rupiah", "value": "IDR" },
		{ "symbol": "₪", "label": "Israeli New Sheqel", "value": "ILS" },
		{ "symbol": "Rs", "label": "Indian Rupee", "value": "INR" },
		{ "symbol": "IQD", "label": "Iraqi Dinar", "value": "IQD" },
		{ "symbol": "IRR", "label": "Iranian Rial", "value": "IRR" },
		{ "symbol": "Ikr", "label": "Icelandic Króna", "value": "ISK" },
		{ "symbol": "J$", "label": "Jamaican Dollar", "value": "JMD" },
		{ "symbol": "JD", "label": "Jordanian Dinar", "value": "JOD" },
		{ "symbol": "¥", "label": "Japanese Yen", "value": "JPY" },
		{ "symbol": "Ksh", "label": "Kenyan Shilling", "value": "KES" },
		{ "symbol": "KHR", "label": "Cambodian Riel", "value": "KHR" },
		{ "symbol": "CF", "label": "Comorian Franc", "value": "KMF" },
		{ "symbol": "₩", "label": "South Korean Won", "value": "KRW" },
		{ "symbol": "KD", "label": "Kuwaiti Dinar", "value": "KWD" },
		{ "symbol": "KZT", "label": "Kazakhstani Tenge", "value": "KZT" },
		{ "symbol": "LB£", "label": "Lebanese Pound", "value": "LBP" },
		{ "symbol": "SLRs", "label": "Sri Lankan Rupee", "value": "LKR" },
		{ "symbol": "Lt", "label": "Lithuanian Litas", "value": "LTL" },
		{ "symbol": "Ls", "label": "Latvian Lats", "value": "LVL" },
		{ "symbol": "LD", "label": "Libyan Dinar", "value": "LYD" },
		{ "symbol": "MAD", "label": "Moroccan Dirham", "value": "MAD" },
		{ "symbol": "MDL", "label": "Moldovan Leu", "value": "MDL" },
		{ "symbol": "MGA", "label": "Malagasy Ariary", "value": "MGA" },
		{ "symbol": "MKD", "label": "Macedonian Denar", "value": "MKD" },
		{ "symbol": "MMK", "label": "Myanma Kyat", "value": "MMK" },
		{ "symbol": "MOP$", "label": "Macanese Pataca", "value": "MOP" },
		{ "symbol": "MURs", "label": "Mauritian Rupee", "value": "MUR" },
		{ "symbol": "MX$", "label": "Mexican Peso", "value": "MXN" },
		{ "symbol": "RM", "label": "Malaysian Ringgit", "value": "MYR" },
		{ "symbol": "MTn", "label": "Mozambican Metical", "value": "MZN" },
		{ "symbol": "N$", "label": "Namibian Dollar", "value": "NAD" },
		{ "symbol": "₦", "label": "Nigerian Naira", "value": "NGN" },
		{ "symbol": "C$", "label": "Nicaraguan Córdoba", "value": "NIO" },
		{ "symbol": "Nkr", "label": "Norwegian Krone", "value": "NOK" },
		{ "symbol": "NPRs", "label": "Nepalese Rupee", "value": "NPR" },
		{ "symbol": "NZ$", "label": "New Zealand Dollar", "value": "NZD" },
		{ "symbol": "OMR", "label": "Omani Rial", "value": "OMR" },
		{ "symbol": "B/.", "label": "Panamanian Balboa", "value": "PAB" },
		{ "symbol": "S/.", "label": "Peruvian Nuevo Sol", "value": "PEN" },
		{ "symbol": "₱", "label": "Philippine Peso", "value": "PHP" },
		{ "symbol": "PKRs", "label": "Pakistani Rupee", "value": "PKR" },
		{ "symbol": "zł", "label": "Polish Zloty", "value": "PLN" },
		{ "symbol": "₲", "label": "Paraguayan Guarani", "value": "PYG" },
		{ "symbol": "QR", "label": "Qatari Rial", "value": "QAR" },
		{ "symbol": "RON", "label": "Romanian Leu", "value": "RON" },
		{ "symbol": "din.", "label": "Serbian Dinar", "value": "RSD" },
		{ "symbol": "RUB", "label": "Russian Ruble", "value": "RUB" },
		{ "symbol": "RWF", "label": "Rwandan Franc", "value": "RWF" },
		{ "symbol": "SR", "label": "Saudi Riyal", "value": "SAR" },
		{ "symbol": "SDG", "label": "Sudanese Pound", "value": "SDG" },
		{ "symbol": "Skr", "label": "Swedish Krona", "value": "SEK" },
		{ "symbol": "S$", "label": "Singapore Dollar", "value": "SGD" },
		{ "symbol": "Ssh", "label": "Somali Shilling", "value": "SOS" },
		{ "symbol": "SY£", "label": "Syrian Pound", "value": "SYP" },
		{ "symbol": "฿", "label": "Thai Baht", "value": "THB" },
		{ "symbol": "DT", "label": "Tunisian Dinar", "value": "TND" },
		{ "symbol": "T$", "label": "Tongan Paʻanga", "value": "TOP" },
		{ "symbol": "TL", "label": "Turkish Lira", "value": "TRY" },
		{ "symbol": "TT$", "label": "Trinidad and Tobago Dollar", "value": "TTD" },
		{ "symbol": "NT$", "label": "New Taiwan Dollar", "value": "TWD" },
		{ "symbol": "TSh", "label": "Tanzanian Shilling", "value": "TZS" },
		{ "symbol": "₴", "label": "Ukrainian Hryvnia", "value": "UAH" },
		{ "symbol": "USh", "label": "Ugandan Shilling", "value": "UGX" },
		{ "symbol": "$U", "label": "Uruguayan Peso", "value": "UYU" },
		{ "symbol": "UZS", "label": "Uzbekistan Som", "value": "UZS" },
		{ "symbol": "Bs.F.", "label": "Venezuelan Bolívar", "value": "VEF" },
		{ "symbol": "₫", "label": "Vietnamese Dong", "value": "VND" },
		{ "symbol": "FCFA", "label": "CFA Franc BEAC", "value": "XAF" },
		{ "symbol": "CFA", "label": "CFA Franc BCEAO", "value": "XOF" },
		{ "symbol": "YR", "label": "Yemeni Rial", "value": "YER" },
		{ "symbol": "R", "label": "South African Rand", "value": "ZAR" },
		{ "symbol": "ZK", "label": "Zambian Kwacha", "value": "ZMK" },
		{ "symbol": "ZWL$", "label": "Zimbabwean Dollar", "value": "ZWL" }]';
	}

	/**
	 * Prepare Price Range value.
	 *
	 * @since   4.1.3
	 * @version 1.3.0 Moved from Pro.
	 *
	 * @param  string $priceRange The price range to prepare.
	 * @return string             The prepared price range.
	 */
	protected function preparePriceRange( $priceRange ) {
		$count = strlen( trim( $priceRange ) );
		if ( 0 === $count ) {
			return '';
		}

		if ( 5 < $count ) {
			$count = 5;
		}

		return str_repeat( '$', $count );
	}

	/**
	 * Placeholder method.
	 *
	 * @since 1.3.0
	 *
	 * @param  string $value
	 * @return string
	 */
	public function macrosToSmartTags( $value ) {
		return $value;
	}

	/**
	 * Import all the terms of a custom location taxonomy to AIOSEO's taxonomy.
	 *
	 * @since 1.3.0
	 *
	 * @param  string $taxonomy The taxonomy name.
	 * @return array            The imported terms.
	 */
	protected function importTaxonomyTerms( $taxonomy ) {
		$locationCategories = get_terms( [
			'taxonomy'   => $taxonomy,
			'hide_empty' => false
		] );

		// The taxonomy probably doesn't exist.
		if ( is_wp_error( $locationCategories ) ) {
			return [];
		}

		$importedCategories = [];
		foreach ( $locationCategories as $locationCategory ) {
			$importedCategories[ $locationCategory->term_id ] = $this->importLocationCategory( $locationCategory );
		}

		// Fix parent reference.
		foreach ( $importedCategories as &$importedCategory ) {
			if ( ! empty( $importedCategory->oldParentId ) ) {
				$newParentId = $importedCategories[ $importedCategory->oldParentId ]->term_id;
				if ( $newParentId !== $importedCategory->parent ) {
					$importedCategory->parent = $newParentId;
					wp_update_term( $importedCategory->term_id, $importedCategory->taxonomy, [ 'parent' => $importedCategory->parent ] );
				}
			}
		}

		return $importedCategories;
	}

	/**
	 * Import a location category.
	 *
	 * @since 1.3.0
	 *
	 * @param  \WP_Term $locationCategory The location category to import.
	 * @return \WP_Term                   The imported location category.
	 */
	private function importLocationCategory( $locationCategory ) {
		$term = get_term_by( 'slug', $locationCategory->slug, aioseoLocalBusiness()->taxonomy->getName() );
		if ( ! $term ) {
			$term = wp_insert_term( $locationCategory->name, aioseoLocalBusiness()->taxonomy->getName(), [
				'description' => $locationCategory->description,
				'slug'        => $locationCategory->slug
			] );

			// Return new \WP_Term object.
			$term = get_term( $term['term_id'], aioseoLocalBusiness()->taxonomy->getName() );
		}

		// Reference old id to fix parent-child relationships.
		$term->oldParentId = $locationCategory->parent;  /** @phpstan-ignore-line */

		return $term;
	}
}app/Import/Plugins/SeoPress.php000064400000014076151727277700012522 0ustar00<?php
namespace AIOSEO\Plugin\Addon\LocalBusiness\Import\Plugins;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

use AIOSEO\Plugin\Addon\LocalBusiness\Import;

/**
 * Imports the Local Business settings.
 *
 * @since   4.1.4
 * @version 1.3.0 Moved from Pro.
 */
class SeoPress extends Import\Importer {
	/**
	 * List of options.
	 *
	 * @since   4.1.4
	 * @version 1.3.0 Moved from Pro.
	 *
	 * @var array
	 */
	private $options = [];

	/**
	 * A list of plugins to look for to import.
	 *
	 * @since 1.3.0
	 *
	 * @var array
	 */
	public $plugins = [
		[
			'name'     => 'SEOPress PRO',
			'version'  => '4.0',
			'basename' => 'wp-seopress-pro/seopress-pro.php',
			'slug'     => 'seopress-pro'
		]
	];

	/**
	 * Import.
	 *
	 * @since   4.1.4
	 * @version 1.3.0 Moved from Pro.
	 *
	 */
	public function doImport() {
		$this->options = get_option( 'seopress_pro_option_name' );
		if ( empty( $this->options ) ) {
			return;
		}

		if ( ! empty( $this->options['seopress_local_business_type'] ) ) {
			$this->importLocalBusinessType( $this->options['seopress_local_business_type'] );
		}

		$this->importLocalBusinessAddress();
		$this->importLocalBusinessPriceRange();
		$this->importOpeningHourSettings();
		$this->importMapSettings();
	}

	/**
	 * Imports the Local Business price range.
	 *
	 * @since   4.1.4
	 * @version 1.3.0 Moved from Pro.
	 *
	 * @return void
	 */
	private function importLocalBusinessPriceRange() {
		if ( empty( $this->options['seopress_local_business_price_range'] ) ) {
			return;
		}

		$priceRange = $this->preparePriceRange( $this->options['seopress_local_business_price_range'] );
		if ( empty( $priceRange ) ) {
			return;
		}

		aioseo()->options->localBusiness->locations->business->payment->priceRange = $priceRange;
	}

	/**
	 * Imports the Local Business address.
	 *
	 * @since   4.1.4
	 * @version 1.3.0 Moved from Pro.
	 *
	 * @return void
	 */
	private function importLocalBusinessAddress() {
		if ( ! empty( $this->options['seopress_local_business_address_country'] ) ) {
			$this->importLocalBusinessCountry( $this->options['seopress_local_business_address_country'] );
		}

		$settings = [
			'seopress_local_business_street_address'   => [
				'type'      => 'string',
				'newOption' => [ 'localBusiness', 'locations', 'business', 'address', 'streetLine1' ]
			],
			'seopress_local_business_address_locality' => [
				'type'      => 'string',
				'newOption' => [ 'localBusiness', 'locations', 'business', 'address', 'city' ]
			],
			'seopress_local_business_address_region'   => [
				'type'      => 'string',
				'newOption' => [ 'localBusiness', 'locations', 'business', 'address', 'state' ]
			],
			'seopress_local_business_postal_code'      => [
				'type'      => 'string',
				'newOption' => [ 'localBusiness', 'locations', 'business', 'address', 'zipCode' ]
			],
			'seopress_local_business_phone'            => [
				'type'      => 'string',
				'newOption' => [ 'localBusiness', 'locations', 'business', 'contact', 'phone' ]
			],
		];

		$this->mapOldToNew( $settings, $this->options );
	}

	/**
	 * Imports the Local Business Opening Hours settings.
	 *
	 * @since   4.1.4
	 * @version 1.3.0 Moved from Pro.
	 *
	 * @return void
	 */
	private function importOpeningHourSettings() {
		$openingHours = $this->options['seopress_local_business_opening_hours'];
		if ( empty( $openingHours ) ) {
			return;
		}

		aioseo()->options->localBusiness->openingHours->use24hFormat = true;

		$days = [ 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday' ];

		foreach ( $openingHours as $key => $settings ) {
			if ( ! aioseo()->options->localBusiness->openingHours->days->has( $days[ $key ] ) ) {
				continue;
			}

			// Reset some defaults.
			aioseo()->options->localBusiness->openingHours->days->{$days[ $key ]}->closed  = false;
			aioseo()->options->localBusiness->openingHours->days->{$days[ $key ]}->open24h = false;

			// In SEOPress the option is called 'open' but being true means the business is closed.
			// That can be seen in the options where the label for the field is "Closed all the day?".
			// Also, can be seen in the schema output where it checks if the key exists, which means closed.
			if ( ! empty( $settings['open'] ) ) {
				aioseo()->options->localBusiness->openingHours->days->{$days[ $key ]}->closed = true;
			}

			// Closed this day.
			if ( empty( $settings['am']['open'] ) && empty( $settings['pm']['open'] ) ) {
				aioseo()->options->localBusiness->openingHours->days->{$days[ $key ]}->closed = true;
			}

			$meridiem = 'am';
			$openTime = $settings[ $meridiem ]['start']['hours'] . ':' . $settings[ $meridiem ]['start']['mins'];

			// Morning from 00:00 to 23:59 means open 24 hours.
			if ( '00:00' === $openTime && '23:59' === $settings[ $meridiem ]['end']['hours'] . ':' . $settings[ $meridiem ]['end']['mins'] ) {
				aioseo()->options->localBusiness->openingHours->days->{$days[ $key ]}->open24h = true;
			}

			if ( ! empty( $settings['pm']['open'] ) ) {
				$meridiem = 'pm';

				if ( empty( $settings['am']['open'] ) ) {
					$openTime = $settings[ $meridiem ]['start']['hours'] . ':' . $settings[ $meridiem ]['start']['mins'];
				}
			}

			$closeTime = $settings[ $meridiem ]['end']['hours'] . ':' . $settings[ $meridiem ]['end']['mins'];

			aioseo()->options->localBusiness->openingHours->days->{ $days[ $key ] }->openTime  = $openTime;
			aioseo()->options->localBusiness->openingHours->days->{ $days[ $key ] }->closeTime = $closeTime;
		}
	}

	/**
	 * Imports the Local Business map settings.
	 *
	 * @since 1.3.0
	 *
	 * @return void
	 */
	private function importMapSettings() {
		if ( ! empty( $this->options['seopress_local_business_lat'] ) && ! empty( $this->options['seopress_local_business_lon'] ) ) {
			aioseo()->options->localBusiness->maps->mapOptions->center->lat = $this->options['seopress_local_business_lat'];
			aioseo()->options->localBusiness->maps->mapOptions->center->lng = $this->options['seopress_local_business_lon'];
		}

		if ( ! empty( $this->options['seopress_local_business_place_id'] ) ) {
			aioseo()->options->localBusiness->maps->placeId = $this->options['seopress_local_business_place_id'];
		}
	}
}app/Import/Plugins/RankMath.php000064400000013070151727277700012455 0ustar00<?php
namespace AIOSEO\Plugin\Addon\LocalBusiness\Import\Plugins;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

use AIOSEO\Plugin\Addon\LocalBusiness\Import;

/**
 * Migrates the Local Business settings.
 *
 * These are contained in the Title & Meta section of Rank Math.
 *
 * @since   4.0.0
 * @version 1.3.0 Moved from Pro.
 */
class RankMath extends Import\Importer {
	/**
	 * List of options.
	 *
	 * @since   4.2.7
	 * @version 1.3.0 Moved from Pro.
	 *
	 * @var array
	 */
	private $options = [];

	/**
	 * A list of plugins to look for to import.
	 *
	 * @since 1.3.0
	 *
	 * @var array
	 */
	public $plugins = [
		[
			'name'     => 'Rank Math SEO',
			'version'  => '1.0',
			'basename' => 'seo-by-rank-math/rank-math.php',
			'slug'     => 'rank-math-seo'
		]
	];

	/**
	 * Import.
	 *
	 * @since   4.0.0
	 * @version 1.3.0 Moved from Pro.
	 */
	public function doImport() {
		$this->options = get_option( 'rank-math-options-titles' );
		if ( empty( $this->options ) ) {
			return;
		}

		if ( isset( $this->options['local_business_type'] ) ) {
			$this->importLocalBusinessType( $this->options['local_business_type'] );
		}

		if ( ! empty( $this->options['email'] ) ) {
			aioseo()->options->localBusiness->locations->business->contact->email = $this->options['email'];
		}

		$this->importLocalBusinessAddress();
		$this->importPhoneNumber();
		$this->importLocalBusinessPriceRange();
		$this->importOpeningHourSettings();
		$this->importMapSettings();
	}

	/**
	 * Migrates the Local Business price range.
	 *
	 * @since   4.0.0
	 * @version 1.3.0 Moved from Pro.
	 *
	 * @return void
	 */
	private function importLocalBusinessPriceRange() {
		if ( empty( $this->options['price_range'] ) ) {
			return;
		}

		$priceRange = $this->preparePriceRange( $this->options['price_range'] );
		if ( empty( $priceRange ) ) {
			return;
		}

		aioseo()->options->localBusiness->locations->business->payment->priceRange = $priceRange;
	}

	/**
	 * Migrates the Local Business address.
	 *
	 * @since   4.0.0
	 * @version 1.3.0 Moved from Pro.
	 *
	 * @return void
	 */
	private function importLocalBusinessAddress() {
		if ( empty( $this->options['local_address'] ) ) {
			return;
		}

		if ( ! empty( $this->options['local_address']['addressCountry'] ) ) {
			$this->importLocalBusinessCountry( $this->options['local_address']['addressCountry'] );
		}

		$settings = [
			'streetAddress'   => [
				'type'      => 'string',
				'newOption' => [ 'localBusiness', 'locations', 'business', 'address', 'streetLine1' ]
			],
			'addressLocality' => [
				'type'      => 'string',
				'newOption' => [ 'localBusiness', 'locations', 'business', 'address', 'city' ]
			],
			'addressRegion'   => [
				'type'      => 'string',
				'newOption' => [ 'localBusiness', 'locations', 'business', 'address', 'state' ]
			],
			'postalCode'      => [
				'type'      => 'string',
				'newOption' => [ 'localBusiness', 'locations', 'business', 'address', 'zipCode' ]
			],
		];

		aioseo()->importExport->rankMath->helpers->mapOldToNew( $settings, $this->options['local_address'] );
	}

	/**
	 * Migrates the Local Business opening hour settings.
	 *
	 * @since   4.0.0
	 * @version 1.3.0 Moved from Pro.
	 *
	 * @return void
	 */
	private function importOpeningHourSettings() {
		if ( ! empty( $this->options['opening_hours_format'] ) ) {
			aioseo()->options->localBusiness->openingHours->use24hFormat = 'off' === $this->options['opening_hours_format'];
		}

		if ( empty( $this->options['opening_hours'] ) ) {
			return;
		}

		$days = aioseo()->options->localBusiness->openingHours->days->all();

		foreach ( $days as $name => $values ) {
			aioseo()->options->localBusiness->openingHours->days->$name->closed = true;

			$importDay = array_filter( $this->options['opening_hours'], function ( $day ) use ( $name ) {
				return strtolower( $day['day'] ) === $name && ! empty( $day['time'] );
			} );

			$importDay = current( $importDay );
			if ( empty( $importDay ) ) {
				continue;
			}

			aioseo()->options->localBusiness->openingHours->days->$name->closed = false;

			preg_match( '#^(\d{1,2}:\d{2})-(\d{1,2}:\d{2})$#', $importDay['time'], $matches );
			if ( ! empty( $matches[1] ) ) {
				aioseo()->options->localBusiness->openingHours->days->$name->openTime = str_pad( $matches[1], 5, '0', STR_PAD_LEFT );
			}

			if ( ! empty( $matches[2] ) ) {
				aioseo()->options->localBusiness->openingHours->days->$name->closeTime = str_pad( $matches[2], 5, '0', STR_PAD_LEFT );
			}

			if ( '00:00' === $matches[1] && '23:59' === $matches[2] ) {
				aioseo()->options->localBusiness->openingHours->days->$name->open24h = true;
			}
		}
	}

	/**
	 * Imports the Local Business map settings.
	 *
	 * @since 1.3.0
	 *
	 * @return void
	 */
	private function importMapSettings() {
		if ( ! empty( $this->options['maps_api_key'] ) ) {
			aioseo()->options->localBusiness->maps->apiKey = $this->options['maps_api_key'];
			aioseo()->options->localBusiness->maps->apiKeyValid = true;
		}

		if ( ! empty( $this->options['geo'] ) ) {
			list( $lat, $lng ) = explode( ',', $this->options['geo'] );
			aioseo()->options->localBusiness->maps->mapOptions->center->lat = $lat;
			aioseo()->options->localBusiness->maps->mapOptions->center->lng = $lng;
		}
	}

	/**
	 * Imports the phone number.
	 *
	 * @since 1.3.0
	 *
	 * @return void
	 */
	private function importPhoneNumber() {
		if ( ! is_array( $this->options['phone_numbers'] ) || empty( $this->options['phone_numbers'] ) ) {
			return;
		}

		$phoneNumber = current( $this->options['phone_numbers'] );
		if ( ! empty( $phoneNumber['number'] ) ) {
			$this->importLocalBusinessPhoneNumber( $phoneNumber['number'] );
		}
	}
}app/Import/Plugins/YoastSeo.php000064400000036453151727277700012530 0ustar00<?php
namespace AIOSEO\Plugin\Addon\LocalBusiness\Import\Plugins;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

use AIOSEO\Plugin\Common\Models;
use AIOSEO\Plugin\Addon\LocalBusiness\Import;

/**
 * The YoastSEO importer class.
 *
 * @since   4.0.0
 * @version 1.3.0 Moved from Pro.
 */
class YoastSeo extends Import\Importer {
	/**
	 * List of options.
	 *
	 * @since   4.2.7
	 * @version 1.3.0 Moved from Pro.
	 *
	 * @var array
	 */
	private $options = [];

	/**
	 * A list of plugins to look for to import.
	 *
	 * @since 1.3.0
	 *
	 * @var array
	 */
	public $plugins = [
		[
			'name'     => 'Yoast SEO: Local',
			'version'  => '14.9',
			'basename' => 'wpseo-local/local-seo.php',
			'slug'     => 'yoast-local-seo'
		]
	];

	/**
	 * Import.
	 *
	 * @since   4.0.0
	 * @version 1.3.0 Moved from Pro.
	 *
	 * @return void
	 */
	public function doImport() {
		$this->options = get_option( 'wpseo_local' );
		if ( empty( $this->options ) ) {
			return;
		}

		// Yoast SEO doesn't have a setting for this, so we'll use the Organization Name.
		aioseo()->options->localBusiness->locations->business->name = aioseo()->options->searchAppearance->global->schema->organizationName;

		if ( ! empty( $this->options['business_type'] ) ) {
			$this->importLocalBusinessType( $this->options['business_type'] );
		}

		if ( ! empty( $this->options['location_country'] ) ) {
			$this->importLocalBusinessCountry( $this->options['location_country'] );
		}

		if ( ! empty( $this->options['location_phone'] ) ) {
			$this->importLocalBusinessPhoneNumber( $this->options['location_phone'] );
		}

		if ( ! empty( $this->options['location_fax'] ) ) {
			$this->importLocalBusinessFaxNumber( $this->options['location_fax'] );
		}

		if ( ! empty( $this->options['location_currencies_accepted'] ) ) {
			$currencies = array_filter( explode( ',', $this->options['location_currencies_accepted'] ) );
			$this->importCurrencies( $currencies );
		}

		$settings = [
			'location_email'            => [
				'type'      => 'string',
				'newOption' => [ 'localBusiness', 'locations', 'business', 'contact', 'email' ]
			],
			'location_address'          => [
				'type'      => 'string',
				'newOption' => [ 'localBusiness', 'locations', 'business', 'address', 'streetLine1' ]
			],
			'location_address_2'        => [
				'type'      => 'string',
				'newOption' => [ 'localBusiness', 'locations', 'business', 'address', 'streetLine2' ]
			],
			'location_city'             => [
				'type'      => 'string',
				'newOption' => [ 'localBusiness', 'locations', 'business', 'address', 'city' ]
			],
			'location_state'            => [
				'type'      => 'string',
				'newOption' => [ 'localBusiness', 'locations', 'business', 'address', 'state' ]
			],
			'location_zipcode'          => [
				'type'      => 'string',
				'newOption' => [ 'localBusiness', 'locations', 'business', 'address', 'zipCode' ]
			],
			'location_vat_id'           => [
				'type'      => 'string',
				'newOption' => [ 'localBusiness', 'locations', 'business', 'ids', 'vat' ]
			],
			'location_tax_id'           => [
				'type'      => 'string',
				'newOption' => [ 'localBusiness', 'locations', 'business', 'ids', 'tax' ]
			],
			'location_coc_id'           => [
				'type'      => 'string',
				'newOption' => [ 'localBusiness', 'locations', 'business', 'ids', 'chamberOfCommerce' ]
			],
			'location_price_range'      => [
				'type'      => 'string',
				'newOption' => [ 'localBusiness', 'locations', 'business', 'payment', 'priceRange' ]
			],
			'location_payment_accepted' => [
				'type'      => 'string',
				'newOption' => [ 'localBusiness', 'locations', 'business', 'payment', 'methods' ]
			],
			'location_area_served'      => [
				'type'      => 'string',
				'newOption' => [ 'localBusiness', 'locations', 'business', 'areaServed' ]
			],
		];

		$this->mapOldToNew( $settings, $this->options );

		$this->importAddressFormat();
		$this->importOpeningHourSettings();
		$this->importMapSettings();
		$this->importMultipleLocations();
	}

	/**
	 * Imports the Local Business opening hour settings.
	 *
	 * @since   4.0.0
	 * @version 1.3.0 Moved from Pro.
	 *
	 * @return void
	 */
	private function importOpeningHourSettings() {
		if ( ! empty( $this->options['hide_opening_hours'] ) ) {
			aioseo()->options->localBusiness->openingHours->show = 'off' === $this->options['hide_opening_hours'];
		}

		if ( ! empty( $this->options['open_247'] ) ) {
			aioseo()->options->localBusiness->openingHours->alwaysOpen = 'on' === $this->options['open_247'];
		}

		if ( ! empty( $this->options['closed_label'] ) ) {
			aioseo()->options->localBusiness->openingHours->labels->closed = $this->options['closed_label'];
		}

		if ( ! empty( $this->options['open_24h_label'] ) ) {
			aioseo()->options->localBusiness->openingHours->labels->alwaysOpen = $this->options['open_24h_label'];
		}

		if ( ! empty( $this->options['opening_hours_24h'] ) ) {
			aioseo()->options->localBusiness->openingHours->use24hFormat = 'on' === $this->options['opening_hours_24h'];
		}

		$days = $this->formatOpeningHoursDays( $this->options );
		foreach ( $days as $day => $dayOptions ) {
			foreach ( $dayOptions as $option => $value ) {
				aioseo()->options->localBusiness->openingHours->days->$day->$option = $value;
			}
		}
	}

	/**
	 * Formats the opening hours.
	 *
	 * @since 1.3.0
	 *
	 * @param  array $options The options array from Yoast.
	 * @return array          The formatted opening hours.
	 */
	private function formatOpeningHoursDays( $options ) {
		$openingHoursDays = [];
		$days             = aioseo()->options->localBusiness->openingHours->days->all();
		foreach ( $days as $name => $values ) {
			if ( ! empty( $options[ "opening_hours_{$name}_24h" ] ) ) {
				$openingHoursDays[ $name ]['open24h'] = 'on' === $options[ "opening_hours_{$name}_24h" ];
			}

			if ( ! empty( $options[ "opening_hours_{$name}_from" ] ) ) {
				if ( 'closed' === $options[ "opening_hours_{$name}_from" ] ) {
					$openingHoursDays[ $name ]['closed'] = true;
					continue;
				}
				$openingHoursDays[ $name ]['openTime'] = $options[ "opening_hours_{$name}_from" ];
			}

			if ( ! empty( $options[ "opening_hours_{$name}_to" ] ) ) {
				if ( 'closed' === $options[ "opening_hours_{$name}_to" ] ) {
					$openingHoursDays[ $name ]['closed'] = true;
					continue;
				}
				$openingHoursDays[ $name ]['closeTime'] = $options[ "opening_hours_{$name}_to" ];
			}
		}

		return $openingHoursDays;
	}

	/**
	 * Imports the Address format.
	 *
	 * @since 1.3.0
	 *
	 * @return void
	 */
	private function importAddressFormat() {
		if ( empty( $this->options['address_format'] ) ) {
			return;
		}

		$formatMap = [
			'address-state-postal'       => '#streetLineOne #streetLineTwo #city, #state #zipCode',
			'address-state-postal-comma' => '#streetLineOne #streetLineTwo #city, #state, #zipCode',
			'address-postal-city-state'  => '#streetLineOne #streetLineTwo #zipCode #city, #state',
			'address-postal'             => '#streetLineOne #streetLineTwo #city #zipCode',
			'address-postal-comma'       => '#streetLineOne #streetLineTwo #city, #zipCode',
			'address-city'               => '#streetLineOne #streetLineTwo #city',
			'postal-address'             => '#zipCode #state #city #streetLineOne #streetLineTwo'
		];

		if ( empty( $formatMap[ $this->options['address_format'] ] ) ) {
			return;
		}

		aioseo()->options->localBusiness->locations->business->address->addressFormat = $formatMap[ $this->options['address_format'] ];
	}

	/**
	 * Imports multiple locations.
	 *
	 * @since 1.3.0
	 *
	 * @return void
	 */
	private function importMultipleLocations() {
		aioseo()->options->localBusiness->locations->general->multiple = ! empty( $this->options['use_multiple_locations'] ) && 'on' === $this->options['use_multiple_locations'];

		if ( ! empty( $this->options['locations_label_singular'] ) ) {
			aioseo()->options->localBusiness->locations->general->singleLabel = $this->options['locations_label_singular'];
		}

		if ( ! empty( $this->options['locations_label_plural'] ) ) {
			aioseo()->options->localBusiness->locations->general->pluralLabel = $this->options['locations_label_plural'];
		}

		if ( ! empty( $this->options['locations_slug'] ) ) {
			aioseo()->options->localBusiness->locations->general->useCustomSlug = true;
			aioseo()->options->localBusiness->locations->general->customSlug    = $this->options['locations_slug'];
		}

		if ( ! empty( $this->options['locations_taxo_slug'] ) ) {
			aioseo()->options->localBusiness->locations->general->useCustomCategorySlug = true;
			aioseo()->options->localBusiness->locations->general->customCategorySlug    = $this->options['locations_taxo_slug'];
		}

		if ( ! empty( $this->options['local_enhanced_search'] ) && 'on' === $this->options['local_enhanced_search'] ) {
			aioseo()->options->localBusiness->locations->general->enhancedSearch = true;
		}

		if ( aioseo()->options->localBusiness->locations->general->multiple ) {
			// Query wpseo-local categories.
			$importedCategories = $this->importTaxonomyTerms( 'wpseo_locations_category' );

			// Query wpseo-local locations.
			$locations = get_posts( [
				'post_type'      => 'wpseo_locations',
				'posts_per_page' => -1,
				'post_status'    => 'any'
			] );

			foreach ( $locations as $location ) {
				$this->importLocation( $location, $importedCategories );
			}
		}
	}

	/**
	 * Imports a Location.
	 *
	 * @since 1.3.0
	 *
	 * @param  \WP_Post $yoastLocation      The location to import.
	 * @param  array    $importedCategories The imported categories.
	 * @return void
	 */
	private function importLocation( $yoastLocation, $importedCategories ) {
		// Get the Yoast meta.
		$yoastMetaRaw = get_post_meta( $yoastLocation->ID );

		// Filter only needed data.
		$yoastMetaRaw = array_filter( $yoastMetaRaw, function ( $key ) {
			return 0 === strpos( $key, '_wpseo_' );
		}, ARRAY_FILTER_USE_KEY );

		// Flatten the Yoast meta.
		$yoastMetaRaw = array_map( function ( $n ) {
			return $n[0];
		}, $yoastMetaRaw );

		// Normalize meta.
		$yoastMeta = [];
		foreach ( $yoastMetaRaw as $key => $value ) {
			$yoastMeta[ preg_replace( '%^_wpseo_%', '', $key ) ] = $value;
		}

		// Location and business data.
		$locationData = [
			'business_name'                => [ 'locations', 'business', 'name' ],
			'business_type'                => [ 'locations', 'business', 'businessType' ],
			'business_location_logo'       => [ 'locations', 'business', 'image' ],
			'business_address'             => [ 'locations', 'business', 'address', 'streetLine1' ],
			'business_address_2'           => [ 'locations', 'business', 'address', 'streetLine2' ],
			'business_city'                => [ 'locations', 'business', 'address', 'city' ],
			'business_state'               => [ 'locations', 'business', 'address', 'state' ],
			'business_zipcode'             => [ 'locations', 'business', 'address', 'zipCode' ],
			'business_country'             => [ 'locations', 'business', 'address', 'country' ],
			'business_phone'               => [ 'locations', 'business', 'contact', 'phone' ],
			'business_fax'                 => [ 'locations', 'business', 'contact', 'fax' ],
			'business_email'               => [ 'locations', 'business', 'contact', 'email' ],
			'business_vat_id'              => [ 'locations', 'business', 'ids', 'vat' ],
			'business_tax_id'              => [ 'locations', 'business', 'ids', 'tax' ],
			'business_coc_id'              => [ 'locations', 'business', 'ids', 'chamberOfCommerce' ],
			'business_price_range'         => [ 'locations', 'business', 'payment', 'priceRange' ],
			'business_currencies_accepted' => [ 'locations', 'business', 'payment', 'currenciesAccepted' ],
			'business_payment_accepted'    => [ 'locations', 'business', 'payment', 'methods' ],
			'business_area_served'         => [ 'locations', 'business', 'areaServed' ]
		];

		// Create the data array.
		$dataArray = [];
		foreach ( $locationData as $metaKey => $locationKeys ) {
			$metaValue = ! empty( $yoastMeta[ $metaKey ] ) ? $yoastMeta[ $metaKey ] : null;

			if ( ! empty( $metaValue ) ) {
				// Treat currencies.
				if ( 'business_currencies_accepted' === $metaKey ) {
					$metaValue = array_filter( explode( ',', $metaValue ) );
					$metaValue = $this->formatCurrencies( $metaValue );
					if ( ! empty( $metaValue['supported'] ) ) {
						$metaValue = wp_json_encode( $metaValue['supported'] );
					}
				}

				$metaValue = aioseo()->helpers->createMultidimensionalArray( $locationKeys, $metaValue );
				$dataArray = array_merge_recursive( $dataArray, $metaValue );
			}
		}

		// Opening hours.
		$dataArray['openingHours'] = [
			'useDefaults'  => false,
			'show'         => true,
			'use24hFormat' => 'on' === $yoastMeta['format_24h'],
			'alwaysOpen'   => 'on' === $yoastMeta['open_247'],
			'days'         => $this->formatOpeningHoursDays( $yoastMeta )
		];

		// Maps.
		if ( ! empty( $yoastMeta['coordinates_lat'] ) && ! empty( $yoastMeta['coordinates_long'] ) ) {
			$dataArray['maps'] = [
				'mapOptions'   => [
					'center' => [
						'lat' => $yoastMeta['coordinates_lat'],
						'lng' => $yoastMeta['coordinates_long']
					]
				],
				'customMarker' => ! empty( $yoastMeta['business_location_custom_marker'] ) ? $yoastMeta['business_location_custom_marker'] : ''
			];
		}

		// Check if the location already exists.
		$wpPost = aioseoLocalBusiness()->locations->getLocationByName( $yoastLocation->post_name );

		// Create the location if it doesn't exist.
		if ( empty( $wpPost ) ) {
			$wpPost            = clone $yoastLocation;
			$wpPost->ID        = null;
			$wpPost->post_type = aioseoLocalBusiness()->postType->getName();
			$wpPost            = wp_insert_post( $wpPost );
			$wpPost            = get_post( $wpPost );
		}

		// Set the location categories.
		$categories       = wp_get_post_terms( $yoastLocation->ID, 'wpseo_locations_category' );
		$mappedCategories = [];
		foreach ( $categories as $category ) {
			if ( ! is_object( $category ) ) {
				$category = (object) $category;
			}

			if ( empty( $category->term_id ) ) {
				continue;
			}

			$importedCategory = ! empty( $importedCategories[ $category->term_id ] ) ? $importedCategories[ $category->term_id ] : null;
			if ( $importedCategory ) {
				$mappedCategories[] = $importedCategory->term_id;
			}
		}

		wp_set_post_terms( $wpPost->ID, $mappedCategories, aioseoLocalBusiness()->taxonomy->getName() );

		// Set AIOSEO's location data.
		$aioseoLocation = Models\Post::getPost( $wpPost->ID );
		$aioseoLocation->local_seo = $dataArray;
		$aioseoLocation->save();
	}

	/**
	 * Imports the Local Business map settings.
	 *
	 * @since 1.3.0
	 *
	 * @return void
	 */
	private function importMapSettings() {
		if ( ! empty( $this->options['googlemaps_api_key'] ) ) {
			aioseo()->options->localBusiness->maps->apiKey = $this->options['googlemaps_api_key'];
			aioseo()->options->localBusiness->maps->apiKeyValid = true;
		}

		if ( ! empty( $this->options['map_view_style'] ) ) {
			aioseo()->options->localBusiness->maps->mapOptions->mapTypeId = strtolower( $this->options['map_view_style'] );
		}

		if ( ! empty( $this->options['location_coords_lat'] ) && ! empty( $this->options['location_coords_long'] ) ) {
			aioseo()->options->localBusiness->maps->mapOptions->center->lat = $this->options['location_coords_lat'];
			aioseo()->options->localBusiness->maps->mapOptions->center->lng = $this->options['location_coords_long'];
		}

		if ( ! empty( $this->options['local_custom_marker'] ) ) {
			aioseo()->options->localBusiness->maps->customMarker = wp_get_attachment_image_url( $this->options['local_custom_marker'], 'full' );
		}
	}
}app/Import/Importer.php000064400000001254151727277700011131 0ustar00<?php
namespace AIOSEO\Plugin\Addon\LocalBusiness\Import;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Imports local business info from other plugins.
 *
 * @since 1.3.0
 */
class Importer extends Helpers {
	/**
	 * The list of plugins.
	 *
	 * @since 1.3.0
	 *
	 * @var array
	 */
	protected $plugins = [];

	/**
	 * The post action name.
	 *
	 * @since 1.3.0
	 *
	 * @param Import $importer The main importer class.
	 */
	public function __construct( $importer ) {
		parent::__construct();

		$plugins = $this->plugins;
		foreach ( $plugins as $key => $plugin ) {
			$plugins[ $key ]['class'] = $this;
		}
		$importer->addPlugins( $plugins );
	}
}app/Import/Import.php000064400000004567151727277700010614 0ustar00<?php
namespace AIOSEO\Plugin\Addon\LocalBusiness\Import;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Handles the importing/exporting of settings and SEO data.
 *
 * @since 1.3.0
 */
class Import {
	/**
	 * Set up an array of plugins for importing.
	 *
	 * @since 1.3.0
	 *
	 * @var array
	 */
	private $plugins = [];

	/**
	 * Class constructor.
	 *
	 * @since 1.3.0
	 */
	public function __construct() {
		new Plugins\YoastSeo( $this );
		new Plugins\SeoPress( $this );
		new Plugins\RankMath( $this );
	}

	/**
	 * Starts an import.
	 *
	 * @since 1.3.0
	 *
	 * @param  string $plugin The slug of the plugin to import.
	 * @return void
	 */
	public function startImport( $plugin ) {
		foreach ( $this->plugins as $pluginData ) {
			if ( $pluginData['slug'] === $plugin ) {
				$pluginData['class']->doImport();
				do_action( 'aioseo_local_seo_imported', $plugin );

				return;
			}
		}
	}

	/**
	 * Adds plugins to the import.
	 *
	 * @since 1.3.0
	 *
	 * @param  array $plugins The plugins to add.
	 * @return void
	 */
	public function addPlugins( $plugins ) {
		$this->plugins = array_merge( $this->plugins, $plugins );
	}

	/**
	 * Get the plugins we allow importing from.
	 *
	 * @since 1.3.0
	 *
	 * @return array
	 */
	public function plugins() {
		require_once ABSPATH . 'wp-admin/includes/plugin.php';
		$plugins          = [];
		$installedPlugins = array_keys( get_plugins() );
		foreach ( $this->plugins as $importerPlugin ) {
			$data = [
				'slug'      => $importerPlugin['slug'],
				'name'      => $importerPlugin['name'],
				'version'   => null,
				'canImport' => false,
				'basename'  => $importerPlugin['basename'],
				'installed' => false,
				'activated' => false
			];

			if ( in_array( $importerPlugin['basename'], $installedPlugins, true ) ) {
				$pluginData = get_file_data( trailingslashit( WP_PLUGIN_DIR ) . $importerPlugin['basename'], [
					'name'    => 'Plugin Name',
					'version' => 'Version',
				] );

				$canImport = false;
				if ( version_compare( $importerPlugin['version'], $pluginData['version'], '<=' ) ) {
					$canImport = true;
				}

				$data['name']      = $pluginData['name'];
				$data['version']   = $pluginData['version'];
				$data['canImport'] = $canImport;
				$data['installed'] = true;
				$data['activated'] = is_plugin_active( $importerPlugin['basename'] );
			}

			$plugins[] = $data;
		}

		return $plugins;
	}
}app/LocalBusiness.php000064400000024011151727277700010620 0ustar00<?php
namespace AIOSEO\Plugin\Addon\LocalBusiness {
	// Exit if accessed directly.
	if ( ! defined( 'ABSPATH' ) ) {
		exit;
	}

	/**
	 * Main class.
	 *
	 * @since 1.0.0
	 */
	final class LocalBusiness {
		/**
		 * Holds the instance of the plugin currently in use.
		 *
		 * @since 1.0.0
		 *
		 * @var \AIOSEO\Plugin\Addon\LocalBusiness\LocalBusiness
		 */
		private static $instance = null;

		/**
		 * Plugin version for enqueueing, etc.
		 * The value is retrieved from the version constant.
		 *
		 * @since 1.0.0
		 *
		 * @var string
		 */
		public $version = '';

		/**
		 * Instance of the Admin class.
		 *
		 * @since 1.1.0
		 *
		 * @var Admin\Admin
		 */
		public $admin = null;

		/**
		 * Instance of the Usage class.
		 *
		 * @since 1.2.10
		 *
		 * @var Admin\Usage
		 */
		public $usage = null;

		/**
		 * Instance of the Locations class.
		 *
		 * @since 1.1.0
		 *
		 * @var Locations\Locations
		 */
		public $locations = null;

		/**
		 * Instance of the Shortcodes class.
		 *
		 * @since 1.1.0
		 *
		 * @var Shortcodes\Shortcodes
		 */
		public $shortcodes = null;

		/**
		 * Instance of the Location postType class.
		 *
		 * @since 1.1.0
		 *
		 * @var Admin\Location
		 */
		public $postType = null;

		/**
		 * Instance of the Location taxonomy class.
		 *
		 * @since 1.1.0
		 *
		 * @var Admin\LocationCategory
		 */
		public $taxonomy = null;

		/**
		 * Instance of the Blocks class containing all blocks.
		 *
		 * @since 1.1.0
		 *
		 * @var Blocks\Blocks
		 */
		public $blocks = null;

		/**
		 * Instance of the Widgets class registering all widgets.
		 *
		 * @since 1.1.0
		 *
		 * @var Widgets\Widgets
		 */
		public $widgets = null;

		/**
		 * Instance of the Schema class.
		 *
		 * @since 1.1.0
		 *
		 * @var Schema\Schema
		 */
		public $schema = null;

		/**
		 * Instance of the Tags class.
		 *
		 * @since 1.1.0
		 *
		 * @var Utils\Tags
		 */
		public $tags = null;

		/**
		 * Instance of the Helpers class.
		 *
		 * @since 1.1.0
		 *
		 * @var Utils\Helpers
		 */
		public $helpers = null;

		/**
		 * Instance of the Search class.
		 *
		 * @since 1.1.0
		 *
		 * @var Main\Search
		 */
		public $search = null;

		/**
		 * Instance of the Templates class.
		 *
		 * @since 1.1.0
		 *
		 * @var Utils\Templates
		 */
		public $templates = null;

		/**
		 * Instance of the Access class.
		 *
		 * @since 1.1.0
		 *
		 * @var Utils\Access
		 */
		public $access = null;

		/**
		 * Instance of the Activate class.
		 *
		 * @since 1.1.0
		 *
		 * @var Main\Activate
		 */
		public $activate = null;

		/**
		 * Instance of the Map class.
		 *
		 * @since 1.1.3
		 *
		 * @var Locations\Maps
		 */
		public $maps = null;

		/**
		 * Instance of the API class.
		 *
		 * @since 1.1.3
		 *
		 * @var Api\Api
		 */
		public $api = null;

		/**
		 * Whether we're in a dev environment.
		 *
		 * @since 1.2.4
		 *
		 * @var bool
		 */
		public $isDev = false;

		/**
		 * Cache class instance.
		 *
		 * @since 1.2.12
		 *
		 * @var Utils\Cache
		 */
		public $cache = null;

		/**
		 * Assets class instance.
		 *
		 * @since 1.2.12
		 *
		 * @var Utils\Assets
		 */
		public $assets = null;

		/**
		 * InternalOptions class instance.
		 *
		 * @since 1.2.12
		 *
		 * @var Utils\InternalOptions
		 */
		public $internalOptions = null;

		/**
		 * Updates class instance.
		 *
		 * @since 1.2.12
		 *
		 * @var Main\Updates
		 */
		public $updates = null;

		/**
		 * Import class instance.
		 *
		 * @since 1.3.0
		 *
		 * @var Import\Import
		 */
		public $import = null;

		/**
		 * Knowledge Graph Organization graph class instance.
		 *
		 * @since 1.3.3
		 *
		 * @var Schema\Graphs\KgOrganization
		 */
		public $kgOrganization;

		/**
		 * Main LocalBusiness Instance.
		 *
		 * Insures that only one instance of the addon exists in memory at any one
		 * time. Also prevents needing to define globals all over the place.
		 *
		 * @since 1.0.0
		 *
		 * @return \AIOSEO\Plugin\Addon\LocalBusiness\LocalBusiness
		 */
		public static function instance() {
			if ( null === self::$instance || ! self::$instance instanceof self ) {
				self::$instance = new self();
				self::$instance->constants();
				self::$instance->includes();
				self::$instance->load();

				add_action( 'init', [ self::$instance, 'registerStyles' ] );
			}

			return self::$instance;
		}

		/**
		 * Setup plugin constants.
		 * All the path/URL related constants are defined in main plugin file.
		 *
		 * @since 1.0.0
		 *
		 * @return void
		 */
		private function constants() {
			$defaultHeaders = [
				'name'    => 'Plugin Name',
				'version' => 'Version',
			];

			$pluginData = get_file_data( AIOSEO_LOCAL_BUSINESS_FILE, $defaultHeaders );

			$constants = [
				'AIOSEO_LOCAL_BUSINESS_VERSION' => $pluginData['version']
			];

			foreach ( $constants as $constant => $value ) {
				if ( ! defined( $constant ) ) {
					define( $constant, $value );
				}
			}

			$this->version = AIOSEO_LOCAL_BUSINESS_VERSION;
		}

		/**
		 * Including the new files with PHP 5.3 style.
		 *
		 * @since 1.0.0
		 *
		 * @return void
		 */
		private function includes() {
			$dependencies = [
				'/vendor/autoload.php'
			];

			foreach ( $dependencies as $path ) {
				if ( ! file_exists( AIOSEO_LOCAL_BUSINESS_DIR . $path ) ) {
					// Something is not right.
					status_header( 500 );
					wp_die( esc_html__( 'Plugin is missing required dependencies. Please contact support for more information.', 'aioseo-local-business' ) );
				}
				require AIOSEO_LOCAL_BUSINESS_DIR . $path;
			}

			$this->setDev();
		}

		/**
		 * Load the version of the plugin we are currently using.
		 *
		 * @since 1.2.4
		 *
		 * @return void
		 */
		private function setDev() {
			if (
				! class_exists( '\Dotenv\Dotenv' ) ||
				! file_exists( AIOSEO_LOCAL_BUSINESS_DIR . '/build/.env' )
			) {
				return;
			}

			$dotenv = \Dotenv\Dotenv::createUnsafeImmutable( AIOSEO_LOCAL_BUSINESS_DIR, '/build/.env' );
			$dotenv->load();

			$domain = strtolower( getenv( 'VITE_AIOSEO_LOCAL_BUSINESS_DOMAIN' ) );
			if ( ! empty( $domain ) ) {
				$this->isDev = true;
			}
		}

		/**
		 * Register styles.
		 *
		 * @since 1.1.0
		 *
		 * @return void
		 */
		public function registerStyles() {
			aioseoLocalBusiness()->assets->registerCss( 'src/assets/scss/business-info.scss' );
			aioseoLocalBusiness()->assets->registerCss( 'src/assets/scss/opening-hours.scss' );
		}

		/**
		 * Load our classes.
		 *
		 * @since 1.0.0
		 *
		 * @return void
		 */
		public function load() {
			aioseo()->helpers->loadTextDomain( 'aioseo-local-business' );

			$this->cache           = new Utils\Cache();
			$this->assets          = new Utils\Assets();
			$this->internalOptions = new Utils\InternalOptions();
			$this->updates         = new Main\Updates();
			$this->admin           = new Admin\Admin();
			$this->usage           = new Admin\Usage();
			$this->postType        = new Admin\Location();
			$this->taxonomy        = new Admin\LocationCategory();
			$this->locations       = new Locations\Locations();
			$this->maps            = new Locations\Maps();
			$this->shortcodes      = new Shortcodes\Shortcodes();
			$this->blocks          = new Blocks\Blocks();
			$this->widgets         = new Widgets\Widgets();
			$this->tags            = new Utils\Tags();
			$this->helpers         = new Utils\Helpers();
			$this->search          = new Main\Search();
			$this->templates       = new Utils\Templates();
			$this->access          = new Utils\Access();
			$this->schema          = new Schema\Schema();
			$this->activate        = new Main\Activate();
			$this->api             = new Api\Api();
			$this->import          = new Import\Import();
			$this->kgOrganization  = new Schema\Graphs\KgOrganization();

			// Load into main aioseo instance.
			aioseo()->addons->loadAddon( 'localBusiness', $this );
		}
	}
}

namespace {
	// Exit if accessed directly.
	if ( ! defined( 'ABSPATH' ) ) {
		exit;
	}

	/**
	 * The function which returns the one LocalBusiness instance.
	 *
	 * @since 1.0.0
	 *
	 * @return \AIOSEO\Plugin\Addon\LocalBusiness\LocalBusiness
	 */
	function aioseoLocalBusiness() {
		return \AIOSEO\Plugin\Addon\LocalBusiness\LocalBusiness::instance();
	}

	if ( ! function_exists( 'aioseo_local_business_info' ) ) {
		/**
		 * Global function for business info output.
		 *
		 * @param  array $args
		 * @return void
		 */
		function aioseo_local_business_info( $args = [] ) {
			$shortcodeArgs = [];
			foreach ( $args as $key => $value ) {
				$shortcodeArgs[ aioseo()->helpers->toSnakeCase( $key ) ] = $value;
			}

			echo aioseoLocalBusiness()->shortcodes->businessInfo( $shortcodeArgs ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
		}
	}

	if ( ! function_exists( 'aioseo_local_opening_hours' ) ) {
		/**
		 * Global function for opening hours output.
		 *
		 * @param  array $args Opening hours arguments.
		 * @return void
		 */
		function aioseo_local_opening_hours( $args = [] ) {
			$shortcodeArgs = [];
			foreach ( $args as $key => $value ) {
				$shortcodeArgs[ aioseo()->helpers->toSnakeCase( $key ) ] = $value;
			}

			echo aioseoLocalBusiness()->shortcodes->openingHours( $shortcodeArgs ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
		}
	}

	if ( ! function_exists( 'aioseo_local_locations' ) ) {
		/**
		 * Global function for locations output.
		 *
		 * @param  array $args Locations arguments.
		 * @return void
		 */
		function aioseo_local_locations( $args = [] ) {
			$shortcodeArgs = [];
			foreach ( $args as $key => $value ) {
				$shortcodeArgs[ aioseo()->helpers->toSnakeCase( $key ) ] = $value;
			}

			echo aioseoLocalBusiness()->shortcodes->locations( $shortcodeArgs ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
		}
	}

	if ( ! function_exists( 'aioseo_local_map' ) ) {
		/**
		 * Global function for locations output.
		 *
		 * @param  array $args Locations arguments.
		 * @return void
		 */
		function aioseo_local_map( $args = [] ) {
			$shortcodeArgs = [];
			foreach ( $args as $key => $value ) {
				$shortcodeArgs[ aioseo()->helpers->toSnakeCase( $key ) ] = $value;
			}

			echo aioseoLocalBusiness()->shortcodes->map( $shortcodeArgs ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
		}
	}
}app/Api/Maps.php000064400000002710151727277710007466 0ustar00<?php
namespace AIOSEO\Plugin\Addon\LocalBusiness\Api;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Map class for the API.
 *
 * @since 1.1.3
 */
class Maps {
	/**
	 * Check if an API key has access to a library.
	 *
	 * @since 1.1.3
	 *
	 * @param  \WP_REST_Request  $request The REST Request
	 * @return \WP_REST_Response          The response.
	 */
	public static function checkApiAccess( $request ) {
		$params = $request->get_json_params();

		if ( empty( $params['apiKey'] ) || empty( $params['apiName'] ) ) {
			return new \WP_REST_Response( [
				'success' => false
			], 400 );
		}

		$updateOption = false;
		switch ( $params['apiName'] ) {
			case 'places/embed':
				$updateOption = 'mapsEmbedApiEnabled';
				$url          = add_query_arg( [
					'key' => $params['apiKey'],
					'q'   => 'New+York'
				], 'https://www.google.com/maps/embed/v1/place' );
				break;
		}

		if ( empty( $url ) ) {
			return new \WP_REST_Response( [
				'success' => false
			], 400 );
		}

		$checkApiAccess = wp_remote_get( $url );

		if ( 200 === wp_remote_retrieve_response_code( $checkApiAccess ) ) {
			if ( $updateOption ) {
				aioseo()->options->localBusiness->maps->{$updateOption} = true;
			}

			return new \WP_REST_Response( [
				'success' => true
			], 200 );
		}

		if ( $updateOption ) {
			aioseo()->options->localBusiness->maps->{$updateOption} = false;
		}

		return new \WP_REST_Response( [
			'success' => false
		], 400 );
	}
}app/Api/Api.php000064400000002206151727277710007277 0ustar00<?php
namespace AIOSEO\Plugin\Addon\LocalBusiness\Api;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Api class for the admin.
 *
 * @since 1.1.3
 */
class Api {
	/**
	 * The routes we use in the rest API.
	 *
	 * @since 1.1.3
	 *
	 * @var array
	 */
	protected $routes = [
		// phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
		// phpcs:disable Generic.Files.LineLength.MaxExceeded
		'POST' => [
			'local-business/maps/check-api-enabled' => [
				'callback' => [ 'Maps', 'checkApiAccess', 'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Api' ],
				'access'   => 'aioseo_local_seo_settings'
			],
			'local-business/import-plugins'         => [
				'callback' => [ 'Import', 'importPlugins', 'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Api' ],
				'access'   => 'aioseo_local_seo_settings'
			]
		]
		// phpcs:enable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
		// phpcs:enable Generic.Files.LineLength.MaxExceeded
	];

	/**
	 * Get all the routes to register.
	 *
	 * @since 1.1.3
	 *
	 * @return array An array of routes.
	 */
	public function getRoutes() {
		return $this->routes;
	}
}app/Api/Import.php000064400000001633151727277710010043 0ustar00<?php
namespace AIOSEO\Plugin\Addon\LocalBusiness\Api;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Import class for the API.
 *
 * @since 1.3.0
 */
class Import {
	/**
	 * Import data from a plugin.
	 *
	 * @since 1.3.0
	 *
	 * @param  \WP_REST_Request  $request The REST Request
	 * @return \WP_REST_Response          The response.
	 */
	public static function importPlugins( $request ) {
		$plugins = $request->get_json_params();

		if ( empty( $plugins ) || ! is_array( $plugins ) ) {
			return new \WP_REST_Response( [
				'success' => false
			], 400 );
		}

		foreach ( $plugins as $plugin ) {
			if ( empty( $plugin['plugin'] ) ) {
				continue;
			}

			aioseoLocalBusiness()->import->startImport( $plugin['plugin'] );
		}

		return new \WP_REST_Response( [
			'success'              => true,
			'localBusinessOptions' => aioseo()->options->localBusiness->all(),
		], 200 );
	}
}app/Locations/Maps.php000064400000007026151727277710010715 0ustar00<?php
namespace AIOSEO\Plugin\Addon\LocalBusiness\Locations;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * The Maps class.
 *
 * @since 1.1.3
 */
class Maps {
	/**
	 * Map load event.
	 *
	 * @since 1.1.3
	 *
	 * @var string
	 */
	public $mapLoadEvent = 'aioseo-local-map-load';

	/**
	 * Class constructor.
	 *
	 * @since 1.1.3
	 */
	public function __construct() {
		add_action( 'init', [ $this, 'init' ] );
		add_action( 'admin_enqueue_scripts', [ $this, 'initAdmin' ] );
	}

	/**
	 * Init the class by registering our scripts.
	 *
	 * @since 1.1.3
	 *
	 * @return void
	 */
	public function init() {
		// Here we just register scripts and styles which will be enqueued when the block is used.
		$this->registerScripts();
		$this->registerStyles();
	}

	/**
	 * Init the class by registering our scripts and enqueuing if we're on Gutenberg.
	 *
	 * @since 1.1.3
	 *
	 * @return void
	 */
	public function initAdmin() {
		// Enqueuing from the block rendering does not work in the admin.
		// So we enqueue our scripts here if we're in the block editor for a nice map preview.
		$screen = aioseo()->helpers->getCurrentScreen();
		if ( method_exists( $screen, 'is_block_editor' ) && $screen->is_block_editor() ) {
			$this->enqueues();
		}
	}

	/**
	 * Register scripts.
	 *
	 * @since 1.1.3
	 *
	 * @return void
	 */
	public function registerScripts() {
		aioseoLocalBusiness()->assets->registerJs( 'src/js-api-loader.js', [], null, [] );

		aioseoLocalBusiness()->assets->registerJs( 'src/map.js', [], [
			'apiKey'       => aioseo()->options->localBusiness->maps->apiKey,
			'mapLoadEvent' => $this->mapLoadEvent
		], 'aioseoMapOptions' );
	}

	/**
	 * Registers styles.
	 *
	 * @since 1.1.3
	 *
	 * @return void
	 */
	public function registerStyles() {
		aioseoLocalBusiness()->assets->registerCss( 'src/assets/scss/location-map.scss' );
	}

	/**
	 * Enqueues needed scripts and styles.
	 *
	 * @since 1.1.3
	 *
	 * @return void
	 */
	public function enqueues() {
		aioseoLocalBusiness()->assets->enqueueCss( 'src/assets/scss/location-map.scss' );
		aioseoLocalBusiness()->assets->enqueueJs( 'src/js-api-loader.js' );
		aioseoLocalBusiness()->assets->enqueueJs( 'src/map.js' );
	}

	/**
	 * Adds inline script to start a map.
	 *
	 * @since 1.1.3
	 *
	 * @param  string $data Data to be encoded.
	 * @return void
	 */
	public function mapStartEvent( $data ) {
		$data = wp_json_encode( $data );

		wp_add_inline_script( aioseoLocalBusiness()->assets->jsHandle( 'src/map.js' ), "
			document.dispatchEvent(new CustomEvent('{$this->mapLoadEvent}', {
				detail : $data
			}))
			"
		);
	}

	/**
	 * Adds map information in the Rest API for a Location.
	 *
	 * @since 1.1.3
	 *
	 * @param  object      $object The rest object.
	 * @return object|null         Map information.
	 */
	public function restMapInfo( $object ) {
		$location = aioseoLocalBusiness()->locations->getLocation( $object['id'] );

		if ( empty( $location->maps ) ) {
			return null;
		}

		$location->maps->infoWindowContent = $this->getMarkerInfoWindow( $location );

		return $location->maps;
	}

	/**
	 * Returns the template for the marker's info window.
	 *
	 * @since 1.1.3
	 *
	 * @param  object $locationData The location data.
	 * @return string               Marker's info window template.
	 */
	public function getMarkerInfoWindow( $locationData ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
		$template = aioseoLocalBusiness()->templates->locateTemplate( 'MapMarkerInfoWindow.php' );

		ob_start();

		require $template;

		return ob_get_clean();
	}
}app/Locations/Locations.php000064400000031647151727277710011756 0ustar00<?php
namespace AIOSEO\Plugin\Addon\LocalBusiness\Locations;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

use AIOSEO\Plugin\Common\Models;

/**
 * The Locations class.
 *
 * @since 1.1.0
 */
class Locations {
	/**
	 * Returns all locations.
	 *
	 * @since 1.1.0
	 *
	 * @param  array $args Query args to be passed down to WP_Query.
	 * @return array       An array of WP_Post.
	 */
	public function getLocations( $args = [] ) {
		$args = wp_parse_args( $args, [
			'post_type'              => aioseoLocalBusiness()->postType->getName(),
			'no_found_rows'          => true,
			'update_post_meta_cache' => false,
			'update_post_term_cache' => false,
			'posts_per_page'         => 100,
			'post_status'            => 'publish'
		] );

		$args = apply_filters( 'aioseo_local_business_get_locations_args', $args );

		$posts = new \WP_Query( $args );

		return apply_filters( 'aioseo_local_business_get_locations_posts', $posts->posts, $args );
	}

	/**
	 * Returns the JSON data for the given location.
	 *
	 * @since 1.1.0
	 *
	 * @param  integer      $postId The post id.
	 * @return false|object         The decoded JSON data.
	 */
	public function getLocation( $postId ) {
		if ( ! get_post( $postId ) ) {
			return false;
		}

		$post = Models\Post::getPost( $postId );

		return apply_filters( 'aioseo_local_business_get_location', $post->local_seo, $postId );
	}

	/**
	 * Returns the locations categories.
	 *
	 * @since 1.1.0
	 *
	 * @return array List of category objects.
	 */
	public function getLocationCategories() {
		$args = apply_filters( 'aioseo_local_business_get_location_category_args', [
			'taxonomy' => aioseoLocalBusiness()->taxonomy->getName(),
			'orderby'  => 'name'
		] );

		return apply_filters( 'aioseo_local_business_get_location_categories', get_categories( $args ), $args );
	}

	/**
	 * Returns locations by category.
	 *
	 * @since 1.1.0
	 *
	 * @param  integer $termId The term id.
	 * @param  array   $args   Query args to be passed down to WP_Query.
	 * @return array           An array of WP_Post.
	 */
	public function getLocationsByCategory( $termId, $args = [] ) {
		$args = wp_parse_args( $args, [
			'post_type'      => aioseoLocalBusiness()->postType->getName(),
			'post_status'    => 'publish',
			'posts_per_page' => 100,
			'tax_query'      => [ // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
				[
					'taxonomy' => aioseoLocalBusiness()->taxonomy->getName(),
					'field'    => 'term_id',
					'terms'    => $termId
				],
			]
		] );

		$args = apply_filters( 'aioseo_local_business_get_locations_by_category_args', $args, $termId );

		$posts = new \WP_Query( $args );

		return apply_filters( 'aioseo_local_business_get_locations_by_category_posts', $posts->posts, $args, $termId );
	}

	/**
	 * Outputs the business info.
	 *
	 * @since 1.1.0
	 *
	 * @param  integer|string $postId   The location ID or 'global' keyword.
	 * @param  array          $instance An array of attributes used by the view.
	 * @return void
	 */
	public function outputBusinessInfo( $postId, $instance = [] ) {
		$postId = $this->maybeGlobalLocationId( $postId );

		if ( ! $this->canLocationIdRender( $postId ) ) {
			return;
		}

		$locationData = $this->getLocation( $postId );
		if ( 'global' === $postId ) {
			$locationData = aioseoLocalBusiness()->helpers->getLocalBusinessOptions();
		}

		$locationData = ! empty( $locationData->locations->business ) ? $locationData->locations->business : null;

		$locationData = ! empty( $instance['dataObject'] ) ? json_decode( $instance['dataObject'] ) : $locationData;

		if ( empty( $locationData ) ) {
			return;
		}

		// Parse defaults.
		$instance = wp_parse_args( $instance, [
			'class'           => '',
			'showLabels'      => true,
			'showIcons'       => true,
			'showAddress'     => true,
			'showName'        => true,
			'showVat'         => true,
			'showTax'         => true,
			'showChamberId'   => true,
			'showPhone'       => true,
			'showFax'         => true,
			'showCountryCode' => true,
			'showEmail'       => true,
			'addressFormat'   => '#streetLine1, #streetLine2 #newLine #zipCode - #city - #state #newLine #country',
			'addressLabel'    => __( 'Address:', 'aioseo-local-business' ),
			'emailLabel'      => __( 'Email:', 'aioseo-local-business' ),
			'phoneLabel'      => __( 'Phone:', 'aioseo-local-business' ),
			'faxLabel'        => __( 'Fax:', 'aioseo-local-business' ),
			'vatIdLabel'      => __( 'VAT ID:', 'aioseo-local-business' ),
			'taxIdLabel'      => __( 'Tax ID:', 'aioseo-local-business' )
		] );

		$instance['class'] .= $instance['showLabels'] ? '' : ' hide-label ';
		$instance['class'] .= $instance['showIcons'] ? '' : ' hide-icon ';

		$instance     = apply_filters( 'aioseo_local_business_output_business_info_instance', $instance, $postId, $locationData );
		$locationData = apply_filters( 'aioseo_local_business_output_business_info_location_data', $locationData, $instance, $postId );

		aioseoLocalBusiness()->assets->enqueueCss( 'src/assets/scss/business-info.scss' );

		$template = aioseoLocalBusiness()->templates->locateTemplate( 'BusinessInfo.php' );

		require $template;
	}

	/**
	 * Outputs a list of locations by category.
	 *
	 * @since 1.1.0
	 *
	 * @param  integer $termId   The term ID.
	 * @param  array   $instance An array of attributes used by the view.
	 * @return void
	 */
	public function outputLocationCategory( $termId, $instance = [] ) {
		if ( ! $this->canLocationIdRender( $termId ) ) {
			return;
		}

		$locations = $this->getLocationsByCategory( $termId );
		if ( empty( $locations ) ) {
			return sprintf(
				// Translators: 1 - The post type plural label.
				__( 'No %1$s found', 'aioseo-local-business' ),
				aioseoLocalBusiness()->postType->getPluralLabel()
			);
		}

		$instance = wp_parse_args( $instance, [
			'class' => '',
		] );

		$instance  = apply_filters( 'aioseo_local_business_output_location_category_instance', $instance, $termId, $locations );
		$locations = apply_filters( 'aioseo_local_business_output_location_category_location_data', $locations, $instance, $termId );

		$template = aioseoLocalBusiness()->templates->locateTemplate( 'Locations.php' );

		require $template;
	}

	/**
	 * Outputs the opening hours.
	 *
	 * @since 1.1.0
	 *
	 * @param  integer|string $postId   The location ID or 'global' keyword.
	 * @param  array          $instance An array of attributes used by the view.
	 * @return void
	 */
	public function outputOpeningHours( $postId, $instance = [] ) {
		$postId = $this->maybeGlobalLocationId( $postId );

		if ( ! $this->canLocationIdRender( $postId ) ) {
			return;
		}

		$locationData = $this->getLocation( $postId );

		$openingHoursData = ! empty( $instance['dataObject'] ) ?
			json_decode( $instance['dataObject'] ) :
			( ! empty( $locationData->openingHours ) ? $locationData->openingHours : null );

		if ( 'global' === $postId || ! empty( $openingHoursData->useDefaults ) ) {
			$locationData = aioseoLocalBusiness()->helpers->getLocalBusinessOptions();
			$openingHoursData = $locationData->openingHours;
		}

		if ( empty( $openingHoursData->show ) ) {
			if ( aioseo()->blocks->isGBEditor() ) {
				return __( 'Your Opening Hours settings are disabled. Please enable them to use the Opening Hours block.', 'aioseo-local-business' );
			}

			return;
		}

		// Parse defaults.
		$instance = wp_parse_args( $instance, [ // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
			'class'         => '',
			'showTitle'     => true,
			'showIcons'     => true,
			'showMonday'    => true,
			'showTuesday'   => true,
			'showWednesday' => true,
			'showThursday'  => true,
			'showFriday'    => true,
			'showSaturday'  => true,
			'showSunday'    => true
		] );

		$instance         = apply_filters( 'aioseo_local_business_output_opening_hours_instance', $instance, $postId, $openingHoursData );
		$openingHoursData = apply_filters( 'aioseo_local_business_output_opening_hours_data', $openingHoursData, $instance, $postId );

		aioseoLocalBusiness()->assets->enqueueCss( 'src/assets/scss/opening-hours.scss' );

		$template = aioseoLocalBusiness()->templates->locateTemplate( 'OpeningHours.php' );

		require $template;
	}

	/**
	 * Outputs a list of categories.
	 *
	 * @since 1.1.1
	 *
	 * @param  array $instance An array of attributes used by the view.
	 * @return void
	 */
	public function outputLocationCategories( $instance = [] ) {
		$categories = $this->getLocationCategories();
		if ( empty( $categories ) ) {
			return sprintf(
				// Translators: 1 - The post type plural label.
				__( 'No %1$s found', 'aioseo-local-business' ),
				aioseoLocalBusiness()->taxonomy->getPluralLabel()
			);
		}

		$instance = wp_parse_args( $instance, [
			'class' => '',
		] );

		$instance   = apply_filters( 'aioseo_local_business_output_location_categories_instance', $instance );
		$categories = apply_filters( 'aioseo_local_business_output_location_categories', $categories, $instance );

		$template = aioseoLocalBusiness()->templates->locateTemplate( 'LocationCategories.php' );

		require $template;
	}

	/**
	 * Outputs a map.
	 *
	 * @since 1.1.3
	 *
	 * @param  array $instance An array of attributes used by the view.
	 * @return void
	 */
	public function outputLocationMap( $postId, $instance = [] ) {
		$postId = $this->maybeGlobalLocationId( $postId );
		if ( ! $this->canLocationIdRender( $postId ) ) {
			return;
		}

		$locationData = $this->getLocation( $postId );
		if ( 'global' === $postId ) {
			$locationData = aioseoLocalBusiness()->helpers->getLocalBusinessOptions();
		}

		$locationMapData = ! empty( $locationData->maps ) ? $locationData->maps : null;

		$locationMapData = ! empty( $instance['dataObject'] ) ? json_decode( $instance['dataObject'] ) : $locationMapData;

		if ( empty( $locationMapData ) ) {
			return;
		}

		// Parse defaults.
		$instance = wp_parse_args( $instance, [
			'class'      => '',
			'showLabels' => true,
			'showIcons'  => true,
			'width'      => '100%',
			'height'     => '450px'
		] );

		$instance['class'] .= $instance['showLabels'] ? '' : ' hide-label ';
		$instance['class'] .= $instance['showIcons'] ? '' : ' hide-icon ';

		$instance        = apply_filters( 'aioseo_local_business_output_location_map_instance', $instance, $postId, $locationData );
		$locationMapData = apply_filters( 'aioseo_local_business_output_location_map_data', $locationMapData, $instance, $postId );

		$instance['mapId'] = uniqid( 'aioseo-local-map-' );

		$customMarker = ! empty( $instance['customMarker'] ) ? $instance['customMarker'] : $locationMapData->customMarker;

		aioseoLocalBusiness()->maps->enqueues();

		// Frontend only.
		if ( ! is_admin() ) {
			aioseoLocalBusiness()->maps->mapStartEvent( [
				'element'           => '#' . $instance['mapId'],
				'mapOptions'        => $locationMapData->mapOptions,
				'customMarker'      => ! empty( $customMarker ) ? $customMarker : aioseo()->options->localBusiness->maps->customMarker,
				'placeId'           => aioseo()->options->localBusiness->maps->mapsEmbedApiEnabled ? $locationMapData->placeId : null,
				'instance'          => $instance,
				'infoWindowContent' => aioseoLocalBusiness()->maps->getMarkerInfoWindow( $locationData )
			] );
		}

		$template = aioseoLocalBusiness()->templates->locateTemplate( 'Map.php' );

		require $template;
	}

	/**
	 * Determine if we should be loading our global information.
	 *
	 * @since 1.1.0
	 *
	 * @param  void|string|integer $locationId The post ID.
	 * @return mixed|string                    The post ID or 'global'.
	 */
	private function maybeGlobalLocationId( $locationId ) {
		if ( empty( $locationId ) && ! aioseo()->options->localBusiness->locations->general->multiple ) {
			$locationId = 'global';
		}

		return $locationId;
	}

	/**
	 * Returns if a given location ID is allowed to render.
	 *
	 * @since 1.1.0
	 *
	 * @param  string|integer|null $locationId The postId.
	 * @return bool                            Should the location be rendered.
	 */
	private function canLocationIdRender( $locationId = null ) {
		if (
			( aioseo()->options->localBusiness->locations->general->multiple && 'global' === $locationId ) ||
			! aioseo()->options->localBusiness->locations->general->multiple && 'global' !== $locationId
		) {
			return false;
		}

		return true;
	}

	/**
	 * Formats a phone number with the country code.
	 *
	 * @since 1.1.0
	 *
	 * @param  string $phoneNumber     The phone number.
	 * @param  bool   $keepCountryCode Keep the country code.
	 * @return string                  Formatted phone number.
	 */
	public function formatPhone( $phoneNumber, $keepCountryCode = true ) {
		return $keepCountryCode ? $phoneNumber : preg_replace( '/^\+[0-9]+\s/', '', $phoneNumber );
	}

	/**
	 * Finds a location based on the name.
	 *
	 * @since 1.3.0
	 *
	 * @param  string $locationName The location name.
	 * @return array                The location found.
	 */
	public function getLocationByName( $locationName, $status = 'any' ) {
		$foundLocations = $this->getLocations( [
			'name'        => sanitize_title( $locationName ),
			'post_status' => $status
		] );

		return is_array( $foundLocations ) ? current( $foundLocations ) : [];
	}
}app/Shortcodes/Shortcodes.php000064400000011510151727277710012305 0ustar00<?php
namespace AIOSEO\Plugin\Addon\LocalBusiness\Shortcodes;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * The Shortcodes class.
 *
 * @since 1.1.0
 */
class Shortcodes {
	/**
	 * The shortcodes.
	 *
	 * @since 1.1.0
	 *
	 * @var string Array of shortcodes and callbacks.
	 */
	protected $shortcodes = [
		'aioseo_local_business_info' => 'businessInfo',
		'aioseo_local_locations'     => 'locations',
		'aioseo_local_opening_hours' => 'openingHours',
		'aioseo_local_map'           => 'map'
	];

	/**
	 * Class constructor.
	 *
	 * @since 1.1.0
	 */
	public function __construct() {
		foreach ( $this->shortcodes as $shortcode => $callback ) {
			add_shortcode( $shortcode, [ $this, $callback ] );
		}
	}

	/**
	 * Business info shortcode. Displays a location's business information.
	 *
	 * @since 1.1.0
	 *
	 * @param  array  $attributes The shortcode attributes.
	 * @return string             The rendered shortcode.
	 */
	public function businessInfo( $attributes = [] ) {
		$defaults = [
			'location_id'       => '',
			'show_labels'       => true,
			'show_icons'        => true,
			'show_name'         => true,
			'show_address'      => true,
			'show_phone'        => true,
			'show_fax'          => true,
			'show_country_code' => true,
			'show_email'        => true,
			'show_vat'          => true,
			'show_tax'          => true,
			'show_chamber_id'   => true,
			'address_label'     => __( 'Address:', 'aioseo-local-business' ),
			'vat_id_label'      => __( 'VAT ID:', 'aioseo-local-business' ),
			'tax_id_label'      => __( 'Tax ID:', 'aioseo-local-business' ),
			'phone_label'       => __( 'Phone:', 'aioseo-local-business' ),
			'fax_label'         => __( 'Fax:', 'aioseo-local-business' ),
			'email_label'       => __( 'Email:', 'aioseo-local-business' )
		];

		$attributes = $this->convertSnakeCaseAttributes( $attributes, $defaults );

		ob_start();

		aioseoLocalBusiness()->locations->outputBusinessInfo( absint( $attributes['locationId'] ), $attributes );

		return ob_get_clean();
	}

	/**
	 * Locations shortcode. Displays a list of locations based on a location category.
	 *
	 * @since 1.1.0
	 *
	 * @param  array  $attr The shortcode attributes.
	 * @return string       The rendered shortcode.
	 */
	public function locations( $attr = [] ) {
		$attr = shortcode_atts( [ 'category_id' => '' ], $attr, 'aioseo_locations' );

		$attributes = [ 'categoryId' => $attr['category_id'] ];

		ob_start();

		aioseoLocalBusiness()->locations->outputLocationCategory( absint( $attributes['categoryId'] ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped

		return ob_get_clean();
	}

	/**
	 * Opening hours shortcode. Displays a location's opening hours.
	 *
	 * @since 1.1.0
	 *
	 * @param  array  $attributes The shortcode attributes.
	 * @return string             The rendered shortcode.
	 */
	public function openingHours( $attributes = [] ) {
		$defaults = [
			'location_id'    => '',
			'show_title'     => true,
			'show_icons'     => true,
			'show_monday'    => true,
			'show_tuesday'   => true,
			'show_wednesday' => true,
			'show_thursday'  => true,
			'show_friday'    => true,
			'show_saturday'  => true,
			'show_sunday'    => true,
			'label'          => __( 'Our Opening Hours:', 'aioseo-local-business' )
		];

		$attributes = $this->convertSnakeCaseAttributes( $attributes, $defaults );

		ob_start();

		aioseoLocalBusiness()->locations->outputOpeningHours( absint( $attributes['locationId'] ), $attributes ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped

		return ob_get_clean();
	}

	/**
	 * Map shortcode. Displays a location's map.
	 *
	 * @since 1.1.3
	 *
	 * @param  array  $attributes The shortcode attributes.
	 * @return string             The rendered shortcode.
	 */
	public function map( $attributes = [] ) {
		$defaults = [
			'location_id' => '',
			'show_label'  => true,
			'show_icon'   => true,
			'width'       => '100%',
			'height'      => '450px',
			'label'       => __( 'Our location:', 'aioseo-local-business' )
		];

		$attributes = $this->convertSnakeCaseAttributes( $attributes, $defaults );

		ob_start();

		aioseoLocalBusiness()->locations->outputLocationMap( absint( $attributes['locationId'] ), $attributes );

		return ob_get_clean();
	}

	/**
	 * Convert snake case attributes to camel case.
	 *
	 * @since 4.1.5
	 *
	 * @param  array $attributes The attributes.
	 * @param  array $defaults   The defaults.
	 * @return array             Converted attributes.
	 */
	private function convertSnakeCaseAttributes( $attributes, $defaults = [] ) {
		$attributes = wp_parse_args( $attributes, $defaults );

		$convertedAttributes = [];
		foreach ( $attributes as $key => $value ) {
			$convertedAttributes[ aioseo()->helpers->toCamelCase( $key ) ] = is_bool( $defaults[ $key ] )
				? filter_var( $value, FILTER_VALIDATE_BOOLEAN )
				: $value;
		}

		return $convertedAttributes;
	}
}app/Views/LocationCategories.php000064400000001074151727277710012732 0ustar00<?php
/**
 * This is the output for Local Business - Locations by Category on the frontend.
 *
 * @since 1.1.1
 * @version 1.1.1
 */

// phpcs:disable VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}
?>
<ul class="aioseo-location-categories <?php echo esc_attr( $instance['class'] ); ?>">
	<?php foreach ( $categories as $category ) { ?>
		<li>
			<a href="<?php echo esc_attr( get_term_link( $category ) ); ?>"><?php echo esc_html( $category->name ); ?></a>
		</li>
	<?php } ?>
</ul>app/Views/MapMarkerInfoWindow.php000064400000002440151727277710013035 0ustar00<?php
/**
 * This is the output for Local Business - Map Info Window on the frontend.
 *
 * @since 1.1.3
 * @version 1.2.1
 */

// phpcs:disable VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

$businessData = ! empty( $locationData->locations->business ) ? $locationData->locations->business : null;
?>
<div class="marker-info-window">
	<?php if ( $businessData ) : ?>
		<?php if ( ! empty( $businessData->name ) ) : ?>
			<div class="name">
				<?php echo esc_html( $businessData->name ) ?>
			</div>
		<?php endif; ?>
		<?php
		$haveAddress = array_filter( (array) $businessData->address );
		unset( $haveAddress['addressFormat'] );
		if ( ! empty( $haveAddress ) ) :
			?>
			<div class="address">
				<?php
				$value = aioseoLocalBusiness()->tags->replaceTags( aioseo()->helpers->decodeHtmlEntities( $businessData->address->addressFormat ), null, $businessData->address );
				echo nl2br( wp_kses_post( $value ) );
				?>
			</div>
		<?php endif; ?>
		<?php if ( ! empty( $businessData->contact->phoneFormatted ) ) : ?>
			<div class="telephone">
				<?php echo esc_html( aioseoLocalBusiness()->locations->formatPhone( $businessData->contact->phoneFormatted ) ); ?>
			</div>
		<?php endif; ?>
	<?php endif; ?>
</div>app/Views/Map.php000064400000003754151727277720007701 0ustar00<?php
/**
 * This is the output for Local Business - Map on the frontend.
 *
 * @since 1.1.3
 * @version 1.1.3
 */

// phpcs:disable VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

$mapStyle = [
	'width'  => is_numeric( $instance['width'] ) ? $instance['width'] . 'px' : $instance['width'],
	'height' => is_numeric( $instance['height'] ) ? $instance['height'] . 'px' : $instance['height']
];

$mapStyle = array_reduce( array_keys( $mapStyle ), function ( $carry, $key ) use ( $mapStyle ) {
	return $carry . $key . ': ' . $mapStyle[ $key ] . '; ';
} );
?>
<div class="aioseo-location-map <?php echo esc_attr( $instance['class'] ); ?>">
	<?php
	$svgIcon = apply_filters( 'aioseo_local_business_map_icon', '<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M11 11.14L9.83 5.2C9.04 4.77 8.5 3.95 8.5 3C8.5 1.62 9.62 0.499999 11 0.499999C12.38 0.499999 13.5 1.62 13.5 3C13.5 3.95 12.96 4.77 12.17 5.2L11 11.14ZM11 1.5C10.17 1.5 9.5 2.17 9.5 3C9.5 3.83 10.17 4.5 11 4.5C11.83 4.5 12.5 3.83 12.5 3C12.5 2.17 11.83 1.5 11 1.5ZM12.72 6.3L11 13.68L9.27 6.38L5 4.97L2.98023e-08 6.97V15.97L5 13.97L11.12 16L16 13.97V4.97L12.72 6.3Z" fill="currentcolor"/></svg>' ); // phpcs:ignore Generic.Files.LineLength.MaxExceeded
	?>
	<div class="d-flex">
		<?php if ( $instance['showIcon'] ) : ?>
			<div class="col-auto">
				<span class="icon map">
					<?php echo aioseo()->helpers->escSvg( $svgIcon ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
				</span>
			</div>
		<?php endif; ?>
		<?php if ( $instance['showLabel'] ) : ?>
			<div class="col">
				<div class="label">
					<?php echo esc_html( $instance['label'] ); ?>
				</div>
			</div>
		<?php endif; ?>
	</div>
	<div id="<?php echo esc_attr( $instance['mapId'] ) ?>" class="aioseo-local-map" style="<?php echo esc_attr( $mapStyle ) ?>">
		<?php aioseo()->templates->getTemplate( 'parts/loader.php' ); ?>
	</div>
</div>app/Views/Locations.php000064400000001176151727277720011113 0ustar00<?php
/**
 * This is the output for Local Business - Locations by Category on the frontend.
 *
 * @since 1.1.0
 * @version 1.1.0
 */

// phpcs:disable VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

?>
<ul class="aioseo-category-locations aioseo-category-locations-<?php echo esc_attr( $termId ); ?> <?php echo esc_attr( $instance['class'] ); ?>">
	<?php foreach ( $locations as $location ) { ?>
		<li>
			<a href="<?php echo esc_attr( get_permalink( $location ) ); ?>"><?php echo esc_html( $location->post_title ); ?></a>
		</li>
	<?php } ?>
</ul>app/Views/OpeningHours.php000064400000007163151727277720011602 0ustar00<?php
/**
 * This is the output for Local Business - Opening Hours on the frontend.
 *
 * @since 1.1.0
 * @version 1.1.3
 */

// phpcs:disable VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

$svg = apply_filters( 'aioseo_local_business_opening_hours_icon', '<svg xmlns="http://www.w3.org/2000/svg" width="25" height="25" viewBox="0 0 20 20" fill="currentColor"><rect x="0" fill="none" width="20" height="20"/><g><path fill="currentColor" d="M10 2c4.42 0 8 3.58 8 8s-3.58 8-8 8-8-3.58-8-8 3.58-8 8-8zm0 14c3.31 0 6-2.69 6-6s-2.69-6-6-6-6 2.69-6 6 2.69 6 6 6zm-.71-5.29c.07.05.14.1.23.15l-.02.02L14 13l-3.03-3.19L10 5l-.97 4.81h.01c0 .02-.01.05-.02.09S9 9.97 9 10c0 .28.1.52.29.71z"/></g></svg>' ); // phpcs:ignore Generic.Files.LineLength.MaxExceeded
?>
<div class="aioseo-location aioseo-opening-hours aioseo-hours-<?php echo absint( $postId ); ?> <?php echo esc_attr( $instance['class'] ); ?>">
	<div class="col">
		<div class="d-flex">
			<?php if ( $instance['showIcons'] ) : ?>
				<div class="col-auto">
					<span class="aioseo-opening-hours-icon"><?php echo aioseo()->helpers->escSvg( $svg ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></span>
				</div>
			<?php endif; ?>
			<div class="col">
				<?php if ( $instance['showTitle'] ) : ?>
					<div>
						<strong>
							<?php echo esc_html( $instance['label'] ); ?>
						</strong>
					</div>
				<?php endif; ?>
				<table
					aria-label="<?php echo esc_html( $instance['label'] ); ?>"
					class="aioseo-opening-hours-table aioseo-opening-hours-<?php echo esc_attr( $postId ); ?>"
				>
					<?php
					$daysOfTheWeek = [
						'sunday'    => __( 'Sunday', 'aioseo-local-business' ),
						'monday'    => __( 'Monday', 'aioseo-local-business' ),
						'tuesday'   => __( 'Tuesday', 'aioseo-local-business' ),
						'wednesday' => __( 'Wednesday', 'aioseo-local-business' ),
						'thursday'  => __( 'Thursday', 'aioseo-local-business' ),
						'friday'    => __( 'Friday', 'aioseo-local-business' ),
						'saturday'  => __( 'Saturday', 'aioseo-local-business' )
					];
					foreach ( (array) $openingHoursData->days as $key => $day ) :
						if ( $instance[ 'show' . ucfirst( $key ) ] ) :
							?>
							<tr>
								<td class="weekday">
									<?php echo esc_html( $daysOfTheWeek[ $key ] ); ?>
								</td>
								<td class="hours">
									<?php
									if ( $day->open24h || $openingHoursData->alwaysOpen ) {
										if ( $openingHoursData->labels->alwaysOpen ) {
											echo esc_html( $openingHoursData->labels->alwaysOpen );
										} else {
											$open  = '00:00';
											$close = '23:59';
											if ( ! $openingHoursData->use24hFormat ) {
												$open  = strtoupper( ( new \DateTime( $open ) )->format( 'h:i a' ) );
												$close = strtoupper( ( new \DateTime( $close ) )->format( 'h:i a' ) );
											}
											echo esc_html( $open ) . ' - ' . esc_html( $close );
										}
									} elseif ( $day->closed ) {
										$closedString = $openingHoursData->labels->closed ?: __( 'closed', 'aioseo-local-business' );
										echo esc_html( $closedString );
									} else {
										if ( ! $openingHoursData->use24hFormat ) {
											$day->openTime  = strtoupper( ( new \DateTime( $day->openTime ) )->format( 'h:i a' ) );
											$day->closeTime = strtoupper( ( new \DateTime( $day->closeTime ) )->format( 'h:i a' ) );
										}
										echo esc_html( $day->openTime ) . ' - ' . esc_html( $day->closeTime );
									}
									?>
								</td>
							</tr>
						<?php endif; ?>
					<?php endforeach; ?>
				</table>
			</div>
		</div>
	</div>
</div>app/Views/BusinessInfo.php000064400000020541151727277720011564 0ustar00<?php
/**
 * This is the output for Local Business Information on the frontend.
 *
 * @since 1.1.0
 * @version 1.2.1
 */

// phpcs:disable VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

?>
<div class="aioseo-location-box aioseo-location-<?php echo absint( $postId ); ?> <?php echo esc_attr( $instance['class'] ); ?>"> <?php // phpcs:ignore Generic.Files.LineLength.MaxExceeded ?>
	<?php
	$svgLocationPin = apply_filters( 'aioseo_local_business_info_location_icon', '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" width="26" height="26" fill="currentColor"><rect x="0" fill="none" width="20" height="20"/><g><path fill="currentColor" d="M10 2C6.69 2 4 4.69 4 8c0 2.02 1.17 3.71 2.53 4.89.43.37 1.18.96 1.85 1.83.74.97 1.41 2.01 1.62 2.71.21-.7.88-1.74 1.62-2.71.67-.87 1.42-1.46 1.85-1.83C14.83 11.71 16 10.02 16 8c0-3.31-2.69-6-6-6zm0 2.56c1.9 0 3.44 1.54 3.44 3.44S11.9 11.44 10 11.44 6.56 9.9 6.56 8 8.1 4.56 10 4.56z"/></g></svg>' ); // phpcs:ignore Generic.Files.LineLength.MaxExceeded
	$svgPhone       = apply_filters( 'aioseo_local_business_info_phone_icon', '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" width="20" height="20" fill="currentColor"><rect x="0" fill="none" width="20" height="20"/><g><path fill="currentColor" d="M12.06 6l-.21-.2c-.52-.54-.43-.79.08-1.3l2.72-2.75c.81-.82.96-1.21 1.73-.48l.21.2zm.53.45l4.4-4.4c.7.94 2.34 3.47 1.53 5.34-.73 1.67-1.09 1.75-2 3-1.85 2.11-4.18 4.37-6 6.07-1.26.91-1.31 1.33-3 2-1.8.71-4.4-.89-5.38-1.56l4.4-4.4 1.18 1.62c.34.46 1.2-.06 1.8-.66 1.04-1.05 3.18-3.18 4-4.07.59-.59 1.12-1.45.66-1.8zM1.57 16.5l-.21-.21c-.68-.74-.29-.9.52-1.7l2.74-2.72c.51-.49.75-.6 1.27-.11l.2.21z"/></g></svg>' ); // phpcs:ignore Generic.Files.LineLength.MaxExceeded
	$svgFax         = apply_filters( 'aioseo_local_business_info_fax_icon', '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" width="20" height="20" fill="currentColor"><rect x="0" fill="none" width="20" height="20"/><g><path d="M12 11H7v1h5v-1zm1 4H7v1h6v-1zm-3-2H7v1h3v-1zm7-7h-2V2H5v4H3c-.6 0-1 .4-1 1v5c0 .6.4 1 1 1h2v5h10v-5h2c.6 0 1-.4 1-1V7c0-.6-.4-1-1-1zm-3 11H6v-7h8v7zm0-11H6V3h8v3zm2 3h-1V8h1v1z"/></g></svg>' ); // phpcs:ignore Generic.Files.LineLength.MaxExceeded
	$svgEmail       = apply_filters( 'aioseo_local_business_info_email_icon', '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" width="23" height="23" fill="currentColor"><rect x="0" fill="none" width="20" height="20"/><g><path fill="currentColor" d="M3.87 4h13.25C18.37 4 19 4.59 19 5.79v8.42c0 1.19-.63 1.79-1.88 1.79H3.87c-1.25 0-1.88-.6-1.88-1.79V5.79c0-1.2.63-1.79 1.88-1.79zm6.62 8.6l6.74-5.53c.24-.2.43-.66.13-1.07-.29-.41-.82-.42-1.17-.17l-5.7 3.86L4.8 5.83c-.35-.25-.88-.24-1.17.17-.3.41-.11.87.13 1.07z"/></g></svg>' ); // phpcs:ignore Generic.Files.LineLength.MaxExceeded
	?>
	<?php if ( $instance['showAddress'] || $instance['showVat'] || $instance['showTax'] || $instance['showChamberId'] || $instance['showName'] ) { ?>
		<div class="col">
			<div class="d-flex">
				<?php if ( $instance['showIcons'] ) : ?>
					<div class="col-auto">
						<span class="icon location"><?php echo aioseo()->helpers->escSvg( $svgLocationPin ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></span>
					</div>
				<?php endif; ?>
				<div class="col">

					<?php if ( ! $instance['showAddress'] && $instance['showName'] && ! empty( $locationData->name ) ) { ?>
						<div class="aioseo-name-details">
							<strong><?php echo esc_html( $locationData->name ); ?></strong>
						</div>
					<?php } ?>

					<?php if ( $instance['showAddress'] ) { ?>


						<?php if ( $instance['showLabels'] ) : ?>
							<div class="label">
								<?php echo esc_html( $instance['addressLabel'] ); ?>
							</div>
						<?php endif; ?>

						<div class="aioseo-address-details">
							<?php if ( $instance['showName'] && ! empty( $locationData->name ) ) { ?>
								<div class="aioseo-name-details">
									<strong><?php echo esc_html( $locationData->name ); ?></strong>
								</div>
							<?php } ?>
							<?php
							$haveAddress = array_filter( (array) $locationData->address );
							unset( $haveAddress['addressFormat'] );
							if ( ! empty( $haveAddress ) ) {
								$value = aioseoLocalBusiness()->tags->replaceTags( aioseo()->helpers->decodeHtmlEntities( $locationData->address->addressFormat ), $postId, $locationData->address );
								echo nl2br( wp_kses_post( $value ) );
							}
							?>
						</div>
					<?php } ?>

					<div class="aioseo-tax-details">
						<?php if ( $instance['showVat'] && ! empty( $locationData->ids->vat ) ) { ?>
							<div>

								<?php if ( $instance['showLabels'] ) : ?>
									<div class="label">
										<?php echo esc_html( $instance['vatIdLabel'] ); ?>
									</div>
								<?php endif; ?>

								<span class="item">
									<?php echo esc_html( $locationData->ids->vat ); ?>
								</span>
							</div>
						<?php } ?>
						<?php if ( $instance['showTax'] && ! empty( $locationData->ids->tax ) ) { ?>
							<div>
								<?php if ( $instance['showLabels'] ) : ?>
									<div class="label">
										<?php echo esc_html( $instance['taxIdLabel'] ); ?>
									</div>
								<?php endif; ?>
								<span class="item">
									<?php echo esc_html( $locationData->ids->tax ); ?>
								</span>
							</div>
						<?php } ?>
						<?php if ( $instance['showChamberId'] && ! empty( $locationData->ids->chamberID ) ) { ?>
							<div>
								<?php if ( $instance['showLabels'] ) : ?>
									<div class="label">
										<?php esc_html_e( 'Chamber ID:', 'aioseo-local-business' ); ?>
									</div>
								<?php endif; ?>
								<span class="item">
									<?php echo esc_html( $locationData->ids->chamberID ); ?>
								</span>
							</div>
						<?php } ?>
					</div>
				</div>
			</div>
		</div>
	<?php } ?>

	<div class="col-auto">
		<div class="aioseo-contact-details">
			<?php if ( $instance['showPhone'] && ! empty( $locationData->contact->phoneFormatted ) ) { ?>
				<div class="d-flex">
					<?php if ( $instance['showIcons'] ) : ?>
						<div class="col-auto">
							<span class="icon"><?php echo aioseo()->helpers->escSvg( $svgPhone ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></span>
						</div>
					<?php endif; ?>

					<div class="col">
						<?php if ( $instance['showLabels'] ) : ?>
							<div class="label">
								<?php echo esc_html( $instance['phoneLabel'] ); ?>
							</div>
						<?php endif; ?>
						<span class="item">
							<?php echo esc_html( aioseoLocalBusiness()->locations->formatPhone( $locationData->contact->phoneFormatted, $instance['showCountryCode'] ) ); ?>
						</span>
					</div>
				</div>
			<?php } ?>

			<?php if ( $instance['showFax'] && ! empty( $locationData->contact->faxFormatted ) ) { ?>
				<div class="d-flex">
					<?php if ( $instance['showIcons'] ) : ?>
						<div class="col-auto">
							<span class="icon"><?php echo $svgFax; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></span>
						</div>
					<?php endif; ?>

					<div class="col">
						<?php if ( $instance['showLabels'] ) : ?>
							<div class="label">
								<?php echo esc_html( $instance['faxLabel'] ); ?>
							</div>
						<?php endif; ?>
						<span class="item">
							<?php echo esc_html( aioseoLocalBusiness()->locations->formatPhone( $locationData->contact->faxFormatted, $instance['showCountryCode'] ) ); ?>
						</span>
					</div>
				</div>
			<?php } ?>

			<?php if ( $instance['showEmail'] && ! empty( $locationData->contact->email ) ) { ?>
				<div class="d-flex">
					<?php if ( $instance['showIcons'] ) : ?>
						<div class="col-auto">
							<span class="icon"><?php echo aioseo()->helpers->escSvg( $svgEmail ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></span>
						</div>
					<?php endif; ?>

					<div class="col">
						<?php if ( $instance['showLabels'] ) : ?>
							<div class="label">
								<?php echo esc_html( $instance['emailLabel'] ); ?>
							</div>
						<?php endif; ?>
						<span class="item">
					<?php if ( is_email( $locationData->contact->email ) ) { ?>
						<a href="mailto:<?php echo esc_attr( $locationData->contact->email ) ?>">
					<?php } ?>
					<?php echo esc_html( $locationData->contact->email ); ?>
					<?php if ( is_email( $locationData->contact->email ) ) { ?>
						</a>
					<?php } ?>
				</span>
					</div>
				</div>
			<?php } ?>
		</div>
	</div>
</div>app/Blocks/LocationCategories.php000064400000001431151727277720013050 0ustar00<?php
namespace AIOSEO\Plugin\Addon\LocalBusiness\Blocks;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * The Locations block class.
 *
 * @since 1.1.1
 */
class LocationCategories {
	/**
	 * Class constructor.
	 *
	 * @since 1.1.1
	 */
	public function __construct() {
		aioseo()->blocks->registerBlock(
			'aioseo/locationcategories', [
				'render_callback' => [ $this, 'render' ]
			]
		);
	}

	/**
	 * Renders the block.
	 *
	 * @since 1.1.1
	 *
	 * @param  array  $blockAttributes The block attributes.
	 * @return string                  The output from the output buffering.
	 */
	public function render( $blockAttributes ) {
		ob_start();

		aioseoLocalBusiness()->locations->outputLocationCategories( $blockAttributes );

		return ob_get_clean();
	}
}app/Blocks/Blocks.php000064400000002230151727277730010506 0ustar00<?php
namespace AIOSEO\Plugin\Addon\LocalBusiness\Blocks;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * The blocks class.
 *
 * @since 1.1.0
 */
class Blocks {
	/**
	 * Instance of the BusinessInfo block class.
	 *
	 * @since 1.1.0
	 *
	 * @var BusinessInfo
	 */
	public $businessInfo;

	/**
	 * Instance of the Locations block class.
	 *
	 * @since 1.1.0
	 *
	 * @var Locations
	 */
	public $locations;

	/**
	 * Instance of the Opening Hours block class.
	 *
	 * @since 1.1.0
	 *
	 * @var OpeningHours
	 */
	public $openingHours;

	/**
	 * Instance of the Location Categories block class.
	 *
	 * @since 1.1.1
	 *
	 * @var LocationCategories
	 */
	public $locationCategories;

	/**
	 * Instance of the Map block class.
	 *
	 * @since 1.1.3
	 *
	 * @var Map
	 */
	public $map;

	/**
	 * Class constructor.
	 * Initializes our blocks.
	 *
	 * @since 1.1.0
	 */
	public function __construct() {
		$this->businessInfo       = new BusinessInfo();
		$this->locations          = new Locations();
		$this->openingHours       = new OpeningHours();
		$this->locationCategories = new LocationCategories();
		$this->map                = new Map();
	}
}app/Blocks/Map.php000064400000004223151727277730010012 0ustar00<?php
namespace AIOSEO\Plugin\Addon\LocalBusiness\Blocks;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * The Map block class.
 *
 * @since 1.1.3
 */
class Map {
	/**
	 * Class constructor.
	 *
	 * @since 1.1.3
	 */
	public function __construct() {
		aioseo()->blocks->registerBlock(
			'aioseo/locationmap', [
				'attributes'      => [
					'locationId'   => [
						'type'    => 'number',
						'default' => null
					],
					'showLabel'    => [
						'type'    => 'boolean',
						'default' => true
					],
					'showIcon'     => [
						'type'    => 'boolean',
						'default' => true
					],
					'customMarker' => [
						'type'    => 'string',
						'default' => null
					],
					'width'        => [
						'type'    => 'string',
						'default' => '100%'
					],
					'height'       => [
						'type'    => 'string',
						'default' => '450px'
					],
					'label'        => [
						'type'    => 'string',
						'default' => __( 'Our location:', 'aioseo-local-business' ),
					],
					'dataObject'   => [
						'type'    => 'string',
						'default' => null
					],
					'updated'      => [
						'type'    => 'string',
						'default' => time()
					]
				],
				'render_callback' => [ $this, 'render' ],
				'editor_style'    => aioseoLocalBusiness()->assets->cssHandle( 'src/assets/scss/location-map.scss' )
			]
		);
	}

	/**
	 * Renders the block.
	 *
	 * @since 1.1.3
	 *
	 * @param  array  $blockAttributes The block attributes.
	 * @return string                  The output from the output buffering.
	 */
	public function render( $blockAttributes ) {
		$locationId = ! empty( $blockAttributes['locationId'] ) ? $blockAttributes['locationId'] : '';

		if ( $locationId ) {
			$location = aioseoLocalBusiness()->locations->getLocation( $locationId );
			if ( ! $location ) {
				return sprintf(
					// Translators: 1 - Local Business Post Type Label.
					__( 'Please fill in your Business Info for this %1$s.', 'aioseo-local-business' ),
					aioseoLocalBusiness()->postType->getSingleLabel()
				);
			}
		}

		ob_start();

		aioseoLocalBusiness()->locations->outputLocationMap( $locationId, $blockAttributes );

		return ob_get_clean();
	}
}app/Blocks/Locations.php000064400000002235151727277730011231 0ustar00<?php
namespace AIOSEO\Plugin\Addon\LocalBusiness\Blocks;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * The Locations block class.
 *
 * @since 1.1.0
 */
class Locations {
	/**
	 * Class constructor.
	 *
	 * @since 1.1.0
	 */
	public function __construct() {
		$this->register();
	}

	/**
	 * Registers the block.
	 *
	 * @since 1.1.0
	 *
	 * @return void
	 */
	public function register() {
		aioseo()->blocks->registerBlock(
			'aioseo/locations', [
				'attributes'      => [
					'categoryId' => [
						'type'    => 'number',
						'default' => 0,
					],
				],
				'render_callback' => [ $this, 'render' ],
			]
		);
	}

	/**
	 * Renders the block.
	 *
	 * @since 1.1.0
	 *
	 * @param  array  $blockAttributes The block attributes.
	 * @return string                  The output from the output buffering.
	 */
	public function render( $blockAttributes ) {
		if ( empty( $blockAttributes['categoryId'] ) || ! is_numeric( $blockAttributes['categoryId'] ) ) {
			return;
		}

		ob_start();

		aioseoLocalBusiness()->locations->outputLocationCategory( absint( $blockAttributes['categoryId'] ), $blockAttributes );

		return ob_get_clean();
	}
}app/Blocks/OpeningHours.php000064400000005416151727277730011722 0ustar00<?php
namespace AIOSEO\Plugin\Addon\LocalBusiness\Blocks;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * The Opening Hours block class.
 *
 * @since 1.1.0
 */
class OpeningHours {
	/**
	 * Class constructor.
	 *
	 * @since 1.1.0
	 */
	public function __construct() {
		$this->register();
	}

	/**
	 * Register block type.
	 *
	 * @since 1.1.0
	 *
	 * @return void
	 */
	public function register() {
		aioseo()->blocks->registerBlock(
			'aioseo/openinghours', [
				'attributes'      => [
					'locationId'    => [
						'type'    => 'number',
						'default' => null,
					],
					'layout'        => [
						'type'    => 'string',
						'default' => 'classic',
					],
					'showTitle'     => [
						'type'    => 'boolean',
						'default' => true,
					],
					'showIcons'     => [
						'type'    => 'boolean',
						'default' => true,
					],
					'showMonday'    => [
						'type'    => 'boolean',
						'default' => true,
					],
					'showTuesday'   => [
						'type'    => 'boolean',
						'default' => true,
					],
					'showWednesday' => [
						'type'    => 'boolean',
						'default' => true,
					],
					'showThursday'  => [
						'type'    => 'boolean',
						'default' => true,
					],
					'showFriday'    => [
						'type'    => 'boolean',
						'default' => true,
					],
					'showSaturday'  => [
						'type'    => 'boolean',
						'default' => true,
					],
					'showSunday'    => [
						'type'    => 'boolean',
						'default' => true,
					],
					'label'         => [
						'type'    => 'string',
						'default' => __( 'Our Opening Hours:', 'aioseo-local-business' ),
					],
					'dataObject'    => [
						'type'    => 'string',
						'default' => null
					],
					'updated'       => [
						'type'    => 'string',
						'default' => time()
					]
				],
				'render_callback' => [ $this, 'render' ],
				'editor_style'    => aioseoLocalBusiness()->assets->cssHandle( 'src/assets/scss/opening-hours.scss' )
			]
		);
	}

	/**
	 * Render block callback.
	 *
	 * @since 1.1.0
	 *
	 * @param  array       $blockAttributes The block attributes.
	 * @return string|void                  The output from the output buffering.
	 */
	public function render( $blockAttributes ) {
		$locationId = ! empty( $blockAttributes['locationId'] ) ? $blockAttributes['locationId'] : '';

		if ( $locationId ) {
			$location = aioseoLocalBusiness()->locations->getLocation( $locationId );
			if ( ! $location ) {
				return sprintf(
					// Translators: 1 - Local Business Post Type Label.
					__( 'Please fill in your Opening Hours for this %1$s.', 'aioseo-local-business' ),
					aioseoLocalBusiness()->postType->getSingleLabel()
				);
			}
		}

		ob_start();

		aioseoLocalBusiness()->locations->outputOpeningHours( $locationId, $blockAttributes );

		return ob_get_clean();
	}
}app/Blocks/BusinessInfo.php000064400000006732151727277730011713 0ustar00<?php
namespace AIOSEO\Plugin\Addon\LocalBusiness\Blocks;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * The BusinessInfo block class.
 *
 * @since 1.1.0
 */
class BusinessInfo {
	/**
	 * Class constructor.
	 *
	 * @since 1.1.0
	 */
	public function __construct() {
		$this->register();
	}

	/**
	 * Registers the block.
	 *
	 * @since 1.1.0
	 *
	 * @return void
	 */
	public function register() {
		aioseo()->blocks->registerBlock(
			'aioseo/businessinfo', [
				'attributes'      => [
					'locationId'      => [
						'type'    => 'number',
						'default' => null,
					],
					'showLabels'      => [
						'type'    => 'boolean',
						'default' => true,
					],
					'addressLabel'    => [
						'type'    => 'string',
						'default' => __( 'Address:', 'aioseo-local-business' ),
					],
					'vatIdLabel'      => [
						'type'    => 'string',
						'default' => __( 'VAT ID:', 'aioseo-local-business' ),
					],
					'taxIdLabel'      => [
						'type'    => 'string',
						'default' => __( 'Tax ID:', 'aioseo-local-business' ),
					],
					'phoneLabel'      => [
						'type'    => 'string',
						'default' => __( 'Phone:', 'aioseo-local-business' ),
					],
					'faxLabel'        => [
						'type'    => 'string',
						'default' => __( 'Fax:', 'aioseo-local-business' ),
					],
					'emailLabel'      => [
						'type'    => 'string',
						'default' => __( 'Email:', 'aioseo-local-business' ),
					],
					'showIcons'       => [
						'type'    => 'boolean',
						'default' => true,
					],
					'showName'        => [
						'type'    => 'boolean',
						'default' => true,
					],
					'showAddress'     => [
						'type'    => 'boolean',
						'default' => true,
					],
					'showPhone'       => [
						'type'    => 'boolean',
						'default' => true,
					],
					'showFax'         => [
						'type'    => 'boolean',
						'default' => true,
					],
					'showCountryCode' => [
						'type'    => 'boolean',
						'default' => true,
					],
					'showEmail'       => [
						'type'    => 'boolean',
						'default' => true,
					],
					'showVat'         => [
						'type'    => 'boolean',
						'default' => true,
					],
					'showTax'         => [
						'type'    => 'boolean',
						'default' => true,
					],
					'showChamberId'   => [
						'type'    => 'boolean',
						'default' => false,
					],
					'dataObject'      => [
						'type'    => 'string',
						'default' => null
					],
					'updated'         => [
						'type'    => 'string',
						'default' => time()
					]
				],
				'render_callback' => [ $this, 'render' ],
				'editor_style'    => aioseoLocalBusiness()->assets->cssHandle( 'src/assets/scss/business-info.scss' )
			]
		);
	}

	/**
	 * Renders the block.
	 *
	 * @since 1.1.0
	 *
	 * @param  array  $blockAttributes The block attributes.
	 * @return string                  The output from the output buffering.
	 */
	public function render( $blockAttributes ) {
		$locationId = ! empty( $blockAttributes['locationId'] ) ? $blockAttributes['locationId'] : '';

		if ( $locationId ) {
			$location = aioseoLocalBusiness()->locations->getLocation( $locationId );
			if ( ! $location ) {
				return sprintf(
					// Translators: 1 - Local Business Post Type Label.
					__( 'Please fill in your Business Info for this %1$s.', 'aioseo-local-business' ),
					aioseoLocalBusiness()->postType->getSingleLabel()
				);
			}
		}

		ob_start();

		aioseoLocalBusiness()->locations->outputBusinessInfo( $locationId, $blockAttributes );

		return ob_get_clean();
	}
}app/Models/Post.php000064400000006575151727277740010245 0ustar00<?php
namespace AIOSEO\Plugin\Addon\LocalBusiness\Models;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Class Post
 *
 * @since 1.1.3
 */
class Post {
	/**
	 * Returns a JSON object with default local seo options.
	 *
	 * @since 1.1.3
	 *
	 * @param  string $existingOptions The existing options in JSON.
	 * @return string                  The existing options with defaults added in JSON.
	 */
	public static function parseLocalSeoOptions( $existingOptions = '' ) {
		$defaults = [
			'locations'    => [
				'business' => [
					'name'         => '',
					'businessType' => '',
					'image'        => '',
					'areaServed'   => '',
					'urls'         => [
						'website'     => '',
						'aboutPage'   => '',
						'contactPage' => ''
					],
					'address'      => [
						'streetLine1'   => '',
						'streetLine2'   => '',
						'zipCode'       => '',
						'city'          => '',
						'state'         => '',
						'country'       => '',
						'addressFormat' => "#streetLineOne\n#streetLineTwo\n#city, #state #zipCode"
					],
					'contact'      => [
						'email'          => '',
						'phone'          => '',
						'phoneFormatted' => '',
						'fax'            => '',
						'faxFormatted'   => ''
					],
					'ids'          => [
						'vat'               => '',
						'tax'               => '',
						'chamberOfCommerce' => ''
					],
					'payment'      => [
						'priceRange'         => '',
						'currenciesAccepted' => '',
						'methods'            => ''
					],
				],
			],
			'openingHours' => [
				'useDefaults'  => true,
				'show'         => true,
				'alwaysOpen'   => false,
				'use24hFormat' => false,
				'timezone'     => '',
				'labels'       => [
					'closed'     => '',
					'alwaysOpen' => ''
				],
				'days'         => [
					'monday'    => [
						'open24h'   => false,
						'closed'    => false,
						'openTime'  => '09:00',
						'closeTime' => '17:00'
					],
					'tuesday'   => [
						'open24h'   => false,
						'closed'    => false,
						'openTime'  => '09:00',
						'closeTime' => '17:00'
					],
					'wednesday' => [
						'open24h'   => false,
						'closed'    => false,
						'openTime'  => '09:00',
						'closeTime' => '17:00'
					],
					'thursday'  => [
						'open24h'   => false,
						'closed'    => false,
						'openTime'  => '09:00',
						'closeTime' => '17:00'
					],
					'friday'    => [
						'open24h'   => false,
						'closed'    => false,
						'openTime'  => '09:00',
						'closeTime' => '17:00'
					],
					'saturday'  => [
						'open24h'   => false,
						'closed'    => false,
						'openTime'  => '09:00',
						'closeTime' => '17:00'
					],
					'sunday'    => [
						'open24h'   => false,
						'closed'    => false,
						'openTime'  => '09:00',
						'closeTime' => '17:00'
					]
				]
			],
			'maps'         => [
				'mapOptions'   => [
					'center'    => [
						'lat' => 47.6205063, // Space Needle, Seattle - WA
						'lng' => - 122.3492774
					],
					'zoom'      => 16,
					'mapTypeId' => 'roadmap'
				],
				'customMarker' => '',
				'placeId'      => ''
			]
		];

		if ( empty( $existingOptions ) ) {
			return $defaults;
		}

		if ( ! aioseo()->helpers->isJsonString( $existingOptions ) ) {
			$existingOptions = wp_json_encode( $existingOptions );
		}

		// Decode as an array to merge with the defaults.
		$existingOptions = json_decode( $existingOptions, true );

		return array_replace_recursive( $defaults, $existingOptions );
	}
}app/Main/Search.php000064400000021175151727277740010157 0ustar00<?php
namespace AIOSEO\Plugin\Addon\LocalBusiness\Main;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

use AIOSEO\Plugin\Common\Models;

/**
 * The Search class..
 *
 * @since 1.1.0
 */
class Search {
	/**
	 * The JSON searchable fields.
	 *
	 * @since 1.1.0
	 *
	 * @var string An array of searchable fields.
	 */
	private $searchFields = [
		'streetLine1',
		'city',
		'zipCode'
	];

	/**
	 * Whether to force an enhanced query.
	 *
	 * @since 1.1.0
	 *
	 * @var boolean
	 */
	private $forceEnhance = false;

	/**
	 * Holds the errors if there are any.
	 *
	 * @since 1.1.0
	 *
	 * @var bool|string
	 */
	private $errorDetected = false;

	/**
	 * Class constructor.
	 *
	 * @since 1.1.0
	 */
	public function __construct() {
		$this->hooks();
	}

	/**
	 * Hook where we need to enhance the search query.
	 *
	 * @since 1.1.0
	 *
	 * @return void
	 */
	public function hooks() {
		add_action( 'pre_get_posts', [ $this, 'maybeEnhanceSearch' ], 50 );
		add_filter( 'the_excerpt', [ $this, 'maybeEnhanceSearchResults' ], 50 );
	}

	/**
	 * Filter the query join and search to include location results.
	 *
	 * @since 1.1.0
	 *
	 * @param  \WP_Query $query The main query.
	 * @return void
	 */
	public function maybeEnhanceSearch( $query ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
		// phpcs:disable HM.Security.ValidatedSanitizedInput.InputNotSanitized, HM.Security.NonceVerification.Recommended
		$enhance = (
			is_search() &&
			! is_admin() &&
			$this->enhancedSearchActivated() &&
			(
				! isset( $_GET['post_type'] ) ||
				aioseoLocalBusiness()->postType->getName() === sanitize_text_field( wp_unslash( $_GET['post_type'] ) )
			)
		);
		// phpcs:enable

		if ( $this->forceEnhance || $enhance ) {
			remove_action( 'pre_get_posts', [ $this, 'maybeEnhanceSearch' ], 50 );

			add_filter( 'posts_join', [ $this, 'join' ], 50 );
			add_filter( 'posts_search', [ $this, 'search' ], 50, 2 );
			add_filter( 'posts_groupby', [ $this, 'groupBy' ] );
			add_filter( 'posts_results', [ $this, 'maybeDisableEnhancedSearch' ] );
		}
	}

	/**
	 * Left join the aioseo_posts table.
	 *
	 * @since 1.1.0
	 *
	 * @param  string $join The current $join string.
	 * @return string       Adds a left join for location info on table aioseo_posts.
	 */
	public function join( $join ) {
		remove_filter( 'posts_join', [ $this, 'join' ], 50 );

		$join .= ' LEFT JOIN ' . aioseo()->core->db->db->prefix . 'aioseo_posts aioseo_posts ON ( ' . aioseo()->core->db->db->posts . '.ID = aioseo_posts.post_id AND aioseo_posts.local_seo IS NOT NULL ) '; // phpcs:ignore Generic.Files.LineLength.MaxExceeded

		return $join;
	}

	/**
	 * Adds our search parameters.
	 *
	 * @since 1.1.0
	 *
	 * @param  string    $searchWhere The current search where string.
	 * @param  \WP_Query $query       The current WP_Query.
	 * @return string                 Adds to the search where string to account for locations.
	 */
	public function search( $searchWhere, $query = null ) {
		remove_filter( 'posts_search', [ $this, 'search' ], 50 );

		$searchTerms = $query->get( 'search_terms' );
		if ( empty( $searchTerms ) ) {
			return $searchWhere;
		}

		$localWhere    = ' ( ';
		$localWhereAnd = '';
		foreach ( $searchTerms as $item ) {
			$localWhere    .= $localWhereAnd . $this->getLocalWhere( $item );
			$localWhereAnd = ' AND ';
		}
		$localWhere .= ' ) ';

		$searchWhere = preg_replace( '/^\s*AND\s*\(/', " AND ( {$localWhere} OR ( ", $searchWhere );
		$searchWhere .= ' ) ';

		return $searchWhere;
	}

	/**
	 * Group by ID.
	 *
	 * @since 1.1.0
	 *
	 * @param  string $groupBy The current groupBy string.
	 * @return string          Adds a group_by string for query consistency.
	 */
	public function groupBy( $groupBy ) {
		if ( empty( $groupBy ) ) {
			$groupBy = aioseo()->core->db->db->posts . '.ID ';
		}

		return $groupBy;
	}

	/**
	 * We'll proactively disable enhanced search if a SQL error was found.
	 *
	 * @since 1.1.0
	 *
	 * @param  array $posts The found search posts.
	 * @return array        The found search posts ( $posts ) is not modified.
	 */
	public function maybeDisableEnhancedSearch( $posts ) {
		$this->errorDetected = aioseo()->core->db->lastError();

		// Let's do nothing if the enhanced search is deactivated.
		if ( ! $this->enhancedSearchActivated() ) {
			return $posts;
		}

		// Let's clear the notification if the search is working again.
		$notification = Models\Notification::getNotificationByName( 'local-business-enhanced-search' );
		if ( '' === $this->errorDetected ) {
			$notification->delete();

			return $posts;
		}

		if ( ! $notification->exists() ) {
			// Let user know we've found an error.
			Models\Notification::addNotification( [
				'slug'              => uniqid(),
				'addon'             => 'localBusiness',
				'notification_name' => 'local-business-enhanced-search',
				'title'             => __( 'Local Business - Enhanced Search', 'aioseo-local-business' ),
				'content'           => sprintf(
				// Translators: 1 - Opening link tag, 2 - Closing link tag.
					__( 'Enhanced Search cannot be enabled on your website because there is a search query conflict. To learn more about this, %1$sclick here%2$s.', 'aioseo-local-business' ), // phpcs:ignore Generic.Files.LineLength.MaxExceeded
					'<a href="' . aioseo()->helpers->utmUrl( trailingslashit( AIOSEO_MARKETING_URL ) . 'docs/enhanced-search-query-conflict/', 'notifications-center', 'v3-migration-title-formats-blank' ) . '" target="_blank">', // phpcs:ignore Generic.Files.LineLength.MaxExceeded
					'</a>'
				),
				'type'              => 'error',
				'level'             => [ 'all' ],
				'button1_label'     => '',
				'button1_action'    => '',
				'start'             => gmdate( 'Y-m-d H:i:s' )
			] );
		}

		// Disable enhanced search if an error was found.
		aioseo()->options->localBusiness->locations->general->enhancedSearch = false;

		// Clear this cache so the test can run again.
		aioseoLocalBusiness()->cache->delete( 'enhancedSearchResult' );

		return $posts;
	}

	/**
	 * Return a full where string query.
	 *
	 * @since 1.1.0
	 *
	 * @param  string $term Search term separated on WP_Query.
	 * @return string       A where for a single search term for each searchField.
	 */
	private function getLocalWhere( $term ) {
		$exclusionPrefix = apply_filters( 'wp_query_search_exclusion_prefix', '-' );

		$exclude = $exclusionPrefix && ( substr( $term, 0, 1 ) === $exclusionPrefix );
		if ( $exclude ) {
			$likeOp = 'NOT REGEXP';
			$term   = substr( $term, 1 );
		} else {
			$likeOp = 'REGEXP';
		}

		$localWhere = ' ( ';
		$likeTerms  = [];
		foreach ( $this->searchFields as $field ) {
			$likeTerms[] = "\"$field\":\"[^\"]*{$term}";
		}

		$localWhere .= aioseo()->core->db->db->prepare( " aioseo_posts.local_seo {$likeOp} %s", implode( '|', $likeTerms ) );

		$localWhere .= ' ) ';

		return $localWhere;
	}

	/**
	 * Adds html output to the search results.
	 *
	 * @since 1.1.0
	 *
	 * @param  string $excerpt The current post excerpt.
	 * @return string          A excerpt enhanced with the location business info.
	 */
	public function maybeEnhanceSearchResults( $excerpt ) {
		if (
			is_search() &&
			! is_admin() &&
			aioseo()->options->localBusiness->locations->general->enhancedSearch &&
			aioseo()->options->localBusiness->locations->general->enhancedSearchExcerpt
		) {
			if ( get_post_type() === aioseoLocalBusiness()->postType->getName() ) {
				ob_start(); ?>
				<div class="aioseo-local-seo-details">
					<?php aioseoLocalBusiness()->locations->outputBusinessInfo( get_the_ID() ); ?>
				</div>
				<?php
				$excerpt = ob_get_clean();
			}
		}

		return $excerpt;
	}

	/**
	 * Test if our enhanced search does not throw any SQL errors.
	 *
	 * @since 1.1.0
	 *
	 * @return bool Search query succeeded.
	 */
	public function testSearch() {
		$testResult = aioseoLocalBusiness()->cache->get( 'enhancedSearchResult' );
		if ( null !== $testResult ) {
			return $testResult;
		}

		$this->forceEnhance = true;
		new \WP_Query( [
			's'              => 'test',
			'posts_per_page' => 1
		] );
		$this->forceEnhance = false;

		$testResult = ! $this->errorDetected;

		// We'll only cache the result if the query is working.
		// When it stops working we proactively disable enhancedSearch and clear this cache so the test can run until the query is fixed.
		if ( $testResult ) {
			aioseoLocalBusiness()->cache->update( 'enhancedSearchResult', true, 0 );
		}

		return $testResult;
	}

	/**
	 * Returns if multiple locations + enhanced search are both on.
	 *
	 * @since 1.2.9.1
	 *
	 * @return bool Is activated.
	 */
	private function enhancedSearchActivated() {
		return aioseo()->options->localBusiness->locations->general->multiple &&
				aioseo()->options->localBusiness->locations->general->enhancedSearch;
	}
}app/Main/Activate.php000064400000001013151727277740010477 0ustar00<?php
namespace AIOSEO\Plugin\Addon\LocalBusiness\Main;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Activate class.
 *
 * @since 1.1.0
 */
class Activate {
	/**
	 * Construct method.
	 *
	 * @since 1.1.0
	 */
	public function __construct() {
		register_deactivation_hook( AIOSEO_LOCAL_BUSINESS_FILE, [ $this, 'deactivate' ] );
	}

	/**
	 * Runs on deactivation.
	 *
	 * @since 1.1.0
	 *
	 * @return void
	 */
	public function deactivate() {
		aioseo()->options->flushRewriteRules();
	}
}app/Main/Updates.php000064400000003366151727277740010361 0ustar00<?php
namespace AIOSEO\Plugin\Addon\LocalBusiness\Main;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Updater class.
 *
 * @since 1.0.1.2
 */
class Updates {
	/**
	 * Class constructor.
	 *
	 * @since 1.0.1.2
	 */
	public function __construct() {
		if ( wp_doing_ajax() || wp_doing_cron() ) {
			return;
		}

		add_action( 'aioseo_run_updates', [ $this, 'runUpdates' ], 1000 );
		add_action( 'aioseo_run_updates', [ $this, 'updateLatestVersion' ], 3000 );
	}

	/**
	 * Runs our migrations.
	 *
	 * @since 1.0.1.2
	 *
	 * @return void
	 */
	public function runUpdates() {
		$lastActiveVersion = aioseoLocalBusiness()->internalOptions->internal->lastActiveVersion;
		if ( version_compare( $lastActiveVersion, '1.1.0.2', '<' ) ) {
			$this->fixBusinessType();
		}
	}

	/**
	 * Updates the latest version after all migrations and updates have run.
	 *
	 * @since 1.0.1.2
	 *
	 * @return void
	 */
	public function updateLatestVersion() {
		if ( aioseoLocalBusiness()->internalOptions->internal->lastActiveVersion === aioseoLocalBusiness()->version ) {
			return;
		}

		aioseoLocalBusiness()->internalOptions->internal->lastActiveVersion = aioseoLocalBusiness()->version;

		// Bust the DB cache so we can make sure that everything is fresh.
		aioseo()->core->db->bustCache();
	}

	/**
	 * Updates the Business Type if it was previously incorrectly stored as JSON.
	 *
	 * @since 1.0.1.2
	 *
	 * @return void
	 */
	private function fixBusinessType() {
		if ( ! aioseo()->options->has( 'localBusiness' ) ) {
			return;
		}

		$businessType = aioseo()->options->localBusiness->locations->business->businessType;
		if ( is_array( $businessType ) ) {
			aioseo()->options->localBusiness->locations->business->businessType = $businessType['value'];
		}
	}
}languages/aioseo-local-business.pot000064400000033731151727277740013470 0ustar00# Copyright (C) 2024 All in One SEO Team
# This file is distributed under the same license as the AIOSEO - Local Business plugin.
msgid ""
msgstr ""
"Project-Id-Version: AIOSEO - Local Business 1.3.1\n"
"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/aioseo-local-business\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"POT-Creation-Date: 2024-04-26T10:18:10+00:00\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"X-Generator: WP-CLI 2.9.0\n"
"X-Domain: aioseo-local-business\n"

#. Plugin Name of the plugin
msgid "AIOSEO - Local Business"
msgstr ""

#. Plugin URI of the plugin
#. Author URI of the plugin
msgid "https://aioseo.com"
msgstr ""

#. Description of the plugin
msgid "Adds Local Business support to All in One SEO."
msgstr ""

#. Author of the plugin
msgid "All in One SEO Team"
msgstr ""

#. Translators: 1 - The plugin short name ("AIOSEO").
#: app/Admin/Admin.php:41
msgid "%1$s Local Business"
msgstr ""

#: app/Admin/Location.php:76
msgid "Location"
msgstr ""

#: app/Admin/Location.php:92
msgid "Locations"
msgstr ""

#. Translators: 1 - Singular post type name.
#: app/Admin/Location.php:155
msgctxt "Post Type"
msgid "%1$s Archives"
msgstr ""

#. Translators: 1 - Singular post type name.
#: app/Admin/Location.php:157
msgctxt "Post Type"
msgid "%1$s Attributes"
msgstr ""

#. Translators: 1 - Singular post type name.
#: app/Admin/Location.php:159
msgctxt "Post Type"
msgid "Parent %1$s:"
msgstr ""

#. Translators: 1 - Plural post type name.
#: app/Admin/Location.php:161
msgctxt "Post Type"
msgid "All %1$s"
msgstr ""

#. Translators: 1 - Singular post type name.
#: app/Admin/Location.php:163
msgctxt "Post Type"
msgid "Add New %1$s"
msgstr ""

#. Translators: 1 - Singular post type name.
#: app/Admin/Location.php:165
msgctxt "Post Type"
msgid "New %1$s"
msgstr ""

#. Translators: 1 - Singular post type name.
#: app/Admin/Location.php:167
msgctxt "Post Type"
msgid "Edit %1$s"
msgstr ""

#. Translators: 1 - Singular post type name.
#: app/Admin/Location.php:169
msgctxt "Post Type"
msgid "Update %1$s"
msgstr ""

#. Translators: 1 - Singular or plural post type name.
#: app/Admin/Location.php:171
#: app/Admin/Location.php:173
msgctxt "Post Type"
msgid "View %1$s"
msgstr ""

#. Translators: 1 - Singular post type name.
#: app/Admin/Location.php:175
msgctxt "Post Type"
msgid "Search %1$s"
msgstr ""

#. Translators: 1 - Singular post type name.
#: app/Admin/Location.php:177
msgctxt "Post Type"
msgid "Insert into %1$s"
msgstr ""

#. Translators: 1 - Singular post type name.
#: app/Admin/Location.php:179
msgctxt "Post Type"
msgid "Uploaded to this %1$s"
msgstr ""

#. Translators: 1 - Plural post type name.
#: app/Admin/Location.php:181
msgctxt "Post Type"
msgid "%1$s list"
msgstr ""

#. Translators: 1 - Plural post type name.
#: app/Admin/Location.php:183
msgctxt "Post Type"
msgid "%1$s list navigation"
msgstr ""

#. Translators: 1 - Plural post type name.
#: app/Admin/Location.php:185
msgctxt "Post Type"
msgid "Filter %1$s list"
msgstr ""

#. Translators: 1 - The plugin name ("All in One SEO").
#: app/Admin/Location.php:192
msgid "%1$s uses these locations to generate Local Business schema markup."
msgstr ""

#: app/Admin/LocationCategory.php:85
msgid "Location Category"
msgstr ""

#: app/Admin/LocationCategory.php:96
msgid "Location Categories"
msgstr ""

#. Translators: 1 - Plural post type name.
#: app/Admin/LocationCategory.php:131
msgctxt "Taxonomy"
msgid "All %1$s"
msgstr ""

#. Translators: 1 - Singular post type name.
#: app/Admin/LocationCategory.php:133
msgctxt "Taxonomy"
msgid "Parent %1$s"
msgstr ""

#. Translators: 1 - Singular post type name.
#: app/Admin/LocationCategory.php:135
msgctxt "Taxonomy"
msgid "Parent %1$s:"
msgstr ""

#. Translators: 1 - Singular post type name.
#: app/Admin/LocationCategory.php:137
msgctxt "Taxonomy"
msgid "Add New %1$s"
msgstr ""

#. Translators: 1 - Singular post type name.
#: app/Admin/LocationCategory.php:139
msgctxt "Taxonomy"
msgid "Edit %1$s"
msgstr ""

#. Translators: 1 - Singular post type name.
#: app/Admin/LocationCategory.php:141
msgctxt "Taxonomy"
msgid "Update %1$s"
msgstr ""

#. Translators: 1 - Singular or plural post type name.
#: app/Admin/LocationCategory.php:143
#: app/Admin/LocationCategory.php:145
msgctxt "Taxonomy"
msgid "View %1$s"
msgstr ""

#. Translators: 1 - Plural post type name.
#: app/Admin/LocationCategory.php:147
msgctxt "Taxonomy"
msgid "Search %1$s"
msgstr ""

#. Translators: 1 - Plural post type name.
#: app/Admin/LocationCategory.php:149
msgctxt "Taxonomy"
msgid "No %1$s"
msgstr ""

#. Translators: 1 - Plural post type name.
#: app/Admin/LocationCategory.php:151
msgctxt "Taxonomy"
msgid "%1$s list navigation"
msgstr ""

#. Translators: 1 - Plural post type name.
#: app/Admin/LocationCategory.php:153
msgctxt "Taxonomy"
msgid "Filter %1$s list"
msgstr ""

#: app/Blocks/BusinessInfo.php:45
#: app/Locations/Locations.php:150
#: app/Shortcodes/Shortcodes.php:62
#: app/Widgets/BusinessInfo.php:44
msgid "Address:"
msgstr ""

#: app/Blocks/BusinessInfo.php:49
#: app/Locations/Locations.php:154
#: app/Shortcodes/Shortcodes.php:63
#: app/Widgets/BusinessInfo.php:45
msgid "VAT ID:"
msgstr ""

#: app/Blocks/BusinessInfo.php:53
#: app/Locations/Locations.php:155
#: app/Shortcodes/Shortcodes.php:64
#: app/Widgets/BusinessInfo.php:46
msgid "Tax ID:"
msgstr ""

#: app/Blocks/BusinessInfo.php:57
#: app/Locations/Locations.php:152
#: app/Shortcodes/Shortcodes.php:65
#: app/Widgets/BusinessInfo.php:47
msgid "Phone:"
msgstr ""

#: app/Blocks/BusinessInfo.php:61
#: app/Locations/Locations.php:153
#: app/Shortcodes/Shortcodes.php:66
#: app/Widgets/BusinessInfo.php:48
msgid "Fax:"
msgstr ""

#: app/Blocks/BusinessInfo.php:65
#: app/Locations/Locations.php:151
#: app/Shortcodes/Shortcodes.php:67
#: app/Widgets/BusinessInfo.php:49
msgid "Email:"
msgstr ""

#. Translators: 1 - Local Business Post Type Label.
#: app/Blocks/BusinessInfo.php:138
#: app/Blocks/Map.php:83
msgid "Please fill in your Business Info for this %1$s."
msgstr ""

#: app/Blocks/Map.php:50
#: app/Shortcodes/Shortcodes.php:146
#: app/Widgets/Map.php:38
msgid "Our location:"
msgstr ""

#: app/Blocks/OpeningHours.php:81
#: app/Shortcodes/Shortcodes.php:119
#: app/Widgets/OpeningHours.php:43
msgid "Our Opening Hours:"
msgstr ""

#. Translators: 1 - Local Business Post Type Label.
#: app/Blocks/OpeningHours.php:114
msgid "Please fill in your Opening Hours for this %1$s."
msgstr ""

#: app/Import/Notifications.php:63
msgid "Re-Enter Business Type in Local Business"
msgstr ""

#. Translators: 1 - The country.
#: app/Import/Notifications.php:66
msgid ""
"For technical reasons, we were unable to migrate the business type you entered for your Local Business schema markup.\n"
"\t\t\t\tPlease enter it (%1$s) again by using the dropdown menu."
msgstr ""

#: app/Import/Notifications.php:70
#: app/Import/Notifications.php:94
#: app/Import/Notifications.php:118
#: app/Import/Notifications.php:142
#: app/Import/Notifications.php:172
msgid "Fix Now"
msgstr ""

#: app/Import/Notifications.php:72
#: app/Import/Notifications.php:96
#: app/Import/Notifications.php:120
#: app/Import/Notifications.php:144
#: app/Import/Notifications.php:174
msgid "Remind Me Later"
msgstr ""

#: app/Import/Notifications.php:87
msgid "Re-Enter Country in Local Business"
msgstr ""

#. Translators: 1 - The country.
#: app/Import/Notifications.php:90
msgid ""
"For technical reasons, we were unable to migrate the country you entered for your Local Business schema markup.\n"
"\t\t\t\tPlease enter it (%1$s) again by using the dropdown menu."
msgstr ""

#: app/Import/Notifications.php:111
msgid "Invalid Phone Number for Local SEO"
msgstr ""

#. Translators: 1 - The phone number.
#: app/Import/Notifications.php:114
msgid ""
"The phone number that you previously entered for your Local Business schema markup is invalid.\n"
"\t\t\t\t\tAs it needs to be internationally formatted, please enter it (%1$s) again with the country code, e.g. +1 (555) 555-1234."
msgstr ""

#: app/Import/Notifications.php:135
msgid "Invalid Fax Number for Local SEO"
msgstr ""

#. Translators: 1 - The fax number.
#: app/Import/Notifications.php:138
msgid ""
"The fax number that you previously entered for your Local Business schema markup is invalid.\n"
"\t\t\t\t\tAs it needs to be internationally formatted, please enter it (%1$s) again with the country code, e.g. +1 (555) 555-1234."
msgstr ""

#: app/Import/Notifications.php:165
msgid "Invalid Currencies for Local SEO"
msgstr ""

#. Translators: 1 - The phone number.
#: app/Import/Notifications.php:168
msgid ""
"One or more currencies that you previously entered for your Local Business schema markup are invalid.\n"
"\t\t\t\t\tPlease select these again using our dropdown menu.</br>%1$s"
msgstr ""

#: app/LocalBusiness.php:308
msgid "Plugin is missing required dependencies. Please contact support for more information."
msgstr ""

#. Translators: 1 - The post type plural label.
#: app/Locations/Locations.php:189
#: app/Locations/Locations.php:278
msgid "No %1$s found"
msgstr ""

#: app/Locations/Locations.php:235
msgid "Your Opening Hours settings are disabled. Please enable them to use the Opening Hours block."
msgstr ""

#: app/Main/Search.php:193
msgid "Local Business - Enhanced Search"
msgstr ""

#. Translators: 1 - Opening link tag, 2 - Closing link tag.
#: app/Main/Search.php:196
msgid "Enhanced Search cannot be enabled on your website because there is a search query conflict. To learn more about this, %1$sclick here%2$s."
msgstr ""

#: app/Utils/Tags.php:74
msgid "Address Line 1"
msgstr ""

#: app/Utils/Tags.php:75
msgid "Your Address Line 1"
msgstr ""

#: app/Utils/Tags.php:80
msgid "Address Line 2"
msgstr ""

#: app/Utils/Tags.php:81
msgid "Your Address Line 2"
msgstr ""

#: app/Utils/Tags.php:86
msgid "Zip Code"
msgstr ""

#: app/Utils/Tags.php:87
msgid "Your Zip Code"
msgstr ""

#: app/Utils/Tags.php:92
msgid "City"
msgstr ""

#: app/Utils/Tags.php:93
msgid "Your City"
msgstr ""

#: app/Utils/Tags.php:98
msgid "State"
msgstr ""

#: app/Utils/Tags.php:99
msgid "Your State"
msgstr ""

#: app/Utils/Tags.php:104
msgid "Country"
msgstr ""

#: app/Utils/Tags.php:105
msgid "Your Country"
msgstr ""

#: app/Views/BusinessInfo.php:97
msgid "Chamber ID:"
msgstr ""

#: app/Views/OpeningHours.php:40
#: app/Widgets/OpeningHours.php:224
msgid "Sunday"
msgstr ""

#: app/Views/OpeningHours.php:41
#: app/Widgets/OpeningHours.php:188
msgid "Monday"
msgstr ""

#: app/Views/OpeningHours.php:42
#: app/Widgets/OpeningHours.php:194
msgid "Tuesday"
msgstr ""

#: app/Views/OpeningHours.php:43
#: app/Widgets/OpeningHours.php:200
msgid "Wednesday"
msgstr ""

#: app/Views/OpeningHours.php:44
#: app/Widgets/OpeningHours.php:206
msgid "Thursday"
msgstr ""

#: app/Views/OpeningHours.php:45
#: app/Widgets/OpeningHours.php:212
msgid "Friday"
msgstr ""

#: app/Views/OpeningHours.php:46
#: app/Widgets/OpeningHours.php:218
msgid "Saturday"
msgstr ""

#: app/Views/OpeningHours.php:70
msgid "closed"
msgstr ""

#: app/Widgets/BusinessInfo.php:59
msgid "Display a location information."
msgstr ""

#: app/Widgets/BusinessInfo.php:68
msgid "AIOSEO Local - Business Info"
msgstr ""

#: app/Widgets/BusinessInfo.php:143
#: app/Widgets/Locations.php:119
#: app/Widgets/Map.php:116
#: app/Widgets/OpeningHours.php:136
msgid "Title:"
msgstr ""

#: app/Widgets/BusinessInfo.php:156
#: app/Widgets/Map.php:129
#: app/Widgets/OpeningHours.php:149
msgid "Location:"
msgstr ""

#: app/Widgets/BusinessInfo.php:166
#: app/Widgets/Map.php:139
#: app/Widgets/OpeningHours.php:159
msgid "Select your location"
msgstr ""

#: app/Widgets/BusinessInfo.php:171
#: app/Widgets/Map.php:144
#: app/Widgets/OpeningHours.php:164
msgid "No locations available"
msgstr ""

#: app/Widgets/BusinessInfo.php:180
msgid "Show labels"
msgstr ""

#: app/Widgets/BusinessInfo.php:186
#: app/Widgets/OpeningHours.php:179
msgid "Show icons"
msgstr ""

#: app/Widgets/BusinessInfo.php:190
msgid "Business Info:"
msgstr ""

#: app/Widgets/BusinessInfo.php:195
msgid "Business name"
msgstr ""

#: app/Widgets/BusinessInfo.php:201
#: app/Widgets/BusinessInfo.php:244
msgid "Address"
msgstr ""

#: app/Widgets/BusinessInfo.php:207
msgid "Phone Number"
msgstr ""

#: app/Widgets/BusinessInfo.php:213
msgid "Fax Number"
msgstr ""

#: app/Widgets/BusinessInfo.php:219
msgid "Show Country Code"
msgstr ""

#: app/Widgets/BusinessInfo.php:225
msgid "Email address"
msgstr ""

#: app/Widgets/BusinessInfo.php:231
msgid "Show VAT ID"
msgstr ""

#: app/Widgets/BusinessInfo.php:237
msgid "Show Tax ID"
msgstr ""

#: app/Widgets/BusinessInfo.php:240
msgid "Labels:"
msgstr ""

#: app/Widgets/BusinessInfo.php:245
msgid "Vat ID"
msgstr ""

#: app/Widgets/BusinessInfo.php:246
msgid "Tax ID"
msgstr ""

#: app/Widgets/BusinessInfo.php:247
msgid "Phone"
msgstr ""

#: app/Widgets/BusinessInfo.php:248
msgid "Fax"
msgstr ""

#: app/Widgets/BusinessInfo.php:249
msgid "Email"
msgstr ""

#: app/Widgets/BusinessInfo.php:267
#: app/Widgets/OpeningHours.php:241
msgid "After widget text:"
msgstr ""

#: app/Widgets/Locations.php:42
msgid "Display a list of locations by category."
msgstr ""

#: app/Widgets/Locations.php:51
msgid "AIOSEO Local - Locations by Category"
msgstr ""

#: app/Widgets/Locations.php:131
msgid "Category:"
msgstr ""

#: app/Widgets/Locations.php:138
msgid "Select a category"
msgstr ""

#: app/Widgets/Locations.php:145
msgid "No categories available"
msgstr ""

#: app/Widgets/Map.php:47
msgid "Display a location map."
msgstr ""

#: app/Widgets/Map.php:56
msgid "AIOSEO Local - Map"
msgstr ""

#: app/Widgets/Map.php:153
msgid "Show label"
msgstr ""

#: app/Widgets/Map.php:159
msgid "Show icon"
msgstr ""

#: app/Widgets/Map.php:164
msgid "Width"
msgstr ""

#: app/Widgets/Map.php:176
msgid "Height"
msgstr ""

#: app/Widgets/Map.php:188
#: app/Widgets/OpeningHours.php:229
msgid "Label"
msgstr ""

#: app/Widgets/OpeningHours.php:53
msgid "Display opening hours of a location."
msgstr ""

#: app/Widgets/OpeningHours.php:62
msgid "AIOSEO Local - Opening Hours"
msgstr ""

#: app/Widgets/OpeningHours.php:173
msgid "Show title"
msgstr ""

#: app/Widgets/OpeningHours.php:183
msgid "Opening Hours"
msgstr ""
vendor/composer/installed.json000064400000000106151727277740012562 0ustar00{
    "packages": [],
    "dev": false,
    "dev-package-names": []
}
vendor/composer/autoload_static.php000064400000014267151727277740013615 0ustar00<?php

// autoload_static.php @generated by Composer

namespace Composer\Autoload;

class ComposerStaticInite81999b5328d21adc9e1de3db9aedc05
{
    public static $prefixLengthsPsr4 = array (
        'A' => 
        array (
            'AIOSEO\\Plugin\\Addon\\LocalBusiness\\' => 34,
        ),
    );

    public static $prefixDirsPsr4 = array (
        'AIOSEO\\Plugin\\Addon\\LocalBusiness\\' => 
        array (
            0 => __DIR__ . '/../..' . '/app',
        ),
    );

    public static $classMap = array (
        'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Admin\\Admin' => __DIR__ . '/../..' . '/app/Admin/Admin.php',
        'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Admin\\Location' => __DIR__ . '/../..' . '/app/Admin/Location.php',
        'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Admin\\LocationCategory' => __DIR__ . '/../..' . '/app/Admin/LocationCategory.php',
        'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Admin\\Usage' => __DIR__ . '/../..' . '/app/Admin/Usage.php',
        'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Api\\Api' => __DIR__ . '/../..' . '/app/Api/Api.php',
        'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Api\\Import' => __DIR__ . '/../..' . '/app/Api/Import.php',
        'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Api\\Maps' => __DIR__ . '/../..' . '/app/Api/Maps.php',
        'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Blocks\\Blocks' => __DIR__ . '/../..' . '/app/Blocks/Blocks.php',
        'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Blocks\\BusinessInfo' => __DIR__ . '/../..' . '/app/Blocks/BusinessInfo.php',
        'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Blocks\\LocationCategories' => __DIR__ . '/../..' . '/app/Blocks/LocationCategories.php',
        'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Blocks\\Locations' => __DIR__ . '/../..' . '/app/Blocks/Locations.php',
        'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Blocks\\Map' => __DIR__ . '/../..' . '/app/Blocks/Map.php',
        'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Blocks\\OpeningHours' => __DIR__ . '/../..' . '/app/Blocks/OpeningHours.php',
        'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Import\\Helpers' => __DIR__ . '/../..' . '/app/Import/Helpers.php',
        'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Import\\Import' => __DIR__ . '/../..' . '/app/Import/Import.php',
        'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Import\\Importer' => __DIR__ . '/../..' . '/app/Import/Importer.php',
        'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Import\\Notifications' => __DIR__ . '/../..' . '/app/Import/Notifications.php',
        'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Import\\Plugins\\RankMath' => __DIR__ . '/../..' . '/app/Import/Plugins/RankMath.php',
        'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Import\\Plugins\\SeoPress' => __DIR__ . '/../..' . '/app/Import/Plugins/SeoPress.php',
        'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Import\\Plugins\\YoastSeo' => __DIR__ . '/../..' . '/app/Import/Plugins/YoastSeo.php',
        'AIOSEO\\Plugin\\Addon\\LocalBusiness\\LocalBusiness' => __DIR__ . '/../..' . '/app/LocalBusiness.php',
        'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Locations\\Locations' => __DIR__ . '/../..' . '/app/Locations/Locations.php',
        'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Locations\\Maps' => __DIR__ . '/../..' . '/app/Locations/Maps.php',
        'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Main\\Activate' => __DIR__ . '/../..' . '/app/Main/Activate.php',
        'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Main\\Search' => __DIR__ . '/../..' . '/app/Main/Search.php',
        'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Main\\Updates' => __DIR__ . '/../..' . '/app/Main/Updates.php',
        'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Models\\Post' => __DIR__ . '/../..' . '/app/Models/Post.php',
        'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Schema\\Graphs\\Base' => __DIR__ . '/../..' . '/app/Schema/Graphs/Base.php',
        'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Schema\\Graphs\\KgOrganization' => __DIR__ . '/../..' . '/app/Schema/Graphs/KgOrganization.php',
        'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Schema\\Graphs\\LocalBusiness' => __DIR__ . '/../..' . '/app/Schema/Graphs/LocalBusiness.php',
        'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Schema\\Schema' => __DIR__ . '/../..' . '/app/Schema/Schema.php',
        'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Shortcodes\\Shortcodes' => __DIR__ . '/../..' . '/app/Shortcodes/Shortcodes.php',
        'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Utils\\Access' => __DIR__ . '/../..' . '/app/Utils/Access.php',
        'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Utils\\Assets' => __DIR__ . '/../..' . '/app/Utils/Assets.php',
        'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Utils\\Cache' => __DIR__ . '/../..' . '/app/Utils/Cache.php',
        'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Utils\\Helpers' => __DIR__ . '/../..' . '/app/Utils/Helpers.php',
        'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Utils\\InternalOptions' => __DIR__ . '/../..' . '/app/Utils/InternalOptions.php',
        'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Utils\\Tags' => __DIR__ . '/../..' . '/app/Utils/Tags.php',
        'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Utils\\Templates' => __DIR__ . '/../..' . '/app/Utils/Templates.php',
        'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Widgets\\BusinessInfo' => __DIR__ . '/../..' . '/app/Widgets/BusinessInfo.php',
        'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Widgets\\Locations' => __DIR__ . '/../..' . '/app/Widgets/Locations.php',
        'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Widgets\\Map' => __DIR__ . '/../..' . '/app/Widgets/Map.php',
        'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Widgets\\OpeningHours' => __DIR__ . '/../..' . '/app/Widgets/OpeningHours.php',
        'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Widgets\\Widgets' => __DIR__ . '/../..' . '/app/Widgets/Widgets.php',
        'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
    );

    public static function getInitializer(ClassLoader $loader)
    {
        return \Closure::bind(function () use ($loader) {
            $loader->prefixLengthsPsr4 = ComposerStaticInite81999b5328d21adc9e1de3db9aedc05::$prefixLengthsPsr4;
            $loader->prefixDirsPsr4 = ComposerStaticInite81999b5328d21adc9e1de3db9aedc05::$prefixDirsPsr4;
            $loader->classMap = ComposerStaticInite81999b5328d21adc9e1de3db9aedc05::$classMap;

        }, null, ClassLoader::class);
    }
}
vendor/composer/InstalledVersions.php000064400000037417151727277740014110 0ustar00<?php

/*
 * This file is part of Composer.
 *
 * (c) Nils Adermann <naderman@naderman.de>
 *     Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Composer;

use Composer\Autoload\ClassLoader;
use Composer\Semver\VersionParser;

/**
 * This class is copied in every Composer installed project and available to all
 *
 * See also https://getcomposer.org/doc/07-runtime.md#installed-versions
 *
 * To require its presence, you can require `composer-runtime-api ^2.0`
 *
 * @final
 */
class InstalledVersions
{
    /**
     * @var mixed[]|null
     * @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
     */
    private static $installed;

    /**
     * @var bool|null
     */
    private static $canGetVendors;

    /**
     * @var array[]
     * @psalm-var array<string, array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
     */
    private static $installedByVendor = array();

    /**
     * Returns a list of all package names which are present, either by being installed, replaced or provided
     *
     * @return string[]
     * @psalm-return list<string>
     */
    public static function getInstalledPackages()
    {
        $packages = array();
        foreach (self::getInstalled() as $installed) {
            $packages[] = array_keys($installed['versions']);
        }

        if (1 === \count($packages)) {
            return $packages[0];
        }

        return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
    }

    /**
     * Returns a list of all package names with a specific type e.g. 'library'
     *
     * @param  string   $type
     * @return string[]
     * @psalm-return list<string>
     */
    public static function getInstalledPackagesByType($type)
    {
        $packagesByType = array();

        foreach (self::getInstalled() as $installed) {
            foreach ($installed['versions'] as $name => $package) {
                if (isset($package['type']) && $package['type'] === $type) {
                    $packagesByType[] = $name;
                }
            }
        }

        return $packagesByType;
    }

    /**
     * Checks whether the given package is installed
     *
     * This also returns true if the package name is provided or replaced by another package
     *
     * @param  string $packageName
     * @param  bool   $includeDevRequirements
     * @return bool
     */
    public static function isInstalled($packageName, $includeDevRequirements = true)
    {
        foreach (self::getInstalled() as $installed) {
            if (isset($installed['versions'][$packageName])) {
                return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false;
            }
        }

        return false;
    }

    /**
     * Checks whether the given package satisfies a version constraint
     *
     * e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
     *
     *   Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
     *
     * @param  VersionParser $parser      Install composer/semver to have access to this class and functionality
     * @param  string        $packageName
     * @param  string|null   $constraint  A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
     * @return bool
     */
    public static function satisfies(VersionParser $parser, $packageName, $constraint)
    {
        $constraint = $parser->parseConstraints((string) $constraint);
        $provided = $parser->parseConstraints(self::getVersionRanges($packageName));

        return $provided->matches($constraint);
    }

    /**
     * Returns a version constraint representing all the range(s) which are installed for a given package
     *
     * It is easier to use this via isInstalled() with the $constraint argument if you need to check
     * whether a given version of a package is installed, and not just whether it exists
     *
     * @param  string $packageName
     * @return string Version constraint usable with composer/semver
     */
    public static function getVersionRanges($packageName)
    {
        foreach (self::getInstalled() as $installed) {
            if (!isset($installed['versions'][$packageName])) {
                continue;
            }

            $ranges = array();
            if (isset($installed['versions'][$packageName]['pretty_version'])) {
                $ranges[] = $installed['versions'][$packageName]['pretty_version'];
            }
            if (array_key_exists('aliases', $installed['versions'][$packageName])) {
                $ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
            }
            if (array_key_exists('replaced', $installed['versions'][$packageName])) {
                $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
            }
            if (array_key_exists('provided', $installed['versions'][$packageName])) {
                $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
            }

            return implode(' || ', $ranges);
        }

        throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
    }

    /**
     * @param  string      $packageName
     * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
     */
    public static function getVersion($packageName)
    {
        foreach (self::getInstalled() as $installed) {
            if (!isset($installed['versions'][$packageName])) {
                continue;
            }

            if (!isset($installed['versions'][$packageName]['version'])) {
                return null;
            }

            return $installed['versions'][$packageName]['version'];
        }

        throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
    }

    /**
     * @param  string      $packageName
     * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
     */
    public static function getPrettyVersion($packageName)
    {
        foreach (self::getInstalled() as $installed) {
            if (!isset($installed['versions'][$packageName])) {
                continue;
            }

            if (!isset($installed['versions'][$packageName]['pretty_version'])) {
                return null;
            }

            return $installed['versions'][$packageName]['pretty_version'];
        }

        throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
    }

    /**
     * @param  string      $packageName
     * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
     */
    public static function getReference($packageName)
    {
        foreach (self::getInstalled() as $installed) {
            if (!isset($installed['versions'][$packageName])) {
                continue;
            }

            if (!isset($installed['versions'][$packageName]['reference'])) {
                return null;
            }

            return $installed['versions'][$packageName]['reference'];
        }

        throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
    }

    /**
     * @param  string      $packageName
     * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
     */
    public static function getInstallPath($packageName)
    {
        foreach (self::getInstalled() as $installed) {
            if (!isset($installed['versions'][$packageName])) {
                continue;
            }

            return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
        }

        throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
    }

    /**
     * @return array
     * @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}
     */
    public static function getRootPackage()
    {
        $installed = self::getInstalled();

        return $installed[0]['root'];
    }

    /**
     * Returns the raw installed.php data for custom implementations
     *
     * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
     * @return array[]
     * @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}
     */
    public static function getRawData()
    {
        @trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);

        if (null === self::$installed) {
            // only require the installed.php file if this file is loaded from its dumped location,
            // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
            if (substr(__DIR__, -8, 1) !== 'C') {
                self::$installed = include __DIR__ . '/installed.php';
            } else {
                self::$installed = array();
            }
        }

        return self::$installed;
    }

    /**
     * Returns the raw data of all installed.php which are currently loaded for custom implementations
     *
     * @return array[]
     * @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
     */
    public static function getAllRawData()
    {
        return self::getInstalled();
    }

    /**
     * Lets you reload the static array from another file
     *
     * This is only useful for complex integrations in which a project needs to use
     * this class but then also needs to execute another project's autoloader in process,
     * and wants to ensure both projects have access to their version of installed.php.
     *
     * A typical case would be PHPUnit, where it would need to make sure it reads all
     * the data it needs from this class, then call reload() with
     * `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
     * the project in which it runs can then also use this class safely, without
     * interference between PHPUnit's dependencies and the project's dependencies.
     *
     * @param  array[] $data A vendor/composer/installed.php data set
     * @return void
     *
     * @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $data
     */
    public static function reload($data)
    {
        self::$installed = $data;
        self::$installedByVendor = array();
    }

    /**
     * @return array[]
     * @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
     */
    private static function getInstalled()
    {
        if (null === self::$canGetVendors) {
            self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
        }

        $installed = array();

        if (self::$canGetVendors) {
            foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
                if (isset(self::$installedByVendor[$vendorDir])) {
                    $installed[] = self::$installedByVendor[$vendorDir];
                } elseif (is_file($vendorDir.'/composer/installed.php')) {
                    /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
                    $required = require $vendorDir.'/composer/installed.php';
                    $installed[] = self::$installedByVendor[$vendorDir] = $required;
                    if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
                        self::$installed = $installed[count($installed) - 1];
                    }
                }
            }
        }

        if (null === self::$installed) {
            // only require the installed.php file if this file is loaded from its dumped location,
            // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
            if (substr(__DIR__, -8, 1) !== 'C') {
                /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
                $required = require __DIR__ . '/installed.php';
                self::$installed = $required;
            } else {
                self::$installed = array();
            }
        }

        if (self::$installed !== array()) {
            $installed[] = self::$installed;
        }

        return $installed;
    }
}
vendor/composer/autoload_classmap.php000064400000011331151727277740014116 0ustar00<?php

// autoload_classmap.php @generated by Composer

$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);

return array(
    'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Admin\\Admin' => $baseDir . '/app/Admin/Admin.php',
    'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Admin\\Location' => $baseDir . '/app/Admin/Location.php',
    'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Admin\\LocationCategory' => $baseDir . '/app/Admin/LocationCategory.php',
    'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Admin\\Usage' => $baseDir . '/app/Admin/Usage.php',
    'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Api\\Api' => $baseDir . '/app/Api/Api.php',
    'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Api\\Import' => $baseDir . '/app/Api/Import.php',
    'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Api\\Maps' => $baseDir . '/app/Api/Maps.php',
    'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Blocks\\Blocks' => $baseDir . '/app/Blocks/Blocks.php',
    'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Blocks\\BusinessInfo' => $baseDir . '/app/Blocks/BusinessInfo.php',
    'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Blocks\\LocationCategories' => $baseDir . '/app/Blocks/LocationCategories.php',
    'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Blocks\\Locations' => $baseDir . '/app/Blocks/Locations.php',
    'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Blocks\\Map' => $baseDir . '/app/Blocks/Map.php',
    'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Blocks\\OpeningHours' => $baseDir . '/app/Blocks/OpeningHours.php',
    'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Import\\Helpers' => $baseDir . '/app/Import/Helpers.php',
    'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Import\\Import' => $baseDir . '/app/Import/Import.php',
    'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Import\\Importer' => $baseDir . '/app/Import/Importer.php',
    'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Import\\Notifications' => $baseDir . '/app/Import/Notifications.php',
    'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Import\\Plugins\\RankMath' => $baseDir . '/app/Import/Plugins/RankMath.php',
    'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Import\\Plugins\\SeoPress' => $baseDir . '/app/Import/Plugins/SeoPress.php',
    'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Import\\Plugins\\YoastSeo' => $baseDir . '/app/Import/Plugins/YoastSeo.php',
    'AIOSEO\\Plugin\\Addon\\LocalBusiness\\LocalBusiness' => $baseDir . '/app/LocalBusiness.php',
    'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Locations\\Locations' => $baseDir . '/app/Locations/Locations.php',
    'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Locations\\Maps' => $baseDir . '/app/Locations/Maps.php',
    'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Main\\Activate' => $baseDir . '/app/Main/Activate.php',
    'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Main\\Search' => $baseDir . '/app/Main/Search.php',
    'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Main\\Updates' => $baseDir . '/app/Main/Updates.php',
    'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Models\\Post' => $baseDir . '/app/Models/Post.php',
    'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Schema\\Graphs\\Base' => $baseDir . '/app/Schema/Graphs/Base.php',
    'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Schema\\Graphs\\KgOrganization' => $baseDir . '/app/Schema/Graphs/KgOrganization.php',
    'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Schema\\Graphs\\LocalBusiness' => $baseDir . '/app/Schema/Graphs/LocalBusiness.php',
    'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Schema\\Schema' => $baseDir . '/app/Schema/Schema.php',
    'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Shortcodes\\Shortcodes' => $baseDir . '/app/Shortcodes/Shortcodes.php',
    'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Utils\\Access' => $baseDir . '/app/Utils/Access.php',
    'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Utils\\Assets' => $baseDir . '/app/Utils/Assets.php',
    'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Utils\\Cache' => $baseDir . '/app/Utils/Cache.php',
    'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Utils\\Helpers' => $baseDir . '/app/Utils/Helpers.php',
    'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Utils\\InternalOptions' => $baseDir . '/app/Utils/InternalOptions.php',
    'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Utils\\Tags' => $baseDir . '/app/Utils/Tags.php',
    'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Utils\\Templates' => $baseDir . '/app/Utils/Templates.php',
    'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Widgets\\BusinessInfo' => $baseDir . '/app/Widgets/BusinessInfo.php',
    'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Widgets\\Locations' => $baseDir . '/app/Widgets/Locations.php',
    'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Widgets\\Map' => $baseDir . '/app/Widgets/Map.php',
    'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Widgets\\OpeningHours' => $baseDir . '/app/Widgets/OpeningHours.php',
    'AIOSEO\\Plugin\\Addon\\LocalBusiness\\Widgets\\Widgets' => $baseDir . '/app/Widgets/Widgets.php',
    'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
);
vendor/composer/LICENSE000064400000002056151727277740010723 0ustar00
Copyright (c) Nils Adermann, Jordi Boggiano

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

vendor/composer/ClassLoader.php000064400000037772151727277740012640 0ustar00<?php

/*
 * This file is part of Composer.
 *
 * (c) Nils Adermann <naderman@naderman.de>
 *     Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Composer\Autoload;

/**
 * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
 *
 *     $loader = new \Composer\Autoload\ClassLoader();
 *
 *     // register classes with namespaces
 *     $loader->add('Symfony\Component', __DIR__.'/component');
 *     $loader->add('Symfony',           __DIR__.'/framework');
 *
 *     // activate the autoloader
 *     $loader->register();
 *
 *     // to enable searching the include path (eg. for PEAR packages)
 *     $loader->setUseIncludePath(true);
 *
 * In this example, if you try to use a class in the Symfony\Component
 * namespace or one of its children (Symfony\Component\Console for instance),
 * the autoloader will first look for the class under the component/
 * directory, and it will then fallback to the framework/ directory if not
 * found before giving up.
 *
 * This class is loosely based on the Symfony UniversalClassLoader.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 * @author Jordi Boggiano <j.boggiano@seld.be>
 * @see    https://www.php-fig.org/psr/psr-0/
 * @see    https://www.php-fig.org/psr/psr-4/
 */
class ClassLoader
{
    /** @var \Closure(string):void */
    private static $includeFile;

    /** @var string|null */
    private $vendorDir;

    // PSR-4
    /**
     * @var array<string, array<string, int>>
     */
    private $prefixLengthsPsr4 = array();
    /**
     * @var array<string, list<string>>
     */
    private $prefixDirsPsr4 = array();
    /**
     * @var list<string>
     */
    private $fallbackDirsPsr4 = array();

    // PSR-0
    /**
     * List of PSR-0 prefixes
     *
     * Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2')))
     *
     * @var array<string, array<string, list<string>>>
     */
    private $prefixesPsr0 = array();
    /**
     * @var list<string>
     */
    private $fallbackDirsPsr0 = array();

    /** @var bool */
    private $useIncludePath = false;

    /**
     * @var array<string, string>
     */
    private $classMap = array();

    /** @var bool */
    private $classMapAuthoritative = false;

    /**
     * @var array<string, bool>
     */
    private $missingClasses = array();

    /** @var string|null */
    private $apcuPrefix;

    /**
     * @var array<string, self>
     */
    private static $registeredLoaders = array();

    /**
     * @param string|null $vendorDir
     */
    public function __construct($vendorDir = null)
    {
        $this->vendorDir = $vendorDir;
        self::initializeIncludeClosure();
    }

    /**
     * @return array<string, list<string>>
     */
    public function getPrefixes()
    {
        if (!empty($this->prefixesPsr0)) {
            return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
        }

        return array();
    }

    /**
     * @return array<string, list<string>>
     */
    public function getPrefixesPsr4()
    {
        return $this->prefixDirsPsr4;
    }

    /**
     * @return list<string>
     */
    public function getFallbackDirs()
    {
        return $this->fallbackDirsPsr0;
    }

    /**
     * @return list<string>
     */
    public function getFallbackDirsPsr4()
    {
        return $this->fallbackDirsPsr4;
    }

    /**
     * @return array<string, string> Array of classname => path
     */
    public function getClassMap()
    {
        return $this->classMap;
    }

    /**
     * @param array<string, string> $classMap Class to filename map
     *
     * @return void
     */
    public function addClassMap(array $classMap)
    {
        if ($this->classMap) {
            $this->classMap = array_merge($this->classMap, $classMap);
        } else {
            $this->classMap = $classMap;
        }
    }

    /**
     * Registers a set of PSR-0 directories for a given prefix, either
     * appending or prepending to the ones previously set for this prefix.
     *
     * @param string              $prefix  The prefix
     * @param list<string>|string $paths   The PSR-0 root directories
     * @param bool                $prepend Whether to prepend the directories
     *
     * @return void
     */
    public function add($prefix, $paths, $prepend = false)
    {
        $paths = (array) $paths;
        if (!$prefix) {
            if ($prepend) {
                $this->fallbackDirsPsr0 = array_merge(
                    $paths,
                    $this->fallbackDirsPsr0
                );
            } else {
                $this->fallbackDirsPsr0 = array_merge(
                    $this->fallbackDirsPsr0,
                    $paths
                );
            }

            return;
        }

        $first = $prefix[0];
        if (!isset($this->prefixesPsr0[$first][$prefix])) {
            $this->prefixesPsr0[$first][$prefix] = $paths;

            return;
        }
        if ($prepend) {
            $this->prefixesPsr0[$first][$prefix] = array_merge(
                $paths,
                $this->prefixesPsr0[$first][$prefix]
            );
        } else {
            $this->prefixesPsr0[$first][$prefix] = array_merge(
                $this->prefixesPsr0[$first][$prefix],
                $paths
            );
        }
    }

    /**
     * Registers a set of PSR-4 directories for a given namespace, either
     * appending or prepending to the ones previously set for this namespace.
     *
     * @param string              $prefix  The prefix/namespace, with trailing '\\'
     * @param list<string>|string $paths   The PSR-4 base directories
     * @param bool                $prepend Whether to prepend the directories
     *
     * @throws \InvalidArgumentException
     *
     * @return void
     */
    public function addPsr4($prefix, $paths, $prepend = false)
    {
        $paths = (array) $paths;
        if (!$prefix) {
            // Register directories for the root namespace.
            if ($prepend) {
                $this->fallbackDirsPsr4 = array_merge(
                    $paths,
                    $this->fallbackDirsPsr4
                );
            } else {
                $this->fallbackDirsPsr4 = array_merge(
                    $this->fallbackDirsPsr4,
                    $paths
                );
            }
        } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
            // Register directories for a new namespace.
            $length = strlen($prefix);
            if ('\\' !== $prefix[$length - 1]) {
                throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
            }
            $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
            $this->prefixDirsPsr4[$prefix] = $paths;
        } elseif ($prepend) {
            // Prepend directories for an already registered namespace.
            $this->prefixDirsPsr4[$prefix] = array_merge(
                $paths,
                $this->prefixDirsPsr4[$prefix]
            );
        } else {
            // Append directories for an already registered namespace.
            $this->prefixDirsPsr4[$prefix] = array_merge(
                $this->prefixDirsPsr4[$prefix],
                $paths
            );
        }
    }

    /**
     * Registers a set of PSR-0 directories for a given prefix,
     * replacing any others previously set for this prefix.
     *
     * @param string              $prefix The prefix
     * @param list<string>|string $paths  The PSR-0 base directories
     *
     * @return void
     */
    public function set($prefix, $paths)
    {
        if (!$prefix) {
            $this->fallbackDirsPsr0 = (array) $paths;
        } else {
            $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
        }
    }

    /**
     * Registers a set of PSR-4 directories for a given namespace,
     * replacing any others previously set for this namespace.
     *
     * @param string              $prefix The prefix/namespace, with trailing '\\'
     * @param list<string>|string $paths  The PSR-4 base directories
     *
     * @throws \InvalidArgumentException
     *
     * @return void
     */
    public function setPsr4($prefix, $paths)
    {
        if (!$prefix) {
            $this->fallbackDirsPsr4 = (array) $paths;
        } else {
            $length = strlen($prefix);
            if ('\\' !== $prefix[$length - 1]) {
                throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
            }
            $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
            $this->prefixDirsPsr4[$prefix] = (array) $paths;
        }
    }

    /**
     * Turns on searching the include path for class files.
     *
     * @param bool $useIncludePath
     *
     * @return void
     */
    public function setUseIncludePath($useIncludePath)
    {
        $this->useIncludePath = $useIncludePath;
    }

    /**
     * Can be used to check if the autoloader uses the include path to check
     * for classes.
     *
     * @return bool
     */
    public function getUseIncludePath()
    {
        return $this->useIncludePath;
    }

    /**
     * Turns off searching the prefix and fallback directories for classes
     * that have not been registered with the class map.
     *
     * @param bool $classMapAuthoritative
     *
     * @return void
     */
    public function setClassMapAuthoritative($classMapAuthoritative)
    {
        $this->classMapAuthoritative = $classMapAuthoritative;
    }

    /**
     * Should class lookup fail if not found in the current class map?
     *
     * @return bool
     */
    public function isClassMapAuthoritative()
    {
        return $this->classMapAuthoritative;
    }

    /**
     * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
     *
     * @param string|null $apcuPrefix
     *
     * @return void
     */
    public function setApcuPrefix($apcuPrefix)
    {
        $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
    }

    /**
     * The APCu prefix in use, or null if APCu caching is not enabled.
     *
     * @return string|null
     */
    public function getApcuPrefix()
    {
        return $this->apcuPrefix;
    }

    /**
     * Registers this instance as an autoloader.
     *
     * @param bool $prepend Whether to prepend the autoloader or not
     *
     * @return void
     */
    public function register($prepend = false)
    {
        spl_autoload_register(array($this, 'loadClass'), true, $prepend);

        if (null === $this->vendorDir) {
            return;
        }

        if ($prepend) {
            self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
        } else {
            unset(self::$registeredLoaders[$this->vendorDir]);
            self::$registeredLoaders[$this->vendorDir] = $this;
        }
    }

    /**
     * Unregisters this instance as an autoloader.
     *
     * @return void
     */
    public function unregister()
    {
        spl_autoload_unregister(array($this, 'loadClass'));

        if (null !== $this->vendorDir) {
            unset(self::$registeredLoaders[$this->vendorDir]);
        }
    }

    /**
     * Loads the given class or interface.
     *
     * @param  string    $class The name of the class
     * @return true|null True if loaded, null otherwise
     */
    public function loadClass($class)
    {
        if ($file = $this->findFile($class)) {
            $includeFile = self::$includeFile;
            $includeFile($file);

            return true;
        }

        return null;
    }

    /**
     * Finds the path to the file where the class is defined.
     *
     * @param string $class The name of the class
     *
     * @return string|false The path if found, false otherwise
     */
    public function findFile($class)
    {
        // class map lookup
        if (isset($this->classMap[$class])) {
            return $this->classMap[$class];
        }
        if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
            return false;
        }
        if (null !== $this->apcuPrefix) {
            $file = apcu_fetch($this->apcuPrefix.$class, $hit);
            if ($hit) {
                return $file;
            }
        }

        $file = $this->findFileWithExtension($class, '.php');

        // Search for Hack files if we are running on HHVM
        if (false === $file && defined('HHVM_VERSION')) {
            $file = $this->findFileWithExtension($class, '.hh');
        }

        if (null !== $this->apcuPrefix) {
            apcu_add($this->apcuPrefix.$class, $file);
        }

        if (false === $file) {
            // Remember that this class does not exist.
            $this->missingClasses[$class] = true;
        }

        return $file;
    }

    /**
     * Returns the currently registered loaders keyed by their corresponding vendor directories.
     *
     * @return array<string, self>
     */
    public static function getRegisteredLoaders()
    {
        return self::$registeredLoaders;
    }

    /**
     * @param  string       $class
     * @param  string       $ext
     * @return string|false
     */
    private function findFileWithExtension($class, $ext)
    {
        // PSR-4 lookup
        $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;

        $first = $class[0];
        if (isset($this->prefixLengthsPsr4[$first])) {
            $subPath = $class;
            while (false !== $lastPos = strrpos($subPath, '\\')) {
                $subPath = substr($subPath, 0, $lastPos);
                $search = $subPath . '\\';
                if (isset($this->prefixDirsPsr4[$search])) {
                    $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
                    foreach ($this->prefixDirsPsr4[$search] as $dir) {
                        if (file_exists($file = $dir . $pathEnd)) {
                            return $file;
                        }
                    }
                }
            }
        }

        // PSR-4 fallback dirs
        foreach ($this->fallbackDirsPsr4 as $dir) {
            if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
                return $file;
            }
        }

        // PSR-0 lookup
        if (false !== $pos = strrpos($class, '\\')) {
            // namespaced class name
            $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
                . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
        } else {
            // PEAR-like class name
            $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
        }

        if (isset($this->prefixesPsr0[$first])) {
            foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
                if (0 === strpos($class, $prefix)) {
                    foreach ($dirs as $dir) {
                        if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
                            return $file;
                        }
                    }
                }
            }
        }

        // PSR-0 fallback dirs
        foreach ($this->fallbackDirsPsr0 as $dir) {
            if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
                return $file;
            }
        }

        // PSR-0 include paths.
        if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
            return $file;
        }

        return false;
    }

    /**
     * @return void
     */
    private static function initializeIncludeClosure()
    {
        if (self::$includeFile !== null) {
            return;
        }

        /**
         * Scope isolated include.
         *
         * Prevents access to $this/self from included files.
         *
         * @param  string $file
         * @return void
         */
        self::$includeFile = \Closure::bind(static function($file) {
            include $file;
        }, null, null);
    }
}
vendor/composer/installed.php000064400000001437151727277740012410 0ustar00<?php return array(
    'root' => array(
        'name' => 'awesomemotive/aioseo-local-business',
        'pretty_version' => 'dev-develop',
        'version' => 'dev-develop',
        'reference' => '44761df19943557b2a14d53aa26e02c8f6c0903b',
        'type' => 'library',
        'install_path' => __DIR__ . '/../../',
        'aliases' => array(),
        'dev' => false,
    ),
    'versions' => array(
        'awesomemotive/aioseo-local-business' => array(
            'pretty_version' => 'dev-develop',
            'version' => 'dev-develop',
            'reference' => '44761df19943557b2a14d53aa26e02c8f6c0903b',
            'type' => 'library',
            'install_path' => __DIR__ . '/../../',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
    ),
);
vendor/composer/autoload_real.php000064400000002077151727277740013245 0ustar00<?php

// autoload_real.php @generated by Composer

class ComposerAutoloaderInite81999b5328d21adc9e1de3db9aedc05
{
    private static $loader;

    public static function loadClassLoader($class)
    {
        if ('Composer\Autoload\ClassLoader' === $class) {
            require __DIR__ . '/ClassLoader.php';
        }
    }

    /**
     * @return \Composer\Autoload\ClassLoader
     */
    public static function getLoader()
    {
        if (null !== self::$loader) {
            return self::$loader;
        }

        spl_autoload_register(array('ComposerAutoloaderInite81999b5328d21adc9e1de3db9aedc05', 'loadClassLoader'), true, true);
        self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
        spl_autoload_unregister(array('ComposerAutoloaderInite81999b5328d21adc9e1de3db9aedc05', 'loadClassLoader'));

        require __DIR__ . '/autoload_static.php';
        call_user_func(\Composer\Autoload\ComposerStaticInite81999b5328d21adc9e1de3db9aedc05::getInitializer($loader));

        $loader->register(true);

        return $loader;
    }
}
vendor/composer/autoload_psr4.php000064400000000317151727277740013205 0ustar00<?php

// autoload_psr4.php @generated by Composer

$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);

return array(
    'AIOSEO\\Plugin\\Addon\\LocalBusiness\\' => array($baseDir . '/app'),
);
vendor/composer/autoload_namespaces.php000064400000000213151727277740014427 0ustar00<?php

// autoload_namespaces.php @generated by Composer

$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);

return array(
);
vendor/autoload.php000064400000001403151727277740010403 0ustar00<?php

// autoload.php @generated by Composer

if (PHP_VERSION_ID < 50600) {
    if (!headers_sent()) {
        header('HTTP/1.1 500 Internal Server Error');
    }
    $err = 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
    if (!ini_get('display_errors')) {
        if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
            fwrite(STDERR, $err);
        } elseif (!headers_sent()) {
            echo $err;
        }
    }
    trigger_error(
        $err,
        E_USER_ERROR
    );
}

require_once __DIR__ . '/composer/autoload_real.php';

return ComposerAutoloaderInite81999b5328d21adc9e1de3db9aedc05::getLoader();
bun.lockb000064400000102010151727277740006361 0ustar00#!/usr/bin/env bun
bun-lockfile-format-v0
���C���*��~mTKW>MP�0�~�!�j�,�Px�Z�rY�vitesass�
��	�chokidarfseventsreaddirp5	���is-glob�
���|���bracesI
����	�anymatchrolluppostcss�
�nanoidesbuildH�5�!����������������k�X�D�2�������������#��globbyslashmerge2ignoreglob��oncewrappy�	����
���inheritsinflightw��	�w
�g�fastqreusify����	�W�dir-glob�	�����|���fs-extraY�jsonfileN��	����rtlcss�escaladedotenv
�ǣ{_U (iG	�xR�B��hx+գr�k�\3N�X&,>g��]?/�������"�"R�U`��a�|]�=��nw�f�.Ou5
��ڄ�3D��t�Њd��Viܞ�C��������=��5hX2T���*/H��[�u��U뒵Pl�H�A�\a���L�s�H���$|_O$"�֋#=Cw�6L̶���z>+�����|�U��周G��;�#j�����h_:x�t�?��b�	�l`��֑�A*(3��ѡ�(�O�Fi]r?'���~j�%�ۃfF1��i�<�4m���G&�@
D鞮v�0B�_��0���cZ��1R�6eR>�+��P�F�m�5�^h�a�� Y�sl[;��`n���	)e�����f��R���e�jfW��YNE���~_LqȰB��xa
Ĝ��'|v��^Q��pY�����(ɍ|�xWQ�M]�������g�,������6n��1?�%|��měX�Q˿,��;UJv�J�0N�]$ET?�,\�M�Z��)�)�M���;H^z�JK�d������s���˕�h�ܻ�!s,��p����Z/��Y��/Z�&�=9�����a�]}�5Y~"��JETΈKè�8�v�8J��2�x	���`t�G� ��Q����sP���Jɚ��pO��w��5�l�M[x_{�\�{�� E6</�/�l80�l�/�l�b��
�@0�	�1�B�B�,:�f8��8��8�>:�xD��6��<�8D��J��>�4�S<��D��:�$8�k5��7��<�+4�j8�ZE��G��I�/	E�t	I��	G�
E�I
I��
M��
I�(O�wM��G�I�TE��I��M�/
G�v
K��
I�
M�WI��L�F�Y5�
�2��4�4�G0��H��0�4�G:��G��<�+D�o8��8��>�:��<��?�3�B6�xE��@�F�^?��8��:�>�V7��8��A�8�e@��8��?�:�V?��D��4�-N�{8��5� !""$%(((>>>>>>>>>>>>>>>>>>>>>>>CCKKKKQQRRSUUUUWW\^`aacdddeeegggjjkkklnrrr !""$%(((>>>>>>>>>>>>>>>>>>>>>>>CCKKKKQQRRSUUUUWW\^`aacdddeeegggjjkkklnrrr�����fԟb�Rc@Ilԉ�A��NuMk'i����H��#1��Z���@�EV�c(�o�����V��\���G>�;8�`���3�07���l*�? �dH4��+U@σ^"���O
��$I��#\ߴ��GE�T�V���0��`�  ��W��0G]f9 �	n���v+xK�	P#K�3�	p�g�Nx��R����~�޻�x���Y(���<���U�Y��2#9I���O���W������Cwq\D��o�����45�����|�O�܍���I�%��2��u�1H�C���]t�RT�8�uP2U�1\�8ӂ?d-kN�:���}�/��9ڂe���m��ﵦ0ʪd��-���X����[L����M6�e͠�3��C������'�n���Y߰4(�>��~�ߏy�ݩ)��I��o �A'j~C>�m�:=UL���%M�xt͎a6T!���<|�
\������x@X�U�h���%:��D]t�m+./c� V�T��	��l�7ZТ�u��-�S/�JF��UK=�j�����oNu�D��u0P�肛BF6>v��>b��G0,��
��Rk!�ߦ`�V�X��m���͗ã����BC�]{a�7���>�u�fd��񄴧�k��RJ��I���
�MՋ��f�=-�I2~2͜(�J2+����%�[;~�~����H@힮IG��x���d�a�:�<���vǻ:��;YW���G�;��1R)�6[�DO���N5��nH}�KyW3���F_��
�7-'�A�h')(xv�^��4�5�$�h����p�SI��ء�=�x^C��m>��4��(��� I��"X���Fq���gN��slc��������d��y�l�gmÜ��3#9D�+�l�i/Y���o˦��Q���
,�n�#=r}6�����
�&�&Y:ϭ��X��\�=m����)d���~�������=�LR`�-��o��qL�›�Q�� ��B��Ee���J�u
�k}67�*�(=���h���D������>��!+����1m�0�I7��t�mg�ý�ꆷI?��|z�"�4F��`���&!���H�<����P�~H:{��*]�:^-S*� DW4�{�D>���gg�
�M4��K�t
<9J���6��efTV�]�q���(�^J��~げ4w)����v]��;�Tp��ܦ��O+�W����Dl���R�d�t�:�G��s盽�T��*B�;����dQdF0��V��7>�|%�%�jd����ǮdV_wI$��zÖ��q�>���z����j��D �~�]�*�W��aL!1H�Bv��Lj_{�*�\�+�M�&�Z�ǜ��'������˔{��8��Z�2F	��r�-��!���1��ɼ�#[�T�USF�Fŷ��n&��ȶ$���g*0�nN�{�Z�c�H�gX�	N���G�m�gd�
h�A���c��֋��P"F%̱̂CP���q걡�T�Ʋ��;e~�u���dN�%A�.�"S��T���Q�G��RS�d;���H���_���7_EĢ
�k��@�'�M����	�/�ǣ��wE��OkF��<��f��B�Gr.�m�S��m�W� y�Z��-���9=<HSK������]���M�L�c#5�Č�o��{�a� B�|��/���6 �'���u�G�-b
���&dh9�������:6�bim�s�� ���}T�WZU4
��9�'��DO}��>@�6�DZtb��UZ�Q�EM����s����o�(P���t�j�9d�!7��噒1|��'��q7�0� {��a+.A��s2���?� ��{{1=Т1�Kk�z&N'���m�捐J�i;#��#s`�A����\��O�Rim��%>Y��h��+;�2(�ET���&V��7�7��C��M� �[f㌴�Q���驄�
�Q,�����80�j�k,Z�.�TVFZ��L��*���B_���O�N�
bg�{�� �ͷ�c,�`��kX�ѭlCz!}�s������Y�������Γ;��'�/\����2�U!Y,h�^p�W~
�9��g*H��0Q,D
c���kA���23A	l3k��0eKsFq4)��Π\F�j��"Xs�y'5ɀ6rВ��%��C�ݬ*	�aj����6{ �m7Y��FbO|
��@�H�9E,rG��#w�^iv\�͌�Vu��T��=���*�q:o�7ΉT�1���φ絳IҜ¥AYN���l��$�u���S��C,�cە��C��:���̪�W��{ ��\Y3��[B|κ�!����&��%?��[
�"�^OA5(u8ѳ�����^�z�!>��{���Ht��-��j��v�ڪ9�J�
_Tl{L&ن�p_�/��R)�k�Ϸ.�2c�{}i�ڡ3(��s�Ko!��i�����[�aa�1#�ot'��ǐŧ�Q *�U�����aYz$�`�P�8pmt����6=&b�Ai`��; oe�$��p9�������(��<����јpj���/�7���6�D0�\w��}�l_��#�枞�{r��my�@����	,91)ʠ�.��0��0`�/����Ȥ`��@��
�u�
��p�4�,����zh��ͺ[w
G�~O*��`�S�9��z����v��*�\�)x�	�;��t;���J�%È$aQ��v�#U�"B���+oG[�S3KiOL�$�/���ԝ3dt��X�G%��dO���ݴ��M�{����{���@f	�,�`ݕ�����VX%_㞴@��Έւ�\���∺��7�0.�3cf��|z��'��&�;Q�R-7>+%�!;D5]0�
k���<��׺�V8u�1����1%��ɂ4a
b��s�f�o����-.(�L2?�2�5��$*�nS��^	�Dj�h��.�
�$dݗ�{nHG��tX��;��º�Z:��K��/����آ�L�*�
�v1kD/q����ݶ����W��+�YZ�<+� ��C���*	����<��0^zq�͒��b�̾�xSR���<C̛��Ī\�P�����͇h0X�W����3�����1�+8�q5�B,�rd��Մ6��5
=�:���(�+&�̏gA�M��[b-+5���Gȶ�S����2��5����~�I+po��a�h�U(���Ƅ��y}-o��||�x��[k���Eu�@�̀t3"���3��	[�|���?/ ��\C]oD��Y�(#8�t�N	�W7;�OΗ3 ��?��ђN:��40��l}�q������}�:@w��A5�{ב
�b��Z�.n"0Զ�U�u1C��p�?y��5�Tt̺Tـ�G(���K�bE�UH�˰������$XaM�>.V	��X��v�3`FF�K�$
���6V��Bv{����i/@�G�{S��Ʃ4�w�"#)��C�8_$닏ԟ��OT0z��O<0��{�B��7�։���C�@pB�M�iwT�vYTe�@.hJ��O�����غg�3;�o�'����w���8����4lz������]�N�'�wL����}^H!LF��OK\T�����;�1��A�)@�\Gt���q��9'�z�b<5410�U��~�"+9c�]͝�9��<ܛ—�W�t�1@�=Q,�q~��C�+�����:�+�1`�u�k�#�q�=;�!��D�
���U�ú�_&
�L�����H5󻟣7Z�q�[�Z����;�*���0�A~�F�?h�=ҭ@�~kN�FG���V�*uq�f��e��;q�]�0�wht[ے����<ބ�P�1^���@���+���N�!�oCd�GХ��'��@i}�t����i[?%!!	��9?��=���i~?bV�[<�
��9v {�8�g�n�1�Δ�$(�Yد���3��J�x�W+�u��!
����>�݈��<��/���~�®:��(���V�D��6[����Q�[~���YG�"�2�:����$��?8�)iJ�!fpj�H�1�#�T��;��g�D�(�]���NI��0�?���
�li�;�+[x����@��O�8a�£�Im�ϰ���Z���}S}R:�B�T�-�k^g
Cy��/��
�P�"�1W�X���A̾[j�gc�cW�
�冔HX�;��&�ė�#��.��f��p�]���R67[`�3Ht��O�t��B�`~S`Y��(�g��1m�R���l�Ƞ��d�ғ���˖(5!�We��Yg�zJ��C��qM�	u�U���h!��X�9�86��I�{(��N�񍼎l�@����6�A�F���.�jw��DS���Ʒ}�ސ!u�O�?R(�+��$��o]Sb\|k���ׂ�P��x�Ai���2h�S������E������Bm�(*��p���N��璝�+�u���1�o�9:4(n�`u;z�Jf=�ʦ.0����F�^ɔ|��<YM���M�ЁK0����-P�wa�4K�-�Q������P���h���1���G6�I�����%��C2h�N	�2�������*�6
YBp�"�d/����!z=����HFHO>�����@���g��[��wa"���x�/#�n�-�����|��7)=M+�uU�'���8���IZJ֧ё��(f38���eb�E�7�q�y����\C��q,���c�����p��Ѧ3s ��J�2��L
����B}3	��LW��c���IӦ�2	R��_:�,=�[Z�U=׸�%�3����Kl����S ���T{�	�wx����1��;��yY���y��+��xZ�����Љ�A?�Z����LeL[̩t!�H-4������3�wL�.l9���๳Ma.�yw�����|�b�ӬcП�l�53 u�P��M���V�J7k�e?�(v���>�d�vI�K"\e�O'9ɏ9鉙B������~����+5�5�xX
����N+EP+8�Qi*$U��H�k�y�&����da(i���p
l0�����4�'���#HP�>��T ��O�P����T(�P[ %��B�s?-���E�ˎ�������k]è�/�?�0�S�1��F���P�^#��Y�֕~�^��z�N�q��]���ɋΕ��e��b���`Na�C\>ѬԒ��Q��uG|��9_�)�-�{>l���ͻ����E��s�ČU�߰�FT�wm(l�RX@^Ô�������RE�y�9�p�Bp5y�ד��{3Y]�v�W��/�&?�i.�(�/Eh���U��y:�ʬ������Scj�:h���Cq�?�i����i��=�fO����y�8��L�>
.dd{�nXB;&�u���@���TJ�aw�I����;6��ٙ�MH�E�%�Sq�`nɚ����Iv�P3ԗ���y��x7rJX
2��UH}��ά��K�M�֋��������:f���D����l��T��6�5�{>[x�]O�C?����V[�x�'V�k�}�FAړA��\C��O���� �C�(kvʲ
�.|�Y���7��_w�$(��o����W���"�
����"����S��d���Ȯ�H5U�˦�ª9���2��/�r��W���ꗩD���X�G��$-��2��f�9ɕ;�4k���=����&1�d{���ŻI��L~��hH�e#a����Y ���?E�xlb�.���Zc VH!8�]Q�+�"�3�fT����'���N��(�aB@|[��^!vite5�sasssass.jssasssass.jssasssass.jssasssass.jssasssass.jssasssass.jssasssass.jssasssass.jssasssass.jssasssass.jssasssass.jssasssass.jssasssass.jssasssass.jssasssass.jssasssass.jssasssass.jssasssass.jsrollup\�rollup\�rollup\�nanoid�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�esbuild_�rtlcss�
�rtlcss�
�rtlcss�
�rtlcss�
��Y�Y
<src.install.lockfile.Tree> 20 sizeof, 4 alignof
��������Y�Y\[
<u32> 4 sizeof, 4 alignof
lm>?@ABnoq%kghiCDEFGHIJ()*+,-./0123456789:;<=edWX[KLMNOP#^_VR b`ST!c�[P]
<u32> 4 sizeof, 4 alignof
YU/������������������������
	

	.-,+*)('&%$#"! TSO10LKI@5432?>=9768:<;78HB3AECDHFGJNMRQPRMVXW�]i
<[26]u8> 26 sizeof, 1 alignof
dotenv{�\�{��^16.3.1�Q����sP�^4.0.6#�5�^h�a��^3.4.0sassR�B��hx^1.63.6vite(iG	�x^4.3.9esbuild#=Cw�^0.18.10postcss\a���L�^8.4.27rollupPl�H�A�^3.27.1fsevents������~2.3.2less�<���
�*p�~�us�8�2^1.21.0sassR�B��hx*stylus?�a3>��*sugarss��cԿȮ*terser�Z��#A^5.4.0|��/Z�&�=9>= 14chokidarg��]?/����	�3N�X&,>^4.0.0�
�+գr�k�\��anymatch�u��U뒵~3.1.2braces�����~3.0.2�����C���~5.1.2��D��t�Њ~2.1.0is-globw�f�.Ou5~4.0.1��|]�=��n~3.0.0readdirp"�"R�~3.6.0fsevents������~2.3.25	�U`��a�^2.2.1�
�
��ڄ�3^2.1.1|�d��Viܞ�^2.0.0is-globw�f�.Ou5^4.0.1I
�=��5h^7.0.1��X2T���^5.0.1�	�*/H��[�^7.0.0��|]�=��n^3.0.05	�U`��a�^2.0.4fsevents������~2.3.2nanoid_O$"�֋^3.3.6�
�s�H���$|^1.0.0�
�+գr�k�\^1.0.2���P�F�m�0.18.20��R>�+�0.18.20����1R�6e0.18.20���0���cZ0.18.20��0B�_�0.18.20�
D鞮v�0.18.20���G&�@0.18.202�i�<�4m�0.18.20D�ۃfF1��0.18.20X���~j�%�0.18.20k��Fi]r?'�0.18.20��ѡ�(�O0.18.20��*(3��0.18.20��l`��֑�A0.18.20��b�	�0.18.20���t�?��0.18.20����h_:x0.18.20���;�#j���0.18.20��周G�0.18.20!���|�U�0.18.205�>+���0.18.20H�6L̶���z0.18.20��G� ��^8.0.1�	�	���`t�^1.1.0fs-extra]}�5Y~"^8.1.0globby��`n��10.0.1� Y�sl[;^3.0.0��Z/��Y�^7.1.1����p����^2.1.0dir-globs���˕�h^3.0.1�	�1?�%|�^3.0.3globfW��YNE^7.1.3ignore�R���e�j^5.1.1merge2���f�^1.2.3slash�	)e��^3.0.0w��6n��^1.0.0inflight�,�����^1.0.4inherits����g2�	��^Q��p^3.1.1onceȰB��xa
^1.3.0�����~_Lq^1.0.0wrappyĜ��'|v�1��Y�����(^1.1.7��WQ�M]���^1.0.0�
�ɍ|�x0.0.1onceȰB��xa
^1.3.0wrappyĜ��'|v�1W��d������^2.0.2g��,��;UJ^1.2.3�����C���^5.1.2merge2���f�^1.3.0w
��měX�Q�^4.0.4braces�����^3.0.25	�U`��a�^2.3.1���M�Z��)2.1.5fastqv�J�0N�]^1.6.0reusify$ET?�,\^1.0.4W��d������2.0.5���)�M���^1.1.9	�;H^z�JK^1.2.2�	��ܻ�!s,^4.0.0�������a�*|��/Z�&�=9*N�8J��2�x^4.2.0jsonfileKè�8�v�^4.0.0Y���JETΈ^0.1.0N�8J��2�x^4.1.6|��/Z�&�=9*rtlcss��Jɚ��p4.1.0postcss\a���L�^8.4.21escalade�l�M[x_^3.1.1�
�s�H���$|^1.0.0postcss\a���L�^8.4.21�O��w��5^3.1.1^i^i
<src.install.semver.ExternalString> 16 sizeof, 8 alignof
�ip�
<u8> 1 sizeof, 1 alignof
aioseo-local-businesspostcss-rtlcssrollup-plugin-copybin/vite.jshttps://registry.npmjs.org/vite/-/vite-4.4.9.tgzlightningcss@types/nodehttps://registry.npmjs.org/sass/-/sass-1.66.1.tgz>=3.0.0 <4.0.0immutable>=0.6.2 <2.0.0source-map-jshttps://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgzhttps://registry.npmjs.org/immutable/-/immutable-4.3.4.tgzhttps://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgzglob-parentis-binary-pathnormalize-pathhttps://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgzhttps://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgzpicomatchhttps://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgzhttps://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgzhttps://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgzis-extglobhttps://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgzhttps://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgzbinary-extensionshttps://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgzhttps://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgzhttps://registry.npmjs.org/braces/-/braces-3.0.2.tgzfill-rangehttps://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgzto-regex-rangehttps://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgzis-numberhttps://registry.npmjs.org/is-number/-/is-number-7.0.0.tgzhttps://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgzdist/bin/rolluphttps://registry.npmjs.org/rollup/-/rollup-3.29.1.tgzhttps://registry.npmjs.org/postcss/-/postcss-8.4.29.tgzpicocolorshttps://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgzbin/nanoid.cjshttps://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgzbin/esbuildhttps://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz@esbuild/android-arm@esbuild/android-arm64@esbuild/android-x64@esbuild/darwin-arm64@esbuild/darwin-x64@esbuild/freebsd-arm64@esbuild/freebsd-x64@esbuild/linux-arm@esbuild/linux-arm64@esbuild/linux-ia32@esbuild/linux-loong64@esbuild/linux-mips64el@esbuild/linux-ppc64@esbuild/linux-riscv64@esbuild/linux-s390x@esbuild/linux-x64@esbuild/netbsd-x64@esbuild/openbsd-x64@esbuild/sunos-x64@esbuild/win32-arm64@esbuild/win32-ia32@esbuild/win32-x64https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgzhttps://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgzhttps://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgzhttps://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgzhttps://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgzhttps://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgzhttps://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgzhttps://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgzhttps://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgzhttps://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgzhttps://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgzhttps://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgzhttps://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgzhttps://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgzhttps://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgzhttps://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgzhttps://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgzhttps://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgzhttps://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgzhttps://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgzhttps://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgzhttps://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgzhttps://registry.npmjs.org/rollup-plugin-copy/-/rollup-plugin-copy-3.5.0.tgz@types/fs-extracoloretteis-plain-objecthttps://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.1.tgzhttps://registry.npmjs.org/globby/-/globby-10.0.1.tgz@types/globarray-unionfast-globhttps://registry.npmjs.org/slash/-/slash-3.0.0.tgzhttps://registry.npmjs.org/merge2/-/merge2-1.4.1.tgzhttps://registry.npmjs.org/ignore/-/ignore-5.2.4.tgzhttps://registry.npmjs.org/glob/-/glob-7.2.3.tgzfs.realpathminimatchpath-is-absolutehttps://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgzhttps://registry.npmjs.org/once/-/once-1.4.0.tgzhttps://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgzhttps://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgzbrace-expansionhttps://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgzbalanced-matchconcat-maphttps://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgzhttps://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgzhttps://registry.npmjs.org/inherits/-/inherits-2.0.4.tgzhttps://registry.npmjs.org/inflight/-/inflight-1.0.6.tgzhttps://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgzhttps://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz@nodelib/fs.stat@nodelib/fs.walkmicromatchhttps://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgzhttps://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz@nodelib/fs.scandirhttps://registry.npmjs.org/fastq/-/fastq-1.15.0.tgzhttps://registry.npmjs.org/reusify/-/reusify-1.0.4.tgzhttps://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgzrun-parallelhttps://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgzqueue-microtaskhttps://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgzhttps://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgzhttps://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgzpath-typehttps://registry.npmjs.org/path-type/-/path-type-4.0.0.tgzhttps://registry.npmjs.org/array-union/-/array-union-2.1.0.tgzhttps://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz@types/minimatchhttps://registry.npmjs.org/@types/node/-/node-20.6.0.tgzhttps://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgzhttps://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgzgraceful-fsuniversalifyhttps://registry.npmjs.org/universalify/-/universalify-0.1.2.tgzhttps://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgzhttps://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgzhttps://registry.npmjs.org/colorette/-/colorette-1.4.0.tgzhttps://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.1.2.tgzhttps://registry.npmjs.org/postcss-rtlcss/-/postcss-rtlcss-4.0.7.tgzbin/rtlcss.jshttps://registry.npmjs.org/rtlcss/-/rtlcss-4.1.0.tgzstrip-json-commentshttps://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgzhttps://registry.npmjs.org/escalade/-/escalade-3.1.1.tgzhttps://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz