PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 4.12.0
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v4.12.0
5.11.1 5.11.0 5.10.2 5.10.1 trunk 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.1.0 1.1.1 1.1.2 1.1.3 1.2.0 1.3.0 1.3.1 1.3.2 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.1.0 4.1.1 4.1.2 4.1.3 4.10.0 4.11.0 4.12.0 4.13.0 4.13.2 4.13.3 4.13.4 4.13.5 4.14.0 4.14.1 4.14.2 4.15.0 4.15.1 4.15.2 4.15.3 4.2.0 4.3.0 4.3.1 4.4.1 4.4.2 4.5.0 4.6.0 5.0.1 5.0.2 5.0.3 5.0.4 5.0.5 5.0.6 5.0.7 5.0.8 5.1.0 5.1.1 5.1.2 5.1.3 5.1.4 5.1.5 5.1.6 5.1.7 5.10.0 5.2.0 5.2.1 5.2.2 5.3.0 5.3.1 5.3.2 5.3.3 5.6.0 5.6.1 5.7.0 5.7.1 5.8.0 5.8.1 5.8.2
matomo / classes / WpMatomo / Site / Sync.php
matomo / classes / WpMatomo / Site Last commit date
Sync 4 years ago Sync.php 4 years ago
Sync.php
305 lines
1 <?php
2 /**
3 * Matomo - free/libre analytics platform
4 *
5 * @link https://matomo.org
6 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
7 * @package matomo
8 */
9
10 namespace WpMatomo\Site;
11
12 use Exception;
13 use Piwik\Access;
14 use Piwik\Config;
15 use Piwik\Container\StaticContainer;
16 use Piwik\Intl\Data\Provider\CurrencyDataProvider;
17 use Piwik\Plugins\SitesManager;
18 use Piwik\Plugins\SitesManager\Model;
19 use WP_Site;
20 use WpMatomo\Bootstrap;
21 use WpMatomo\Installer;
22 use WpMatomo\Logger;
23 use WpMatomo\Settings;
24 use WpMatomo\Site;
25 use WpMatomo\Site\Sync\SyncConfig;
26
27 if ( ! defined( 'ABSPATH' ) ) {
28 exit; // if accessed directly
29 }
30
31 /**
32 * Properties coming from matomo
33 * phpcs:disable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
34 */
35 class Sync {
36 const MAX_LENGTH_SITE_NAME = 90;
37
38 /**
39 * @var Logger
40 */
41 private $logger;
42
43 /**
44 * @var Settings
45 */
46 private $settings;
47
48 /**
49 * @var SyncConfig
50 */
51 private $config_sync;
52
53 public function __construct( Settings $settings ) {
54 $this->logger = new Logger();
55 $this->settings = $settings;
56 $this->config_sync = new SyncConfig( $settings );
57 }
58
59 public function register_hooks() {
60 add_action( 'update_option_blogname', [ $this, 'sync_current_site_ignore_error' ] );
61 add_action( 'update_option_home', [ $this, 'sync_current_site_ignore_error' ] );
62 add_action( 'update_option_siteurl', [ $this, 'sync_current_site_ignore_error' ] );
63 add_action( 'update_option_timezone_string', [ $this, 'sync_current_site_ignore_error' ] );
64 add_action( 'matomo_setting_change_track_ecommerce', [ $this, 'sync_current_site_ignore_error' ] );
65 add_action( 'matomo_setting_change_site_currency', [ $this, 'sync_current_site_ignore_error' ] );
66 }
67
68 public function sync_current_site_ignore_error() {
69 if ( ! is_plugin_active( 'matomo/matomo.php' ) ) {
70 // @see https://github.com/matomo-org/matomo-for-wordpress/issues/577
71 return;
72 }
73 try {
74 $this->sync_current_site();
75 } catch ( Exception $e ) {
76 $this->logger->log( 'Ignoring site sync error: ' . $e->getMessage() );
77 $this->logger->log_exception( 'sync_site_ignore', $e );
78 }
79 }
80
81 public function sync_all() {
82 $succeed_all = true;
83
84 Bootstrap::do_bootstrap();
85
86 if ( is_multisite() && function_exists( 'get_sites' ) ) {
87 foreach ( get_sites() as $site ) {
88 /** @var WP_Site $site */
89 switch_to_blog( $site->blog_id );
90 try {
91 $installer = new Installer( $this->settings );
92 if ( ! $installer->looks_like_it_is_installed() ) {
93 $this->logger->log( sprintf( 'Matomo was not installed yet for blog: %s installing now.', $site->blog_id ) );
94
95 // prevents error that it wouldn't fully install matomo for a different site as it would think it already did install it etc.
96 // and would otherwise think plugins are already activated etc
97 Bootstrap::set_not_bootstrapped();
98 $config = Config::getInstance();
99 $installed = $config->PluginsInstalled;
100 $installed['PluginsInstalled'] = [];
101 $config->PluginsInstalled = $installed;
102
103 if ( $installer->can_be_installed() ) {
104 $installer->install();
105 } else {
106 continue;
107 }
108 }
109
110 $success = $this->sync_site( $site->blog_id, $site->blogname, $site->siteurl );
111 } catch ( Exception $e ) {
112 $success = false;
113 // we don't want to rethrow exception otherwise some other blogs might never sync
114 $this->logger->log( 'Matomo error syncing site: ' . $e->getMessage() );
115 }
116
117 $succeed_all = $succeed_all && $success;
118 restore_current_blog();
119 }
120 } else {
121 $success = $this->sync_current_site();
122 $succeed_all = $succeed_all && $success;
123 }
124
125 return $succeed_all;
126 }
127
128 public function sync_current_site() {
129 return $this->sync_site( get_current_blog_id(), get_bloginfo( 'name' ), get_bloginfo( 'url' ) );
130 }
131
132 public function sync_site( $blog_id, $blog_name, $blog_url ) {
133 Bootstrap::do_bootstrap();
134 $this->logger->log( 'Matomo is now syncing blogId ' . $blog_id );
135
136 $idsite = Site::get_matomo_site_id( $blog_id );
137
138 if ( empty( $blog_name ) ) {
139 $blog_name = esc_html__( 'Default', 'matomo' );
140 } else {
141 $blog_name = substr( $blog_name, 0, self::MAX_LENGTH_SITE_NAME );
142 }
143
144 $track_ecommerce = (int) $this->settings->get_global_option( 'track_ecommerce' );
145 $site_currency = $this->settings->get_global_option( Settings::SITE_CURRENCY );
146 $detected_timezone = $this->detect_timezone();
147
148 $data_provider = StaticContainer::get( CurrencyDataProvider::class );
149 $valid_currencies = $data_provider->getCurrencyList();
150 if ( ! array_key_exists( $site_currency, $valid_currencies ) ) {
151 $site_currency = 'USD';
152 }
153
154 if ( ! empty( $idsite ) ) {
155 $this->logger->log( 'Matomo site is known for blog (' . $idsite . ')... will update' );
156
157 $sites_manager_model = new Model();
158 $site = $sites_manager_model->getSiteFromId( $idsite );
159 if ( ! empty( $site ) ) {
160 // if site doesn't exist for some reason then we have to create it
161 if ( $site['name'] !== $blog_name
162 || $site['main_url'] !== $blog_url
163 // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
164 || $site['ecommerce'] != $track_ecommerce
165 || $site['currency'] !== $site_currency
166 || $site['timezone'] !== $detected_timezone ) {
167
168 /** @var WP_Site $site */
169 $params = [
170 'name' => $blog_name,
171 'main_url' => $blog_url,
172 'ecommerce' => $track_ecommerce,
173 'currency' => $site_currency,
174 'timezone' => $detected_timezone,
175 ];
176 $sites_manager_model->updateSite( $params, $idsite );
177
178 do_action( 'matomo_site_synced', $idsite, $blog_id );
179
180 // no actual setting changed but we make sure the tracking code will be updated after an update
181 $this->settings->apply_tracking_related_changes( [] );
182 }
183
184 $this->config_sync->sync_config_for_current_site();
185
186 return true;
187 }
188 }
189
190 $this->logger->log( 'Matomo site is not known for blog... will create site' );
191
192 /** @var WP_Site $site */
193 $idsite = null;
194
195 $this->set_enable_sites_admin( 1 );
196
197 Access::doAsSuperUser(
198 function () use ( $blog_name, $blog_url, $detected_timezone, $track_ecommerce, &$idsite, $site_currency ) {
199 SitesManager\API::unsetInstance();
200 // we need to unset the instance to make sure it fetches the
201 // up to date dependencies eg current plugin manager etc
202
203 $idsite = SitesManager\API::getInstance()->addSite(
204 $blog_name,
205 [ $blog_url ],
206 $track_ecommerce,
207 $site_search = null,
208 $search_keyword_parameters = null,
209 $search_category_parameters = null,
210 $excluded_ips = null,
211 $excluded_query_parameters = null,
212 $detected_timezone,
213 $site_currency
214 );
215 }
216 );
217 $this->set_enable_sites_admin( 0 );
218
219 $this->logger->log( 'Matomo created site with ID ' . $idsite . ' for blog' );
220
221 if ( ! is_numeric( $idsite ) || 0 === $idsite || '0' === $idsite ) {
222 $this->logger->log( sprintf( 'Creating the website failed: %s', wp_json_encode( $blog_id ) ) );
223
224 return false;
225 }
226
227 Site::map_matomo_site_id( $blog_id, $idsite );
228
229 $this->config_sync->sync_config_for_current_site();
230
231 do_action( 'matomo_site_synced', $idsite, $blog_id );
232
233 return true;
234 }
235
236 private function set_enable_sites_admin( $enabled ) {
237 $general = Config::getInstance()->General;
238 $general['enable_sites_admin'] = (int) $enabled;
239 Config::getInstance()->General = $general;
240 }
241
242 private function detect_timezone() {
243 $timezone = get_option( 'timezone_string' );
244
245 if ( $timezone && $this->check_and_try_to_set_default_timezone( $timezone ) ) {
246 return $timezone;
247 }
248
249 // older WordPress
250 $utc_offset = (int) get_option( 'gmt_offset', 0 );
251
252 if ( 0 === $utc_offset ) {
253 return 'UTC';
254 }
255
256 $utc_offset_in_seconds = $utc_offset * 3600;
257 $timezone = timezone_name_from_abbr( '', $utc_offset_in_seconds );
258
259 if ( $timezone && $this->check_and_try_to_set_default_timezone( $timezone ) ) {
260 return $timezone;
261 }
262
263 $dst = (bool) gmdate( 'I' );
264 foreach ( timezone_abbreviations_list() as $abbr ) {
265 foreach ( $abbr as $city ) {
266 if ( $dst === (bool) $city['dst']
267 && $city['timezone_id']
268 && (int) $city['offset'] === $utc_offset_in_seconds ) {
269 return $city['timezone_id'];
270 }
271 }
272 }
273
274 if ( is_numeric( $utc_offset ) ) {
275 if ( $utc_offset > 0 ) {
276 $timezone = 'UTC+' . $utc_offset;
277 } else {
278 $timezone = 'UTC' . $utc_offset;
279 }
280
281 if ( $this->check_and_try_to_set_default_timezone( $timezone ) ) {
282 return $timezone;
283 }
284 }
285
286 return 'UTC';
287 }
288
289 private function check_and_try_to_set_default_timezone( $timezone ) {
290 try {
291 Access::doAsSuperUser(
292 function () use ( $timezone ) {
293 // make sure we're loading the latest instance with all up to date dependencies... mainly needed for tests
294 SitesManager\API::unsetInstance();
295 SitesManager\API::getInstance()->setDefaultTimezone( $timezone );
296 }
297 );
298 } catch ( Exception $e ) {
299 return false;
300 }
301
302 return true;
303 }
304 }
305