Skip to content

Commit

Permalink
pkp#10328 Refactor announcement controller and re-add Repository
Browse files Browse the repository at this point in the history
  • Loading branch information
Vitaliy-1 committed Sep 9, 2024
1 parent 204ca6a commit ee7760a
Show file tree
Hide file tree
Showing 8 changed files with 242 additions and 69 deletions.
17 changes: 0 additions & 17 deletions api/v1/_submissions/PKPBackendSubmissionsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Route;
use PKP\announcement\Announcement;
use PKP\API\v1\submissions\AnonymizeData;
use PKP\config\Config;
use PKP\core\PKPBaseController;
Expand Down Expand Up @@ -142,15 +141,6 @@ public function getGroupRoutes(): void
Role::ROLE_ID_REVIEWER,
])
]);

// Endpoint for testing new User Model; TODO remove before merging
Route::get('testUserModel', $this->getTestUsers(...))
->name('_submission.getTestUsers')
->middleware([
self::roleAuthorizer([
Role::ROLE_ID_MANAGER,
])
]);
}
}

Expand Down Expand Up @@ -518,11 +508,4 @@ protected function canAccessAllSubmissions(): bool
$userRoles = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_USER_ROLES);
return !empty(array_intersect([Role::ROLE_ID_SITE_ADMIN, Role::ROLE_ID_MANAGER], $userRoles));
}

public function getTestUsers(Request $illuminateRequest): JsonResponse
{
$announcement = Announcement::find(1);

return response()->json($announcement->datePosted, Response::HTTP_OK);
}
}
69 changes: 31 additions & 38 deletions api/v1/announcements/PKPAnnouncementController.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,10 @@
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Bus;
use Illuminate\Support\Facades\Route;
use PKP\announcement\Collector;
use PKP\announcement\Announcement;
use PKP\context\Context;
use PKP\core\exceptions\StoreTemporaryFileException;
use PKP\core\PKPApplication;
use PKP\core\PKPBaseController;
use PKP\core\PKPRequest;
use PKP\db\DAORegistry;
Expand Down Expand Up @@ -124,7 +125,7 @@ public function authorize(PKPRequest $request, array &$args, array $roleAssignme
*/
public function get(Request $illuminateRequest): JsonResponse
{
$announcement = Repo::announcement()->get((int) $illuminateRequest->route('announcementId'));
$announcement = Announcement::find((int) $illuminateRequest->route('announcementId'));

if (!$announcement) {
return response()->json([
Expand All @@ -133,7 +134,7 @@ public function get(Request $illuminateRequest): JsonResponse
}

// The assocId in announcements should always point to the contextId
if ($announcement->getData('assocId') !== $this->getRequest()->getContext()?->getId()) {
if ($announcement->getAttribute('assocId') !== $this->getRequest()->getContext()?->getId()) {
return response()->json([
'error' => __('api.announcements.400.contextsNotMatched')
], Response::HTTP_BAD_REQUEST);
Expand All @@ -149,43 +150,38 @@ public function get(Request $illuminateRequest): JsonResponse
*/
public function getMany(Request $illuminateRequest): JsonResponse
{
$collector = Repo::announcement()->getCollector()
->limit(self::DEFAULT_COUNT)
->offset(0);
$announcements = Announcement::limit(self::DEFAULT_COUNT)->offset(0);

foreach ($illuminateRequest->query() as $param => $val) {
switch ($param) {
case 'typeIds':
$collector->filterByTypeIds(
$announcements->withTypeIds(
array_map('intval', paramToArray($val))
);
break;
case 'count':
$collector->limit(min((int) $val, self::MAX_COUNT));
$announcements->limit(min((int) $val, self::MAX_COUNT));
break;
case 'offset':
$collector->offset((int) $val);
$announcements->offset((int) $val);
break;
case 'searchPhrase':
$collector->searchPhrase($val);
$announcements->withSearchPhrase($val);
break;
}
}

if ($this->getRequest()->getContext()) {
$collector->filterByContextIds([$this->getRequest()->getContext()->getId()]);
$announcements->withContextIds([$this->getRequest()->getContext()->getId()]);
} else {
$collector->withSiteAnnouncements(Collector::SITE_ONLY);
$announcements->withContextIds([PKPApplication::SITE_CONTEXT_ID]);
}


Hook::run('API::announcements::params', [$collector, $illuminateRequest]);

$announcements = $collector->getMany();
Hook::run('API::announcements::params', [$announcements, $illuminateRequest]);

return response()->json([
'itemsMax' => $collector->getCount(),
'items' => Repo::announcement()->getSchemaMap()->summarizeMany($announcements)->values(),
'itemsMax' => $announcements->count(),
'items' => Repo::announcement()->getSchemaMap()->summarizeMany($announcements->get())->values(),
], Response::HTTP_OK);
}

Expand All @@ -209,27 +205,22 @@ public function add(Request $illuminateRequest): JsonResponse
return response()->json($errors, Response::HTTP_BAD_REQUEST);
}

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

try {
$announcementId = Repo::announcement()->add($announcement);
$announcement = Announcement::create($params);
} catch (StoreTemporaryFileException $e) {
$announcementId = $e->dataObject->getId();
$announcementId = $e->getDataObjectId();
if ($announcementId) {
$announcement = Repo::announcement()->get($announcementId);
Repo::announcement()->delete($announcement);
Announcement::destroy([$announcementId]);
}
return response()->json([
'image' => [__('api.400.errorUploadingImage')]
], Response::HTTP_BAD_REQUEST);
}

$announcement = Repo::announcement()->get($announcementId);

$sendEmail = (bool) filter_var($params['sendEmail'], FILTER_VALIDATE_BOOLEAN);

if ($context) {
$this->notifyUsers($request, $context, $announcementId, $sendEmail);
$this->notifyUsers($request, $context, $announcement->getKey(), $sendEmail);
}

return response()->json(Repo::announcement()->getSchemaMap()->map($announcement), Response::HTTP_OK);
Expand All @@ -243,27 +234,28 @@ public function edit(Request $illuminateRequest): JsonResponse
$request = $this->getRequest();
$context = $request->getContext();

$announcement = Repo::announcement()->get((int) $illuminateRequest->route('announcementId'));
/** @var Announcement $announcement */
$announcement = Announcement::find((int) $illuminateRequest->route('announcementId'));

if (!$announcement) {
return response()->json([
'error' => __('api.announcements.404.announcementNotFound')
], Response::HTTP_NOT_FOUND);
}

if ($announcement->getData('assocType') !== Application::get()->getContextAssocType()) {
if ($announcement->getAttribute('assocType') !== Application::get()->getContextAssocType()) {
throw new Exception('Announcement has an assocType that did not match the context.');
}

// Don't allow to edit an announcement from one context from a different context's endpoint
if ($request->getContext()?->getId() !== $announcement->getData('assocId')) {
if ($request->getContext()?->getId() !== $announcement->getAttribute('assocId')) {
return response()->json([
'error' => __('api.announcements.400.contextsNotMatched')
], Response::HTTP_FORBIDDEN);
}

$params = $this->convertStringsToSchema(PKPSchemaService::SCHEMA_ANNOUNCEMENT, $illuminateRequest->input());
$params['id'] = $announcement->getId();
$params['id'] = $announcement->getKey();
$params['typeId'] ??= null;

$primaryLocale = $context ? $context->getPrimaryLocale() : $request->getSite()->getPrimaryLocale();
Expand All @@ -275,15 +267,15 @@ public function edit(Request $illuminateRequest): JsonResponse
}

try {
Repo::announcement()->edit($announcement, $params);
$announcement->update($params);
} catch (StoreTemporaryFileException $e) {
Repo::announcement()->delete($announcement);
$announcement->delete(); // TODO do we really need to delete an announcement if the image upload fails?
return response()->json([
'image' => [__('api.400.errorUploadingImage')]
], Response::HTTP_BAD_REQUEST);
}

$announcement = Repo::announcement()->get($announcement->getId());
$announcement = Announcement::find($announcement->getKey());

return response()->json(Repo::announcement()->getSchemaMap()->map($announcement), Response::HTTP_OK);
}
Expand All @@ -295,28 +287,29 @@ public function delete(Request $illuminateRequest): JsonResponse
{
$request = $this->getRequest();

$announcement = Repo::announcement()->get((int) $illuminateRequest->route('announcementId'));
/** @var Announcement $announcement */
$announcement = Announcement::find((int) $illuminateRequest->route('announcementId'));

if (!$announcement) {
return response()->json([
'error' => __('api.announcements.404.announcementNotFound')
], Response::HTTP_NOT_FOUND);
}

if ($announcement->getData('assocType') !== Application::get()->getContextAssocType()) {
if ($announcement->getAttribute('assocType') !== Application::get()->getContextAssocType()) {
throw new Exception('Announcement has an assocType that did not match the context.');
}

// Don't allow to delete an announcement from one context from a different context's endpoint
if ($request->getContext()?->getId() !== $announcement->getData('assocId')) {
if ($request->getContext()?->getId() !== $announcement->getAttribute('assocId')) {
return response()->json([
'error' => __('api.announcements.400.contextsNotMatched')
], Response::HTTP_FORBIDDEN);
}

$announcementProps = Repo::announcement()->getSchemaMap()->map($announcement);

Repo::announcement()->delete($announcement);
$announcement->delete();

return response()->json($announcementProps, Response::HTTP_OK);
}
Expand Down
87 changes: 81 additions & 6 deletions classes/announcement/Announcement.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,14 @@
use APP\core\Application;
use APP\file\PublicFileManager;
use Eloquence\Behaviours\HasCamelCasing;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\DB;
use PKP\context\Context;
use PKP\core\Core;
use PKP\core\exceptions\StoreTemporaryFileException;
use PKP\core\PKPApplication;
use PKP\core\PKPString;
use PKP\core\traits\ModelWithSettings;
use PKP\db\DAORegistry;
Expand All @@ -30,6 +33,10 @@
use PKP\plugins\Hook;
use PKP\services\PKPSchemaService;

/**
* @method static \Illuminate\Database\Eloquent\Builder withContextIds(array $contextIds) accepts valid context IDs or PKPApplication::SITE_CONTEXT_ID as an array values
* @method static \Illuminate\Database\Eloquent\Builder withTypeIds(array $typeIds) filters results by announcement type IDs
*/
class Announcement extends Model
{
use HasCamelCasing;
Expand All @@ -44,6 +51,13 @@ class Announcement extends Model
public const UPDATED_AT = null;
protected string $settingsTable = 'announcement_settings';

/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = ['assocType', 'assocId', 'typeId', 'title', 'image', 'description', 'descriptionShort'];

/**
* @inheritDoc
*/
Expand Down Expand Up @@ -122,14 +136,75 @@ public function save(array $options = [])
}

/**
* Get the base URL for announcement file uploads
* Filter by context IDs, accepts PKPApplication::SITE_CONTEXT_ID as a parameter array value if include site wide
*/
protected function scopeWithContextIds(EloquentBuilder $builder, array $contextIds): EloquentBuilder
{
$filteredIds = [];
$siteWide = false;
foreach ($contextIds as $contextId) {
if ($contextId == PKPApplication::SITE_CONTEXT_ID) {
$siteWide = true;
continue;
}

$filteredIds[] = $contextId;
}

return $builder->where('assoc_type', Application::get()->getContextAssocType())
->whereIn('assoc_id', $filteredIds)
->when($siteWide, fn (EloquentBuilder $builder) => $builder->orWhereNull('assoc_id'));
}

/**
* Filter by announcement type IDs
*/
public static function getFileUploadBaseUrl(?Context $context = null): string
protected function scopeWithTypeIds(EloquentBuilder $builder, array $typeIds): EloquentBuilder
{
return join('/', [
Application::get()->getRequest()->getPublicFilesUrl($context),
static::IMAGE_SUBDIRECTORY,
]);
return $builder->whereIn('type_id', $typeIds);
}

/**
*
* @param string $date Optionally filter announcements by those
* not expired until $date (YYYY-MM-DD)
*/
protected function scopeWithActiveByDate(EloquentBuilder $builder, string $date = ''): EloquentBuilder
{
return $builder->where('a.date_expire', '>', empty($date) ? Core::getCurrentDate() : $date)
->orWhereNull('a.date_expire');
}

/**
* Filter announcements by those matching a search query
*/
protected function scopeWithSearchPhrase(EloquentBuilder $builder, string $searchPhrase): EloquentBuilder
{
$words = explode(' ', $searchPhrase);
if (!count($words)) {
return $builder;
}

return $builder->whereIn('announcement_id', function ($builder) use ($words) {
$builder->select('announcement_id')->from($this->getSettingsTable());
foreach ($words as $word) {
$word = strtolower(addcslashes($word, '%_'));
$builder->where(function ($builder) use ($word) {
$builder->where(function ($builder) use ($word) {
$builder->where('setting_name', 'title');
$builder->where(DB::raw('lower(setting_value)'), 'LIKE', "%{$word}%");
})
->orWhere(function ($builder) use ($word) {
$builder->where('setting_name', 'descriptionShort');
$builder->where(DB::raw('lower(setting_value)'), 'LIKE', "%{$word}%");
})
->orWhere(function ($builder) use ($word) {
$builder->where('setting_name', 'description');
$builder->where(DB::raw('lower(setting_value)'), 'LIKE', "%{$word}%");
});
});
}
});
}

/**
Expand Down
Loading

0 comments on commit ee7760a

Please sign in to comment.