Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple author affiliations #10460

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 151 additions & 0 deletions api/v1/rors/PKPRorController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
<?php

/**
* @file api/v1/rors/PKPRorController.php
*
* Copyright (c) 2024 Simon Fraser University
* Copyright (c) 2024 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class PKPRorController
*
* @ingroup api_v1_rors
*
* @brief Controller class to handle API requests for ror operations.
*
*/

namespace PKP\API\v1\rors;

use APP\facades\Repo;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Route;
use PKP\core\PKPBaseController;
use PKP\core\PKPRequest;
use PKP\plugins\Hook;
use PKP\security\authorization\ContextRequiredPolicy;
use PKP\security\authorization\PolicySet;
use PKP\security\authorization\RoleBasedHandlerOperationPolicy;
use PKP\security\authorization\UserRolesRequiredPolicy;
use PKP\security\Role;

class PKPRorController extends PKPBaseController
{
/** @var int The default number of rors to return in one request */
public const DEFAULT_COUNT = 30;

/** @var int The maximum number of rors to return in one request */
public const MAX_COUNT = 100;

/**
* @copydoc \PKP\core\PKPBaseController::getHandlerPath()
*/
public function getHandlerPath(): string
{
return 'rors';
}

/**
* @copydoc \PKP\core\PKPBaseController::getRouteGroupMiddleware()
*/
public function getRouteGroupMiddleware(): array
{
return [
'has.user',
'has.context',
self::roleAuthorizer([
Role::ROLE_ID_MANAGER,
Role::ROLE_ID_SUB_EDITOR,
Role::ROLE_ID_ASSISTANT,
Role::ROLE_ID_AUTHOR,
]),
];
}

/**
* @copydoc \PKP\core\PKPBaseController::getGroupRoutes()
*/
public function getGroupRoutes(): void
{
Route::get('{rorId}', $this->get(...))
->name('ror.getRor')
->whereNumber('rorId');

Route::get('', $this->getMany(...))
->name('ror.getMany');
}

/**
* @copydoc \PKP\core\PKPBaseController::authorize()
*/
public function authorize(PKPRequest $request, array &$args, array $roleAssignments): bool
{
$this->addPolicy(new UserRolesRequiredPolicy($request), true);

$rolePolicy = new PolicySet(PolicySet::COMBINING_PERMIT_OVERRIDES);

$this->addPolicy(new ContextRequiredPolicy($request));

foreach ($roleAssignments as $role => $operations) {
$rolePolicy->addPolicy(new RoleBasedHandlerOperationPolicy($request, $role, $operations));
}

$this->addPolicy($rolePolicy);

return parent::authorize($request, $args, $roleAssignments);
}

/**
* Get a single ror
*/
public function get(Request $illuminateRequest): JsonResponse
{
if (!Repo::ror()->exists((int) $illuminateRequest->route('rorId'))) {
return response()->json([
'error' => __('api.rors.404.rorNotFound')
GaziYucel marked this conversation as resolved.
Show resolved Hide resolved
], Response::HTTP_OK);
}

$ror = Repo::ror()->get((int) $illuminateRequest->route('rorId'));

return response()->json(Repo::ror()->getSchemaMap()->map($ror), Response::HTTP_OK);
}

/**
* Get a collection of rors
*
* @hook API::rors::params [[$collector, $illuminateRequest]]
*/
public function getMany(Request $illuminateRequest): JsonResponse
{
$collector = Repo::ror()->getCollector()
->limit(self::DEFAULT_COUNT)
->offset(0);

foreach ($illuminateRequest->query() as $param => $val) {
switch ($param) {
case 'count':
$collector->limit(min((int) $val, self::MAX_COUNT));
break;
case 'offset':
$collector->offset((int) $val);
break;
case 'searchPhrase':
$collector->filterBySearchPhrase($val);
$collector->filterByIsActive(true);
break;
}
}

Hook::call('API::rors::params', [$collector, $illuminateRequest]);

$rors = $collector->getMany();

return response()->json([
'itemsMax' => $collector->getCount(),
'items' => Repo::ror()->getSchemaMap()->summarizeMany($rors->values())->values(),
], Response::HTTP_OK);
}
}
46 changes: 42 additions & 4 deletions api/v1/submissions/PKPSubmissionController.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
/**
* @file api/v1/submissions/PKPSubmissionController.php
*
* Copyright (c) 2023 Simon Fraser University
* Copyright (c) 2023 John Willinsky
* Copyright (c) 2023-2024 Simon Fraser University
* Copyright (c) 2023-2024 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class PKPSubmissionController
Expand Down Expand Up @@ -517,7 +517,7 @@ protected function getSubmissionCollector(array $queryParams): Collector
break;
case 'isUnassigned':
$collector->filterByisUnassigned(true);
break;
break;
}
}

Expand Down Expand Up @@ -663,7 +663,7 @@ public function add(Request $illuminateRequest): JsonResponse

// Create an author record from the submitter's user account
if ($submitAsUserGroup->roleId === Role::ROLE_ID_AUTHOR) {
$author = Repo::author()->newAuthorFromUser($request->getUser());
$author = Repo::author()->newAuthorFromUser($request->getUser(), $submission, $context);
$author->setData('publicationId', $publication->getId());
$author->setUserGroupId($submitAsUserGroup->id);
$authorId = Repo::author()->add($author);
Expand Down Expand Up @@ -1544,6 +1544,22 @@ public function addContributor(Request $illuminateRequest): JsonResponse
}

$author = Repo::author()->newDataObject($params);

$affiliations = [];
foreach ($params['affiliations'] as $affiliationParam) {
$affiliationErrors = Repo::affiliation()->validate(null, $affiliationParam, $submission, $submissionContext);
if (!empty($affiliationErrors)) {
return response()->json($affiliationErrors, Response::HTTP_BAD_REQUEST);
}
$affiliation = Repo::affiliation()->newDataObject($affiliationParam);
// rorObject will be an array, replace with ror object
if($affiliation->getRor() !== null){
$affiliation->setData('rorObject', Repo::ror()->newDataObject($affiliationParam['rorObject']));
}
$affiliations[] = $affiliation;
}
$author->setAffiliations($affiliations);

$newId = Repo::author()->add($author);
$author = Repo::author()->get($newId);

Expand Down Expand Up @@ -1668,6 +1684,28 @@ public function editContributor(Request $illuminateRequest): JsonResponse
return response()->json($errors, Response::HTTP_BAD_REQUEST);
}

$affiliations = [];
foreach ($params['affiliations'] as $affiliationParam) {
$affiliationErrors = Repo::affiliation()->validate(null, $affiliationParam, $submission, $submissionContext);
if (!empty($affiliationErrors)) {
return response()->json($affiliationErrors, Response::HTTP_BAD_REQUEST);
}
// Create a new affiliation object even if an existing affiliation is edited.
// This way we will have the list of all actual affiliations from the edited form.
// Later, in Repo::affiliation()->saveAffiliations(), we compare this list with
// the author affiliations that exist in the DB, to see if any was removed in the edited form.
$affiliation = Repo::affiliation()->newDataObject($affiliationParam);
// rorObject will be an array, replace with ror object
if($affiliation->getRor() !== null){
$affiliation->setData('rorObject', Repo::ror()->newDataObject($affiliationParam['rorObject']));
}
$affiliations[] = $affiliation;
}
$author->setAffiliations($affiliations);
// remove affiliations parameters because we have already set them properly for the author
// so that they are not considered once again when editing the author below
unset($params['affiliations']);

Repo::author()->edit($author, $params);
$author = Repo::author()->get($author->getId());

Expand Down
128 changes: 128 additions & 0 deletions classes/affiliation/Affiliation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
<?php

/**
* @file classes/affiliation/Affiliation.php
*
* Copyright (c) 2024 Simon Fraser University
* Copyright (c) 2024 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class Affiliation
*
* @ingroup affiliation
*
* @see DAO
*
* @brief Basic class describing an affiliation.
*/

namespace PKP\affiliation;

use APP\facades\Repo;
use PKP\core\DataObject;
use PKP\i18n\LocaleConversion;
use PKP\ror\Ror;

class Affiliation extends DataObject
{
/**
* Get the default/fall back locale the values should exist for
* (see LocalizedData trait)
*/
public function getDefaultLocale(): ?string
{
return Repo::author()->get($this->getAuthorId())->getDefaultLocale();
}

/**
* Get author id
*/
public function getAuthorId(): int
{
return $this->getData('authorId');
}

/**
* Set author id
*/
public function setAuthorId(int $authorId): void
{
$this->setData('authorId', $authorId);
}

/**
* Get the ROR ID
*/
public function getRor(): ?string
{
return $this->getData('ror');
}

/**
* Set the ROR ID
*/
public function setRor(?string $ror): void
{
$this->setData('ror', $ror);
}

/**
* Get the ROR object
*/
public function getRorObject(): ?Ror
{
return $this->getData('rorObject');
}

/**
* Get affiliation name, considering also the ROR names
* Use getName() if only non ROR i.e.
* manually entered affiliation names should be considered.
*/
public function getAffiliationName(?string $locale = null, ?array $allowedLocales = []): string|array|null
{
if ($rorObject = $this->getData('rorObject')) {
if ($locale == null) {
// try to map ROR locales to all submission locales
$names = [];
foreach ($allowedLocales as $allowedLocale) {
$rorLocale = LocaleConversion::getIso1FromLocale($allowedLocale);
$names[$allowedLocale] = $rorObject->getName($rorLocale) ?? $rorObject->getName($rorObject->getDisplayLocale());
}
return $names;
}
$rorLocale = LocaleConversion::getIso1FromLocale($locale);
return $rorObject->getName($rorLocale) ?? $rorObject->getName($rorObject->getDisplayLocale());
}
return $this->getData('name', $locale);
}

/**
* Get name or manually entered affiliation i.e. not considering ROR names
*/
public function getName(?string $locale = null): string|array|null
{
return $this->getData('name', $locale);
}

/**
* Set name
*/
public function setName(string|array|null $name, ?string $locale = null): void
{
$this->setData('name', $name, $locale);
}

/**
* Get localized affiliation name.
*/
public function getLocalizedName(?string $preferredLocale = null): string|null
{
$rorObject = $this->getRorObject();
if ($rorObject) {
$preferredLocale = $preferredLocale ? LocaleConversion::getIso1FromLocale($preferredLocale) : $preferredLocale;
return $rorObject->getLocalizedName($preferredLocale);
}
return $this->getLocalizedData('name', $preferredLocale);
}
}
Loading