CLI::PrintLine('Found ' . $target['name'] . '-' . $target['version'] . ' in repo ' . $target['source_url']); $compversion = $target['version']; $output = []; exec('gpg --homedir "' . GPG_HOMEDIR . '" --list-public-keys "' . $target['key'] . '"', $output, $result); if($result > 0){ // Key validation failed! CLI::PrintError(implode("\n", $output) . "\n" . 'Is the key ' . $target['key'] . ' installed?'); die(); } /*else{ CLI::PrintLine($output); }*/ // Setup the remote file that will be used to download from. $file = new \Core\Filestore\Backends\FileRemote($target['location']); $filesize = $file->getFilesize(true); CLI::PrintActionStart('Downloading ' . $target['location'] . ' (' . $filesize . ')'); $file->username = $target['source_username']; $file->password = $target['source_password']; $obj = $file->getContentsObject(); // Getting the object simply sets it up, it doesn't download the contents yet. $obj->getContents(); CLI::PrintActionStatus('ok'); if(!($obj instanceof \Core\Filestore\Contents\ContentASC)){ CLI::PrintError($target['location'] . ' does not appear to be a valid GPG signed archive'); die(); } // The object's key must also match what's in the repo. if($obj->getKey() != $target['key']){
/** * Sync all features from the licensing servers and keep the values cached locally. */ public static function Sync(){ // This feature relies on a valid server id. if(!defined('SERVER_ID')){ return null; } if(strlen(SERVER_ID) != 32){ return null; } $urls = []; $features = self::Singleton()->_features; foreach($features as $d){ if(!in_array($d['url'], $urls)){ $urls[] = $d['url']; } } // URLs now is an array of all update servers to pull licensed features from. foreach($urls as $u){ $r = new \Core\Filestore\Backends\FileRemote(); $r->setRequestHeader('X-Core-Server-ID', SERVER_ID); $r->setFilename($u . '/licenser.json'); $contents = $r->getContents(); var_dump($r, $contents); die(); } }
/** * Sync the user back to the linked Facebook account. * * <h3>Usage:</h3> * <pre class="code"> * $auth->syncUser($_POST['access-token']); * </pre> * * @param string $access_token A valid access token for the user to sync up. * * @return bool True or false on success. */ public function syncUser($access_token) { try { $facebook = new \Facebook(['appId' => FACEBOOK_APP_ID, 'secret' => FACEBOOK_APP_SECRET]); $facebook->setAccessToken($access_token); /** @var array $user_profile The array of user data from Facebook */ $user_profile = $facebook->api('/me'); } catch (\Exception $e) { return false; } $user = $this->_usermodel; if (!$user->exists()) { // Some config options for new accounts only. $profiles = $user->get('external_profiles'); if (!is_array($profiles)) { $profiles = []; } $profiles[] = [['type' => 'facebook', 'url' => $user_profile['link'], 'title' => 'Facebook Profile']]; $user->set('external_profiles', $profiles); // Another component from the user-social component. // This needs to be unique, so do a little fudging if necessary. try { $user->set('username', $user_profile['username']); } catch (\ModelValidationException $e) { $user->set('username', $user_profile['username'] . '-' . \Core\random_hex(3)); } // Sync the user avatar. $f = new \Core\Filestore\Backends\FileRemote('http://graph.facebook.com/' . $user_profile['id'] . '/picture?type=large'); $dest = \Core\Filestore\Factory::File('public/user/avatar/' . $f->getBaseFilename()); $f->copyTo($dest); $user->set('avatar', 'public/user/avatar/' . $dest->getBaseFilename()); } // Get all user configs and load in anything possible. $user->set('first_name', $user_profile['first_name']); $user->set('last_name', $user_profile['last_name']); $user->set('gender', ucwords($user_profile['gender'])); $user->set('facebook_id', $user_profile['id']); $user->set('facebook_link', $user_profile['link']); $user->set('facebook_access_token', $facebook->getAccessToken()); }
/** * @param mixed $value * * @return bool */ public function setValue($value) { if ($this->get('required') && !$value) { $this->_error = $this->get('label') . ' is required.'; return false; } // _link_ allows users to paste in a URL for a given file. This is then copied locally as normal. // In order to detect this, I need to look for the presence of a protocol indicator and this element needs // to have allowlink set. if($this->get('allowlink') && strpos($value, '_link_://') === 0){ $n = $this->get('name'); $value = substr($value, 9); // Source $f = new \Core\Filestore\Backends\FileRemote($value); if(!$f->exists()){ $this->_error = 'Remote file does not seem to exist'; return false; } // Destination $nf = \Core\Filestore\Factory::File($this->get('basedir') . '/' . $f->getBaseFilename()); // do NOT copy the contents over until the accept check has been ran! // Now that I have a file object, (in the temp filesystem still), I should validate the filetype // to see if the developer wanted a strict "accept" type to be requested. // If present, I'll have something to run through and see if the file matches. // I need the destination now because I need to full filename if an extension is requested in the accept. if($this->get('accept')){ $acceptcheck = \Core\check_file_mimetype($this->get('accept'), $f->getMimetype(), $nf->getExtension()); // Now that all the mimetypes have run through, I can see if one matched. if($acceptcheck != ''){ $this->_error = $acceptcheck; return false; } } // Now all the checks should be completed and I can safely copy the file away from the temporary filesystem. $f->copyTo($nf); $value = $nf->getFilename(false); } elseif(($this->get('browsable') || $this->get('browseable')) && strpos($value, '_browse_://public') === 0){ $n = $this->get('name'); $value = substr($value, 11); // Source $f = \Core\Filestore\Factory::File($value); if(!$f->exists()){ $this->_error = 'File does not seem to exist'; return false; } // Now that I have a file object, I still need to validate that this file was what the user was supposed to select. // If present, I'll have something to run through and see if the file matches. if($this->get('accept')){ $acceptcheck = \Core\check_file_mimetype($this->get('accept'), $f->getMimetype(), $f->getExtension()); // Now that all the mimetypes have run through, I can see if one matched. if($acceptcheck != ''){ $this->_error = $acceptcheck; return false; } } } elseif ($value == '_upload_') { $n = $this->get('name'); // Because PHP will have different sources depending if the name has [] in it... if (strpos($n, '][') !== false) { // This is a 2+ nested array value. preg_match_all('#\[([^\]]*)\]#', $n, $matches); $p1 = substr($n, 0, strpos($n, '[')); $src =& $_FILES[$p1]; $in = array( 'name' => $src['name'], 'type' => $src['type'], 'tmp_name' => $src['tmp_name'], 'error' => $src['error'], 'size' => $src['size'], ); foreach($matches[1] as $next){ $in['name'] =& $in['name'][$next]; $in['type'] =& $in['type'][$next]; $in['tmp_name'] =& $in['tmp_name'][$next]; $in['error'] =& $in['error'][$next]; $in['size'] =& $in['size'][$next]; } } elseif (strpos($n, '[') !== false) { // This is a single array value. $p1 = substr($n, 0, strpos($n, '[')); $p2 = substr($n, strpos($n, '[') + 1, -1); if (!isset($_FILES[$p1])) { $this->_error = 'No file uploaded for ' . $this->get('label'); return false; } $in = array( 'name' => $_FILES[$p1]['name'][$p2], 'type' => $_FILES[$p1]['type'][$p2], 'tmp_name' => $_FILES[$p1]['tmp_name'][$p2], 'error' => $_FILES[$p1]['error'][$p2], 'size' => $_FILES[$p1]['size'][$p2], ); } else { $in =& $_FILES[$n]; } if (!isset($in)) { $this->_error = 'No file uploaded for ' . $this->get('label'); return false; } else { $error = \Core\translate_upload_error($in['error']); if($error != ''){ $this->_error = $error; return false; } // Source $f = \Core\Filestore\Factory::File($in['tmp_name']); // Destination // Make sure the filename is sanitized. // Also, limit the new filename to 40 characters. $newbasename = substr(\Core\str_to_url($in['name'], true), 0, 40); $nf = \Core\Filestore\Factory::File($this->get('basedir') . '/' . $newbasename); // do NOT copy the contents over until the accept check has been ran! // Now that I have a file object, (in the temp filesystem still), I should validate the filetype // to see if the developer wanted a strict "accept" type to be requested. // If present, I'll have something to run through and see if the file matches. // I need the destination now because I need to full filename if an extension is requested in the accept. if($this->get('accept')){ $acceptcheck = \Core\check_file_mimetype($this->get('accept'), $f->getMimetype(), $nf->getExtension()); // Now that all the mimetypes have run through, I can see if one matched. if($acceptcheck != ''){ $this->_error = $acceptcheck; return false; } } // Now all the checks should be completed and I can safely copy the file away from the temporary filesystem. $f->copyTo($nf); $value = $nf->getFilename(false); } } $this->_attributes['value'] = $value; return true; }
public function testGetMTime() { $file1 = new \Core\Filestore\Backends\FileRemote($this->_testfile); $this->assertFalse($file1->getMTime()); }
public static function PerformInstall($type, $name, $version, $dryrun = false, $verbose = false){ if($verbose){ // These are needed to force the output to be sent immediately. while ( @ob_end_flush() ); // even if there is no nested output buffer if(function_exists('apache_setenv')){ // This function doesn't exist in CGI mode :/ apache_setenv('no-gzip', '1'); } ini_set('output_buffering','on'); ini_set('zlib.output_compression', 0); ob_implicit_flush(); // Give some basic styles for this output. echo '<html> <head> <!-- Yes, the following is 1024 spaces. This is because some browsers have a 1Kb buffer before they start rendering text --> ' . str_repeat(" ", 1024) . ' <style> body { background: none repeat scroll 0 0 black; color: #22EE33; font-family: monospace; } </style> </head> <body>'; } $timer = microtime(true); // Give this script a few more seconds to run. set_time_limit(max(90, ini_get('max_execution_time'))); // This will get a list of all available updates and their sources :) if($verbose) self::_PrintHeader('Retrieving Updates'); $updates = UpdaterHelper::GetUpdates(); if($verbose){ self::_PrintInfo('Found ' . $updates['sitecount'] . ' repository site(s)!', $timer); self::_PrintInfo('Found ' . $updates['pkgcount'] . ' packages!', $timer); } // A list of changes that are to be applied, (mainly for the dry run). $changes = array(); // Target in on the specific object we're installing. Useful for a shortcut. switch($type){ case 'core': $initialtarget = &$updates['core']; break; case 'components': $initialtarget = &$updates['components'][$name]; break; case 'themes': $initialtarget = &$updates['themes'][$name]; break; default: return [ 'status' => 0, 'message' => '[' . $type . '] is not a valid installation type!', ]; } // This is a special case for testing the installer UI. $test = ($type == 'core' && $version == '99.1337~(test)'); if($test && $verbose){ self::_PrintHeader('Performing a test installation!'); } if($test){ if($verbose){ self::_PrintInfo('Sleeping for a few seconds... because servers are always slow when you don\'t want them to be!', $timer); } sleep(4); // Also overwrite some of the target's information. $repo = UpdateSiteModel::Find(null, 1); $initialtarget['source'] = 'repo-' . $repo->get('id'); $initialtarget['location'] = 'http://corepl.us/api/2_4/tests/updater-test.tgz.asc'; $initialtarget['destdir'] = ROOT_PDIR; $initialtarget['key'] = 'B2BEDCCB'; $initialtarget['status'] = 'update'; //if($verbose){ // echo '[DEBUG]' . $nl; // var_dump($initialtarget); //} } // Make sure the name and version exist in the updates list. // In theory, the latest version of core is the only one displayed. if(!$test && $initialtarget['version'] != $version){ return [ 'status' => 0, 'message' => $initialtarget['typetitle'] . ' does not have the requested version available.', 'debug' => [ 'versionrequested' => $version, 'versionfound' => $initialtarget['version'], ], ]; } // A queue of components to check. $pendingqueue = array($initialtarget); // A queue of components that will be installed that have satisfied dependencies. $checkedqueue = array(); // This will assemble the list of required installs in the correct order. // If a given dependency can't be met, the installation will be aborted. if($verbose){ self::_PrintHeader('CHECKING DEPENDENCIES'); } do{ $lastsizeofqueue = sizeof($pendingqueue); foreach($pendingqueue as $k => $c){ $good = true; if(isset($c['requires'])){ if($verbose){ self::_PrintInfo('Checking dependencies for ' . $c['typetitle'], $timer); } foreach($c['requires'] as $r){ // Sometimes there will be blank requirements in the metafile. if(!$r['name']) continue; $result = UpdaterHelper::CheckRequirement($r, $checkedqueue, $updates); if($result === false){ // Dependency not met return [ 'status' => 0, 'message' => $c['typetitle'] . ' requires ' . $r['name'] . ' ' . $r['version'] ]; } elseif($result === true){ // Dependency met via either installed components or new components // yay if($verbose){ self::_PrintInfo('Dependency [' . $r['name'] . ' ' . $r['version'] . '] met with already-installed packages.', $timer); } } else{ if($verbose){ self::_PrintInfo('Additional package [' . $result['typetitle'] . '] required to meet dependency [' . $r['name'] . ' ' . $r['version'] . '], adding to queue and retrying!', $timer); } // It's an array of requirements that are needed to satisfy this installation. $pendingqueue = array_merge(array($result), $pendingqueue); $good = false; } } } else{ if($verbose){ self::_PrintInfo('Skipping dependency check for ' . $c['typetitle'] . ', no requirements present', $timer); } // The require key isn't present... OK! // This happens with themes, as they do not have any dependency logic. } if($good === true){ $checkedqueue[] = $c; $changes[] = (($c['status'] == 'update') ? 'Update' : 'Install') . ' ' . $c['typetitle'] . ' ' . $c['version']; unset($pendingqueue[$k]); } } } while(sizeof($pendingqueue) && sizeof($pendingqueue) != $lastsizeofqueue); // Do validation checks on all these changes. I need to make sure I have the GPG key for each one. // This is done here to save having to download the files from the remote server first. foreach($checkedqueue as $target){ // It'll be validated prior to installation anyway. if(!$target['key']) continue; $output = array(); exec('gpg --homedir "' . GPG_HOMEDIR . '" --list-public-keys "' . $target['key'] . '"', $output, $result); if($result > 0){ // Key validation failed! if($verbose){ echo implode("<br/>\n", $output); } return [ 'status' => 0, 'message' => $c['typetitle'] . ' failed GPG verification! Is the key ' . $target['key'] . ' installed?' ]; } } // Check that the queued packages have not been locally modified if installed. if($verbose){ self::_PrintHeader('Checking for local modifications'); } foreach($checkedqueue as $target){ if($target['status'] == 'update'){ switch($target['type']){ case 'core': $c = Core::GetComponent('core'); break; case 'components': $c = Core::GetComponent($target['name']); break; case 'themes': $c = null; break; } if($c){ // Are there changes? if(sizeof($c->getChangedAssets())){ foreach($c->getChangedAssets() as $change){ $changes[] = 'Overwrite locally-modified asset ' . $change; } } if(sizeof($c->getChangedFiles())){ foreach($c->getChangedFiles() as $change){ $changes[] = 'Overwrite locally-modified file ' . $change; } } if(sizeof($c->getChangedTemplates())){ foreach($c->getChangedTemplates() as $change){ $changes[] = 'Overwrite locally-modified template ' . $change; } } } } } // If dry run is enabled, stop here. // After this stage, dragons be let loose from thar cages. if($dryrun){ return [ 'status' => 1, 'message' => 'All dependencies are met, ok to install', 'changes' => $changes, ]; } // Reset changes, in this case it'll be what was installed. $changes = array(); // By now, $checkedqueue will contain all the pending changes, theoretically with // the initially requested package at the end of the list. foreach($checkedqueue as $target){ if($verbose){ self::_PrintHeader('PERFORMING INSTALL (' . strtoupper($target['typetitle']) . ')'); } // This package is already installed and up to date. if($target['source'] == 'installed'){ return [ 'status' => 0, 'message' => $target['typetitle'] . ' is already installed and at the newest version.', ]; } // If this package is coming from a repo, install it from that repo. elseif(strpos($target['source'], 'repo-') !== false){ /** @var $repo UpdateSiteModel */ $repo = new UpdateSiteModel(substr($target['source'], 5)); if($verbose){ self::_PrintInfo('Using repository ' . $repo->get('url') . ' for installation source', $timer); } // Setup the remote file that will be used to download from. $file = new \Core\Filestore\Backends\FileRemote($target['location']); $file->username = $repo->get('username'); $file->password = $repo->get('password'); // The initial HEAD request pulls the metadata for the file, and sees if it exists. if($verbose){ self::_PrintInfo('Performing HEAD lookup on ' . $file->getFilename(), $timer); } if(!$file->exists()){ return [ 'status' => 0, 'message' => $target['location'] . ' does not seem to exist!' ]; } if($verbose){ self::_PrintInfo('Found a(n) ' . $file->getMimetype() . ' file that returned a ' . $file->getStatus() . ' status.', $timer); } // Get file contents will download the file. if($verbose){ self::_PrintInfo('Downloading ' . $file->getFilename(), $timer); } $downloadtimer = microtime(true); $obj = $file->getContentsObject(); // Getting the object simply sets it up, it doesn't download the contents yet. $obj->getContents(); // Now it has :p // How long did it take? if($verbose){ self::_PrintInfo('Downloaded ' . $file->getFilesize(true) . ' in ' . (round(microtime(true) - $downloadtimer, 2) . ' seconds'), $timer); } if(!($obj instanceof \Core\Filestore\Contents\ContentASC)){ return [ 'status' => 0, 'message' => $target['location'] . ' does not appear to be a valid GPG signed archive' ]; } if(!$obj->verify()){ // Maybe it can at least get the key.... if($key = $obj->getKey()){ return [ 'status' => 0, 'message' => 'Unable to locate public key for ' . $key . '. Is it installed?' ]; } return [ 'status' => 0, 'message' => 'Invalid GPG signature for ' . $target['typetitle'], ]; } // The object's key must also match what's in the repo. if($obj->getKey() != $target['key']){ return [ 'status' => 0, 'message' => '!!!WARNING!!!, Key for ' . $target['typetitle'] . ' is valid, but does not match what was expected form the repository data! This could be a major risk!', 'debug' => [ 'detectedkey' => $obj->getKey(), 'expectedkey' => $target['key'], ], ]; } if($verbose){ self::_PrintInfo('Found key ' . $target['key'] . ' for package maintainer, appears to be valid.', $timer); exec('gpg --homedir "' . GPG_HOMEDIR . '" --list-public-keys "' . $target['key'] . '"', $output, $result); foreach($output as $line){ if(trim($line)) self::_PrintInfo(htmlentities($line), $timer); } } if($verbose) self::_PrintInfo('Checking write permissions', $timer); $dir = \Core\directory($target['destdir']); if(!$dir->isWritable()){ return [ 'status' => 0, 'message' => $target['destdir'] . ' is not writable!' ]; } if($verbose) self::_PrintInfo('OK!', $timer); // Decrypt the signed file. if($verbose) self::_PrintInfo('Decrypting signed file', $timer); if(version_compare(Core::GetComponent('core')->getVersionInstalled(), '4.1.1', '<=') && $file->getBaseFilename() == 'download'){ // HACK < 4.1.2 // Retrieve the filename from the last part of the URL. // This is required because the URL may be /download?file=component/blah.tgz.asc $f = substr($file->getFilename(), strrpos($file->getFilename(), '/'), -4); /** @var $localfile \Core\Filestore\File */ $localfile = $obj->decrypt('tmp/updater/' . $f); } else{ /** @var $localfile \Core\Filestore\File */ $localfile = $obj->decrypt('tmp/updater/'); } /** @var $localobj \Core\Filestore\Contents\ContentTGZ */ $localobj = $localfile->getContentsObject(); if($verbose) self::_PrintInfo('OK!', $timer); // This tarball will be extracted to a temporary directory, then copied from there. if($verbose){ self::_PrintInfo('Extracting tarball ' . $localfile->getFilename(), $timer); } $tmpdir = $localobj->extract('tmp/installer-' . Core::RandomHex(4)); // Now that the data is extracted in a temporary directory, extract every file in the destination. /** @var $datadir \Core\Filestore\Directory */ $datadir = $tmpdir->get('data/'); if(!$datadir){ return [ 'status' => 0, 'message' => 'Invalid package, ' . $target['typetitle'] . ', does not contain a "data" directory.' ]; } if($verbose) self::_PrintInfo('OK!', $timer); if($verbose){ self::_PrintInfo('Installing files into ' . $target['destdir'], $timer); } // Will give me an array of Files in the data directory. $files = $datadir->ls(null, true); // Used to get the relative path for each contained file. $datalen = strlen($datadir->getPath()); foreach($files as $file){ if(!$file instanceof \Core\Filestore\Backends\FileLocal) continue; // It's a file, copy it over. // To do so, resolve the directory path inside the temp data dir. $dest = \Core\Filestore\Factory::File($target['destdir'] . substr($file->getFilename(), $datalen)); /** @var $dest \Core\Filestore\Backends\FileLocal */ if($verbose){ self::_PrintInfo('...' . substr($dest->getFilename(''), 0, 67), $timer); } $dest->copyFrom($file, true); } if($verbose) self::_PrintInfo('OK!', $timer); // Cleanup the temp directory if($verbose){ self::_PrintInfo('Cleaning up temporary directory', $timer); } $tmpdir->remove(); if($verbose) self::_PrintInfo('OK!', $timer); $changes[] = 'Installed ' . $target['typetitle'] . ' ' . $target['version']; } } // Clear the cache so the next pageload will pick up on the new components and goodies. \Core\Cache::Flush(); \Core\Templates\Backends\Smarty::FlushCache(); // Yup, that's it. // Just extract the files and Core will autoinstall/autoupgrade everything on the next page view. // yay... return [ 'status' => 1, 'message' => 'Performed all operations successfully!', 'changes' => $changes, ]; }
/** * Import the given data into the destination Model. * * @param array $data Indexed array of records to import/merge from the external source. * @param array $options Any options required for the import, such as merge, key, etc. * @param boolean $output_realtime Set to true to output the log in real time as the import happens. * * @throws Exception * * @return \Core\ModelImportLogger */ public static function Import($data, $options, $output_realtime = false) { $log = new \Core\ModelImportLogger('User Importer', $output_realtime); $merge = isset($options['merge']) ? $options['merge'] : true; $pk = isset($options['key']) ? $options['key'] : null; if(!$pk) { throw new Exception( 'Import requires a "key" field on options containing the primary key to compare against locally.' ); } // Load in members from the group // Set the default group on new accounts, if a default is set. $defaultgroups = \UserGroupModel::Find(["default = 1"]); $groups = []; $gnames = []; foreach($defaultgroups as $g) { /** @var \UserGroupModel $g */ $groups[] = $g->get('id'); $gnames[] = $g->get('name'); } if(sizeof($groups)) { $log->log('Found ' . sizeof($groups) . ' default groups for new users: ' . implode(', ', $gnames)); } else { $log->log('No groups set as default, new users will not belong to any groups.'); } $log->log('Starting ' . ($merge ? '*MERGE*' : '*skipping*' ) . ' import of ' . sizeof($data) . ' users'); foreach($data as $dat) { if(isset($dat[$pk])){ // Only check the information if the primary key is set on this record. if($pk == 'email' || $pk == 'id') { // These are the only two fields on the User object itself. $user = UserModel::Find([$pk . ' = ' . $dat[ $pk ]], 1); } else { $uucm = UserUserConfigModel::Find(['key = ' . $pk, 'value = ' . $dat[ $pk ]], 1); if($uucm) { $user = $uucm->getLink('UserModel'); } else { // Try the lookup from the email address instead. // This will force accounts that exist to be synced up correctly. // The only caveat to this is that users will not be updated with the foreign key if merge is disabled. $user = UserModel::Find(['email = ' . $dat['email']], 1); } } } else{ $user = null; } $status_type = $user ? 'Updated' : 'Created'; if($user && !$merge) { $log->duplicate('Skipped user ' . $user->getLabel() . ', already exists and merge not requested'); // Skip to the next record. continue; } if(!$user) { // All incoming users must have an email address! if(!isset($dat['email'])) { $log->error('Unable to import user without an email address!'); // Skip to the next record. continue; } // Meta fields that may or may not be present, but should be for reporting purposes. if(!isset($dat['registration_ip'])) { $dat['registration_ip'] = REMOTE_IP; } if(!isset($dat['registration_source'])) { $dat['registration_source'] = \Core\user()->exists() ? 'admin' : 'self'; } if(!isset($dat['registration_invitee'])) { $dat['registration_invitee'] = \Core\user()->get('id'); } // New user! $user = new UserModel(); } // No else needed, else is there IS a valid $user object and it's setup ready to go. // Handle all the properties for this user! foreach($dat as $key => $val){ if($key == 'avatar' && strpos($val, '://') !== false){ // Sync the user avatar. $log->actionStart('Downloading ' . $dat['avatar']); $f = new \Core\Filestore\Backends\FileRemote($dat['avatar']); $dest = \Core\Filestore\Factory::File('public/user/avatar/' . $f->getBaseFilename()); if($dest->identicalTo($f)) { $log->actionSkipped(); } else { $f->copyTo($dest); $user->set('avatar', 'public/user/avatar/' . $dest->getBaseFilename()); $log->actionSuccess(); } } elseif($key == 'profiles' && is_array($val)) { $new_profiles = $val; // Pull the current profiles from the account $profiles = $user->get('external_profiles'); if($profiles && is_array($profiles)) { $current_flat = []; foreach($profiles as $current_profile) { $current_flat[] = $current_profile['url']; } // Merge in any *actual* new profile foreach($new_profiles as $new_profile) { if(!in_array($new_profile['url'], $current_flat)) { $profiles[] = $new_profile; } } unset($new_profile, $new_profiles, $current_flat, $current_profile); } else { $profiles = $new_profiles; unset($new_profiles); } $user->set('external_profiles', $profiles); } elseif($key == 'backend'){ // Was a backend requested? // This gets merged instead of replaced entirely. $user->enableAuthDriver($val); } elseif($key == 'groups'){ $user->setGroups($val); } else{ // Default Behaviour, // save the key into whatever field it was set to go to. $user->set($key, $val); } } try { // Set the default groups loaded from the system. if(!$user->exists()){ $user->setGroups($groups); } $status = $user->save(); } catch(Exception $e) { $log->error($e->getMessage()); // Skip to the next. continue; } if($status) { $log->success($status_type . ' user ' . $user->getLabel() . ' successfully!'); } else { $log->skip('Skipped user ' . $user->getLabel() . ', no changes detected.'); } } $log->finalize(); return $log; }