PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 1.3.1
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v1.3.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 / User / Sync.php
matomo / classes / WpMatomo / User Last commit date
Sync.php 5 years ago
Sync.php
287 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\User;
11
12 use Piwik\Access;
13 use Piwik\Access\Role\Admin;
14 use Piwik\Access\Role\View;
15 use Piwik\Access\Role\Write;
16 use Piwik\Auth\Password;
17 use Piwik\Common;
18 use Piwik\Date;
19 use Piwik\Plugin;
20 use Piwik\Plugins\LanguagesManager\API;
21 use Piwik\Plugins\UsersManager\Model;
22 use Piwik\Plugins\UsersManager;
23 use WpMatomo\Bootstrap;
24 use WpMatomo\Capabilities;
25 use WpMatomo\Logger;
26 use WpMatomo\Site;
27 use WpMatomo\User;
28
29 if ( ! defined( 'ABSPATH' ) ) {
30 exit; // if accessed directly
31 }
32
33 class Sync {
34 /**
35 * actually allowed is 100 characters...
36 * but we do -5 to have some room to append `wp_`.$login.XYZ if needed
37 */
38 const MAX_USER_NAME_LENGTH = 95;
39
40 /**
41 * @var Logger
42 */
43 private $logger;
44
45 public function __construct() {
46 $this->logger = new Logger();
47 }
48
49 public function register_hooks() {
50 add_action( 'add_user_role', array( $this, 'sync_current_users' ), $prio = 10, $args = 0 );
51 add_action( 'remove_user_role', array( $this, 'sync_current_users' ), $prio = 10, $args = 0 );
52 add_action( 'add_user_to_blog', array( $this, 'sync_current_users' ), $prio = 10, $args = 0 );
53 add_action( 'remove_user_from_blog', array( $this, 'sync_current_users' ), $prio = 10, $args = 0 );
54 add_action( 'user_register', array( $this, 'sync_current_users' ), $prio = 10, $args = 0 );
55 add_action( 'profile_update', array( $this, 'sync_current_users' ), $prio = 10, $args = 0 );
56 }
57
58 public function sync_all() {
59 if ( function_exists( 'is_multisite' ) && is_multisite() ) {
60 foreach ( get_sites() as $site ) {
61 switch_to_blog( $site->blog_id );
62
63 $idsite = Site::get_matomo_site_id( $site->blog_id );
64
65 try {
66 if ( $idsite ) {
67 $users = $this->get_users( array('blog_id' => $site->blog_id ) );
68 $this->sync_users( $users, $idsite );
69 }
70 } catch ( \Exception $e ) {
71 // we don't want to rethrow exception otherwise some other blogs might never sync
72 $this->logger->log_exception( 'user_sync ', $e );
73 }
74
75 restore_current_blog();
76 }
77 } else {
78 $this->sync_current_users();
79 }
80 }
81
82 private function get_users($options = array())
83 {
84 /** @var \WP_User[] $users */
85 $users = get_users( $options );
86 if (is_multisite()) {
87 $super_admins = get_super_admins();
88 if (!empty($super_admins)) {
89 foreach ($super_admins as $super_admin) {
90 $found = false;
91 foreach ($users as $user) {
92 if ($user->user_login === $super_admin) {
93 $found = true;
94 break;
95 }
96 }
97 if (!$found) {
98 $user = get_user_by('login', $super_admin);
99 if (!empty($user)) {
100 $users[] = $user;
101 }
102 }
103 }
104 }
105 }
106 return $users;
107 }
108
109 public function sync_current_users() {
110 $idsite = Site::get_matomo_site_id( get_current_blog_id() );
111 if ( $idsite ) {
112 $users = $this->get_users();
113 $this->sync_users( $users, $idsite );
114 }
115 }
116
117 /**
118 * Sync all users. Make sure to always pass all sites that exist within a given site... you cannot just sync an individual
119 * user... we would delete all other users
120 *
121 * @param \WP_User[] $users
122 * @param $idsite
123 */
124 protected function sync_users( $users, $idsite ) {
125 Bootstrap::do_bootstrap();
126
127 $this->logger->log( 'Matomo will now sync ' . count( $users ) . ' users' );
128
129 $super_users = array();
130 $logins_with_some_view_access = array( 'anonmyous' ); // may or may not exist... we don't want to delete this user though
131 $user_model = new Model();
132
133 // need to make sure we recreate new instance later with latest dependencies in case they changed
134 API::unsetInstance();
135
136 foreach ( $users as $user ) {
137 $user_id = $user->ID;
138
139 // todo if we used transactions we could commit it after a possibly new access has been added
140 // to prevent UI preventing randomly saying no access between deleting and adding access
141
142 $mapped_matomo_login = User::get_matomo_user_login( $user_id );
143 if ( $mapped_matomo_login ) {
144 $user_model->deleteUserAccess( $mapped_matomo_login, array( $idsite ) );
145 }
146
147 $matomo_login = null;
148
149 if ( user_can( $user, Capabilities::KEY_SUPERUSER ) ) {
150 $matomo_login = $this->ensure_user_exists( $user );
151 $super_users[ $matomo_login ] = $user;
152 $logins_with_some_view_access[] = $matomo_login;
153 } elseif ( user_can( $user, Capabilities::KEY_ADMIN ) ) {
154 $matomo_login = $this->ensure_user_exists( $user );
155 $user_model->addUserAccess( $matomo_login, Admin::ID, array( $idsite ) );
156 $user_model->setSuperUserAccess( $matomo_login, false );
157 $logins_with_some_view_access[] = $matomo_login;
158 } elseif ( user_can( $user, Capabilities::KEY_WRITE ) ) {
159 $matomo_login = $this->ensure_user_exists( $user );
160 $user_model->addUserAccess( $matomo_login, Write::ID, array( $idsite ) );
161 $user_model->setSuperUserAccess( $matomo_login, false );
162 $logins_with_some_view_access[] = $matomo_login;
163 } elseif ( user_can( $user, Capabilities::KEY_VIEW ) ) {
164 $matomo_login = $this->ensure_user_exists( $user );
165 $user_model->addUserAccess( $matomo_login, View::ID, array( $idsite ) );
166 $user_model->setSuperUserAccess( $matomo_login, false );
167 $logins_with_some_view_access[] = $matomo_login;
168 }
169
170 if ( $matomo_login ) {
171 $locale = get_user_locale( $user->ID );
172 $locale_dash = Common::mb_strtolower(str_replace('_', '-', $locale));
173 if ($locale && in_array($locale_dash, ['zh-cn', 'zh-tw', 'pt-br', 'es-ar'], true)) {
174 $parts = [$locale_dash];
175 } else {
176 $parts = explode( '_', $locale );
177 }
178
179 if ( ! empty( $parts[0] ) ) {
180 $lang = $parts[0];
181 if ( Plugin\Manager::getInstance()->isPluginActivated( 'LanguagesManager' )
182 && Plugin\Manager::getInstance()->isPluginInstalled( 'LanguagesManager' )
183 && API::getInstance()->isLanguageAvailable( $lang ) ) {
184 $user_lang_model = new \Piwik\Plugins\LanguagesManager\Model();
185 $user_lang_model->setLanguageForUser( $matomo_login, $lang );
186 }
187 }
188 }
189
190 if ($idsite != 1) {
191 // only needed if the actual site is not the default site... makes sure when they click in Matomo
192 // UI on "Dashboard" that the correct site is being opened by default
193 // eg if the linked site is actually idSite=2.
194 Access::doAsSuperUser(
195 function () use ( $matomo_login, &$idsite ) {
196 try {
197 UsersManager\API::unsetInstance();
198 // we need to unset the instance to make sure it fetches the
199 // up to date dependencies eg current plugin manager etc
200
201 UsersManager\API::getInstance()->setUserPreference(
202 $matomo_login,
203 UsersManager\API::PREFERENCE_DEFAULT_REPORT,
204 $idsite
205 );
206 } catch (\Exception $e) {
207 // ignore any error for now
208 }
209
210 }
211 );
212 }
213 }
214
215 foreach ( $super_users as $matomo_login => $user ) {
216 $user_model->setSuperUserAccess( $matomo_login, true );
217 }
218
219 $logins_with_some_view_access = array_unique( $logins_with_some_view_access );
220 $all_users = $user_model->getUsers( array() );
221 foreach ( $all_users as $all_user ) {
222 if ( ! in_array( $all_user['login'], $logins_with_some_view_access, true )
223 && ! empty( $all_user['login'] ) ) {
224 $user_model->deleteUserOnly( $all_user['login'] );
225 }
226 }
227 }
228
229 /**
230 * @param \WP_User $wp_user
231 */
232 protected function ensure_user_exists( $wp_user ) {
233 $user_model = new Model();
234 $user_id = $wp_user->ID;
235 $login = $wp_user->user_login;
236
237 $matomo_user_login = User::get_matomo_user_login( $user_id );
238 $user_in_matomo = null;
239
240 if ( $matomo_user_login ) {
241 $user_in_matomo = $user_model->getUser( $matomo_user_login );
242 } else {
243 // wp usernames may include whitespace etc
244 $login = preg_replace('/[^A-Za-zÄäÖöÜüß0-9_.@+-]+/D', '_', $login);
245 $login = substr( $login, 0, self::MAX_USER_NAME_LENGTH );
246
247 if ( ! $user_model->getUser( $login ) ) {
248 // username is available...
249 $matomo_user_login = $login;
250 } else {
251 // this username seems taken... lets create another one
252
253 $index = 0;
254 do {
255 if ( ! $index ) {
256 $matomo_user_login = 'wp_' . $login;
257 } else {
258 $matomo_user_login = 'wp_' . $login . $index;
259 }
260
261 $index ++;
262 } while ( $user_model->getUser( $matomo_user_login ) );
263 }
264 }
265
266 if ( ! $matomo_user_login || empty( $user_in_matomo ) ) {
267 $this->logger->log( 'Matomo is now creating a user forUserId ' . $user_id . ' with matomo login ' . $matomo_user_login );
268
269 $now = Date::now()->getDatetime();
270 $password = new Password();
271 // we generate some random password since log in using matomo won't be happening anyway
272 $password = $password->hash( $login . $now . Common::getRandomString( 200 ) . microtime( true ) . Common::generateUniqId() );
273
274 UsersManager\API::unsetInstance(); // make sure latest instance is loaded with all current dependencies... mainly needed for tests
275 $token = UsersManager\API::getInstance()->createTokenAuth( $login );
276 $user_model->addUser( $matomo_user_login, $password, $wp_user->user_email, $login, $token, $now );
277
278 User::map_matomo_user_login( $user_id, $matomo_user_login );
279 } elseif ( $user_in_matomo['email'] !== $wp_user->user_email ) {
280 $this->logger->log( 'Matomo is now updating the email for wpUserID ' . $user_id . ' matomo login ' . $matomo_user_login );
281 $user_model->updateUserFields( $matomo_user_login, array( 'email' => $wp_user->user_email ) );
282 }
283
284 return $matomo_user_login;
285 }
286 }
287