<?php
/**
* @file classes/institution/DAO.php
*
* Copyright (c) 2022 Simon Fraser University
* Copyright (c) 2022 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class DAO
*
* @ingroup institution
*
* @see Institution
*
* @brief Operations for retrieving and modifying Institution objects.
*/
namespace PKP\institution;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\LazyCollection;
use PKP\core\EntityDAO;
use PKP\core\SoftDeleteTrait;
use PKP\core\traits\EntityWithParent;
use PKP\services\PKPSchemaService;
/**
* @template T of Institution
* @extends EntityDAO<T>
*/
class DAO extends EntityDAO
{
use EntityWithParent;
use SoftDeleteTrait;
/** @copydoc EntityDAO::$schema */
public $schema = PKPSchemaService::SCHEMA_INSTITUTION;
/** @copydoc EntityDAO::$table */
public $table = 'institutions';
/** @copydoc EntityDAO::$settingsTable */
public $settingsTable = 'institution_settings';
/** @copydoc EntityDAO::$primaryKeyColumn */
public $primaryKeyColumn = 'institution_id';
/** @copydoc EntityDAO::$primaryTableColumns */
public $primaryTableColumns = [
'id' => 'institution_id',
'contextId' => 'context_id',
'ror' => 'ror',
'deletedAt' => 'deleted_at'
];
/**
* Get the parent object ID column name
*/
public function getParentColumn(): string
{
return 'context_id';
}
/**
* Instantiate a new DataObject
*/
public function newDataObject(): Institution
{
return App::make(Institution::class);
}
/**
* Get the number of institutions matching the configured query
*/
public function getCount(Collector $query): int
{
return $query
->getQueryBuilder()
->select('i.' . $this->primaryKeyColumn)
->count();
}
/**
* Get a list of institution ids matching the configured query
*
* @return Collection<int,int>
*/
public function getIds(Collector $query): Collection
{
return $query
->getQueryBuilder()
->select('i.' . $this->primaryKeyColumn)
->pluck('i.' . $this->primaryKeyColumn);
}
/**
* Get a collection of institutions matching the configured query
* @return LazyCollection<int,T>
*/
public function getMany(Collector $query): LazyCollection
{
$rows = $query
->getQueryBuilder()
->select(['i.*'])
->get();
return LazyCollection::make(function () use ($rows) {
foreach ($rows as $row) {
yield $row->institution_id => $this->fromRow($row);
}
});
}
/**
* Get a collection of deleted institutions matching the configured query
*/
public function getSoftDeleted(Collector $query): LazyCollection
{
$rows = $query
->includeSoftDeletes(true)
->getQueryBuilder()
->whereNotNull('deleted_at')
->select(['i.*'])
->get();
return LazyCollection::make(function () use ($rows) {
foreach ($rows as $row) {
yield $row->institution_id => $this->fromRow($row);
}
});
}
/**
* @copydoc EntityDAO::fromRow()
*/
public function fromRow(object $row): Institution
{
/** @var Institution */
$institution = parent::fromRow($row);
$ipRanges = DB::table('institution_ip')
->where($this->primaryKeyColumn, '=', $institution->getId())
->pluck('ip_string')
->toArray();
$institution->setIPRanges($ipRanges);
return $institution;
}
/**
* @copydoc EntityDAO::insert()
*/
public function insert(Institution $institution): int
{
if (isset($institution->_data['ipRanges'])) {
$ipRanges = $institution->getData('ipRanges');
unset($institution->_data['ipRanges']);
}
$institutionId = parent::_insert($institution);
if (isset($ipRanges)) {
$this->insertIPRanges($institutionId, $ipRanges);
}
return $institutionId;
}
/**
* @copydoc EntityDAO::update()
*/
public function update(Institution $institution): void
{
if (isset($institution->_data['ipRanges'])) {
$ipRanges = $institution->getData('ipRanges');
unset($institution->_data['ipRanges']);
}
parent::_update($institution);
if (isset($ipRanges)) {
$this->deleteIPRanges($institution->getId());
$this->insertIPRanges($institution->getId(), $ipRanges);
}
}
/**
* @copydoc EntityDAO::delete()
*/
public function delete(Institution $institution): void
{
// If the reference in the table institutional_subscriptions exists, soft delete the institution
$shouldSoftDelete = DB::table('institutional_subscriptions')
->where('institution_id', '=', $institution->getId())
->exists();
if ($shouldSoftDelete) {
$this->_softDelete($institution);
} else {
$this->deleteIPRanges($institution->getId());
parent::_delete($institution);
}
}
/**
* Insert institution IP ranges.
*/
protected function insertIPRanges(int $institutionId, array $ipRanges): void
{
if (empty($ipRanges) || empty($institutionId)) {
return;
}
foreach ($ipRanges as $ipRange) {
$ipStart = null;
$ipEnd = null;
$ipRange = trim($ipRange);
// Parse and check single IP string
if (strpos($ipRange, Institution::IP_RANGE_RANGE) === false) {
// Check for wildcards in IP
if (strpos($ipRange, Institution::IP_RANGE_WILDCARD) === false) {
// Get non-CIDR IP
if (strpos($ipRange, '/') === false) {
$ipStart = sprintf('%u', ip2long($ipRange));
// Convert CIDR IP to IP range
} else {
[$cidrIPString, $cidrBits] = explode('/', $ipRange);
if ($cidrBits == 0) {
$cidrMask = 0;
} else {
$cidrMask = (0xffffffff << (32 - $cidrBits));
}
$ipStart = sprintf('%u', ip2long($cidrIPString) & $cidrMask);
if ($cidrBits != 32) {
$ipEnd = sprintf('%u', ip2long($cidrIPString) | (~$cidrMask & 0xffffffff));
}
}
// Convert wildcard IP to IP range
} else {
$ipStart = sprintf('%u', ip2long(str_replace(Institution::IP_RANGE_WILDCARD, '0', $ipRange)));
$ipEnd = sprintf('%u', ip2long(str_replace(Institution::IP_RANGE_WILDCARD, '255', $ipRange)));
}
// Convert wildcard IP range to IP range
} else {
[$ipStart, $ipEnd] = explode(Institution::IP_RANGE_RANGE, $ipRange);
// Replace wildcards in start and end of range
$ipStart = sprintf('%u', ip2long(str_replace(Institution::IP_RANGE_WILDCARD, '0', trim($ipStart))));
$ipEnd = sprintf('%u', ip2long(str_replace(Institution::IP_RANGE_WILDCARD, '255', trim($ipEnd))));
}
// Insert IP or IP range
if ($ipStart != null) {
DB::table('institution_ip')->insert([
'institution_id' => $institutionId,
'ip_string' => $ipRange,
'ip_start' => $ipStart,
'ip_end' => $ipEnd
]);
}
}
}
/**
* Delete institution IP ranges by institution ID.
*/
private function deleteIPRanges(int $institutionId): void
{
DB::table('institution_ip')->where('institution_id', '=', $institutionId)->delete();
}
}
|