PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 4.15.1
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v4.15.1
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 / Updater.php
matomo / classes / WpMatomo Last commit date
Admin 2 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 ErrorNotice.php 2 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
Updater.php
254 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 as PiwikCache;
14 use Piwik\Filesystem;
15 use Piwik\Option;
16 use Piwik\Plugins\Installation\ServerFilesGenerator;
17 use Piwik\SettingsServer;
18 use Piwik\Version;
19 use WP_Upgrader;
20 use WpMatomo\Paths;
21 use WpMatomo\Updater\UpdateInProgressException;
22
23 if ( ! defined( 'ABSPATH' ) ) {
24 exit; // if accessed directly
25 }
26
27 class Updater {
28 const LOCK_NAME = 'matomo_updater';
29
30 /**
31 * @var Settings
32 */
33 private $settings;
34
35 /**
36 * @var Logger
37 */
38 private $logger;
39
40 public function __construct( Settings $settings ) {
41 $this->settings = $settings;
42 $this->logger = new Logger();
43 }
44
45 public function load_plugin_functions() {
46 if ( ! function_exists( 'get_plugin_data' ) ) {
47 require_once ABSPATH . '/wp-admin/includes/plugin.php';
48 }
49
50 return function_exists( 'get_plugin_data' );
51 }
52
53 public function get_plugins_requiring_update() {
54 if ( ! $this->load_plugin_functions() ) {
55 return [];
56 }
57
58 $keys = [];
59 $plugin_files = $GLOBALS['MATOMO_PLUGIN_FILES'];
60 if ( ! in_array( MATOMO_ANALYTICS_FILE, $plugin_files, true ) ) {
61 $plugin_files[] = MATOMO_ANALYTICS_FILE;
62 // making sure this plugin is in the list so when itself gets updated
63 // it will execute the core updates
64 }
65
66 foreach ( $GLOBALS['MATOMO_PLUGIN_FILES'] as $plugin_file ) {
67 $plugin_data = get_plugin_data( $plugin_file, $markup = false, $translate = false );
68
69 $key = Settings::OPTION_PREFIX . 'plugin-version-' . basename( str_ireplace( '.php', '', $plugin_file ) );
70 $installed_ver = get_option( $key );
71 if ( ! $installed_ver || $installed_ver !== $plugin_data['Version'] ) {
72 if ( ! Installer::is_intalled() ) {
73 return [];
74 }
75 $keys[ $key ] = $plugin_data['Version'];
76 }
77 }
78
79 return $keys;
80 }
81
82 public function update_if_needed() {
83 $executed_updates = [];
84
85 $plugins_requiring_update = $this->get_plugins_requiring_update();
86 foreach ( $plugins_requiring_update as $key => $plugin_version ) {
87 try {
88 $this->update();
89 } catch ( UpdateInProgressException $e ) {
90 $this->logger->log( 'Matomo update is already in progress' );
91
92 return; // we also don't execute any further update as they should be executed in another process
93 } catch ( Exception $e ) {
94 $this->logger->log_exception( 'plugin_update', $e );
95 continue;
96 }
97 $executed_updates[] = $key;
98
99 // we're scheduling another update in case there are some dimensions to be updated or anything
100 // we do not do this in the "update" method as otherwise we might be calling this recursively...
101 // it is possible that because the plugins need to be reloaded etc that those updates are not executed right
102 // away but need an actual reload and cache clearance etc
103 wp_schedule_single_event( time() + 15, ScheduledTasks::EVENT_UPDATE );
104
105 update_option( $key, $plugin_version );
106
107 // we make sure to delete cache even if no component was updated eg there may be translation updates etc
108 // and caches need to be invalidated
109 Filesystem::deleteAllCacheOnUpdate();
110 }
111
112 return $executed_updates;
113 }
114
115 public function update() {
116 Bootstrap::do_bootstrap();
117
118 if ( $this->load_plugin_functions() ) {
119 $plugin_data = get_plugin_data( MATOMO_ANALYTICS_FILE, $markup = false, $translate = false );
120
121 $history = $this->settings->get_global_option( 'version_history' );
122 if ( empty( $history ) || ! is_array( $history ) ) {
123 $history = [];
124 }
125
126 if ( ! empty( $plugin_data['Version'] )
127 && ! in_array( $plugin_data['Version'], $history, true ) ) {
128 // this allows us to see which versions of matomo the user was using before this update so we better understand
129 // which version maybe regressed something
130 array_unshift( $history, $plugin_data['Version'] );
131 $history = array_slice( $history, 0, 5 ); // lets keep only the last 5 versions
132 $this->settings->set_global_option( 'version_history', $history );
133 }
134 }
135
136 $this->settings->set_global_option( 'core_version', Version::VERSION );
137 $this->settings->save();
138
139 $paths = new Paths();
140 $paths->clear_cache_dir();
141
142 Option::clearCache();
143 PiwikCache::flushAll();
144
145 \Piwik\Access::doAsSuperUser(
146 function () {
147 self::update_components();
148 self::update_components();
149 }
150 );
151
152 $upload_dir = $paths->get_upload_base_dir();
153
154 $wp_filesystem = $paths->get_file_system();
155 if ( is_dir( $upload_dir ) && is_writable( $upload_dir ) ) {
156 $wp_filesystem->put_contents( $upload_dir . '/index.php', '//hello' );
157 $wp_filesystem->put_contents( $upload_dir . '/index.html', '//hello' );
158 $wp_filesystem->put_contents( $upload_dir . '/index.htm', '//hello' );
159 $wp_filesystem->put_contents(
160 $upload_dir . '/.htaccess',
161 '<Files ~ "(\.mmdb)$">
162 ' . ServerFilesGenerator::getDenyHtaccessContent() . '
163 </Files>
164 <Files ~ "(\.js)$">
165 ' . ServerFilesGenerator::getAllowHtaccessContent() . '
166 </Files>'
167 );
168 }
169 $config_dir = $paths->get_config_ini_path();
170 if ( is_dir( $config_dir ) && is_writable( $config_dir ) ) {
171 $wp_filesystem->put_contents( $config_dir . '/index.php', '//hello' );
172 $wp_filesystem->put_contents( $config_dir . '/index.html', '//hello' );
173 $wp_filesystem->put_contents( $config_dir . '/index.htm', '//hello' );
174 }
175
176 if ( $this->settings->should_disable_addhandler() ) {
177 wp_schedule_single_event( time() + 10, ScheduledTasks::EVENT_DISABLE_ADDHANDLER );
178 }
179 }
180
181 public function is_upgrade_in_progress() {
182 if ( ! self::load_upgrader() ) {
183 return 'no upgrader';
184 }
185
186 if ( self::lock() ) {
187 // we can get the lock meaning no update is in progress
188 self::unlock();
189
190 return false;
191 }
192
193 return true;
194 }
195
196 private static function load_upgrader() {
197 if ( ! class_exists( '\WP_Upgrader', false ) ) {
198 // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
199 @include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
200 }
201
202 return class_exists( '\WP_Upgrader', false );
203 }
204
205 public static function lock() {
206 // prevent the upgrade from being started several times at once
207 // we lock for 4 minutes. In case of major Matomo upgrades the upgrade may take much longer but it should be
208 // safe in this case to run the upgrade several times
209 // important: we always need to use the same timeout otherwise if something did use `create_lock(2)` then
210 // even though another job locked it for 4 minutes, the other job that locks it only for 2 seconds would release
211 // the lock basically since WP does not remember the initialy set release timeout
212 return self::load_upgrader() && WP_Upgrader::create_lock( self::LOCK_NAME, 60 * 4 );
213 }
214
215 public static function unlock() {
216 return self::load_upgrader() && WP_Upgrader::release_lock( self::LOCK_NAME );
217 }
218
219 private static function update_components() {
220 $updater = new \Piwik\Updater();
221 $components_with_update_file = $updater->getComponentUpdates();
222
223 if ( empty( $components_with_update_file ) ) {
224 return false;
225 }
226
227 if ( ! self::lock() ) {
228 throw new UpdateInProgressException();
229 }
230
231 try {
232 SettingsServer::setMaxExecutionTime( 0 );
233
234 if ( function_exists( 'ignore_user_abort' ) ) {
235 // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
236 @ignore_user_abort( true );
237 }
238
239 $result = $updater->updateComponents( $components_with_update_file );
240 } catch ( Exception $e ) {
241 self::unlock();
242 throw $e;
243 }
244 self::unlock();
245
246 if ( ! empty( $result['errors'] ) ) {
247 throw new Exception( 'Error while updating components: ' . implode( ', ', $result['errors'] ) );
248 }
249
250 \Piwik\Updater::recordComponentSuccessfullyUpdated( 'core', Version::VERSION );
251 Filesystem::deleteAllCacheOnUpdate();
252 }
253 }
254