$create_user = (bool) $sync_config->create_user; $ban_user = (bool) $sync_config->ban_user; $unban_user = (bool) $sync_config->unban_user; $notify_user = (bool) $sync_config->notify_user; $log_cleanup_count = sanitise_int($sync_config->log_cleanup_count, false); if (empty($log_cleanup_count)) { $log_cleanup_count = ''; } } // get field config switch ($datasource->datasource_type) { case 'mysql': $ps = new ProfileSyncMySQL($datasource); break; case 'csv': $ps = new ProfileSyncCSV($datasource); break; } if (empty($ps)) { echo elgg_view('output/longtext', ['value' => elgg_echo('profile_sync:admin:sync_configs:edit:no_datasource')]); return; } $datasource_cols = $ps->getColumns(); $profile_fields = elgg_get_config('profile_fields'); $schedule_options = ['hourly' => elgg_echo('profile_sync:interval:hourly'), 'daily' => elgg_echo('profile_sync:interval:daily'), 'weekly' => elgg_echo('profile_sync:interval:weekly'), 'monthly' => elgg_echo('profile_sync:interval:monthly'), 'yearly' => elgg_echo('profile_sync:interval:yearly'), 'manual' => elgg_echo('profile_sync:sync_configs:schedule:manual')]; $override_options = ['1' => elgg_echo('option:yes'), '0' => elgg_echo('option:no')]; // show which datasource echo elgg_format_element('div', [], elgg_format_element('label', ['class' => 'mrs'], elgg_echo('profile_sync:admin:sync_configs:edit:datasource') . ':') . $datasource->title); if (empty($datasource_cols) || empty($profile_fields)) { echo elgg_view('output/longtext', ['value' => elgg_echo('profile_sync:admin:sync_configs:edit:no_columns')]); return;
/** * Run the profile synchronization based on the provided configuration * * @param ElggObject $sync_config The sync configuration * * @return void */ function profile_sync_proccess_configuration(ElggObject $sync_config) { if (!elgg_instanceof($sync_config, 'object', 'profile_sync_config')) { return; } $datasource = $sync_config->getContainerEntity(); if (!elgg_instanceof($datasource, 'object', 'profile_sync_datasource')) { return; } $sync_match = json_decode($sync_config->sync_match, true); $datasource_id = $sync_config->datasource_id; $profile_id = $sync_config->profile_id; $lastrun = (int) $sync_config->lastrun; $ban_user = (bool) $sync_config->ban_user; $unban_user = (bool) $sync_config->unban_user; profile_sync_log($sync_config->getGUID(), "Last run timestamp: {$lastrun} (" . date(elgg_echo('friendlytime:date_format'), $lastrun) . ")" . PHP_EOL); $profile_fields = elgg_get_config('profile_fields'); if (!$ban_user && !$unban_user && empty($sync_match) || $datasource_id === '' || empty($profile_id)) { profile_sync_log($sync_config->getGUID(), 'Configuration error', true); return; } if (!in_array($profile_id, ['name', 'username', 'email']) && !array_key_exists($profile_id, $profile_fields)) { profile_sync_log($sync_config->getGUID(), "Invalid profile identifier: {$profile_id}", true); return; } switch ($datasource->datasource_type) { case 'mysql': $sync_source = new ProfileSyncMySQL($datasource, $lastrun); break; case 'csv': $sync_source = new ProfileSyncCSV($datasource, $lastrun); break; default: profile_sync_log($sync_config->getGUID(), "Invalid datasource type: {$datasource->datasource_type}", true); return; break; } if (!$sync_source->connect()) { profile_sync_log($sync_config->getGUID(), 'Unable to connect to the datasource', true); return; } $datasource_id_fallback = $sync_config->datasource_id_fallback; $profile_id_fallback = $sync_config->profile_id_fallback; $create_user = (bool) $sync_config->create_user; $notify_user = (bool) $sync_config->notify_user; $create_user_name = false; $create_user_email = false; $create_user_username = false; if ($create_user) { profile_sync_log($sync_config->getGUID(), 'User creation is allowed'); foreach ($sync_match as $datasource_col => $datasource_config) { list($datasource_col) = explode(PROFILE_SYNC_DATASOURCE_COL_SEPERATOR, $datasource_col); switch ($datasource_config['profile_field']) { case 'name': $create_user_name = $datasource_col; break; case 'email': $create_user_email = $datasource_col; break; case 'username': $create_user_username = $datasource_col; break; } } if ($create_user_name === false || $create_user_username === false || $create_user_email === false) { profile_sync_log($sync_config->getGUID(), 'Missing information to create users'); profile_sync_log($sync_config->getGUID(), "- name: {$create_user_name}"); profile_sync_log($sync_config->getGUID(), "- email: {$create_user_email}"); profile_sync_log($sync_config->getGUID(), "- username: {$create_user_username}"); $create_user = false; } } if ($ban_user) { profile_sync_log($sync_config->getGUID(), 'Matching users will be banned'); } if ($unban_user) { profile_sync_log($sync_config->getGUID(), 'Matching users will be unbanned'); } if ($ban_user && $create_user) { profile_sync_log($sync_config->getGUID(), 'Both create and ban users is allowed, don\'t know what to do', true); return; } if ($unban_user && $create_user) { profile_sync_log($sync_config->getGUID(), 'Both create and unban users is allowed, don\'t know what to do', true); return; } if ($ban_user && $unban_user) { profile_sync_log($sync_config->getGUID(), 'Both ban and unban users is allowed, don\'t know what to do', true); return; } // start the sync process set_time_limit(0); _elgg_services()->db->disableQueryCache(); $default_access = get_default_access(); $ia = elgg_set_ignore_access(true); $site = elgg_get_site_entity(); // we want to cache entity metadata on first __get() $metadata_cache = _elgg_services()->metadataCache; if ($metadata_cache instanceof ElggVolatileMetadataCache) { // elgg 1.10 $metadata_cache->setIgnoreAccess(false); } $counters = ['source rows' => 0, 'empty source id' => 0, 'duplicate email' => 0, 'duplicate name' => 0, 'duplicate profile field' => 0, 'user not found' => 0, 'user created' => 0, 'user banned' => 0, 'user unbanned' => 0, 'empty attributes' => 0, 'invalid profile field' => 0, 'invalid source field' => 0, 'processed users' => 0]; $base_location = ''; if ($sync_source instanceof ProfileSyncCSV) { // get base path $csv_location = $datasource->csv_location; $csv_filename = basename($csv_location); $base_location = rtrim(str_ireplace($csv_filename, '', $csv_location), DIRECTORY_SEPARATOR); } while (($source_row = $sync_source->fetchRow()) !== false) { $counters['source rows']++; // let other plugins change the row data $params = ['datasource' => $datasource, 'sync_config' => $sync_config, 'source_row' => $source_row]; $source_row = elgg_trigger_plugin_hook('source_row', 'profile_sync', $params, $source_row); if (!is_array($source_row) || empty($source_row[$datasource_id])) { $counters["empty source id"]++; continue; } // find user $datasource_used_id = $datasource_id; $profile_used_id = $profile_id; $datasource_unique_id = elgg_extract($datasource_id, $source_row); $user = profile_sync_find_user($profile_id, $datasource_unique_id, $sync_config, $counters); // fallback user if (empty($user) && $datasource_id_fallback !== '' && !empty($source_row[$datasource_id_fallback]) && !empty($profile_id_fallback)) { // profile_sync_log($sync_config->getGUID(), "User not found: {$profile_id} => {$datasource_unique_id} trying fallback"); $profile_used_id = $profile_id_fallback; $datasource_used_id = $datasource_id_fallback; $datasource_unique_id = elgg_extract($datasource_id_fallback, $source_row); $user = profile_sync_find_user($profile_id_fallback, $datasource_unique_id, $sync_config, $counters); } // check if we need to create a user if (empty($user) && $create_user) { $pwd = generate_random_cleartext_password(); try { // convert to utf-8 $username = profile_sync_filter_var($source_row[$create_user_username]); $name = profile_sync_filter_var($source_row[$create_user_name]); $email = profile_sync_filter_var($source_row[$create_user_email]); $user_guid = register_user($username, $pwd, $name, $email); if (!empty($user_guid)) { $counters['user created']++; profile_sync_log($sync_config->getGUID(), "Created user: {$name}"); $user = get_user($user_guid); if ($notify_user) { $subject = elgg_echo('useradd:subject'); $body = elgg_echo('useradd:body', [$user->name, $site->name, $site->url, $user->username, $pwd]); notify_user($user->getGUID(), $site->getGUID(), $subject, $body); } } } catch (RegistrationException $r) { $name = profile_sync_filter_var($source_row[$create_user_name]); profile_sync_log($sync_config->getGUID(), "Failure creating user: {$name} - {$r->getMessage()}"); } } // did we get a user if (empty($user)) { $counters['user not found']++; profile_sync_log($sync_config->getGUID(), "User not found: {$profile_used_id} => {$datasource_unique_id}"); continue; } else { $counters['processed users']++; } // ban the user if ($ban_user) { // already banned? if (!$user->isBanned()) { $counters['user banned']++; $user->ban("Profile Sync: {$sync_config->title}"); profile_sync_log($sync_config->getGUID(), "User banned: {$user->name} ({$user->username})"); } continue; } // unban the user if ($unban_user) { // already banned? if ($user->isBanned()) { $counters['user unbanned']++; $user->unban(); profile_sync_log($sync_config->getGUID(), "User unbanned: {$user->name} ({$user->username})"); } continue; } // start of profile sync $special_sync_fields = ['name', 'username', 'email', 'user_icon_relative_path', 'user_icon_full_path']; foreach ($sync_match as $datasource_col => $profile_config) { list($datasource_col) = explode(PROFILE_SYNC_DATASOURCE_COL_SEPERATOR, $datasource_col); $profile_field = elgg_extract('profile_field', $profile_config); $access = (int) elgg_extract('access', $profile_config, $default_access); $override = (bool) elgg_extract('always_override', $profile_config, true); if (!in_array($profile_field, $special_sync_fields) && !array_key_exists($profile_field, $profile_fields)) { $counters['invalid profile field']++; continue; } if (!isset($source_row[$datasource_col])) { $counters['invalid source field']++; continue; } $value = elgg_extract($datasource_col, $source_row); $value = profile_sync_filter_var($value); switch ($profile_field) { case 'email': if (!is_email_address($value)) { continue 2; } case 'username': if ($override && $user->username !== $value) { // new username, check for availability if (get_user_by_username($value)) { // already taken profile_sync_log($sync_config->getGUID(), "New username: {$value} for {$user->name} is already taken"); continue 2; } } case 'name': if (empty($value)) { $counters['empty attributes']++; profile_sync_log($sync_config->getGUID(), "Empty user attribute: {$datasource_col} for user {$user->name}"); continue 2; } if (isset($user->{$profile_field}) && !$override) { // don't override profile field // profile_sync_log($sync_config->getGUID(), "Profile field already set: {$profile_field} for user {$user->name}"); continue 2; } // check for the same value if ($user->{$profile_field} === $value) { // same value, no need to update continue 2; } // save user attribute $user->{$profile_field} = $value; $user->save(); break; case 'user_icon_relative_path': // get a user icon based on a relative file path/url // only works with file based datasources (eg. csv) if (!$sync_source instanceof ProfileSyncCSV) { profile_sync_log($sync_config->getGUID(), "Can't fetch relative user icon path in non CSV datasouces: trying user {$user->name}"); continue 2; } // make new icon path if (!empty($value)) { $value = sanitise_filepath($value, false); // prevent abuse (like ../../......) $value = ltrim($value, DIRECTORY_SEPARATOR); // remove beginning / $value = $base_location . DIRECTORY_SEPARATOR . $value; // concat base location and rel path } case 'user_icon_full_path': // get a user icon based on a full file path/url if (!empty($user->icontime) && !$override) { // don't override icon // profile_sync_log($sync_config->getGUID(), "User already has an icon: {$user->name}"); continue 2; } // upload new icon $icon_sizes = elgg_get_config('icon_sizes'); $fh = new ElggFile(); $fh->owner_guid = $user->getGUID(); if (empty($value) && $user->icontime) { // no icon, so unset current icon profile_sync_log($sync_config->getGUID(), "Removing icon for user: {$user->name}"); foreach ($icon_sizes as $size => $icon_info) { $fh->setFilename("profile/{$user->getGUID()}{$size}.jpg"); $fh->delete(); } unset($user->icontime); unset($fh); // on to the next field continue 2; } // try to get the user icon $icon_contents = file_get_contents($value); if (empty($icon_contents)) { profile_sync_log($sync_config->getGUID(), "Unable to fetch user icon: {$value} for user {$user->name}"); continue 2; } // was csv image updated $csv_icontime = @filemtime($value); if ($csv_icontime !== false && isset($user->icontime)) { $csv_icontime = sanitise_int($csv_icontime); $icontime = sanitise_int($user->icontime); if ($csv_icontime === $icontime) { // base image has same modified time as user icontime, so skipp // profile_sync_log($sync_config->getGUID(), "No need to update user icon for user: {$user->name}"); continue 2; } } if ($csv_icontime === false) { $csv_icontime = time(); } // write icon to a temp location for further handling $tmp_icon = tempnam(sys_get_temp_dir(), $user->getGUID()); file_put_contents($tmp_icon, $icon_contents); // resize icon $icon_updated = false; foreach ($icon_sizes as $size => $icon_info) { $icon_contents = get_resized_image_from_existing_file($tmp_icon, $icon_info['w'], $icon_info['h'], $icon_info['square'], 0, 0, 0, 0, $icon_info['upscale']); if (empty($icon_contents)) { continue; } $fh->setFilename("profile/{$user->getGUID()}{$size}.jpg"); $fh->open('write'); $fh->write($icon_contents); $fh->close(); $icon_updated = true; } // did we have a successfull icon upload? if ($icon_updated) { $user->icontime = $csv_icontime; } // cleanup unlink($tmp_icon); unset($fh); break; default: // check overrides if (isset($user->{$profile_field}) && !$override) { // don't override profile field // profile_sync_log($sync_config->getGUID(), "Profile field already set: {$profile_field} for user {$user->name}"); continue 2; } // convert tags if ($profile_fields[$profile_field] === 'tags') { $value = string_to_tag_array($value); } // remove existing value if (empty($value)) { if (isset($user->{$profile_field})) { unset($user->{$profile_field}); } continue 2; } // check for the same value if ($user->{$profile_field} === $value) { // same value, no need to update continue 2; } // profile_sync_log($sync_config->getGUID(), "Updating {$profile_field} with value {$value} old value {$user->$profile_field}"); // get the access of existing profile data $access = profile_sync_get_profile_field_access($user->getGUID(), $profile_field, $access); // save new value $user->setMetadata($profile_field, $value, '', false, $user->getGUID(), $access); break; } } // let others know we updated the user $update_event_params = ['entity' => $user, 'source_row' => $source_row, 'sync_config' => $sync_config, 'datasource' => $datasource]; elgg_trigger_event('update_user', 'profile_sync', $update_event_params); // cache cleanup _elgg_services()->entityCache->remove($user->getGUID()); $metadata_cache->clear($user->getGUID()); } profile_sync_log($sync_config->getGUID(), PHP_EOL . 'End processing: ' . date(elgg_echo('friendlytime:date_format')) . PHP_EOL); foreach ($counters as $name => $count) { profile_sync_log($sync_config->getGUID(), "{$name}: {$count}"); } // close logfile profile_sync_log($sync_config->getGUID(), null, true); // save last run $sync_config->lastrun = time(); // cleanup datasource cache $sync_source->cleanup(); // re-enable db caching _elgg_services()->db->enableQueryCache(); // restore access elgg_set_ignore_access($ia); if ($metadata_cache instanceof ElggVolatileMetadataCache) { // elgg 1.10 $metadata_cache->unsetIgnoreAccess(); } elseif ($metadata_cache instanceof \Elgg\Cache\MetadataCache) { // elgg 1.11+ $metadata_cache->clearAll(); } }