first commit
This commit is contained in:
@@ -0,0 +1,353 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/user/DAO.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2000-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class DAO
|
||||
*
|
||||
* @ingroup user
|
||||
*
|
||||
* @see User
|
||||
*
|
||||
* @brief Operations for retrieving and modifying User objects.
|
||||
*/
|
||||
|
||||
namespace PKP\user;
|
||||
|
||||
use APP\facades\Repo;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\LazyCollection;
|
||||
use PKP\core\DataObject;
|
||||
use PKP\core\EntityDAO;
|
||||
use PKP\identity\Identity;
|
||||
|
||||
/**
|
||||
* @template T of User
|
||||
* @extends EntityDAO<T>
|
||||
*/
|
||||
class DAO extends EntityDAO
|
||||
{
|
||||
/** @copydoc EntityDAO::$schema */
|
||||
public $schema = \PKP\services\PKPSchemaService::SCHEMA_USER;
|
||||
|
||||
/** @copydoc EntityDAO::$table */
|
||||
public $table = 'users';
|
||||
|
||||
/** @copydoc EntityDAO::$settingsTable */
|
||||
public $settingsTable = 'user_settings';
|
||||
|
||||
/** @copydoc EntityDAO::$primarykeyColumn */
|
||||
public $primaryKeyColumn = 'user_id';
|
||||
|
||||
/** @copydoc EntityDAO::$primaryTableColumns */
|
||||
public $primaryTableColumns = [
|
||||
'id' => 'user_id',
|
||||
'userName' => 'username',
|
||||
'password' => 'password',
|
||||
'email' => 'email',
|
||||
'url' => 'url',
|
||||
'phone' => 'phone',
|
||||
'mailingAddress' => 'mailing_address',
|
||||
'billingAddress' => 'billing_address',
|
||||
'country' => 'country',
|
||||
'locales' => 'locales',
|
||||
'gossip' => 'gossip',
|
||||
'dateLastEmail' => 'date_last_email',
|
||||
'dateRegistered' => 'date_registered',
|
||||
'dateValidated' => 'date_validated',
|
||||
'dateLastLogin' => 'date_last_login',
|
||||
'mustChangePassword' => 'must_change_password',
|
||||
'authStr' => 'auth_str',
|
||||
'disabled' => 'disabled',
|
||||
'disabledReason' => 'disabled_reason',
|
||||
'inlineHelp' => 'inline_help',
|
||||
];
|
||||
|
||||
/* These constants are used user-selectable search fields. */
|
||||
public const USER_FIELD_USERID = 'user_id';
|
||||
public const USER_FIELD_USERNAME = 'username';
|
||||
public const USER_FIELD_EMAIL = 'email';
|
||||
public const USER_FIELD_URL = 'url';
|
||||
public const USER_FIELD_INTERESTS = 'interests';
|
||||
public const USER_FIELD_AFFILIATION = 'affiliation';
|
||||
public const USER_FIELD_NONE = null;
|
||||
|
||||
/**
|
||||
* Construct a new User object.
|
||||
*/
|
||||
public function newDataObject()
|
||||
{
|
||||
return new User();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a user
|
||||
*
|
||||
* @param bool $allowDisabled If true, allow fetching a disabled user.
|
||||
*/
|
||||
public function get(int $id, $allowDisabled = false): ?User
|
||||
{
|
||||
$row = DB::table($this->table)
|
||||
->where($this->primaryKeyColumn, $id)
|
||||
->first();
|
||||
/** @var User */
|
||||
$user = $row ? $this->fromRow($row) : null;
|
||||
if (!$allowDisabled && $user?->getDisabled()) {
|
||||
return null;
|
||||
}
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a user exists
|
||||
*/
|
||||
public function exists(int $id): bool
|
||||
{
|
||||
return DB::table($this->table)
|
||||
->where($this->primaryKeyColumn, '=', $id)
|
||||
->exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a collection of users matching the configured query
|
||||
* @return LazyCollection<int,T>
|
||||
*/
|
||||
public function getMany(Collector $query): LazyCollection
|
||||
{
|
||||
$rows = $query
|
||||
->getQueryBuilder()
|
||||
->get();
|
||||
|
||||
return LazyCollection::make(function () use ($rows, $query) {
|
||||
foreach ($rows as $row) {
|
||||
yield $row->user_id => $this->fromRow($row, $query->includeReviewerData);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the total count of users matching the configured query
|
||||
*/
|
||||
public function getCount(Collector $query): int
|
||||
{
|
||||
return $query
|
||||
->getQueryBuilder()
|
||||
->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of ids matching the configured query
|
||||
*
|
||||
* @return Collection<int,int>
|
||||
*/
|
||||
public function getIds(Collector $query): Collection
|
||||
{
|
||||
return $query
|
||||
->getQueryBuilder()
|
||||
->select('u.' . $this->primaryKeyColumn)
|
||||
->pluck('u.' . $this->primaryKeyColumn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a user by username.
|
||||
*
|
||||
* @return ?User
|
||||
*/
|
||||
public function getByUsername(string $username, bool $allowDisabled = false): ?User
|
||||
{
|
||||
$row = DB::table('users')
|
||||
->whereRaw('LOWER(username) = LOWER(?)', [$username])
|
||||
->when(!$allowDisabled, function ($query) {
|
||||
return $query->where('disabled', '=', false);
|
||||
})
|
||||
->get('user_id')
|
||||
->first();
|
||||
|
||||
return $row ? $this->get($row->user_id, $allowDisabled) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a user by email address.
|
||||
*
|
||||
* @return ?User
|
||||
*/
|
||||
public function getByEmail(string $email, bool $allowDisabled = false): ?User
|
||||
{
|
||||
$row = DB::table('users')
|
||||
->whereRaw('LOWER(email) = LOWER(?)', [$email])
|
||||
->when(!$allowDisabled, function ($query) {
|
||||
return $query->where('disabled', '=', false);
|
||||
})
|
||||
->get('user_id')
|
||||
->first();
|
||||
return $row ? $this->get($row->user_id, $allowDisabled) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user by the TDL ID (implicit authentication).
|
||||
*
|
||||
* @param string $authstr
|
||||
* @param bool $allowDisabled
|
||||
*
|
||||
* @return ?User
|
||||
*/
|
||||
public function getUserByAuthStr($authstr, $allowDisabled = true): ?User
|
||||
{
|
||||
$row = DB::table('users')
|
||||
->where('auth_str', $authstr)
|
||||
->when(!$allowDisabled, function ($query) {
|
||||
return $query->where('disabled', 0);
|
||||
})
|
||||
->get('user_id')
|
||||
->first();
|
||||
return $row ? $this->get($row->user_id) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a user by username and (encrypted) password.
|
||||
*
|
||||
* @param string $username
|
||||
* @param string $password encrypted password
|
||||
* @param bool $allowDisabled
|
||||
*
|
||||
* @return ?User
|
||||
*/
|
||||
public function getUserByCredentials($username, $password, $allowDisabled = true): ?User
|
||||
{
|
||||
$row = DB::table('users')
|
||||
->where('username', '=', $username)
|
||||
->where('password', '=', $password)
|
||||
->when(!$allowDisabled, function ($query) {
|
||||
return $query->where('disabled', '=', false);
|
||||
})
|
||||
->get('user_id')
|
||||
->first();
|
||||
return $row ? $this->get($row->user_id) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc EntityDAO::fromRow
|
||||
*
|
||||
*/
|
||||
public function fromRow(object $row, bool $includeReviewerData = false): DataObject
|
||||
{
|
||||
$user = parent::fromRow($row);
|
||||
if ($includeReviewerData) {
|
||||
$user->setData('lastAssigned', $row->last_assigned);
|
||||
$user->setData('incompleteCount', (int) $row->incomplete_count);
|
||||
$user->setData('completeCount', (int) $row->complete_count);
|
||||
$user->setData('declinedCount', (int) $row->declined_count);
|
||||
$user->setData('cancelledCount', (int) $row->cancelled_count);
|
||||
$user->setData('averageTime', (int) $row->average_time);
|
||||
|
||||
// 0 values should return null. They represent a reviewer with no ratings
|
||||
if ($reviewerRating = $row->reviewer_rating) {
|
||||
$user->setData('reviewerRating', max(1, round($reviewerRating)));
|
||||
}
|
||||
}
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc EntityDAO::_insert()
|
||||
*/
|
||||
public function insert(User $user): int
|
||||
{
|
||||
return parent::_insert($user);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc EntityDAO::update()
|
||||
*/
|
||||
public function update(User $user)
|
||||
{
|
||||
parent::_update($user);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc EntityDAO::_delete()
|
||||
*/
|
||||
public function delete(User $user)
|
||||
{
|
||||
parent::_delete($user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update user names when the site primary locale changes.
|
||||
*
|
||||
* @param string $oldLocale
|
||||
* @param string $newLocale
|
||||
*/
|
||||
public function changeSitePrimaryLocale($oldLocale, $newLocale)
|
||||
{
|
||||
// remove all empty user names in the new locale
|
||||
// so that we do not have to take care if we should insert or update them -- we can then only insert them if needed
|
||||
$settingNames = [Identity::IDENTITY_SETTING_GIVENNAME, Identity::IDENTITY_SETTING_FAMILYNAME, 'preferredPublicName'];
|
||||
foreach ($settingNames as $settingName) {
|
||||
DB::delete("DELETE from user_settings WHERE locale = ? AND setting_name = ? AND setting_value = ''", [$newLocale, $settingName]);
|
||||
}
|
||||
// get all names of all users in the new locale
|
||||
$result = DB::select(
|
||||
'SELECT DISTINCT us.user_id, usg.setting_value AS given_name, usf.setting_value AS family_name, usp.setting_value AS preferred_public_name
|
||||
FROM user_settings us
|
||||
LEFT JOIN user_settings usg ON (usg.user_id = us.user_id AND usg.locale = ? AND usg.setting_name = ?)
|
||||
LEFT JOIN user_settings usf ON (usf.user_id = us.user_id AND usf.locale = ? AND usf.setting_name = ?)
|
||||
LEFT JOIN user_settings usp ON (usp.user_id = us.user_id AND usp.locale = ? AND usp.setting_name = ?)',
|
||||
[$newLocale, Identity::IDENTITY_SETTING_GIVENNAME, $newLocale, Identity::IDENTITY_SETTING_FAMILYNAME, $newLocale, 'preferredPublicName']
|
||||
);
|
||||
foreach ($result as $row) {
|
||||
$userId = $row->user_id;
|
||||
if (empty($row->given_name) && empty($row->family_name) && empty($row->preferred_public_name)) {
|
||||
// if no user name exists in the new locale, insert them all
|
||||
foreach ($settingNames as $settingName) {
|
||||
DB::insert(
|
||||
'INSERT INTO user_settings (user_id, locale, setting_name, setting_value)
|
||||
SELECT DISTINCT us.user_id, ?, ?, us.setting_value
|
||||
FROM user_settings us
|
||||
WHERE us.setting_name = ? AND us.locale = ? AND us.user_id = ?',
|
||||
[$newLocale, $settingName, $settingName, $oldLocale, $userId]
|
||||
);
|
||||
}
|
||||
} elseif (empty($row->given_name)) {
|
||||
// if the given name does not exist in the new locale (but one of the other names do exist), insert it
|
||||
DB::insert(
|
||||
'INSERT INTO user_settings (user_id, locale, setting_name, setting_value)
|
||||
SELECT DISTINCT us.user_id, ?, ?, us.setting_value
|
||||
FROM user_settings us
|
||||
WHERE us.setting_name = ? AND us.locale = ? AND us.user_id = ?',
|
||||
[$newLocale, Identity::IDENTITY_SETTING_GIVENNAME, Identity::IDENTITY_SETTING_GIVENNAME, $oldLocale, $userId]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete unvalidated expired users
|
||||
*
|
||||
* @param Carbon $dateTillValid The dateTime till before which user will consider expired
|
||||
* @param array $excludableUsersId The users id to exclude form delete operation
|
||||
*
|
||||
* @return int The number rows affected by DB operation
|
||||
*/
|
||||
public function deleteUnvalidatedExpiredUsers(Carbon $dateTillValid, array $excludableUsersId = [])
|
||||
{
|
||||
$users = DB::table('users')
|
||||
->whereNull('date_validated')
|
||||
->whereNull('date_last_login')
|
||||
->where('date_registered', '<', $dateTillValid)
|
||||
->when(!empty($excludableUsersId), fn ($query) => $query->whereNotIn('id', $excludableUsersId))
|
||||
->get();
|
||||
|
||||
$userRepository = Repo::user();
|
||||
|
||||
$users->each(fn ($user) => $userRepository->delete($userRepository->get($user->user_id, true)));
|
||||
|
||||
return $users->count();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user