PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 5.1.0
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v5.1.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 / Installer.php
matomo / classes / WpMatomo Last commit date
Admin 2 years ago Commands 2 years ago Db 2 years ago Ecommerce 2 years ago Report 2 years ago Site 2 years ago TrackingCode 2 years ago Updater 4 years ago User 2 years ago Workarounds 2 years ago WpStatistics 2 years ago views 4 years ago API.php 2 years ago Access.php 4 years ago AjaxTracker.php 2 years ago Annotations.php 4 years ago Bootstrap.php 2 years ago Capabilities.php 4 years ago Compatibility.php 2 years ago Email.php 2 years ago ErrorNotice.php 2 years ago Installer.php 2 years ago Logger.php 2 years ago OptOut.php 4 years ago Paths.php 2 years ago PluginAdminOverrides.php 2 years ago PrivacyBadge.php 4 years ago RedirectOnActivation.php 4 years ago Referral.php 4 years ago Roles.php 4 years ago ScheduledTasks.php 2 years ago Settings.php 2 years ago Site.php 3 years ago TrackingCode.php 4 years ago Uninstaller.php 2 years ago Updater.php 2 years ago User.php 4 years ago
Installer.php
375 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;
11
12 use Exception;
13 use Piwik\Cache;
14 use Piwik\Common;
15 use Piwik\Config;
16 use Piwik\Container\StaticContainer;
17 use Piwik\DbHelper;
18 use Piwik\Exception\NotYetInstalledException;
19 use Piwik\Plugin\API as PluginApi;
20 use Piwik\SettingsPiwik;
21 use Piwik\Singleton;
22 use WpMatomo\Site\Sync;
23
24 if ( ! defined( 'ABSPATH' ) ) {
25 exit; // if accessed directly
26 }
27
28 class Installer {
29 const OPTION_NAME_INSTALL_DATE = 'matomo-install-date';
30 const OPTION_NAME_INSTALL_VERSION = 'matomo-install-version';
31
32 /**
33 * @var Settings
34 */
35 private $settings;
36
37 /**
38 * @var Logger
39 */
40 private $logger;
41
42 public function __construct( Settings $settings ) {
43 $this->settings = $settings;
44 $this->logger = new Logger();
45 }
46
47 public function register_hooks() {
48 add_action( 'activate_matomo/matomo.php', [ $this, 'install' ] ); // if activate_plugin is invoked with the path to the plugin entrypoint
49 add_action( 'activate_matomo', [ $this, 'install' ] ); // if activate_plugin is invoked with the plugin slug
50 }
51
52 public function looks_like_it_is_installed() {
53 $paths = new Paths();
54 $config_file = $paths->get_config_ini_path();
55
56 $config_dir = dirname( $config_file );
57 if ( ! is_dir( $config_dir ) ) {
58 wp_mkdir_p( $config_dir );
59 }
60
61 return file_exists( $config_file );
62 }
63
64 public static function is_intalled() {
65 try {
66 Bootstrap::do_bootstrap();
67
68 return SettingsPiwik::isMatomoInstalled();
69 } catch ( NotYetInstalledException $e ) {
70 // not yet installed.... we will need to install it
71 return false;
72 } catch ( \Zend_Db_Statement_Exception $e ) {
73 // not yet installed.... we will need to install it
74 return false;
75 }
76 }
77
78 public function can_be_installed() {
79 $paths = new Paths();
80 $upload_dir = $paths->get_upload_base_dir();
81
82 return is_writable( $upload_dir ) || is_writable( dirname( $upload_dir ) );
83 }
84
85 public function install() {
86 if ( ! $this->can_be_installed() ) {
87 return false;
88 }
89
90 try {
91 // prevent session related errors during install making it more stable
92 if ( ! defined( 'PIWIK_ENABLE_SESSION_START' ) ) {
93 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedConstantFound
94 define( 'PIWIK_ENABLE_SESSION_START', false );
95 }
96
97 Bootstrap::do_bootstrap();
98
99 if ( ! SettingsPiwik::isMatomoInstalled() || ! $this->looks_like_it_is_installed() ) {
100 throw new NotYetInstalledException( 'Not yet installed' );
101 }
102
103 return false;
104 } catch ( NotYetInstalledException $e ) {
105 $this->logger->log( 'Matomo is not yet installed... installing now' );
106
107 $db_info = $this->create_db();
108 $this->create_config( $db_info );
109
110 // we're scheduling another update in case there are some dimensions to be updated or anything
111 // it is possible that because the plugins need to be reloaded etc that those updates are not executed right
112 // away but need an actual reload and cache clearance etc
113 wp_schedule_single_event( time() + 30, ScheduledTasks::EVENT_UPDATE );
114
115 // to set up geoip in the background later... don't want this to influence the install
116 wp_schedule_single_event( time() + 35, ScheduledTasks::EVENT_GEOIP );
117
118 // in case something fails with website or user creation
119 // also to set up all the other users
120 wp_schedule_single_event( time() + 45, ScheduledTasks::EVENT_SYNC );
121
122 update_option( self::OPTION_NAME_INSTALL_DATE, time() );
123 $plugin_data = get_plugin_data( MATOMO_ANALYTICS_FILE, $markup = false, $translate = false );
124 if ( ! empty( $plugin_data['Version'] ) ) {
125 update_option( self::OPTION_NAME_INSTALL_VERSION, $plugin_data['Version'] );
126 }
127
128 $this->create_website();
129 $this->create_user(); // we sync users as early as possible to make sure things are set up correctly
130 $this->install_tracker();
131
132 try {
133 $this->logger->log( 'Matomo will now init the environment' );
134 $environment = new \Piwik\Application\Environment( null, Bootstrap::get_extra_di_definitions() );
135 $environment->init();
136 } catch ( Exception $e ) {
137 $this->logger->log( 'Ignoring error environment init' );
138 $this->logger->log_exception( 'install_env_init', $e );
139 }
140
141 try {
142 // should load and install plugins
143 $this->logger->log( 'Matomo will now init the front controller and install plugins etc' );
144 \Piwik\FrontController::unsetInstance(); // make sure we're loading the latest instance
145 $controller = \Piwik\FrontController::getInstance();
146 $controller->init();
147 } catch ( Exception $e ) {
148 $this->logger->log( 'Ignoring error frontcontroller init' );
149 $this->logger->log_exception( 'install_front_init', $e );
150 }
151
152 try {
153 // sync user now again after installing plugins...
154 // before eg the users_language table would not have been available yet
155 $this->create_user();
156 } catch ( Exception $e ) {
157 $this->logger->log_exception( 'install_create_user', $e );
158 }
159
160 try {
161 // update plugins if there are any
162 $this->update_components();
163 } catch ( Exception $e ) {
164 $this->logger->log_exception( 'install_update_comp', $e );
165 }
166
167 $this->logger->log( 'Recording version and url' );
168
169 DbHelper::recordInstallVersion();
170
171 $this->set_matomo_url();
172
173 $this->logger->log( 'Emptying some caches' );
174
175 Singleton::clearAll();
176 PluginApi::unsetAllInstances();
177 Cache::flushAll();
178
179 $this->logger->log( 'Matomo install finished' );
180 }
181
182 return true;
183 }
184
185 public function set_matomo_url() {
186 // note that the full url might not be possible to be set if the cron is executed on cli and it maybe doesn't have
187 // the host or if a plugin overwrites the constant of WP_PLUGIN_URL which is used in plugins_url() to not include domain
188 // see https://www.google.com/url?q=https://wordpress.org/support/topic/no-metrics-showing/%23topic-14362043-replies&source=gmail&ust=1620409922890000&usg=AFQjCNHyzG5-9v0A8bjg8aLVVbYSWxkTxg
189
190 $matomo_url = SettingsPiwik::getPiwikUrl();
191 $plugins_url = plugins_url( 'app', MATOMO_ANALYTICS_FILE );
192 // need to make sure to update plugins url if it changes eg if installed somewhere else or domain changes
193
194 if ( $matomo_url
195 && $plugins_url === $matomo_url
196 && wp_parse_url( $matomo_url, PHP_URL_SCHEME )
197 && wp_parse_url( $matomo_url, PHP_URL_HOST )
198 ) {
199 // if currently no scheme or host is set then we'll make sure to overwrite it
200 return;
201 }
202
203 if ( ! $plugins_url ) {
204 return;
205 }
206
207 $has_host = wp_parse_url( $plugins_url, PHP_URL_HOST );
208
209 if ( ! $has_host ) {
210 return;
211 }
212
213 $has_scheme = wp_parse_url( $plugins_url, PHP_URL_SCHEME );
214
215 if ( ! $has_scheme ) {
216 return;
217 }
218
219 SettingsPiwik::overwritePiwikUrl( $plugins_url );
220 }
221
222 private function install_tracker() {
223 $this->logger->log( 'Matomo is now installing the tracker' );
224 // making sure the tracker will be created in the wp uploads directory
225 $updater = StaticContainer::get( 'Piwik\Plugins\CustomJsTracker\TrackerUpdater' );
226 $updater->update();
227 }
228
229 private function create_db() {
230 $this->logger->log( 'Matomo will now create the database' );
231
232 try {
233 $db_infos = self::get_db_infos();
234 $config = Config::getInstance();
235 if ( isset( $config ) ) {
236 $db_infos = array_merge( $config->database, $db_infos );
237 }
238 $config->database = $db_infos;
239
240 DbHelper::checkDatabaseVersion();
241 } catch ( Exception $e ) {
242 $message = sprintf( 'Database info detection failed with %s in %s:%s.', $e->getMessage(), $e->getFile(), $e->getLine() );
243 throw new Exception( $message, $e->getCode(), $e );
244 }
245
246 $tables_installed = DbHelper::getTablesInstalled();
247 if ( count( $tables_installed ) > 0 ) {
248 // todo define behaviour... might need to ask user how to proceed... but ideally we add check to
249 // see if all tables are there and if so, reuse them...
250 return $db_infos;
251 }
252 DbHelper::createTables();
253 DbHelper::createAnonymousUser();
254 $this->update_components();
255
256 return $db_infos;
257 }
258
259 private function create_config( $db_info ) {
260 $this->logger->log( 'Matomo is now creating the config' );
261 $home_url = home_url();
262 $domain = wp_parse_url( $home_url, PHP_URL_HOST );
263 if ( $domain ) {
264 $port = wp_parse_url( $home_url, PHP_URL_PORT );
265 if ( $port ) {
266 $domain .= ':' . $port;
267 }
268 } else {
269 $domain = $home_url;
270 }
271 $general = [
272 'trusted_hosts' => [ $domain ],
273 'salt' => Common::generateUniqId(),
274 ];
275 $config = Config::getInstance();
276 $path = $config->getLocalPath();
277 if ( ! is_dir( dirname( $path ) ) ) {
278 wp_mkdir_p( dirname( $path ) );
279 }
280 $db_default = [];
281 $general_default = [];
282 if ( $config->database ) {
283 $db_default = $config->database;
284 }
285 // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
286 if ( $config->General ) {
287 // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
288 $general_default = $config->General;
289 }
290 $config->database = array_merge( $db_default, $db_info );
291 // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
292 $config->General = array_merge( $general_default, $general );
293 $config->forceSave();
294
295 $mode = 0664;
296 if ( ! chmod( $config->getLocalPath(), $mode ) ) {
297 $this->logger->log( "Can't chmod " . $config->getLocalPath() );
298 }
299 }
300
301 private function create_website() {
302 $sync = new Sync( $this->settings );
303
304 return $sync->sync_current_site();
305 }
306
307 private function create_user() {
308 $sync = new User\Sync();
309
310 $sync->sync_current_users();
311 }
312
313 /**
314 * @param array $default params
315 *
316 * @return array
317 */
318 public static function get_db_infos( $default = [] ) {
319 global $wpdb;
320
321 $socket = '';
322 $host_data = null;
323 $host = null;
324 $port = 3306;
325 if ( method_exists( $wpdb, 'parse_db_host' ) ) {
326 // WP 4.9+
327 $host_data = $wpdb->parse_db_host( DB_HOST );
328 if ( $host_data ) {
329 list( $host, $port, $socket, $is_ipv6 ) = $host_data;
330 if ( ! $port && ! $socket ) {
331 $port = 3306;
332 }
333 }
334 }
335
336 if ( ! $host_data || ! $host ) {
337 // WP 4.8 and older
338 // in case DB credentials change in WordPress, we need to apply these changes here as well on demand
339 $host_parts = explode( ':', DB_HOST );
340 $host = $host_parts[0];
341 if ( count( $host_parts ) === 2 && is_numeric( $host_parts[1] ) ) {
342 $port = $host_parts[1];
343 } else {
344 $port = 3306;
345 }
346 }
347
348 $charset = $wpdb->charset ? $wpdb->charset : 'utf8';
349
350 $database = [
351 'host' => $host,
352 'port' => $port,
353 'username' => DB_USER,
354 'password' => DB_PASSWORD,
355 'dbname' => DB_NAME,
356 'charset' => $charset,
357 'tables_prefix' => $wpdb->prefix . MATOMO_DATABASE_PREFIX,
358 'adapter' => 'WordPress',
359 ];
360 if ( ! empty( $socket ) ) {
361 $database['unix_socket'] = $socket;
362 }
363 $database = array_merge( $default, $database );
364
365 return $database;
366 }
367
368 private function update_components() {
369 $this->logger->log( 'Matomo will now trigger an update' );
370 Updater::unlock(); // make sure the update can be executed
371 $updater = new Updater( $this->settings );
372 $updater->update();
373 }
374 }
375