PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 4.0.2
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v4.0.2
5.13.0 5.12.1 5.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 6 years ago Sync.php 5 years ago
Sync.php
293 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 Piwik\Access;
13 use Piwik\API\Request;
14 use Piwik\Common;
15 use Piwik\Config;
16 use Piwik\Container\StaticContainer;
17 use Piwik\Intl\Data\Provider\CurrencyDataProvider;
18 use Piwik\Plugins\SitesManager\Model;
19 use Piwik\Plugins\SitesManager;
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 class Sync {
32 const MAX_LENGTH_SITE_NAME = 90;
33
34 /**
35 * @var Logger
36 */
37 private $logger;
38
39 /**
40 * @var Settings
41 */
42 private $settings;
43
44 /**
45 * @var SyncConfig
46 */
47 private $config_sync;
48
49 public function __construct( Settings $settings ) {
50 $this->logger = new Logger();
51 $this->settings = $settings;
52 $this->config_sync = new SyncConfig( $settings );
53 }
54
55 public function register_hooks() {
56 add_action( 'update_option_blogname', array( $this, 'sync_current_site_ignore_error' ) );
57 add_action( 'update_option_home', array( $this, 'sync_current_site_ignore_error' ) );
58 add_action( 'update_option_siteurl', array( $this, 'sync_current_site_ignore_error' ) );
59 add_action( 'update_option_timezone_string', array( $this, 'sync_current_site_ignore_error' ) );
60 add_action( 'matomo_setting_change_track_ecommerce', array( $this, 'sync_current_site_ignore_error' ) );
61 add_action( 'matomo_setting_change_site_currency', array( $this, 'sync_current_site_ignore_error' ) );
62 }
63
64 public function sync_current_site_ignore_error()
65 {
66 try {
67 $this->sync_current_site();
68 } catch (\Exception $e) {
69 $this->logger->log( 'Ignoring site sync error: ' . $e->getMessage());
70 $this->logger->log_exception('sync_site_ignore', $e);
71 }
72 }
73
74 public function sync_all() {
75 $succeed_all = true;
76
77 Bootstrap::do_bootstrap();
78
79 if ( is_multisite() && function_exists( 'get_sites' ) ) {
80 foreach ( get_sites() as $site ) {
81 /** @var \WP_Site $site */
82 switch_to_blog( $site->blog_id );
83 try {
84 $installer = new Installer( $this->settings );
85 if ( ! $installer->looks_like_it_is_installed() ) {
86 $this->logger->log( sprintf( 'Matomo was not installed yet for blog: %s installing now.', $site->blog_id ) );
87
88 // prevents error that it wouldn't fully install matomo for a different site as it would think it already did install it etc.
89 // and would otherwise think plugins are already activated etc
90 Bootstrap::set_not_bootstrapped();
91 $config = Config::getInstance();
92 $installed = $config->PluginsInstalled;
93 $installed['PluginsInstalled'] = array();
94 $config->PluginsInstalled = $installed;
95
96 if ( $installer->can_be_installed() ) {
97 $installer->install();
98 } else {
99 continue;
100 }
101 }
102
103 $success = $this->sync_site( $site->blog_id, $site->blogname, $site->siteurl );
104 } catch ( \Exception $e ) {
105 $success = false;
106 // we don't want to rethrow exception otherwise some other blogs might never sync
107 $this->logger->log( 'Matomo error syncing site: ' . $e->getMessage() );
108 }
109
110 $succeed_all = $succeed_all && $success;
111 restore_current_blog();
112 }
113 } else {
114 $success = $this->sync_current_site();
115 $succeed_all = $succeed_all && $success;
116 }
117
118 return $succeed_all;
119 }
120
121 public function sync_current_site() {
122 return $this->sync_site( get_current_blog_id(), get_bloginfo( 'name' ), get_bloginfo( 'url' ) );
123 }
124
125 public function sync_site( $blog_id, $blog_name, $blog_url ) {
126 Bootstrap::do_bootstrap();
127 $this->logger->log( 'Matomo is now syncing blogId ' . $blog_id );
128
129 $idsite = Site::get_matomo_site_id( $blog_id );
130
131 if ( empty( $blog_name ) ) {
132 $blog_name = esc_html__( 'Default', 'matomo' );
133 } else {
134 $blog_name = substr( $blog_name, 0, self::MAX_LENGTH_SITE_NAME );
135 }
136
137 $track_ecommerce = (int) $this->settings->get_global_option( 'track_ecommerce' );
138 $site_currency = $this->settings->get_global_option( Settings::SITE_CURRENCY );
139 $detected_timezone = $this->detect_timezone();
140
141 $dataProvider = StaticContainer::get(CurrencyDataProvider::class);
142 $valid_currencies = $dataProvider->getCurrencyList();
143 if (!array_key_exists($site_currency, $valid_currencies)){
144 $site_currency = 'USD';
145 }
146
147 if ( ! empty( $idsite ) ) {
148 $this->logger->log( 'Matomo site is known for blog (' . $idsite . ')... will update' );
149
150 $sites_manager_model = new Model();
151 $site = $sites_manager_model->getSiteFromId($idsite);
152 if ($site['name'] != $blog_name
153 || $site['main_url'] != $blog_url
154 || $site['ecommerce'] != $track_ecommerce
155 || $site['currency'] != $site_currency
156 || $site['timezone'] != $detected_timezone) {
157
158 /** @var \WP_Site $site */
159 $params = array(
160 'name' => $blog_name,
161 'main_url' => $blog_url,
162 'ecommerce' => $track_ecommerce,
163 'currency' => $site_currency,
164 'timezone' => $detected_timezone,
165 );
166 $sites_manager_model->updateSite( $params, $idsite );
167
168 do_action( 'matomo_site_synced', $idsite, $blog_id );
169
170 // no actual setting changed but we make sure the tracking code will be updated after an update
171 $this->settings->apply_tracking_related_changes( array() );
172 }
173
174 $this->config_sync->sync_config_for_current_site();
175
176 return true;
177 }
178
179 $this->logger->log( 'Matomo site is not known for blog... will create site' );
180
181 /** @var \WP_Site $site */
182 $idsite = null;
183
184 $this->set_enable_sites_admin( 1 );
185
186 Access::doAsSuperUser(
187 function () use ( $blog_name, $blog_url, $detected_timezone, $track_ecommerce, &$idsite, $site_currency ) {
188 SitesManager\API::unsetInstance();
189 // we need to unset the instance to make sure it fetches the
190 // up to date dependencies eg current plugin manager etc
191
192 $idsite = SitesManager\API::getInstance()->addSite(
193 $blog_name,
194 array( $blog_url ),
195 $track_ecommerce,
196 $site_search = null,
197 $search_keyword_parameters = null,
198 $search_category_parameters = null,
199 $excluded_ips = null,
200 $excluded_query_parameters = null,
201 $detected_timezone,
202 $site_currency
203 );
204 }
205 );
206 $this->set_enable_sites_admin( 0 );
207
208 $this->logger->log( 'Matomo created site with ID ' . $idsite . ' for blog' );
209
210 Site::map_matomo_site_id( $blog_id, $idsite );
211
212 if ( ! is_numeric( $idsite ) || 0 === $idsite || '0' === $idsite ) {
213 $this->logger->log( sprintf( 'Creating the website failed: %s', wp_json_encode( $blog_id ) ) );
214
215 return false;
216 }
217
218 $this->config_sync->sync_config_for_current_site();
219
220 do_action( 'matomo_site_synced', $idsite, $blog_id );
221
222 return true;
223 }
224
225 private function set_enable_sites_admin( $enabled ) {
226 $general = Config::getInstance()->General;
227 $general['enable_sites_admin'] = (int) $enabled;
228 Config::getInstance()->General = $general;
229 }
230
231 private function detect_timezone() {
232 $timezone = get_option( 'timezone_string' );
233
234 if ( $timezone && $this->check_and_try_to_set_default_timezone( $timezone ) ) {
235 return $timezone;
236 }
237
238 // older WordPress
239 $utc_offset = (int) get_option( 'gmt_offset', 0 );
240
241 if ( 0 === $utc_offset ) {
242 return 'UTC';
243 }
244
245 $utc_offset_in_seconds = $utc_offset * 3600;
246 $timezone = timezone_name_from_abbr( '', $utc_offset_in_seconds );
247
248 if ( $timezone && $this->check_and_try_to_set_default_timezone( $timezone ) ) {
249 return $timezone;
250 }
251
252 $dst = (bool) date( 'I' );
253 foreach ( timezone_abbreviations_list() as $abbr ) {
254 foreach ( $abbr as $city ) {
255 if ( $dst === (bool) $city['dst']
256 && $city['timezone_id']
257 && (int) $city['offset'] === $utc_offset_in_seconds ) {
258 return $city['timezone_id'];
259 }
260 }
261 }
262
263 if ( is_numeric( $utc_offset ) ) {
264 if ( $utc_offset > 0 ) {
265 $timezone = 'UTC+' . $utc_offset;
266 } else {
267 $timezone = 'UTC' . $utc_offset;
268 }
269
270 if ( $this->check_and_try_to_set_default_timezone( $timezone ) ) {
271 return $timezone;
272 }
273 }
274
275 return 'UTC';
276 }
277
278 private function check_and_try_to_set_default_timezone( $timezone ) {
279 try {
280 Access::doAsSuperUser(function () use ($timezone) {
281 // make sure we're loading the latest instance with all up to date dependencies... mainly needed for tests
282 SitesManager\API::unsetInstance();
283 SitesManager\API::getInstance()->setDefaultTimezone( $timezone );
284 });
285 } catch ( \Exception $e ) {
286 return false;
287 }
288
289 return true;
290 }
291
292 }
293