Add new questions frontend
This commit is contained in:
parent
e322867716
commit
b5c974b9e3
|
@ -126,7 +126,7 @@ return [
|
||||||
],
|
],
|
||||||
|
|
||||||
// Redirect to this site after logging in or when pressing the top-left button
|
// Redirect to this site after logging in or when pressing the top-left button
|
||||||
// Must be one of news, meetings, user_shifts, angeltypes, user_questions
|
// Must be one of news, meetings, user_shifts, angeltypes, questions
|
||||||
'home_site' => env('HOME_SITE', 'news'),
|
'home_site' => env('HOME_SITE', 'news'),
|
||||||
|
|
||||||
// Number of News shown on one site
|
// Number of News shown on one site
|
||||||
|
|
|
@ -43,6 +43,12 @@ $route->post('/news/{id:\d+}', 'NewsController@comment');
|
||||||
// FAQ
|
// FAQ
|
||||||
$route->get('/faq', 'FaqController@index');
|
$route->get('/faq', 'FaqController@index');
|
||||||
|
|
||||||
|
// Questions
|
||||||
|
$route->get('/questions', 'QuestionsController@index');
|
||||||
|
$route->post('/questions', 'QuestionsController@delete');
|
||||||
|
$route->get('/questions/new', 'QuestionsController@add');
|
||||||
|
$route->post('/questions/new', 'QuestionsController@save');
|
||||||
|
|
||||||
// API
|
// API
|
||||||
$route->get('/api[/{resource:.+}]', 'ApiController@index');
|
$route->get('/api[/{resource:.+}]', 'ApiController@index');
|
||||||
|
|
||||||
|
@ -78,6 +84,17 @@ $route->addGroup(
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Questions
|
||||||
|
$route->addGroup(
|
||||||
|
'/questions',
|
||||||
|
function (RouteCollector $route) {
|
||||||
|
$route->get('', 'Admin\\QuestionsController@index');
|
||||||
|
$route->post('', 'Admin\\QuestionsController@delete');
|
||||||
|
$route->get('/{id:\d+}', 'Admin\\QuestionsController@edit');
|
||||||
|
$route->post('/{id:\d+}', 'Admin\\QuestionsController@save');
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// News
|
// News
|
||||||
$route->addGroup(
|
$route->addGroup(
|
||||||
'/news',
|
'/news',
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Engelsystem\Migrations;
|
||||||
|
|
||||||
|
use Engelsystem\Database\Migration\Migration;
|
||||||
|
|
||||||
|
class CreateQuestionsPermissions extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migration
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
if ($this->schema->hasTable('Privileges')) {
|
||||||
|
$db = $this->schema->getConnection();
|
||||||
|
$db->table('Privileges')->insert([
|
||||||
|
['name' => 'question.add', 'desc' => 'Ask questions'],
|
||||||
|
['name' => 'question.edit', 'desc' => 'Answer questions'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$userGroup = -20;
|
||||||
|
$shiftCoordinatorGroup = -60;
|
||||||
|
$addId = $db->table('Privileges')->where('name', 'question.add')->first()->id;
|
||||||
|
$editId = $db->table('Privileges')->where('name', 'question.edit')->first()->id;
|
||||||
|
$db->table('GroupPrivileges')->insert([
|
||||||
|
['group_id' => $userGroup, 'privilege_id' => $addId],
|
||||||
|
['group_id' => $shiftCoordinatorGroup, 'privilege_id' => $editId],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$db->table('Privileges')
|
||||||
|
->whereIn('name', ['user_questions', 'admin_questions'])
|
||||||
|
->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migration
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
if (!$this->schema->hasTable('Privileges')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$db = $this->schema->getConnection();
|
||||||
|
$db->table('Privileges')
|
||||||
|
->whereIn('name', ['question.add', 'question.edit'])
|
||||||
|
->delete();
|
||||||
|
|
||||||
|
$db->table('Privileges')->insert([
|
||||||
|
['name' => 'user_questions', 'desc' => 'Let users ask questions'],
|
||||||
|
['name' => 'admin_questions', 'desc' => 'Answer user\'s questions'],
|
||||||
|
]);
|
||||||
|
$userGroup = -20;
|
||||||
|
$shiftCoordinatorGroup = -60;
|
||||||
|
$bureaucratGroup = -40;
|
||||||
|
$userQuestionsId = $db->table('Privileges')->where('name', 'user_questions')->first()->id;
|
||||||
|
$adminQuestionsId = $db->table('Privileges')->where('name', 'admin_questions')->first()->id;
|
||||||
|
$db->table('GroupPrivileges')->insert([
|
||||||
|
['group_id' => $userGroup, 'privilege_id' => $userQuestionsId],
|
||||||
|
['group_id' => $shiftCoordinatorGroup, 'privilege_id' => $adminQuestionsId],
|
||||||
|
['group_id' => $bureaucratGroup, 'privilege_id' => $adminQuestionsId],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -30,7 +30,6 @@ $includeFiles = [
|
||||||
__DIR__ . '/../includes/view/AngelTypes_view.php',
|
__DIR__ . '/../includes/view/AngelTypes_view.php',
|
||||||
__DIR__ . '/../includes/view/EventConfig_view.php',
|
__DIR__ . '/../includes/view/EventConfig_view.php',
|
||||||
__DIR__ . '/../includes/view/PublicDashboard_view.php',
|
__DIR__ . '/../includes/view/PublicDashboard_view.php',
|
||||||
__DIR__ . '/../includes/view/Questions_view.php',
|
|
||||||
__DIR__ . '/../includes/view/Rooms_view.php',
|
__DIR__ . '/../includes/view/Rooms_view.php',
|
||||||
__DIR__ . '/../includes/view/ShiftCalendarLane.php',
|
__DIR__ . '/../includes/view/ShiftCalendarLane.php',
|
||||||
__DIR__ . '/../includes/view/ShiftCalendarRenderer.php',
|
__DIR__ . '/../includes/view/ShiftCalendarRenderer.php',
|
||||||
|
@ -68,14 +67,12 @@ $includeFiles = [
|
||||||
__DIR__ . '/../includes/pages/admin_arrive.php',
|
__DIR__ . '/../includes/pages/admin_arrive.php',
|
||||||
__DIR__ . '/../includes/pages/admin_free.php',
|
__DIR__ . '/../includes/pages/admin_free.php',
|
||||||
__DIR__ . '/../includes/pages/admin_groups.php',
|
__DIR__ . '/../includes/pages/admin_groups.php',
|
||||||
__DIR__ . '/../includes/pages/admin_questions.php',
|
|
||||||
__DIR__ . '/../includes/pages/admin_rooms.php',
|
__DIR__ . '/../includes/pages/admin_rooms.php',
|
||||||
__DIR__ . '/../includes/pages/admin_shifts.php',
|
__DIR__ . '/../includes/pages/admin_shifts.php',
|
||||||
__DIR__ . '/../includes/pages/admin_user.php',
|
__DIR__ . '/../includes/pages/admin_user.php',
|
||||||
__DIR__ . '/../includes/pages/guest_login.php',
|
__DIR__ . '/../includes/pages/guest_login.php',
|
||||||
__DIR__ . '/../includes/pages/user_messages.php',
|
__DIR__ . '/../includes/pages/user_messages.php',
|
||||||
__DIR__ . '/../includes/pages/user_myshifts.php',
|
__DIR__ . '/../includes/pages/user_myshifts.php',
|
||||||
__DIR__ . '/../includes/pages/user_questions.php',
|
|
||||||
__DIR__ . '/../includes/pages/user_settings.php',
|
__DIR__ . '/../includes/pages/user_settings.php',
|
||||||
__DIR__ . '/../includes/pages/user_shifts.php',
|
__DIR__ . '/../includes/pages/user_shifts.php',
|
||||||
|
|
||||||
|
|
|
@ -1,166 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
use Carbon\Carbon;
|
|
||||||
use Engelsystem\Models\Question;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
function admin_questions_title()
|
|
||||||
{
|
|
||||||
return __('Answer questions');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders a hint for new questions to answer.
|
|
||||||
*
|
|
||||||
* @return string|null
|
|
||||||
*/
|
|
||||||
function admin_new_questions()
|
|
||||||
{
|
|
||||||
if (current_page() != 'admin_questions') {
|
|
||||||
if (auth()->can('admin_questions')) {
|
|
||||||
$unanswered_questions = Question::unanswered()->count();
|
|
||||||
if ($unanswered_questions > 0) {
|
|
||||||
return '<a href="' . page_link_to('admin_questions') . '">'
|
|
||||||
. __('There are unanswered questions!')
|
|
||||||
. '</a>';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
function admin_questions()
|
|
||||||
{
|
|
||||||
$user = auth()->user();
|
|
||||||
$request = request();
|
|
||||||
|
|
||||||
if (!$request->has('action')) {
|
|
||||||
$unanswered_questions_table = [];
|
|
||||||
$unanswered_questions = Question::unanswered()->orderByDesc('created_at')->get();
|
|
||||||
|
|
||||||
foreach ($unanswered_questions as $question) {
|
|
||||||
/* @var Question $question */
|
|
||||||
$user_source = $question->user;
|
|
||||||
|
|
||||||
$unanswered_questions_table[] = [
|
|
||||||
'from' => User_Nick_render($user_source) . User_Pronoun_render($user_source),
|
|
||||||
'question' => nl2br(htmlspecialchars($question->text)),
|
|
||||||
'created_at' => $question->created_at,
|
|
||||||
'answer' => form([
|
|
||||||
form_textarea('answer', '', ''),
|
|
||||||
form_submit('submit', __('Send'))
|
|
||||||
], page_link_to('admin_questions', ['action' => 'answer', 'id' => $question->id])),
|
|
||||||
'actions' => form([
|
|
||||||
form_submit('submit', __('delete'), 'btn-xs'),
|
|
||||||
], page_link_to('admin_questions', ['action' => 'delete', 'id' => $question->id])),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
$answered_questions_table = [];
|
|
||||||
$answered_questions = Question::answered()->orderByDesc('answered_at')->get();
|
|
||||||
|
|
||||||
foreach ($answered_questions as $question) {
|
|
||||||
/* @var Question $question */
|
|
||||||
$user_source = $question->user;
|
|
||||||
$answer_user_source = $question->answerer;
|
|
||||||
$answered_questions_table[] = [
|
|
||||||
'from' => User_Nick_render($user_source),
|
|
||||||
'question' => nl2br(htmlspecialchars($question->text)),
|
|
||||||
'created_at' => $question->created_at,
|
|
||||||
'answered_by' => User_Nick_render($answer_user_source),
|
|
||||||
'answer' => nl2br(htmlspecialchars($question->answer)),
|
|
||||||
'answered_at' => $question->answered_at,
|
|
||||||
'actions' => form([
|
|
||||||
form_submit('submit', __('delete'), 'btn-xs')
|
|
||||||
], page_link_to('admin_questions', ['action' => 'delete', 'id' => $question->id]))
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
return page_with_title(admin_questions_title(), [
|
|
||||||
'<h2>' . __('Unanswered questions') . '</h2>',
|
|
||||||
table([
|
|
||||||
'from' => __('From'),
|
|
||||||
'question' => __('Question'),
|
|
||||||
'created_at' => __('Asked at'),
|
|
||||||
'answer' => __('Answer'),
|
|
||||||
'actions' => ''
|
|
||||||
], $unanswered_questions_table),
|
|
||||||
'<h2>' . __('Answered questions') . '</h2>',
|
|
||||||
table([
|
|
||||||
'from' => __('From'),
|
|
||||||
'question' => __('Question'),
|
|
||||||
'created_at' => __('Asked at'),
|
|
||||||
'answered_by' => __('Answered by'),
|
|
||||||
'answer' => __('Answer'),
|
|
||||||
'answered_at' => __('Answered at'),
|
|
||||||
'actions' => ''
|
|
||||||
], $answered_questions_table)
|
|
||||||
]);
|
|
||||||
} else {
|
|
||||||
switch ($request->input('action')) {
|
|
||||||
case 'answer':
|
|
||||||
if (
|
|
||||||
$request->has('id')
|
|
||||||
&& preg_match('/^\d{1,11}$/', $request->input('id'))
|
|
||||||
&& $request->hasPostData('submit')
|
|
||||||
) {
|
|
||||||
$question_id = $request->input('id');
|
|
||||||
} else {
|
|
||||||
return error('Incomplete call, missing Question ID.', true);
|
|
||||||
}
|
|
||||||
|
|
||||||
$question = Question::find($question_id);
|
|
||||||
if (!empty($question) && empty($question->answerer_id)) {
|
|
||||||
$answer = trim($request->input('answer'));
|
|
||||||
|
|
||||||
if (!empty($answer)) {
|
|
||||||
$question->answerer_id = $user->id;
|
|
||||||
$question->answer = $answer;
|
|
||||||
$question->answered_at = Carbon::now();
|
|
||||||
$question->save();
|
|
||||||
engelsystem_log(
|
|
||||||
'Question '
|
|
||||||
. $question->text
|
|
||||||
. ' (' . $question->id . ')'
|
|
||||||
. ' answered: '
|
|
||||||
. $answer
|
|
||||||
);
|
|
||||||
throw_redirect(page_link_to('admin_questions'));
|
|
||||||
} else {
|
|
||||||
return error('Enter an answer!', true);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return error('No question found.', true);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'delete':
|
|
||||||
if (
|
|
||||||
$request->has('id')
|
|
||||||
&& preg_match('/^\d{1,11}$/', $request->input('id'))
|
|
||||||
&& $request->hasPostData('submit')
|
|
||||||
) {
|
|
||||||
$question_id = $request->input('id');
|
|
||||||
} else {
|
|
||||||
return error('Incomplete call, missing Question ID.', true);
|
|
||||||
}
|
|
||||||
|
|
||||||
$question = Question::find($question_id);
|
|
||||||
if (!empty($question)) {
|
|
||||||
$question->delete();
|
|
||||||
engelsystem_log('Question deleted: ' . $question->text);
|
|
||||||
throw_redirect(page_link_to('admin_questions'));
|
|
||||||
} else {
|
|
||||||
return error('No question found.', true);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return '';
|
|
||||||
}
|
|
|
@ -1,79 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
use Engelsystem\Models\Question;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
function questions_title()
|
|
||||||
{
|
|
||||||
return __('Ask the Heaven');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
function user_questions()
|
|
||||||
{
|
|
||||||
$user = auth()->user();
|
|
||||||
$request = request();
|
|
||||||
|
|
||||||
if (!$request->has('action')) {
|
|
||||||
$open_questions = $user->questionsAsked()
|
|
||||||
->whereNull('answerer_id')
|
|
||||||
->orderByDesc('created_at')
|
|
||||||
->get();
|
|
||||||
$answered_questions = $user->questionsAsked()
|
|
||||||
->whereNotNull('answerer_id')
|
|
||||||
->orderByDesc('answered_at')
|
|
||||||
->get();
|
|
||||||
|
|
||||||
return Questions_view(
|
|
||||||
$open_questions->all(),
|
|
||||||
$answered_questions->all(),
|
|
||||||
page_link_to('user_questions', ['action' => 'ask'])
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
switch ($request->input('action')) {
|
|
||||||
case 'ask':
|
|
||||||
$question = request()->get('question');
|
|
||||||
if (!empty($question) && $request->hasPostData('submit')) {
|
|
||||||
Question::create([
|
|
||||||
'user_id' => $user->id,
|
|
||||||
'text' => $question,
|
|
||||||
]);
|
|
||||||
|
|
||||||
success(__('You question was saved.'));
|
|
||||||
throw_redirect(page_link_to('user_questions'));
|
|
||||||
} else {
|
|
||||||
return page_with_title(questions_title(), [
|
|
||||||
error(__('Please enter a question!'), true)
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'delete':
|
|
||||||
if (
|
|
||||||
$request->has('id')
|
|
||||||
&& preg_match('/^\d{1,11}$/', $request->input('id'))
|
|
||||||
&& $request->hasPostData('submit')
|
|
||||||
) {
|
|
||||||
$question_id = $request->input('id');
|
|
||||||
} else {
|
|
||||||
return error(__('Incomplete call, missing Question ID.'), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
$question = Question::find($question_id);
|
|
||||||
if (!empty($question) && $question->user_id == $user->id) {
|
|
||||||
$question->delete();
|
|
||||||
throw_redirect(page_link_to('user_questions'));
|
|
||||||
} else {
|
|
||||||
return page_with_title(questions_title(), [
|
|
||||||
error(__('No question found.'), true)
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return '';
|
|
||||||
}
|
|
|
@ -1,5 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use Engelsystem\Models\Question;
|
||||||
use Engelsystem\UserHintsRenderer;
|
use Engelsystem\UserHintsRenderer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -92,16 +93,19 @@ function make_navigation()
|
||||||
$menu = [];
|
$menu = [];
|
||||||
$pages = [
|
$pages = [
|
||||||
'news' => __('News'),
|
'news' => __('News'),
|
||||||
'meetings' => __('Meetings'),
|
'meetings' => [__('Meetings'), 'user_meetings'],
|
||||||
'user_shifts' => __('Shifts'),
|
'user_shifts' => __('Shifts'),
|
||||||
'angeltypes' => __('Angeltypes'),
|
'angeltypes' => __('Angeltypes'),
|
||||||
'user_questions' => __('Ask the Heaven'),
|
'questions' => [__('Ask the Heaven'), 'question.add'],
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach ($pages as $menu_page => $title) {
|
foreach ($pages as $menu_page => $options) {
|
||||||
if (auth()->can($menu_page) || ($menu_page == 'meetings' && auth()->can('user_meetings'))) {
|
if (!menu_is_allowed($menu_page, $options)) {
|
||||||
$menu[] = toolbar_item_link(page_link_to($menu_page), '', $title, $menu_page == $page);
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$title = ((array)$options)[0];
|
||||||
|
$menu[] = toolbar_item_link(page_link_to($menu_page), '', $title, $menu_page == $page);
|
||||||
}
|
}
|
||||||
|
|
||||||
$menu = make_room_navigation($menu);
|
$menu = make_room_navigation($menu);
|
||||||
|
@ -114,7 +118,7 @@ function make_navigation()
|
||||||
'admin_active' => 'Active angels',
|
'admin_active' => 'Active angels',
|
||||||
'admin_user' => 'All Angels',
|
'admin_user' => 'All Angels',
|
||||||
'admin_free' => 'Free angels',
|
'admin_free' => 'Free angels',
|
||||||
'admin_questions' => 'Answer questions',
|
'admin/questions' => ['Answer questions', 'question.edit'],
|
||||||
'shifttypes' => 'Shifttypes',
|
'shifttypes' => 'Shifttypes',
|
||||||
'admin_shifts' => 'Create shifts',
|
'admin_shifts' => 'Create shifts',
|
||||||
'admin_rooms' => 'Rooms',
|
'admin_rooms' => 'Rooms',
|
||||||
|
@ -129,22 +133,17 @@ function make_navigation()
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($admin_pages as $menu_page => $options) {
|
foreach ($admin_pages as $menu_page => $options) {
|
||||||
$options = (array)$options;
|
if (!menu_is_allowed($menu_page, $options)) {
|
||||||
$permissions = $menu_page;
|
continue;
|
||||||
$title = $options[0];
|
|
||||||
|
|
||||||
if (isset($options[1])) {
|
|
||||||
$permissions = $options[1];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auth()->can($permissions)) {
|
$title = ((array)$options)[0];
|
||||||
$admin_menu[] = toolbar_item_link(
|
$admin_menu[] = toolbar_item_link(
|
||||||
page_link_to($menu_page),
|
page_link_to($menu_page),
|
||||||
'',
|
'',
|
||||||
__($title),
|
__($title),
|
||||||
$menu_page == $page
|
$menu_page == $page
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count($admin_menu) > 0) {
|
if (count($admin_menu) > 0) {
|
||||||
|
@ -154,6 +153,24 @@ function make_navigation()
|
||||||
return '<ul class="nav navbar-nav">' . join("\n", $menu) . '</ul>';
|
return '<ul class="nav navbar-nav">' . join("\n", $menu) . '</ul>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $page
|
||||||
|
* @param string|string[] $options
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
function menu_is_allowed(string $page, $options)
|
||||||
|
{
|
||||||
|
$options = (array)$options;
|
||||||
|
$permissions = $page;
|
||||||
|
|
||||||
|
if (isset($options[1])) {
|
||||||
|
$permissions = $options[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return auth()->can($permissions);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds room navigation to the given menu.
|
* Adds room navigation to the given menu.
|
||||||
*
|
*
|
||||||
|
@ -207,3 +224,24 @@ function make_language_select()
|
||||||
}
|
}
|
||||||
return $items;
|
return $items;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders a hint for new questions to answer.
|
||||||
|
*
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
function admin_new_questions()
|
||||||
|
{
|
||||||
|
if (!auth()->can('question.edit') || current_page() == 'admin/questions') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$unanswered_questions = Question::unanswered()->count();
|
||||||
|
if (!$unanswered_questions) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return '<a href="' . page_link_to('/admin/questions') . '">'
|
||||||
|
. __('There are unanswered questions!')
|
||||||
|
. '</a>';
|
||||||
|
}
|
||||||
|
|
|
@ -1,71 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
use Engelsystem\Models\Question;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Question[] $open_questions
|
|
||||||
* @param Question[] $answered_questions
|
|
||||||
* @param string $ask_action
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
function Questions_view(array $open_questions, array $answered_questions, $ask_action)
|
|
||||||
{
|
|
||||||
$open_questions = array_map(
|
|
||||||
static function (Question $question): array {
|
|
||||||
return [
|
|
||||||
'actions' => form(
|
|
||||||
[
|
|
||||||
form_submit('submit', __('delete'), 'btn-default btn-xs')
|
|
||||||
],
|
|
||||||
page_link_to('user_questions', ['action' => 'delete', 'id' => $question->id])
|
|
||||||
),
|
|
||||||
'Question' => nl2br(htmlspecialchars($question->text)),
|
|
||||||
'created_at' => $question->created_at,
|
|
||||||
];
|
|
||||||
},
|
|
||||||
$open_questions
|
|
||||||
);
|
|
||||||
|
|
||||||
$answered_questions = array_map(
|
|
||||||
static function (Question $question): array {
|
|
||||||
return [
|
|
||||||
'Question' => nl2br(htmlspecialchars($question->text)),
|
|
||||||
'created_at' => $question->created_at,
|
|
||||||
'Answer' => nl2br(htmlspecialchars($question->answer)),
|
|
||||||
'answer_user' => User_Nick_render($question->answerer),
|
|
||||||
'answered_at' => $question->answered_at,
|
|
||||||
'actions' => form(
|
|
||||||
[
|
|
||||||
form_submit('submit', __('delete'), 'btn-default btn-xs')
|
|
||||||
],
|
|
||||||
page_link_to('user_questions', ['action' => 'delete', 'id' => $question->id])
|
|
||||||
),
|
|
||||||
];
|
|
||||||
},
|
|
||||||
$answered_questions
|
|
||||||
);
|
|
||||||
|
|
||||||
return page_with_title(questions_title(), [
|
|
||||||
msg(),
|
|
||||||
heading(__('Open questions'), 2),
|
|
||||||
table([
|
|
||||||
'Question' => __('Question'),
|
|
||||||
'created_at' => __('Asked at'),
|
|
||||||
'actions' => ''
|
|
||||||
], $open_questions),
|
|
||||||
heading(__('Answered questions'), 2),
|
|
||||||
table([
|
|
||||||
'Question' => __('Question'),
|
|
||||||
'created_at' => __('Asked at'),
|
|
||||||
'answer_user' => __('Answered by'),
|
|
||||||
'Answer' => __('Answer'),
|
|
||||||
'answered_at' => __('Answered at'),
|
|
||||||
'actions' => ''
|
|
||||||
], $answered_questions),
|
|
||||||
heading(__('Ask the Heaven'), 2),
|
|
||||||
form([
|
|
||||||
form_textarea('question', __('Your Question:'), ''),
|
|
||||||
form_submit('submit', __('Send'))
|
|
||||||
], $ask_action)
|
|
||||||
], true);
|
|
||||||
}
|
|
|
@ -114,3 +114,12 @@ msgstr "FAQ Eintrag erfolgreich gelöscht."
|
||||||
|
|
||||||
msgid "faq.edit.success"
|
msgid "faq.edit.success"
|
||||||
msgstr "FAQ Eintrag erfolgreich aktualisiert."
|
msgstr "FAQ Eintrag erfolgreich aktualisiert."
|
||||||
|
|
||||||
|
msgid "question.delete.success"
|
||||||
|
msgstr "Frage erfolgreich gelöscht."
|
||||||
|
|
||||||
|
msgid "question.add.success"
|
||||||
|
msgstr "Frage erstellt."
|
||||||
|
|
||||||
|
msgid "question.edit.success"
|
||||||
|
msgstr "Frage erfolgreich bearbeitet."
|
||||||
|
|
|
@ -2957,3 +2957,18 @@ msgstr "Frage"
|
||||||
|
|
||||||
msgid "faq.message"
|
msgid "faq.message"
|
||||||
msgstr "Antwort"
|
msgstr "Antwort"
|
||||||
|
|
||||||
|
msgid "question.questions"
|
||||||
|
msgstr "Fragen"
|
||||||
|
|
||||||
|
msgid "question.add"
|
||||||
|
msgstr "Fragen"
|
||||||
|
|
||||||
|
msgid "question.edit"
|
||||||
|
msgstr "Frage bearbeiten"
|
||||||
|
|
||||||
|
msgid "question.question"
|
||||||
|
msgstr "Frage"
|
||||||
|
|
||||||
|
msgid "question.answer"
|
||||||
|
msgstr "Antwort"
|
||||||
|
|
|
@ -110,3 +110,12 @@ msgstr "FAQ entry successfully deleted."
|
||||||
|
|
||||||
msgid "faq.edit.success"
|
msgid "faq.edit.success"
|
||||||
msgstr "FAQ entry successfully updated."
|
msgstr "FAQ entry successfully updated."
|
||||||
|
|
||||||
|
msgid "question.delete.success"
|
||||||
|
msgstr "Question deleted successfully."
|
||||||
|
|
||||||
|
msgid "question.add.success"
|
||||||
|
msgstr "Question added successfully."
|
||||||
|
|
||||||
|
msgid "question.edit.success"
|
||||||
|
msgstr "Question updated successfully."
|
||||||
|
|
|
@ -242,3 +242,18 @@ msgstr "Question"
|
||||||
|
|
||||||
msgid "faq.message"
|
msgid "faq.message"
|
||||||
msgstr "Answer"
|
msgstr "Answer"
|
||||||
|
|
||||||
|
msgid "question.questions"
|
||||||
|
msgstr "Fragen"
|
||||||
|
|
||||||
|
msgid "question.add"
|
||||||
|
msgstr "Ask"
|
||||||
|
|
||||||
|
msgid "question.edit"
|
||||||
|
msgstr "Edit question"
|
||||||
|
|
||||||
|
msgid "question.question"
|
||||||
|
msgstr "Question"
|
||||||
|
|
||||||
|
msgid "question.answer"
|
||||||
|
msgstr "Answer"
|
||||||
|
|
|
@ -18,8 +18,8 @@
|
||||||
</a>
|
</a>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
{% macro button(label, url, type, size) %}
|
{% macro button(label, url, type, size, title) %}
|
||||||
<a href="{{ url }}" class="btn btn-{{ type|default('default') }}{% if size %} btn-{{ size }}{% endif %}">
|
<a href="{{ url }}" class="btn btn-{{ type|default('default') }}{% if size %} btn-{{ size }}{% endif %}"{% if title %} title="{{ title }}"{% endif %}>
|
||||||
{{ label }}
|
{{ label }}
|
||||||
</a>
|
</a>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
{% extends 'layouts/app.twig' %}
|
||||||
|
{% import 'macros/base.twig' as m %}
|
||||||
|
{% import 'macros/form.twig' as f %}
|
||||||
|
|
||||||
|
{% block title %}{{ question and question.id ? __('question.edit') : __('question.add') }}{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="container">
|
||||||
|
<h1>{{ block('title') }}</h1>
|
||||||
|
|
||||||
|
{% include 'layouts/parts/messages.twig' %}
|
||||||
|
|
||||||
|
{% if question and question.id %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<p>
|
||||||
|
{{ m.glyphicon('time') }} {{ question.updated_at.format(__('Y-m-d H:i')) }}
|
||||||
|
|
||||||
|
{% if question.updated_at != question.created_at %}
|
||||||
|
 {{ __('form.updated') }}
|
||||||
|
<br>
|
||||||
|
{{ m.glyphicon('time') }} {{ question.created_at.format(__('Y-m-d H:i')) }}
|
||||||
|
{% endif %}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<form action="" enctype="multipart/form-data" method="post">
|
||||||
|
{{ csrf() }}
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
{{ f.textarea('text', __('question.question'), {'required': true, 'rows': 10, 'value': question ? question.text : ''}) }}
|
||||||
|
</div>
|
||||||
|
<div class="col-md-12">
|
||||||
|
{% if is_admin|default(false) %}
|
||||||
|
{{ f.textarea('answer', __('question.answer'), {'required': true, 'rows': 10, 'value': question ? question.answer : ''}) }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{{ f.submit() }}
|
||||||
|
|
||||||
|
{% if is_admin|default(false) %}
|
||||||
|
{{ f.submit(m.glyphicon('eye-close'), {'name': 'preview', 'btn_type': 'info', 'title': __('form.preview')}) }}
|
||||||
|
|
||||||
|
{% if question and question.id %}
|
||||||
|
{{ f.submit(m.glyphicon('trash'), {'name': 'delete', 'btn_type': 'danger', 'title': __('form.delete')}) }}
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if question %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<h2>{{ __('form.preview') }}</h2>
|
||||||
|
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-body">
|
||||||
|
{{ question.text|nl2br }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if question.answer %}
|
||||||
|
<div class="col-md-11 col-md-offset-1">
|
||||||
|
<div class="panel panel-info">
|
||||||
|
<div class="panel-body">
|
||||||
|
{{ question.answer|markdown }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,83 @@
|
||||||
|
{% extends 'layouts/app.twig' %}
|
||||||
|
{% import 'macros/base.twig' as m %}
|
||||||
|
{% import 'macros/form.twig' as f %}
|
||||||
|
|
||||||
|
{% block title %}
|
||||||
|
{{ __('question.questions') }}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="container">
|
||||||
|
<div class="page-header">
|
||||||
|
<h1>
|
||||||
|
{{ block('title') }}
|
||||||
|
|
||||||
|
{% if not is_admin|default(false) %}
|
||||||
|
{{ m.button(m.glyphicon('plus'), url('questions/new')) }}
|
||||||
|
{% endif %}
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% include 'layouts/parts/messages.twig' %}
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
{% block row %}
|
||||||
|
<div class="col-md-12">
|
||||||
|
{% block questions %}
|
||||||
|
{% for question in questions %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-body">
|
||||||
|
{{ question.text|nl2br }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="panel-footer">
|
||||||
|
{{ m.glyphicon('time') }} {{ question.created_at.format(__('Y-m-d H:i')) }}
|
||||||
|
|
||||||
|
{% if has_permission_to('question.edit') %}
|
||||||
|
{{ m.user(question.user) }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if question.user.id == user.id or has_permission_to('question.edit') %}
|
||||||
|
<form
|
||||||
|
action=""
|
||||||
|
enctype="multipart/form-data"
|
||||||
|
method="post"
|
||||||
|
class="pull-right"
|
||||||
|
>
|
||||||
|
{{ csrf() }}
|
||||||
|
{{ f.hidden('id', question.id) }}
|
||||||
|
{{ f.submit(m.glyphicon('trash'), {'name': 'delete', 'btn_type': 'danger', 'btn_size': 'xs', 'title': __('form.delete')}) }}
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if has_permission_to('question.edit') %}
|
||||||
|
<span class="pull-right">
|
||||||
|
{{ m.button(m.glyphicon('edit'), url('admin/questions/' ~ question.id), null, 'xs') }}
|
||||||
|
</span>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% if question.answer %}
|
||||||
|
<div class="col-md-11 col-md-offset-1">
|
||||||
|
<div class="panel panel-info">
|
||||||
|
<div class="panel-body">
|
||||||
|
{{ question.answer|markdown }}
|
||||||
|
</div>
|
||||||
|
<div class="panel-footer">
|
||||||
|
{{ m.glyphicon('time') }} {{ question.updated_at.format(__('Y-m-d H:i')) }}
|
||||||
|
{{ m.user(question.answerer) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
{% endblock %}
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,177 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Engelsystem\Controllers\Admin;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Engelsystem\Controllers\BaseController;
|
||||||
|
use Engelsystem\Controllers\CleanupModel;
|
||||||
|
use Engelsystem\Controllers\HasUserNotifications;
|
||||||
|
use Engelsystem\Helpers\Authenticator;
|
||||||
|
use Engelsystem\Http\Redirector;
|
||||||
|
use Engelsystem\Http\Request;
|
||||||
|
use Engelsystem\Http\Response;
|
||||||
|
use Engelsystem\Models\Question;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
|
||||||
|
class QuestionsController extends BaseController
|
||||||
|
{
|
||||||
|
use HasUserNotifications;
|
||||||
|
use CleanupModel;
|
||||||
|
|
||||||
|
/** @var Authenticator */
|
||||||
|
protected $auth;
|
||||||
|
|
||||||
|
/** @var LoggerInterface */
|
||||||
|
protected $log;
|
||||||
|
|
||||||
|
/** @var Question */
|
||||||
|
protected $question;
|
||||||
|
|
||||||
|
/** @var Redirector */
|
||||||
|
protected $redirect;
|
||||||
|
|
||||||
|
/** @var Response */
|
||||||
|
protected $response;
|
||||||
|
|
||||||
|
/** @var array */
|
||||||
|
protected $permissions = [
|
||||||
|
'question.add',
|
||||||
|
'question.edit',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Authenticator $auth
|
||||||
|
* @param LoggerInterface $log
|
||||||
|
* @param Question $question
|
||||||
|
* @param Redirector $redirector
|
||||||
|
* @param Response $response
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
Authenticator $auth,
|
||||||
|
LoggerInterface $log,
|
||||||
|
Question $question,
|
||||||
|
Redirector $redirector,
|
||||||
|
Response $response
|
||||||
|
) {
|
||||||
|
$this->auth = $auth;
|
||||||
|
$this->log = $log;
|
||||||
|
$this->question = $question;
|
||||||
|
$this->redirect = $redirector;
|
||||||
|
$this->response = $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Response
|
||||||
|
*/
|
||||||
|
public function index(): Response
|
||||||
|
{
|
||||||
|
$questions = $this->question
|
||||||
|
->orderBy('answer')
|
||||||
|
->orderByDesc('created_at')
|
||||||
|
->get();
|
||||||
|
$this->cleanupModelNullValues($questions);
|
||||||
|
|
||||||
|
return $this->response->withView(
|
||||||
|
'pages/questions/overview.twig',
|
||||||
|
['questions' => $questions, 'is_admin' => true] + $this->getNotifications()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Request $request
|
||||||
|
*
|
||||||
|
* @return Response
|
||||||
|
*/
|
||||||
|
public function delete(Request $request): Response
|
||||||
|
{
|
||||||
|
$data = $this->validate($request, [
|
||||||
|
'id' => 'required|int',
|
||||||
|
'delete' => 'checked',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$question = $this->question->findOrFail($data['id']);
|
||||||
|
$question->delete();
|
||||||
|
|
||||||
|
$this->log->info('Deleted question {question}', ['question' => $question->text]);
|
||||||
|
$this->addNotification('question.delete.success');
|
||||||
|
|
||||||
|
return $this->redirect->to('/admin/questions');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Request $request
|
||||||
|
*
|
||||||
|
* @return Response
|
||||||
|
*/
|
||||||
|
public function edit(Request $request): Response
|
||||||
|
{
|
||||||
|
$id = $request->getAttribute('id');
|
||||||
|
$questions = $this->question->find($id);
|
||||||
|
|
||||||
|
return $this->showEdit($questions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Request $request
|
||||||
|
*
|
||||||
|
* @return Response
|
||||||
|
*/
|
||||||
|
public function save(Request $request): Response
|
||||||
|
{
|
||||||
|
$id = $request->getAttribute('id');
|
||||||
|
/** @var Question $question */
|
||||||
|
$question = $this->question->findOrNew($id);
|
||||||
|
|
||||||
|
$data = $this->validate($request, [
|
||||||
|
'text' => 'required',
|
||||||
|
'answer' => 'required',
|
||||||
|
'delete' => 'optional|checked',
|
||||||
|
'preview' => 'optional|checked',
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!is_null($data['delete'])) {
|
||||||
|
$question->delete();
|
||||||
|
|
||||||
|
$this->log->info('Deleted question "{question}"', ['question' => $question->text]);
|
||||||
|
|
||||||
|
$this->addNotification('question.delete.success');
|
||||||
|
|
||||||
|
return $this->redirect->to('/admin/questions');
|
||||||
|
}
|
||||||
|
|
||||||
|
$question->text = $data['text'];
|
||||||
|
$question->answer = $data['answer'];
|
||||||
|
$question->answered_at = Carbon::now();
|
||||||
|
$question->answerer()->associate($this->auth->user());
|
||||||
|
|
||||||
|
if (!is_null($data['preview'])) {
|
||||||
|
return $this->showEdit($question);
|
||||||
|
}
|
||||||
|
|
||||||
|
$question->save();
|
||||||
|
|
||||||
|
$this->log->info(
|
||||||
|
'Updated questions "{text}": {answer}',
|
||||||
|
['text' => $question->text, 'answer' => $question->answer]
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->addNotification('question.edit.success');
|
||||||
|
|
||||||
|
return $this->redirect->to('/admin/questions');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Question|null $question
|
||||||
|
*
|
||||||
|
* @return Response
|
||||||
|
*/
|
||||||
|
protected function showEdit(?Question $question): Response
|
||||||
|
{
|
||||||
|
$this->cleanupModelNullValues($question);
|
||||||
|
|
||||||
|
return $this->response->withView(
|
||||||
|
'pages/questions/edit.twig',
|
||||||
|
['question' => $question, 'is_admin' => true] + $this->getNotifications()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,145 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Engelsystem\Controllers;
|
||||||
|
|
||||||
|
use Engelsystem\Helpers\Authenticator;
|
||||||
|
use Engelsystem\Http\Exceptions\HttpForbidden;
|
||||||
|
use Engelsystem\Http\Redirector;
|
||||||
|
use Engelsystem\Http\Request;
|
||||||
|
use Engelsystem\Http\Response;
|
||||||
|
use Engelsystem\Models\Question;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
|
||||||
|
class QuestionsController extends BaseController
|
||||||
|
{
|
||||||
|
use HasUserNotifications;
|
||||||
|
use CleanupModel;
|
||||||
|
|
||||||
|
/** @var Authenticator */
|
||||||
|
protected $auth;
|
||||||
|
|
||||||
|
/** @var LoggerInterface */
|
||||||
|
protected $log;
|
||||||
|
|
||||||
|
/** @var Question */
|
||||||
|
protected $question;
|
||||||
|
|
||||||
|
/** @var Redirector */
|
||||||
|
protected $redirect;
|
||||||
|
|
||||||
|
/** @var Response */
|
||||||
|
protected $response;
|
||||||
|
|
||||||
|
/** @var string[] */
|
||||||
|
protected $permissions = [
|
||||||
|
'question.add',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Authenticator $auth
|
||||||
|
* @param LoggerInterface $log
|
||||||
|
* @param Question $question
|
||||||
|
* @param Redirector $redirect
|
||||||
|
* @param Response $response
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
Authenticator $auth,
|
||||||
|
LoggerInterface $log,
|
||||||
|
Question $question,
|
||||||
|
Redirector $redirect,
|
||||||
|
Response $response
|
||||||
|
) {
|
||||||
|
$this->auth = $auth;
|
||||||
|
$this->log = $log;
|
||||||
|
$this->question = $question;
|
||||||
|
$this->redirect = $redirect;
|
||||||
|
$this->response = $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Response
|
||||||
|
*/
|
||||||
|
public function index(): Response
|
||||||
|
{
|
||||||
|
$questions = $this->question
|
||||||
|
->whereUserId($this->auth->user()->id)
|
||||||
|
->orderByDesc('created_at')
|
||||||
|
->get();
|
||||||
|
$this->cleanupModelNullValues($questions);
|
||||||
|
|
||||||
|
return $this->response->withView(
|
||||||
|
'pages/questions/overview.twig',
|
||||||
|
['questions' => $questions] + $this->getNotifications()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Response
|
||||||
|
*/
|
||||||
|
public function add(): Response
|
||||||
|
{
|
||||||
|
return $this->response->withView(
|
||||||
|
'pages/questions/edit.twig',
|
||||||
|
['question' => null] + $this->getNotifications()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Request $request
|
||||||
|
*
|
||||||
|
* @return Response
|
||||||
|
*/
|
||||||
|
public function delete(Request $request): Response
|
||||||
|
{
|
||||||
|
$data = $this->validate(
|
||||||
|
$request,
|
||||||
|
[
|
||||||
|
'id' => 'int|required',
|
||||||
|
'delete' => 'checked',
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$question = $this->question->findOrFail($data['id']);
|
||||||
|
if ($question->user->id != $this->auth->user()->id) {
|
||||||
|
throw new HttpForbidden();
|
||||||
|
}
|
||||||
|
|
||||||
|
$question->delete();
|
||||||
|
|
||||||
|
$this->log->info('Deleted own question {question}', ['question' => $question->text]);
|
||||||
|
$this->addNotification('question.delete.success');
|
||||||
|
|
||||||
|
return $this->redirect->to('/questions');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Request $request
|
||||||
|
*
|
||||||
|
* @return Response
|
||||||
|
*/
|
||||||
|
public function save(Request $request): Response
|
||||||
|
{
|
||||||
|
$data = $this->validate(
|
||||||
|
$request,
|
||||||
|
[
|
||||||
|
'text' => 'required',
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$question = new Question();
|
||||||
|
$question->user()->associate($this->auth->user());
|
||||||
|
$question->text = $data['text'];
|
||||||
|
$question->save();
|
||||||
|
|
||||||
|
$this->log->info(
|
||||||
|
'Asked: {question}',
|
||||||
|
[
|
||||||
|
'question' => $question->text,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->addNotification('question.add.success');
|
||||||
|
|
||||||
|
return $this->redirect->to('/questions');
|
||||||
|
}
|
||||||
|
}
|
|
@ -144,10 +144,6 @@ class LegacyMiddleware implements MiddlewareInterface
|
||||||
$title = messages_title();
|
$title = messages_title();
|
||||||
$content = user_messages();
|
$content = user_messages();
|
||||||
return [$title, $content];
|
return [$title, $content];
|
||||||
case 'user_questions':
|
|
||||||
$title = questions_title();
|
|
||||||
$content = user_questions();
|
|
||||||
return [$title, $content];
|
|
||||||
case 'user_settings':
|
case 'user_settings':
|
||||||
$title = settings_title();
|
$title = settings_title();
|
||||||
$content = user_settings();
|
$content = user_settings();
|
||||||
|
@ -156,10 +152,6 @@ class LegacyMiddleware implements MiddlewareInterface
|
||||||
$title = register_title();
|
$title = register_title();
|
||||||
$content = guest_register();
|
$content = guest_register();
|
||||||
return [$title, $content];
|
return [$title, $content];
|
||||||
case 'admin_questions':
|
|
||||||
$title = admin_questions_title();
|
|
||||||
$content = admin_questions();
|
|
||||||
return [$title, $content];
|
|
||||||
case 'admin_user':
|
case 'admin_user':
|
||||||
$title = admin_user_title();
|
$title = admin_user_title();
|
||||||
$content = admin_user();
|
$content = admin_user();
|
||||||
|
|
|
@ -0,0 +1,267 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Engelsystem\Test\Unit\Controllers\Admin;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Engelsystem\Controllers\Admin\QuestionsController;
|
||||||
|
use Engelsystem\Helpers\Authenticator;
|
||||||
|
use Engelsystem\Http\Exceptions\ValidationException;
|
||||||
|
use Engelsystem\Http\UrlGenerator;
|
||||||
|
use Engelsystem\Http\UrlGeneratorInterface;
|
||||||
|
use Engelsystem\Http\Validation\Validator;
|
||||||
|
use Engelsystem\Models\Question;
|
||||||
|
use Engelsystem\Models\User\User;
|
||||||
|
use Engelsystem\Test\Unit\Controllers\ControllerTest;
|
||||||
|
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||||
|
use PHPUnit\Framework\MockObject\MockObject;
|
||||||
|
|
||||||
|
class QuestionsControllerTest extends ControllerTest
|
||||||
|
{
|
||||||
|
/** @var Authenticator|MockObject */
|
||||||
|
protected $auth;
|
||||||
|
|
||||||
|
/** @var User */
|
||||||
|
protected $user;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Controllers\Admin\QuestionsController::index
|
||||||
|
* @covers \Engelsystem\Controllers\Admin\QuestionsController::__construct
|
||||||
|
*/
|
||||||
|
public function testIndex()
|
||||||
|
{
|
||||||
|
$this->response->expects($this->once())
|
||||||
|
->method('withView')
|
||||||
|
->willReturnCallback(function (string $view, array $data) {
|
||||||
|
$this->assertEquals('pages/questions/overview.twig', $view);
|
||||||
|
$this->assertArrayHasKey('questions', $data);
|
||||||
|
$this->assertArrayHasKey('is_admin', $data);
|
||||||
|
|
||||||
|
$this->assertEquals('Foobar?', $data['questions'][0]->text);
|
||||||
|
$this->assertTrue($data['is_admin']);
|
||||||
|
|
||||||
|
return $this->response;
|
||||||
|
});
|
||||||
|
|
||||||
|
/** @var QuestionsController $controller */
|
||||||
|
$controller = $this->app->get(QuestionsController::class);
|
||||||
|
$controller->index();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Controllers\Admin\QuestionsController::delete
|
||||||
|
*/
|
||||||
|
public function testDeleteInvalidRequest()
|
||||||
|
{
|
||||||
|
/** @var QuestionsController $controller */
|
||||||
|
$controller = $this->app->get(QuestionsController::class);
|
||||||
|
$controller->setValidator($this->app->get(Validator::class));
|
||||||
|
|
||||||
|
$this->expectException(ValidationException::class);
|
||||||
|
$controller->delete($this->request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Controllers\Admin\QuestionsController::delete
|
||||||
|
*/
|
||||||
|
public function testDeleteNotFound()
|
||||||
|
{
|
||||||
|
$this->request = $this->request->withParsedBody(['id' => 42, 'delete' => '1']);
|
||||||
|
|
||||||
|
/** @var QuestionsController $controller */
|
||||||
|
$controller = $this->app->get(QuestionsController::class);
|
||||||
|
$controller->setValidator($this->app->get(Validator::class));
|
||||||
|
|
||||||
|
$this->expectException(ModelNotFoundException::class);
|
||||||
|
$controller->delete($this->request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Controllers\Admin\QuestionsController::delete
|
||||||
|
*/
|
||||||
|
public function testDelete()
|
||||||
|
{
|
||||||
|
$this->request = $this->request->withParsedBody(['id' => 1, 'delete' => '1']);
|
||||||
|
$this->setExpects($this->response, 'redirectTo', ['http://localhost/admin/questions'], $this->response);
|
||||||
|
|
||||||
|
/** @var QuestionsController $controller */
|
||||||
|
$controller = $this->app->get(QuestionsController::class);
|
||||||
|
$controller->setValidator($this->app->get(Validator::class));
|
||||||
|
|
||||||
|
$controller->delete($this->request);
|
||||||
|
|
||||||
|
$this->assertCount(1, Question::all());
|
||||||
|
$this->assertTrue($this->log->hasInfoThatContains('Deleted question'));
|
||||||
|
$this->assertHasNotification('question.delete.success');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Controllers\Admin\QuestionsController::edit
|
||||||
|
* @covers \Engelsystem\Controllers\Admin\QuestionsController::showEdit
|
||||||
|
*/
|
||||||
|
public function testEdit()
|
||||||
|
{
|
||||||
|
$this->request->attributes->set('id', 1);
|
||||||
|
$this->response->expects($this->once())
|
||||||
|
->method('withView')
|
||||||
|
->willReturnCallback(function (string $view, array $data) {
|
||||||
|
$this->assertEquals('pages/questions/edit.twig', $view);
|
||||||
|
$this->assertArrayHasKey('question', $data);
|
||||||
|
$this->assertArrayHasKey('is_admin', $data);
|
||||||
|
|
||||||
|
$this->assertEquals('Question?', $data['question']->text);
|
||||||
|
$this->assertEquals($this->user->id, $data['question']->user->id);
|
||||||
|
$this->assertTrue($data['is_admin']);
|
||||||
|
|
||||||
|
return $this->response;
|
||||||
|
});
|
||||||
|
|
||||||
|
/** @var QuestionsController $controller */
|
||||||
|
$controller = $this->app->get(QuestionsController::class);
|
||||||
|
|
||||||
|
$controller->edit($this->request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Controllers\Admin\QuestionsController::save
|
||||||
|
*/
|
||||||
|
public function testSaveCreateInvalid()
|
||||||
|
{
|
||||||
|
$this->expectException(ValidationException::class);
|
||||||
|
|
||||||
|
/** @var QuestionsController $controller */
|
||||||
|
$controller = $this->app->make(QuestionsController::class);
|
||||||
|
$controller->setValidator(new Validator());
|
||||||
|
$controller->save($this->request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Controllers\Admin\QuestionsController::save
|
||||||
|
*/
|
||||||
|
public function testSaveCreateEdit()
|
||||||
|
{
|
||||||
|
$this->request->attributes->set('id', 2);
|
||||||
|
$body = [
|
||||||
|
'text' => 'Foo?',
|
||||||
|
'answer' => 'Bar!',
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->request = $this->request->withParsedBody($body);
|
||||||
|
$this->response->expects($this->once())
|
||||||
|
->method('redirectTo')
|
||||||
|
->with('http://localhost/admin/questions')
|
||||||
|
->willReturn($this->response);
|
||||||
|
|
||||||
|
/** @var QuestionsController $controller */
|
||||||
|
$controller = $this->app->make(QuestionsController::class);
|
||||||
|
$controller->setValidator(new Validator());
|
||||||
|
|
||||||
|
$controller->save($this->request);
|
||||||
|
|
||||||
|
$this->assertTrue($this->log->hasInfoThatContains('Updated'));
|
||||||
|
$this->assertHasNotification('question.edit.success');
|
||||||
|
|
||||||
|
$question = Question::find(2);
|
||||||
|
$this->assertEquals('Foo?', $question->text);
|
||||||
|
$this->assertEquals('Bar!', $question->answer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Controllers\Admin\QuestionsController::save
|
||||||
|
*/
|
||||||
|
public function testSavePreview()
|
||||||
|
{
|
||||||
|
$this->request->attributes->set('id', 1);
|
||||||
|
$this->request = $this->request->withParsedBody([
|
||||||
|
'text' => 'Foo?',
|
||||||
|
'answer' => 'Bar!',
|
||||||
|
'preview' => '1',
|
||||||
|
]);
|
||||||
|
$this->response->expects($this->once())
|
||||||
|
->method('withView')
|
||||||
|
->willReturnCallback(function ($view, $data) {
|
||||||
|
$this->assertEquals('pages/questions/edit.twig', $view);
|
||||||
|
|
||||||
|
/** @var Question $question */
|
||||||
|
$question = $data['question'];
|
||||||
|
// Contains new text
|
||||||
|
$this->assertEquals('Foo?', $question->text);
|
||||||
|
$this->assertEquals('Bar!', $question->answer);
|
||||||
|
|
||||||
|
return $this->response;
|
||||||
|
});
|
||||||
|
|
||||||
|
/** @var QuestionsController $controller */
|
||||||
|
$controller = $this->app->make(QuestionsController::class);
|
||||||
|
$controller->setValidator(new Validator());
|
||||||
|
|
||||||
|
$controller->save($this->request);
|
||||||
|
|
||||||
|
// Assert no changes
|
||||||
|
$question = Question::find(1);
|
||||||
|
$this->assertEquals('Question?', $question->text);
|
||||||
|
$this->assertEquals('Answer!', $question->answer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Controllers\Admin\QuestionsController::save
|
||||||
|
*/
|
||||||
|
public function testSaveDelete()
|
||||||
|
{
|
||||||
|
$this->request->attributes->set('id', 1);
|
||||||
|
$this->request = $this->request->withParsedBody([
|
||||||
|
'text' => '.',
|
||||||
|
'answer' => '.',
|
||||||
|
'delete' => '1',
|
||||||
|
]);
|
||||||
|
$this->response->expects($this->once())
|
||||||
|
->method('redirectTo')
|
||||||
|
->with('http://localhost/admin/questions')
|
||||||
|
->willReturn($this->response);
|
||||||
|
|
||||||
|
/** @var QuestionsController $controller */
|
||||||
|
$controller = $this->app->make(QuestionsController::class);
|
||||||
|
$controller->setValidator(new Validator());
|
||||||
|
|
||||||
|
$controller->save($this->request);
|
||||||
|
|
||||||
|
$this->assertCount(1, Question::all());
|
||||||
|
$this->assertTrue($this->log->hasInfoThatContains('Deleted question'));
|
||||||
|
$this->assertHasNotification('question.delete.success');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup environment
|
||||||
|
*/
|
||||||
|
public function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->auth = $this->createMock(Authenticator::class);
|
||||||
|
$this->app->instance(Authenticator::class, $this->auth);
|
||||||
|
|
||||||
|
$this->app->bind(UrlGeneratorInterface::class, UrlGenerator::class);
|
||||||
|
|
||||||
|
$this->user = new User([
|
||||||
|
'name' => 'foo',
|
||||||
|
'password' => '',
|
||||||
|
'email' => '',
|
||||||
|
'api_key' => '',
|
||||||
|
'last_login_at' => null,
|
||||||
|
]);
|
||||||
|
$this->user->save();
|
||||||
|
$this->setExpects($this->auth, 'user', null, $this->user, $this->any());
|
||||||
|
|
||||||
|
(new Question([
|
||||||
|
'user_id' => $this->user->id,
|
||||||
|
'text' => 'Question?',
|
||||||
|
'answerer_id' => $this->user->id,
|
||||||
|
'answer' => 'Answer!',
|
||||||
|
'answered_at' => new Carbon(),
|
||||||
|
]))->save();
|
||||||
|
|
||||||
|
(new Question([
|
||||||
|
'user_id' => $this->user->id,
|
||||||
|
'text' => 'Foobar?',
|
||||||
|
]))->save();
|
||||||
|
}
|
||||||
|
}
|
|
@ -35,6 +35,16 @@ abstract class ControllerTest extends TestCase
|
||||||
/** @var Session */
|
/** @var Session */
|
||||||
protected $session;
|
protected $session;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $value
|
||||||
|
* @param string|null $message
|
||||||
|
*/
|
||||||
|
protected function assertHasNotification(string $value, string $message = null)
|
||||||
|
{
|
||||||
|
$messages = $this->session->get('messages', []);
|
||||||
|
$this->assertTrue(in_array($value, $messages), $message ?: 'Session does not contain message "' . $value . '"');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Setup environment
|
* Setup environment
|
||||||
*/
|
*/
|
||||||
|
@ -56,6 +66,7 @@ abstract class ControllerTest extends TestCase
|
||||||
|
|
||||||
$this->session = new Session(new MockArraySessionStorage());
|
$this->session = new Session(new MockArraySessionStorage());
|
||||||
$this->app->instance('session', $this->session);
|
$this->app->instance('session', $this->session);
|
||||||
|
$this->app->instance(Session::class, $this->session);
|
||||||
|
|
||||||
$this->app->bind(UrlGeneratorInterface::class, UrlGenerator::class);
|
$this->app->bind(UrlGeneratorInterface::class, UrlGenerator::class);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,215 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Engelsystem\Test\Unit\Controllers;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Engelsystem\Controllers\QuestionsController;
|
||||||
|
use Engelsystem\Helpers\Authenticator;
|
||||||
|
use Engelsystem\Http\Exceptions\HttpForbidden;
|
||||||
|
use Engelsystem\Http\Exceptions\ValidationException;
|
||||||
|
use Engelsystem\Http\UrlGenerator;
|
||||||
|
use Engelsystem\Http\UrlGeneratorInterface;
|
||||||
|
use Engelsystem\Http\Validation\Validator;
|
||||||
|
use Engelsystem\Models\Question;
|
||||||
|
use Engelsystem\Models\User\User;
|
||||||
|
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||||
|
use PHPUnit\Framework\MockObject\MockObject;
|
||||||
|
|
||||||
|
class QuestionsControllerTest extends ControllerTest
|
||||||
|
{
|
||||||
|
/** @var Authenticator|MockObject */
|
||||||
|
protected $auth;
|
||||||
|
|
||||||
|
/** @var User */
|
||||||
|
protected $user;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Controllers\QuestionsController::index
|
||||||
|
* @covers \Engelsystem\Controllers\QuestionsController::__construct
|
||||||
|
*/
|
||||||
|
public function testIndex()
|
||||||
|
{
|
||||||
|
$this->response->expects($this->once())
|
||||||
|
->method('withView')
|
||||||
|
->willReturnCallback(function (string $view, array $data) {
|
||||||
|
$this->assertEquals('pages/questions/overview.twig', $view);
|
||||||
|
$this->assertArrayHasKey('questions', $data);
|
||||||
|
|
||||||
|
$this->assertEquals('Lorem?', $data['questions'][0]->text);
|
||||||
|
|
||||||
|
return $this->response;
|
||||||
|
});
|
||||||
|
|
||||||
|
/** @var QuestionsController $controller */
|
||||||
|
$controller = $this->app->get(QuestionsController::class);
|
||||||
|
$controller->index();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Controllers\QuestionsController::add
|
||||||
|
*/
|
||||||
|
public function testAdd()
|
||||||
|
{
|
||||||
|
$this->response->expects($this->once())
|
||||||
|
->method('withView')
|
||||||
|
->willReturnCallback(function (string $view, array $data) {
|
||||||
|
$this->assertEquals('pages/questions/edit.twig', $view);
|
||||||
|
$this->assertArrayHasKey('question', $data);
|
||||||
|
$this->assertNull($data['question']);
|
||||||
|
|
||||||
|
return $this->response;
|
||||||
|
});
|
||||||
|
|
||||||
|
/** @var QuestionsController $controller */
|
||||||
|
$controller = $this->app->get(QuestionsController::class);
|
||||||
|
$controller->setValidator(new Validator());
|
||||||
|
|
||||||
|
$controller->add();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Controllers\QuestionsController::delete
|
||||||
|
*/
|
||||||
|
public function testDeleteNotFound()
|
||||||
|
{
|
||||||
|
$this->request = $this->request->withParsedBody([
|
||||||
|
'id' => '3',
|
||||||
|
'delete' => '1',
|
||||||
|
]);
|
||||||
|
|
||||||
|
/** @var QuestionsController $controller */
|
||||||
|
$controller = $this->app->get(QuestionsController::class);
|
||||||
|
$controller->setValidator(new Validator());
|
||||||
|
|
||||||
|
$this->expectException(ModelNotFoundException::class);
|
||||||
|
$controller->delete($this->request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Controllers\QuestionsController::delete
|
||||||
|
*/
|
||||||
|
public function testDeleteNotOwn()
|
||||||
|
{
|
||||||
|
$otherUser = new User([
|
||||||
|
'name' => 'bar',
|
||||||
|
'password' => '',
|
||||||
|
'email' => '.',
|
||||||
|
'api_key' => '',
|
||||||
|
'last_login_at' => null,
|
||||||
|
]);
|
||||||
|
$otherUser->save();
|
||||||
|
(new Question([
|
||||||
|
'user_id' => $otherUser->id,
|
||||||
|
'text' => 'Lorem?',
|
||||||
|
]))->save();
|
||||||
|
$this->request = $this->request->withParsedBody([
|
||||||
|
'id' => '3',
|
||||||
|
'delete' => '1',
|
||||||
|
]);
|
||||||
|
|
||||||
|
/** @var QuestionsController $controller */
|
||||||
|
$controller = $this->app->get(QuestionsController::class);
|
||||||
|
$controller->setValidator(new Validator());
|
||||||
|
|
||||||
|
$this->expectException(HttpForbidden::class);
|
||||||
|
$controller->delete($this->request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Controllers\QuestionsController::delete
|
||||||
|
*/
|
||||||
|
public function testDelete()
|
||||||
|
{
|
||||||
|
$this->request = $this->request->withParsedBody([
|
||||||
|
'id' => '2',
|
||||||
|
'delete' => '1',
|
||||||
|
]);
|
||||||
|
$this->response->expects($this->once())
|
||||||
|
->method('redirectTo')
|
||||||
|
->with('http://localhost/questions')
|
||||||
|
->willReturn($this->response);
|
||||||
|
|
||||||
|
/** @var QuestionsController $controller */
|
||||||
|
$controller = $this->app->get(QuestionsController::class);
|
||||||
|
$controller->setValidator(new Validator());
|
||||||
|
|
||||||
|
$controller->delete($this->request);
|
||||||
|
|
||||||
|
$this->assertCount(1, Question::all());
|
||||||
|
$this->assertTrue($this->log->hasInfoThatContains('Deleted own question'));
|
||||||
|
$this->assertHasNotification('question.delete.success');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Controllers\QuestionsController::save
|
||||||
|
*/
|
||||||
|
public function testSaveInvalid()
|
||||||
|
{
|
||||||
|
/** @var QuestionsController $controller */
|
||||||
|
$controller = $this->app->get(QuestionsController::class);
|
||||||
|
$controller->setValidator(new Validator());
|
||||||
|
|
||||||
|
$this->expectException(ValidationException::class);
|
||||||
|
$controller->save($this->request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Controllers\QuestionsController::save
|
||||||
|
*/
|
||||||
|
public function testSave()
|
||||||
|
{
|
||||||
|
$this->request = $this->request->withParsedBody([
|
||||||
|
'text' => 'Some question?',
|
||||||
|
]);
|
||||||
|
$this->response->expects($this->once())
|
||||||
|
->method('redirectTo')
|
||||||
|
->with('http://localhost/questions')
|
||||||
|
->willReturn($this->response);
|
||||||
|
|
||||||
|
/** @var QuestionsController $controller */
|
||||||
|
$controller = $this->app->get(QuestionsController::class);
|
||||||
|
$controller->setValidator(new Validator());
|
||||||
|
|
||||||
|
$controller->save($this->request);
|
||||||
|
|
||||||
|
$this->assertCount(3, Question::all());
|
||||||
|
$this->assertTrue($this->log->hasInfoThatContains('Asked'));
|
||||||
|
$this->assertHasNotification('question.add.success');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup environment
|
||||||
|
*/
|
||||||
|
public function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->auth = $this->createMock(Authenticator::class);
|
||||||
|
$this->app->instance(Authenticator::class, $this->auth);
|
||||||
|
|
||||||
|
$this->app->bind(UrlGeneratorInterface::class, UrlGenerator::class);
|
||||||
|
|
||||||
|
$this->user = new User([
|
||||||
|
'name' => 'foo',
|
||||||
|
'password' => '',
|
||||||
|
'email' => '',
|
||||||
|
'api_key' => '',
|
||||||
|
'last_login_at' => null,
|
||||||
|
]);
|
||||||
|
$this->user->save();
|
||||||
|
$this->setExpects($this->auth, 'user', null, $this->user, $this->any());
|
||||||
|
|
||||||
|
(new Question([
|
||||||
|
'user_id' => $this->user->id,
|
||||||
|
'text' => 'Lorem?',
|
||||||
|
]))->save();
|
||||||
|
|
||||||
|
(new Question([
|
||||||
|
'user_id' => $this->user->id,
|
||||||
|
'text' => 'Foo?',
|
||||||
|
'answerer_id' => $this->user->id,
|
||||||
|
'answer' => 'Bar!',
|
||||||
|
'answered_at' => new Carbon(),
|
||||||
|
]))->save();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue