      1:   2:   3:   4:   5:   6:   7:   8:   9:  10:  11:  12:  13:  14:  15:  16:  17:  18:  19:  20:  21:  22:  23:  24:  25:  26:  27:  28:  29:  30:  31:  32:  33:  34:  35:  36:  37:  38:  39:  40:  41:  42:  43:  44:  45:  46:  47:  48:  49:  50:  51:  52:  53:  54:  55:  56:  57:  58:  59:  60:  61:  62:  63:  64:  65:  66:  67:  68:  69:  70:  71:  72:  73:  74:  75:  76:  77:  78:  79:  80:  81:  82:  83:  84:  85:  86:  87:  88:  89:  90:  91:  92:  93:  94:  95:  96:  97:  98:  99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199: 200: 201: 202: 203: 204: 205: 206: 207: 208: 209: 210: 211: 212: 213: 214: 215: 216: 217: 218: 219: 220: 221: 222: 223: 224: 225: 226: 227: 228: 229: 230: 231: 232: 233: 234: 235: 236: 237: 238: 239: 240: 241: 242: 243: 244: 245: 246: 247: 248: 249: 250: 251: 252: 253: 254: 255: 256: 257: 258: 259: 260: 261: 262: 263: 264: 265: 266: 267: 268: 269: 270: 271: 272: 273: 274: 275: 276: 277: 278: 279: 280: 281: 282: 283: 284: 285: 286: 287: 288: 289: 290: 291: 292: 293: 294: 295: 296: 297: 298: 299: 300: 301: 302: 303: 304: 305: 306: 307: 308: 309: 310: 311: 312: 313: 314: 315: 316: 317: 318: 319: 320: 321: 322: 323: 324: 325: 326: 327: 328: 329: 330: 331: 332: 333: 334: 335: 336:

    <?php

    namespace WPGMZA;

    /**
     * This class represents the plugin itself. Broadly, this module handles practically all interaction with the platform (WP), loading assets as needed, and hooking into the platforms interface to provide menus etc.
     *
     * It also provides a number of helpful utility functions.
     * @property-read string $spatialFunctionPrefix MySQL versions 8 and above prefix all spatial functions with ST_, previous versions do not. This property will be ST_ where necessary, and an empty string where not. You should use this with all DB calls that make use of spaital functions.
     * @property-read string $gdprCompliance An instance of the GDPRCompliance class.
     */
    class Plugin extends Factory
    {
        const PAGE_MAP_LIST         = "map-list";
        const PAGE_MAP_EDIT         = "map-edit";
        const PAGE_SETTINGS         = "map-settings";
        const PAGE_SUPPORT          = "map-support";

        const PAGE_CATEGORIES       = "categories";
        const PAGE_ADVANCED         = "advanced";
        const PAGE_CUSTOM_FIELDS    = "custom-fields";

        private static $enqueueScriptActions = array(
            'wp_enqueue_scripts',
            'admin_enqueue_scripts',
            'enqueue_block_assets'
        );
        public static $enqueueScriptsFired = false;

        private $_settings;
        private $_gdprCompliance;
        private $_restAPI;
        private $_spatialFunctionPrefix = '';

        protected $scriptLoader;

        private $mysqlVersion = null;
        private $cachedVersion = null;
        private $legacySettings;

        /**
         * Constructor. Called when plugins_loaded fires.
         */
        public function __construct()
        {
            global $wpdb;

            add_filter('load_textdomain_mofile', array($this, 'onLoadTextDomainMOFile'), 10, 2);

            $this->mysqlVersion = $wpdb->get_var('SELECT VERSION()');

            // TODO: Could / should cache this above
            if(!empty($this->mysqlVersion) && preg_match('/^\d+/', $this->mysqlVersion, $majorVersion) && (int)$majorVersion[0] >= 8)
                $this->_spatialFunctionPrefix = 'ST_';

            $this->legacySettings = get_option('WPGMZA_OTHER_SETTINGS');
            if(!$this->legacySettings)
                $this->legacySettings = array();

            // Legacy compatibility
            global $wpgmza_pro_version;

            $this->_settings = new GlobalSettings();
            $this->_restAPI = RestAPI::createInstance();
            $this->gutenbergIntegration = Integration\Gutenberg::createInstance();

            // TODO: This should be in default settings, this code is duplicaetd
            if(!empty($wpgmza_pro_version) && version_compare(trim($wpgmza_pro_version), '7.10.00', '<'))
            {
                $self = $this;

                $settings['wpgmza_maps_engine'] = $settings['engine'] = 'google-maps';

                add_filter('wpgooglemaps_filter_map_div_output', function($output) use ($self) {

                    $loader = new GoogleMapsAPILoader();
                    $loader->registerGoogleMaps();
                    $loader->enqueueGoogleMaps();

                    $self->loadScripts();

                    return $output;

                });
            }

            if(!empty($this->settings->wpgmza_maps_engine))
                $this->settings->engine = $this->settings->wpgmza_maps_engine;

            add_action('init', array($this, 'onInit'));

            foreach(Plugin::$enqueueScriptActions as $action)
            {
                add_action($action, function() use ($action) {
                    Plugin::$enqueueScriptsFired = true;
                }, 1);
            }

            if($this->settings->engine == 'open-layers')
                require_once(plugin_dir_path(__FILE__) . 'open-layers/class.nominatim-geocode-cache.php');

            if($this->settings->wpgmza_settings_marker_pull == '1' && !file_exists(wpgmza_return_marker_path()))
            {
                $this->settings->wpgmza_settings_marker_pull = '0';

                add_action('admin_notices', function() {
                    echo '<div class="error"><p>' . __('<strong>WP Google Maps:</strong> Cannot find the specified XML folder. This has been switched back to the Database method in Maps -> Settings -> Advanced', 'wp-google-maps') . '</p></div>';
                });
            }

        }

        public function __set($name, $value)
        {
            if(isset($this->{"_$name"}))
                throw new \Exception('Property is read only');

            $this->{$name} = $value;
        }

        /**
         * Getter, see property-read above.
         */
        public function __get($name)
        {
            switch($name)
            {
                case 'settings':
                case 'gdprCompliance':
                case 'restAPI':
                case 'spatialFunctionPrefix':
                    return $this->{'_' . $name};
                    break;
            }

            return $this->{$name};
        }

        public function onInit()
        {
            $this->_gdprCompliance = new GDPRCompliance();
        }

        /**
         * This function will cause the plugin scripts to be loaded. Firstly it will initialize an instance of ScriptLoader. If the developer mode setting is enabled, the scripts will be rebuilt.
         *
         * If any of the enqueue scripts, admin enqueue scripts or enqueue block assets (Gutenberg) actions have already fired, this function will immediately ask the script loader to enqueue the plugins scripts and styles.
         *
         * If none of those actions have fired yet, this function will bind to all three and enqueue the scripts at the correct time.
         * @return void
         */
        public function loadScripts()
        {
            $self = $this;

            if(!$this->scriptLoader)
                $this->scriptLoader = new ScriptLoader($this->isProVersion());

            if($this->isInDeveloperMode())
                $this->scriptLoader->build();

            if(Plugin::$enqueueScriptsFired)
            {
                $this->scriptLoader->enqueueScripts();
                $this->scriptLoader->enqueueStyles();
            }
            else
            {
                foreach(Plugin::$enqueueScriptActions as $action)
                {
                    add_action($action, function() use ($self) {
                        $self->scriptLoader->enqueueScripts();
                        $self->scriptLoader->enqueueStyles();
                    });
                }
            }
        }

        public function getLocalizedData()
        {
            $document = new DOMDocument();
            $document->loadPHPFile(plugin_dir_path(__DIR__) . 'html/google-maps-api-error-dialog.html.php');
            $googleMapsAPIErrorDialogHTML = $document->saveInnerBody();

            $strings = new Strings();

            $settings = clone $this->settings;

            $result = apply_filters('wpgmza_plugin_get_localized_data', array(
                'adminurl'              => admin_url(),
                'ajaxurl'               => admin_url('admin-ajax.php'),

                'html'                  => array(
                    'googleMapsAPIErrorDialog' => $googleMapsAPIErrorDialogHTML
                ),

                'resturl'               => preg_replace('#/$#', '', get_rest_url(null, 'wpgmza/v1')),
                'restnonce'             => wp_create_nonce('wp_rest'),

                'settings'              => $settings,
                'currentPage'           => $this->getCurrentPage(),
                'userCanAdministrator'  => (current_user_can('administrator') ? 1 : 0),
                'localized_strings'     => $strings->getLocalizedStrings(),
                'api_consent_html'      => $this->gdprCompliance->getConsentPromptHTML(),
                'basic_version'         => $this->getBasicVersion(),
                '_isProVersion'         => $this->isProVersion(),

                'defaultMarkerIcon'     => Marker::DEFAULT_ICON,

                'is_admin'              => (is_admin() ? 1 : 0),
                'locale'                => get_locale()
            ));

            if(!empty($result->settings->wpgmza_settings_ugm_email_address))
                unset($result->settings->wpgmza_settings_ugm_email_address);

            return $result;
        }

        /**
         * Returns a string stating the current page, relevant to this plugin.  Please refer to the constants on this class for a list of available pages. If the current page is not relevant to this plugin, NULL is returned.
         * @return string|null The current page, where relevant to this plugin, or null
         */
        public function getCurrentPage()
        {
            if(!isset($_GET['page']))
                return null;

            switch($_GET['page'])
            {
                case 'wp-google-maps-menu':
                    if(isset($_GET['action']) && $_GET['action'] == 'edit')
                        return Plugin::PAGE_MAP_EDIT;

                    return Plugin::PAGE_MAP_LIST;
                    break;

                case 'wp-google-maps-menu-settings':
                    return Plugin::PAGE_SETTINGS;
                    break;

                case 'wp-google-maps-menu-support':
                    return Plugin::PAGE_SUPPORT;
                    break;

                case 'wp-google-maps-menu-categories':
                    return Plugin::PAGE_CATEGORIES;
                    break;

                case 'wp-google-maps-menu-advanced':
                    return Plugin::PAGE_ADVANCED;
                    break;

                case 'wp-google-maps-menu-custom-fields':
                    return Plugin::PAGE_CUSTOM_FIELDS;
                    break;
            }

            return null;
        }

        /**
         * Returns true if we are to be using combined or minified JavaScript
         * @return bool True if combined or minified scripts are to be used.
         */
        public function isUsingMinifiedScripts()
        {
            return $this->isInDeveloperMode();
        }

        /**
         * Returns true if the developer mode setting is checked, or if the developer mode cookie is set.
         * @return bool True if in developer mode, by setting or by cookie.
         */
        public function isInDeveloperMode()
        {
            return !(empty($this->settings->developer_mode) && !isset($_COOKIE['wpgmza-developer-mode']));
        }

        /**
         * Check whether we are running the Pro add-on.
         * @return bool True if the Pro add-on is installed and activated.
         */
        public function isProVersion()
        {
            if(defined('WPGMZA_PRO_VERSION') && version_compare(WPGMZA_PRO_VERSION, '7.10.00', '<'))
                return true;    // Pre ProPlugin

            return false;
        }

        /**
         * Returns the plugin version, based on the plugin comment header. This value will be cached if it hasn't been read already.
         * @return string The version string.
         */
        public function getBasicVersion()
        {
            if($this->cachedVersion != null)
                return $this->cachedVersion;

            $subject = file_get_contents(plugin_dir_path(__DIR__) . 'wpGoogleMaps.php');
            if(preg_match('/Version:\s*(.+)/', $subject, $m))
                $this->cachedVersion = trim($m[1]);

            return $this->cachedVersion;
        }

        /**
         * Hooks into load_textdomain_mofile, this function is used to override the WordPress repo translations and force the translations bundled with our plugin to be used. These are more complete and accurate than the WordPress community translations.
         * @param string $mofile Path to the .mo file in question.
         * @param string $domain The text domain
         * @return string
         */
        public function onLoadTextDomainMOFile($mofile, $domain)
        {
            if($domain == 'wp-google-maps')
                $mofile = plugin_dir_path(__DIR__) . 'languages/wp-google-maps-' . get_locale() . '.mo';

            return $mofile;
        }
    }

    add_action('plugins_loaded', function() {

        global $wpgmza;

        if(defined('WPGMZA_PRO_VERSION') && version_compare(WPGMZA_PRO_VERSION, '7.11.00', '<') && class_exists('WPGMZA\\ProPlugin'))
        {
            $wpgmza = new ProPlugin();
            return;
        }

        $wpgmza = Plugin::createInstance();

    });