Skip to content
Snippets Groups Projects
Commit 7293abba authored by Ron Lucke's avatar Ron Lucke
Browse files

StEP #2472

Merge request studip/studip!2296
parent 874bd358
No related branches found
No related tags found
No related merge requests found
Showing
with 581 additions and 23 deletions
...@@ -36,6 +36,12 @@ class Course_CoursewareController extends CoursewareController ...@@ -36,6 +36,12 @@ class Course_CoursewareController extends CoursewareController
$this->licenses = $this->getLicenses(); $this->licenses = $this->getLicenses();
$this->oer_enabled = Config::get()->OERCAMPUS_ENABLED && $GLOBALS['perm']->have_perm(Config::get()->OER_PUBLIC_STATUS); $this->oer_enabled = Config::get()->OERCAMPUS_ENABLED && $GLOBALS['perm']->have_perm(Config::get()->OER_PUBLIC_STATUS);
$this->unitsNotFound = Unit::countBySql('range_id = ?', [Context::getId()]) === 0; $this->unitsNotFound = Unit::countBySql('range_id = ?', [Context::getId()]) === 0;
$this->feedback_settings = json_encode([
'activated' => \Feedback::isActivated(),
'adminPerm' => \Feedback::hasAdminPerm(Context::getId()),
'createPerm' => \Feedback::hasCreatePerm(Context::getId()),
]);
} }
public function index_action(): void public function index_action(): void
......
...@@ -49,7 +49,7 @@ class Course_FeedbackController extends AuthenticatedController ...@@ -49,7 +49,7 @@ class Course_FeedbackController extends AuthenticatedController
$widget->addLink( $widget->addLink(
_('Neues Feedback-Element'), _('Neues Feedback-Element'),
$this->url_for('course/feedback/create_form'), $this->url_for('course/feedback/create_form'),
Icon::create('star') Icon::create('add')
)->asDialog(); )->asDialog();
} }
} }
...@@ -72,6 +72,8 @@ class Course_FeedbackController extends AuthenticatedController ...@@ -72,6 +72,8 @@ class Course_FeedbackController extends AuthenticatedController
'results_visible' => 1, 'results_visible' => 1,
'commentable' => 1, 'commentable' => 1,
'mode' => FeedbackElement::MODE_5STAR_RATING, 'mode' => FeedbackElement::MODE_5STAR_RATING,
'mode' => 1,
'anonymous_entries' => 1,
]); ]);
} }
...@@ -99,6 +101,7 @@ class Course_FeedbackController extends AuthenticatedController ...@@ -99,6 +101,7 @@ class Course_FeedbackController extends AuthenticatedController
'description' => Studip\Markup::purifyHtml(Request::get('description')), 'description' => Studip\Markup::purifyHtml(Request::get('description')),
'results_visible' => intval(Request::get('results_visible')), 'results_visible' => intval(Request::get('results_visible')),
'commentable' => $commentable, 'commentable' => $commentable,
'anonymous_entries' => intval(Request::get('anonymous_entries')),
'mode' => $mode 'mode' => $mode
]); ]);
$feedback->store(); $feedback->store();
...@@ -232,11 +235,13 @@ class Course_FeedbackController extends AuthenticatedController ...@@ -232,11 +235,13 @@ class Course_FeedbackController extends AuthenticatedController
if ($rating == 0) { if ($rating == 0) {
$rating = 1; $rating = 1;
} }
$anonymous = intval(Request::get('anonymous'));
$entry = FeedbackEntry::build([ $entry = FeedbackEntry::build([
'feedback_id' => $this->feedback->id, 'feedback_id' => $this->feedback->id,
'user_id' => $GLOBALS['user']->id, 'user_id' => $GLOBALS['user']->id,
'rating' => $rating, 'rating' => $rating,
'comment' => trim(Request::get('comment')) 'comment' => trim(Request::get('comment')),
'anonymous' => $anonymous,
]); ]);
$entry->store(); $entry->store();
PageLayout::postSuccess(_('Feedback gespeichert')); PageLayout::postSuccess(_('Feedback gespeichert'));
...@@ -268,6 +273,7 @@ class Course_FeedbackController extends AuthenticatedController ...@@ -268,6 +273,7 @@ class Course_FeedbackController extends AuthenticatedController
} }
$entry->comment = trim(Request::get('comment')); $entry->comment = trim(Request::get('comment'));
$entry->rating = $rating; $entry->rating = $rating;
$entry->anonymous = Request::int('anonymous', 0);
$entry->store(); $entry->store();
PageLayout::postSuccess(_('Änderungen gespeichert')); PageLayout::postSuccess(_('Änderungen gespeichert'));
$this->redirect($entry->feedback->getRange()->getRangeUrl()); $this->redirect($entry->feedback->getRange()->getRangeUrl());
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
entry-id="<?= htmlReady(Context::getId()) ?>" entry-id="<?= htmlReady(Context::getId()) ?>"
unit-id="<?= htmlReady($unit_id) ?>" unit-id="<?= htmlReady($unit_id) ?>"
licenses='<?= htmlReady($licenses) ?>' licenses='<?= htmlReady($licenses) ?>'
feedback-settings='<?= htmlReady($feedback_settings) ?>'
> >
</div> </div>
<? endif; ?> <? endif; ?>
...@@ -3,4 +3,5 @@ ...@@ -3,4 +3,5 @@
entry-type="courses" entry-type="courses"
entry-id="<?= Context::getId() ?>" entry-id="<?= Context::getId() ?>"
licenses='<?= $licenses ?>' licenses='<?= $licenses ?>'
feedback-settings='<?= htmlReady($feedback_settings) ?>'
></div> ></div>
...@@ -29,6 +29,12 @@ ...@@ -29,6 +29,12 @@
<textarea name="comment"><?= htmlReady(isset($entry) ? $entry->comment : '') ?></textarea> <textarea name="comment"><?= htmlReady(isset($entry) ? $entry->comment : '') ?></textarea>
</label> </label>
<? endif; ?> <? endif; ?>
<? if ($feedback->anonymous_entries) : ?>
<label>
<input type="checkbox" name="anonymous" value="1" <?= $entry->anonymous ? 'checked' : '' ?> >
<?= _('Kommentar anonym abgeben') ?>
</label>
<? endif; ?>
<div> <div>
<?= Studip\Button::createAccept(_('Absenden'), 'add', ['class' => 'feedback-entry-submit']) ?> <?= Studip\Button::createAccept(_('Absenden'), 'add', ['class' => 'feedback-entry-submit']) ?>
<?= Studip\Button::createCancel(_('Abbrechen'), 'cancel', ['class' => 'feedback-entry-cancel']) ?> <?= Studip\Button::createCancel(_('Abbrechen'), 'cancel', ['class' => 'feedback-entry-cancel']) ?>
......
<article class="studip feedback-entry" data-id="<?= $entry->id ?>"> <article class="studip feedback-entry" data-id="<?= $entry->id ?>">
<header> <header>
<h1> <h1>
<? if (!$entry->anonymous): ?>
<a href="<?= URLHelper::getLink('dispatch.php/profile?username=' . $entry->user->username) ?>"> <a href="<?= URLHelper::getLink('dispatch.php/profile?username=' . $entry->user->username) ?>">
<?= Avatar::getAvatar($entry->user_id)->getImageTag(Avatar::SMALL) ?> <?= Avatar::getAvatar($entry->user_id)->getImageTag(Avatar::SMALL) ?>
<?= htmlReady($entry->user->getFullName()) ?> <?= htmlReady($entry->user->getFullName()) ?>
</a> </a>
<? else: ?>
<?= Avatar::getNobody()->getImageTag(Avatar::SMALL) ?>
<?= _('Anonym') ?>
<? endif; ?>
</h1> </h1>
<nav> <nav>
<? if ($entry->isEditable()) : ?> <? if ($entry->isEditable()) : ?>
......
...@@ -14,7 +14,17 @@ ...@@ -14,7 +14,17 @@
</label> </label>
<label> <label>
<input type="checkbox" name="results_visible" value="1" <?= $feedback->results_visible == 1 ? 'checked' : '' ?>> <input type="checkbox" name="results_visible" value="1" <?= $feedback->results_visible == 1 ? 'checked' : '' ?>>
<?= _('Feedback Ergebnisse nach Antwort sichtbar') ?> <?= _('Feedback-Ergebnisse nach Antwort sichtbar') ?>
</label>
<label>
<input
type="checkbox"
name="anonymous_entries"
value="1"
<?= $this->current_action === 'edit_form' ? 'disabled' : '' ?>
<?= $feedback->anonymous_entries ? 'checked' : '' ?>
>
<?= _('Feedback kann anonym abgegeben werden')?>
</label> </label>
<label> <label>
<input id="comment-activated" type="checkbox" name="commentable" value="1" <? if ($this->current_action == <input id="comment-activated" type="checkbox" name="commentable" value="1" <? if ($this->current_action ==
......
...@@ -102,13 +102,13 @@ ...@@ -102,13 +102,13 @@
$actionMenu = ActionMenu::get()->setContext($feedback->question); $actionMenu = ActionMenu::get()->setContext($feedback->question);
$actionMenu->addLink( $actionMenu->addLink(
$controller->link_for('course/feedback/edit_form/' . $feedback->id), $controller->link_for('course/feedback/edit_form/' . $feedback->id),
_('Feedback-Element bearbeiten'), _('Bearbeiten'),
Icon::create('edit', Icon::ROLE_CLICKABLE, ['size' => 20]), Icon::create('edit', Icon::ROLE_CLICKABLE, ['size' => 20]),
['data-dialog' => ''] ['data-dialog' => '']
); );
$actionMenu->addLink( $actionMenu->addLink(
$controller->link_for('course/feedback/delete/' . $feedback->id), $controller->link_for('course/feedback/delete/' . $feedback->id),
_('Feedback-Element löschen'), _('Löschen'),
Icon::create('trash', Icon::ROLE_CLICKABLE, ['size' => 20]), Icon::create('trash', Icon::ROLE_CLICKABLE, ['size' => 20]),
['onclick' => "return STUDIP.Dialog.confirmAsPost('" . _('Feedback-Element und dazugehörige Einträge löschen?') . "', this.href);"] ['onclick' => "return STUDIP.Dialog.confirmAsPost('" . _('Feedback-Element und dazugehörige Einträge löschen?') . "', this.href);"]
); );
......
<?php
final class AddFeedbackAnonymousEntries extends Migration
{
public function description()
{
return 'Extend feedback tables for anonymous entries';
}
public function up()
{
\DBManager::get()->exec("ALTER TABLE `feedback`
ADD `anonymous_entries` TINYINT(1) NOT NULL DEFAULT 0
AFTER `commentable`
");
\DBManager::get()->exec("ALTER TABLE `feedback_entries`
ADD `anonymous` TINYINT(1) NOT NULL DEFAULT 0
AFTER `rating`
");
}
public function down()
{
\DBManager::get()->exec("ALTER TABLE `feedback` DROP `anonymous_entries`");
\DBManager::get()->exec("ALTER TABLE `feedback_entries` DROP `anonymous`");
}
}
...@@ -11,6 +11,13 @@ ...@@ -11,6 +11,13 @@
interface FeedbackRange interface FeedbackRange
{ {
/**
* Returns the ID of this range.
*
* @return string|integer The ID of the range.
*/
public function getId();
/** /**
* Returns a human-friendly representation of the FeedbackRange object instance's name. * Returns a human-friendly representation of the FeedbackRange object instance's name.
* *
......
...@@ -239,12 +239,20 @@ class RouteMap ...@@ -239,12 +239,20 @@ class RouteMap
private function addAuthenticatedFeedbackRoutes(RouteCollectorProxy $group): void private function addAuthenticatedFeedbackRoutes(RouteCollectorProxy $group): void
{ {
$group->get('/feedback-elements/{id}', Routes\Feedback\FeedbackElementsShow::class); $group->get('/feedback-elements/{id}', Routes\Feedback\FeedbackElementsShow::class);
$group->get('/feedback-elements/{id}/entries', Routes\Feedback\FeedbackEntriesIndex::class);
$group->get('/courses/{id}/feedback-elements', Routes\Feedback\FeedbackElementsByCourseIndex::class); $group->get('/courses/{id}/feedback-elements', Routes\Feedback\FeedbackElementsByCourseIndex::class);
$group->get('/file-refs/{id}/feedback-elements', Routes\Feedback\FeedbackElementsByFileRefIndex::class); $group->get('/file-refs/{id}/feedback-elements', Routes\Feedback\FeedbackElementsByFileRefIndex::class);
$group->get('/folders/{id}/feedback-elements', Routes\Feedback\FeedbackElementsByFolderIndex::class); $group->get('/folders/{id}/feedback-elements', Routes\Feedback\FeedbackElementsByFolderIndex::class);
$group->post('/feedback-elements', Routes\Feedback\FeedbackElementsCreate::class);
$group->patch('/feedback-elements/{id}', Routes\Feedback\FeedbackElementsUpdate::class);
$group->delete('/feedback-elements/{id}', Routes\Feedback\FeedbackElementsDelete::class);
$group->get('/feedback-elements/{id}/entries', Routes\Feedback\FeedbackEntriesIndex::class);
$group->post('/feedback-entries', Routes\Feedback\FeedbackEntriesCreate::class);
$group->get('/feedback-entries/{id}', Routes\Feedback\FeedbackEntriesShow::class); $group->get('/feedback-entries/{id}', Routes\Feedback\FeedbackEntriesShow::class);
$group->patch('/feedback-entries/{id}', Routes\Feedback\FeedbackEntriesUpdate::class);
$group->delete('/feedback-entries/{id}', Routes\Feedback\FeedbackEntriesDelete::class);
} }
private function addAuthenticatedInstitutesRoutes(RouteCollectorProxy $group): void private function addAuthenticatedInstitutesRoutes(RouteCollectorProxy $group): void
......
...@@ -19,6 +19,7 @@ class CoursesUnitsIndex extends JsonApiController ...@@ -19,6 +19,7 @@ class CoursesUnitsIndex extends JsonApiController
protected $allowedIncludePaths = [ protected $allowedIncludePaths = [
'structural-element', 'structural-element',
'creator', 'creator',
'feedback-element',
]; ];
protected $allowedPagingParameters = ['offset', 'limit']; protected $allowedPagingParameters = ['offset', 'limit'];
......
...@@ -67,10 +67,12 @@ class CoursewareInstancesUpdate extends JsonApiController ...@@ -67,10 +67,12 @@ class CoursewareInstancesUpdate extends JsonApiController
return 'Attribute `favorite-block-types` contains an invalid block type.'; return 'Attribute `favorite-block-types` contains an invalid block type.';
} }
} }
} elseif (self::arrayHas($json, 'data.attributes.sequential-progression')) { }
if (self::arrayHas($json, 'data.attributes.sequential-progression')) {
$sequentialProgression = self::arrayGet($json, 'data.attributes.sequential-progression'); $sequentialProgression = self::arrayGet($json, 'data.attributes.sequential-progression');
if (!is_bool($sequentialProgression)) { if (!in_array($sequentialProgression, [0, 1])) {
return 'Attribute `sequential-progression` must be a bool.'; return 'Attribute `sequential-progression` must be 0 or 1.';
} }
} }
...@@ -94,6 +96,20 @@ class CoursewareInstancesUpdate extends JsonApiController ...@@ -94,6 +96,20 @@ class CoursewareInstancesUpdate extends JsonApiController
} }
} }
if (self::arrayHas($json, 'data.attributes.show-feedback-popup')) {
$showFeedbackPopup = self::arrayGet($json, 'data.attributes.show-feedback-popup');
if (!in_array($showFeedbackPopup, [0,1])) {
return 'Attribute `show-feedback-popup` must be 0 or 1.';
}
}
if (self::arrayHas($json, 'data.attributes.show-feedback-in-contentbar')) {
$showFeedbackInContentbar = self::arrayGet($json, 'data.attributes.show-feedback-in-contentbar');
if (!in_array($showFeedbackInContentbar, [0,1])) {
return 'Attribute `show-feedback-in-contentbar` must be 0 or 1.';
}
}
if (self::arrayHas($json, 'data.attributes.certificate-settings')) { if (self::arrayHas($json, 'data.attributes.certificate-settings')) {
$certificateSettings = self::arrayGet($json, 'data.attributes.certificate-settings'); $certificateSettings = self::arrayGet($json, 'data.attributes.certificate-settings');
...@@ -137,6 +153,12 @@ class CoursewareInstancesUpdate extends JsonApiController ...@@ -137,6 +153,12 @@ class CoursewareInstancesUpdate extends JsonApiController
$editingPermissionLevel = $get('data.attributes.editing-permission-level'); $editingPermissionLevel = $get('data.attributes.editing-permission-level');
$instance->setEditingPermissionLevel($editingPermissionLevel); $instance->setEditingPermissionLevel($editingPermissionLevel);
$showFeedbackPopup = $get('data.attributes.show-feedback-popup');
$instance->setShowFeedbackPopup($showFeedbackPopup);
$showFeedbackInContentbar = $get('data.attributes.show-feedback-in-contentbar');
$instance->setShowFeedbackInContentbar($showFeedbackInContentbar);
$certificateSettings = $get('data.attributes.certificate-settings'); $certificateSettings = $get('data.attributes.certificate-settings');
$instance->setCertificateSettings($certificateSettings); $instance->setCertificateSettings($certificateSettings);
......
...@@ -2,45 +2,88 @@ ...@@ -2,45 +2,88 @@
namespace JsonApi\Routes\Feedback; namespace JsonApi\Routes\Feedback;
use Feedback;
use FeedbackElement;
use FeedbackEntry;
use FeedbackRange;
use SimpleORMap;
use User; use User;
/**
* @SuppressWarnings(PHPMD.StaticAccess)
* @SuppressWarnings(PHPMD.TooManyPublicMethods)
*/
class Authority class Authority
{ {
public static function canShowFeedbackElement(User $user, \FeedbackElement $resource) public static function canShowFeedbackElement(User $user, FeedbackElement $resource): bool
{ {
return \Feedback::hasRangeAccess($resource->range_id, $resource->range_type, $user->id); return Feedback::hasRangeAccess($resource->range_id, $resource->range_type, $user->getId());
} }
public static function canIndexFeedbackEntries(User $user, \FeedbackElement $resource) public static function canIndexFeedbackEntries(User $user, FeedbackElement $resource): bool
{ {
return self::canShowFeedbackElement($user, $resource); return self::canShowFeedbackElement($user, $resource);
} }
public static function canSeeResultsOfFeedbackElement(User $user, \FeedbackElement $resource) public static function canSeeResultsOfFeedbackElement(User $user, FeedbackElement $resource): bool
{ {
return self::canIndexFeedbackEntries($user, $resource) && return self::canIndexFeedbackEntries($user, $resource) &&
($resource['results_visible'] || \Feedback::hasAdminPerm($resource['course_id'], $user->id)); ($resource['results_visible'] || \Feedback::hasAdminPerm($resource['course_id'], $user->getId()));
} }
public static function canIndexFeedbackElementsOfCourse(User $user, \Course $course) public static function canIndexFeedbackElementsOfCourse(User $user, \Course $course): bool
{ {
return \Feedback::hasRangeAccess($course->id, \Course::class, $user->id); return \Feedback::hasRangeAccess($course->getId(), \Course::class, $user->getId());
} }
public static function canIndexFeedbackElementsOfFileRef(User $user, \FileRef $fileRef) public static function canIndexFeedbackElementsOfFileRef(User $user, \FileRef $fileRef): bool
{ {
return \Feedback::hasRangeAccess($fileRef->id, \FileRef::class, $user->id); return \Feedback::hasRangeAccess($fileRef->getId(), \FileRef::class, $user->getId());
} }
public static function canIndexFeedbackElementsOfFolder(User $user, \Folder $folder) public static function canIndexFeedbackElementsOfFolder(User $user, \Folder $folder): bool
{ {
return \Feedback::hasRangeAccess($folder->id, \Folder::class, $user->id); return \Feedback::hasRangeAccess($folder->getId(), \Folder::class, $user->getId());
} }
public static function canShowFeedbackEntry(User $user, \FeedbackEntry $resource) public static function canShowFeedbackEntry(User $user, \FeedbackEntry $resource): bool
{ {
$feedbackElement = $resource->feedback; $feedbackElement = $resource->feedback;
return self::canShowFeedbackElement($user, $feedbackElement); return self::canShowFeedbackElement($user, $feedbackElement);
} }
public static function canCreateFeedbackEntry(User $user, FeedbackElement $element): bool
{
return $element->isFeedbackable($user->id);
}
public static function canUpdateFeedbackEntry(User $user, FeedbackEntry $entry): bool
{
return $entry->isEditable($user->id);
}
public static function canDeleteFeedbackEntry(User $user, FeedbackEntry $entry): bool
{
return $entry->isDeletable($user->id);
}
public static function canCreateFeedbackElement(User $user, FeedbackRange $range): bool
{
return $range->isRangeAccessible($user->id)
&& Feedback::hasCreatePerm($range->getRangeCourseId(), $user->id);
}
public static function canUpdateFeedbackElement(User $user, FeedbackElement $element): bool
{
$range = $element->getRange();
return $range->isRangeAccessible($user->id)
&& Feedback::hasAdminPerm($range->getRangeCourseId(), $user->id);
}
public static function canDeleteFeedbackElement(User $user, FeedbackElement $element): bool
{
return self::canUpdateFeedbackElement($user, $element);
}
} }
<?php
namespace JsonApi\Routes\Feedback;
use FeedbackElement;
use FeedbackRange;
use User;
use JsonApi\Errors\AuthorizationFailedException;
use JsonApi\JsonApiController;
use JsonApi\Routes\ValidationTrait;
use JsonApi\Schemas\FeedbackElement as FeedbackElementSchema;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
/**
* Create a FeedbackElement.
*
* @SuppressWarnings(PHPMD.StaticAccess)
*/
class FeedbackElementsCreate extends JsonApiController
{
use RangeTypeAware;
use ValidationTrait;
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*
* @param array $args
*
* @return Response
*/
public function __invoke(Request $request, Response $response, $args)
{
$this->preparePossibleRangeTypes();
$json = $this->validate($request);
$range = $this->getRangeFromJson($json);
$user = $this->getUser($request);
if (!Authority::canCreateFeedbackElement($user, $range)) {
throw new AuthorizationFailedException();
}
$feedbackElement = $this->create($user, $json);
return $this->getCreatedResponse($feedbackElement);
}
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameters)
*
* @param array $json
* @param mixed $data
*
* @return string|void
*/
protected function validateResourceDocument($json, $data)
{
if (!self::arrayHas($json, 'data')) {
return 'Missing `data` member at document´s top level.';
}
if (FeedbackElementSchema::TYPE !== self::arrayGet($json, 'data.type')) {
return 'Invalid `type` of document´s `data`.';
}
if (self::arrayHas($json, 'data.id')) {
return 'New document must not have an `id`.';
}
$required = ['question', 'description', 'mode', 'results-visible', 'is-commentable', 'anonymous-entries'];
foreach ($required as $attribute) {
if (!self::arrayHas($json, 'data.attributes.' . $attribute)) {
return 'Missing `' . $attribute . '` attribute.';
}
}
if (!self::arrayHas($json, 'data.relationships.range')) {
return 'Missing `range` relationship.';
}
if (!$this->getRangeFromJson($json)) {
return 'Invalid `range` relationship.';
}
}
private function getRangeFromJson(array $json): ?FeedbackRange
{
$rangeType = self::arrayGet($json, 'data.relationships.range.data.type');
$rangeId = self::arrayGet($json, 'data.relationships.range.data.id');
if (!isset($this->possibleRangeTypes[$rangeType])) {
return null;
}
$rangeClass = $this->possibleRangeTypes[$rangeType];
return $rangeClass::find($rangeId);
}
private function create(User $user, array $json): FeedbackElement
{
$range = $this->getRangeFromJson($json);
return \FeedbackElement::create([
'range_id' => $range->getId(),
'range_type' => get_class($range),
'user_id' => $user->id,
'question' => self::arrayGet($json, 'data.attributes.question'),
'description' => self::arrayGet($json, 'data.attributes.description'),
'mode' => self::arrayGet($json, 'data.attributes.mode'),
'results_visible' => (int) self::arrayGet($json, 'data.attributes.results-visible'),
'commentable' => (int) self::arrayGet($json, 'data.attributes.is-commentable'),
'anonymous_entries' => (int) self::arrayGet($json, 'data.attributes.anonymous-entries'),
// TODO:
'course_id' => $range->getRangeCourseId(),
]);
}
}
<?php
namespace JsonApi\Routes\Feedback;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;
use JsonApi\Errors\AuthorizationFailedException;
use JsonApi\Errors\RecordNotFoundException;
use JsonApi\JsonApiController;
/**
* Deletes a feedback element.
*
* @SuppressWarnings(PHPMD.StaticAccess)
*/
class FeedbackElementsDelete extends JsonApiController
{
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*
* @param array $args
*
* @return Response
*/
public function __invoke(Request $request, Response $response, $args)
{
$resource = \FeedbackElement::find($args['id']);
if (!$resource) {
throw new RecordNotFoundException();
}
if (!Authority::canDeleteFeedbackElement($this->getUser($request), $resource)) {
throw new AuthorizationFailedException();
}
$resource->delete();
return $this->getCodeResponse(204);
}
}
...@@ -7,20 +7,33 @@ use Psr\Http\Message\ResponseInterface as Response; ...@@ -7,20 +7,33 @@ use Psr\Http\Message\ResponseInterface as Response;
use JsonApi\Errors\AuthorizationFailedException; use JsonApi\Errors\AuthorizationFailedException;
use JsonApi\Errors\RecordNotFoundException; use JsonApi\Errors\RecordNotFoundException;
use JsonApi\JsonApiController; use JsonApi\JsonApiController;
use JsonApi\Schemas\FeedbackElement as FeedbackElementSchema;
/** /**
* Displays a certain feedback element. * Displays a certain feedback element.
*
* @SuppressWarnings(PHPMD.StaticAccess)
*/ */
class FeedbackElementsShow extends JsonApiController class FeedbackElementsShow extends JsonApiController
{ {
protected $allowedIncludePaths = ['author', 'course', 'entries', 'range']; protected $allowedIncludePaths = [
FeedbackElementSchema::REL_AUTHOR,
FeedbackElementSchema::REL_COURSE,
FeedbackElementSchema::REL_ENTRIES,
FeedbackElementSchema::REL_RANGE,
];
/** /**
* @SuppressWarnings(PHPMD.UnusedFormalParameter) * @SuppressWarnings(PHPMD.UnusedFormalParameter)
*
* @param array $args
*
* @return Response
*/ */
public function __invoke(Request $request, Response $response, $args) public function __invoke(Request $request, Response $response, $args)
{ {
if (!$resource = \FeedbackElement::find($args['id'])) { $resource = \FeedbackElement::find($args['id']);
if (!$resource) {
throw new RecordNotFoundException(); throw new RecordNotFoundException();
} }
......
<?php
namespace JsonApi\Routes\Feedback;
use FeedbackElement;
use FeedbackRange;
use User;
use JsonApi\Errors\AuthorizationFailedException;
use JsonApi\Errors\RecordNotFoundException;
use JsonApi\JsonApiController;
use JsonApi\Routes\ValidationTrait;
use JsonApi\Schemas\FeedbackElement as FeedbackElementSchema;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
/**
* Update a FeedbackElement.
*
* @SuppressWarnings(PHPMD.StaticAccess)
*/
class FeedbackElementsUpdate extends JsonApiController
{
use RangeTypeAware;
use ValidationTrait;
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*
* @param array $args
*
* @return Response
*/
public function __invoke(Request $request, Response $response, $args)
{
$this->preparePossibleRangeTypes();
$resource = \FeedbackElement::find($args['id']);
if (!$resource) {
throw new RecordNotFoundException();
}
$json = $this->validate($request);
$user = $this->getUser($request);
if (!Authority::canUpdateFeedbackElement($user, $resource)) {
throw new AuthorizationFailedException();
}
$feedbackElement = $this->update($resource, $json);
return $this->getContentResponse($feedbackElement);
}
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameters)
*
* @param array $json
* @param mixed $data
*
* @return string|void
*/
protected function validateResourceDocument($json, $data)
{
if (!self::arrayHas($json, 'data')) {
return 'Missing `data` member at document´s top level.';
}
if (FeedbackElementSchema::TYPE !== self::arrayGet($json, 'data.type')) {
return 'Invalid `type` of document´s `data`.';
}
if (!self::arrayHas($json, 'data.id')) {
return 'An existing document must have an `id`.';
}
$required = ['question', 'description'];
foreach ($required as $attribute) {
if (!self::arrayHas($json, 'data.attributes.' . $attribute)) {
return 'Missing `' . $attribute . '` attribute.';
}
}
}
private function update(FeedbackElement $feedbackElement, array $json): FeedbackElement
{
$strAttrs = ['question', 'description'];
foreach ($strAttrs as $attribute) {
if (self::arrayHas($json, 'data.attributes.' . $attribute)) {
$feedbackElement[$attribute] = self::arrayGet($json, 'data.attributes.' . $attribute);
}
}
$feedbackElement->store();
return $feedbackElement;
}
}
<?php
namespace JsonApi\Routes\Feedback;
use FeedbackElement;
use FeedbackEntry;
use InvalidArgumentException;
use User;
use JsonApi\Errors\AuthorizationFailedException;
use JsonApi\JsonApiController;
use JsonApi\Routes\ValidationTrait;
use JsonApi\Schemas\FeedbackElement as FeedbackElementSchema;
use JsonApi\Schemas\FeedbackEntry as FeedbackEntrySchema;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
/**
* Create a FeedbackEntry.
*
* @SuppressWarnings(PHPMD.StaticAccess)
*/
class FeedbackEntriesCreate extends JsonApiController
{
use RatingHelper;
use ValidationTrait;
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*
* @param array $args
*
* @return Response
*/
public function __invoke(Request $request, Response $response, $args)
{
$json = $this->validate($request);
$element = $this->getElementFromJson($json);
$user = $this->getUser($request);
if (!Authority::canCreateFeedbackEntry($user, $element)) {
throw new AuthorizationFailedException();
}
$feedbackEntry = $this->create($user, $json);
return $this->getCreatedResponse($feedbackEntry);
}
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameters)
*
* @param array $json
* @param mixed $data
*
* @return string|void
*/
protected function validateResourceDocument($json, $data)
{
if (!self::arrayHas($json, 'data')) {
return 'Missing `data` member at document´s top level.';
}
if (FeedbackEntrySchema::TYPE !== self::arrayGet($json, 'data.type')) {
return 'Invalid `type` of document´s `data`.';
}
if (self::arrayHas($json, 'data.id')) {
return 'New document must not have an `id`.';
}
if (!self::arrayHas($json, 'data.relationships.feedback-element')) {
return 'Missing `feedback-element` relationship.';
}
if (!$this->getElementFromJson($json)) {
return 'Invalid `feedback-element` relationship.';
}
$required = ['rating'];
foreach ($required as $attribute) {
if (!self::arrayHas($json, 'data.attributes.' . $attribute)) {
return 'Missing `' . $attribute . '` attribute.';
}
}
}
private function getElementFromJson(array $json): ?FeedbackElement
{
$relationship = FeedbackEntrySchema::REL_FEEDBACK;
if (!$this->validateResourceObject($json, 'data.relationships.' . $relationship, FeedbackElementSchema::TYPE)) {
return null;
}
$resourceId = self::arrayGet($json, 'data.relationships.' . $relationship . '.data.id');
return FeedbackElement::find($resourceId);
}
private function create(User $user, array $json): FeedbackEntry
{
$element = $this->getElementFromJson($json);
$entry = \FeedbackEntry::build([
'feedback_id' => $element->getId(),
'user_id' => $user->id,
'rating' => $this->getRating($element, (int) self::arrayGet($json, 'data.attributes.rating')),
]);
if ($element['commentable']) {
$entry['comment'] = self::arrayGet($json, 'data.attributes.comment', '');
}
if ($element['anonymous_entries']) {
$entry['anonymous'] = (int) self::arrayGet($json, 'data.attributes.anonymous', '0');
}
$entry->store();
return $entry;
}
}
<?php
namespace JsonApi\Routes\Feedback;
use FeedbackEntry;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;
use JsonApi\Errors\AuthorizationFailedException;
use JsonApi\Errors\RecordNotFoundException;
use JsonApi\JsonApiController;
/**
* Deletes a feedback entry.
*
* @SuppressWarnings(PHPMD.StaticAccess)
*/
class FeedbackEntriesDelete extends JsonApiController
{
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*
* @param array $args
*
* @return Response
*/
public function __invoke(Request $request, Response $response, $args)
{
$resource = FeedbackEntry::find($args['id']);
if (!$resource) {
throw new RecordNotFoundException();
}
if (!Authority::canDeleteFeedbackEntry($this->getUser($request), $resource)) {
throw new AuthorizationFailedException();
}
$resource->delete();
return $this->getCodeResponse(204);
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment