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