controllers/ConsultationController.php
Rama main · réplica de lectura del repositorio original.
<?php
namespace app\controllers;
use app\models\AdminTodoItem;
use app\models\exceptions\ResponseException;
use app\models\settings\{AntragsgruenApp, PrivilegeQueryContext, Privileges};
use app\views\consultation\LayoutHelper;
use app\models\http\{BinaryFileResponse,
HtmlErrorResponse,
HtmlResponse,
JsonResponse,
RedirectResponse,
ResponseInterface,
RestApiResponse};
use app\components\{DateTools, RSSExporter, Tools, UrlHelper};
use app\models\db\{Amendment,
AmendmentComment,
IComment,
IRSSItem,
Consultation,
MotionComment,
repostory\MotionRepository,
SpeechQueue,
User,
UserNotification};
use app\models\exceptions\Internal;
use app\models\forms\ConsultationActivityFilterForm;
use app\models\proposedProcedure\Factory;
class ConsultationController extends Base
{
public const VIEW_ID_HOME = 'home';
public const VIEW_ID_INDEX = 'index';
/**
* @throws \Exception
*/
public function beforeAction($action): bool
{
if ($action->id === self::VIEW_ID_HOME) {
foreach (AntragsgruenApp::getActivePlugins() as $plugin) {
if ($plugin::siteHomeIsAlwaysPublic()) {
$this->allowNotLoggedIn = true;
}
}
}
return parent::beforeAction($action);
}
public function actionSearch(): ResponseInterface
{
$query = $this->getRequestValue('query');
if (!$query || trim($query) == '') {
$this->getHttpSession()->setFlash('error', \Yii::t('con', 'search_no_query'));
return new RedirectResponse(UrlHelper::createUrl('consultation/index'));
}
if ($this->getPostValue('query')) {
return new RedirectResponse(UrlHelper::createUrl(['consultation/search', 'query' => $query]));
}
$results = $this->consultation->fulltextSearch($query, [
'backTitle' => \Yii::t('con', 'search_results_bc'),
'backUrl' => UrlHelper::createUrl(['consultation/search', 'query' => $query]),
]);
return new HtmlResponse($this->render(
'search_results',
[
'query' => $query,
'results' => $results
]
));
}
public function actionFeeds(int $page = 0): HtmlResponse
{
$this->layout = 'column2';
$this->consultationSidebar($this->consultation);
$form = new ConsultationActivityFilterForm($this->consultation);
$form->setPage(intval($page));
return new HtmlResponse($this->render('feeds', [
'admin' => User::havePrivilege($this->consultation, Privileges::PRIVILEGE_CONTENT_EDIT, null),
]));
}
public function actionFeedmotions(): BinaryFileResponse
{
$newest = MotionRepository::getNewestByConsultation($this->consultation, 20);
$feed = new RSSExporter();
if ($this->consultation->getSettings()->logoUrl) {
$feed->setImage($this->consultation->getSettings()->logoUrl);
} else {
$feed->setImage('/img/logo.png');
}
$feed->setTitle($this->consultation->title . ': ' . \Yii::t('con', 'feed_motions'));
$feed->setBaseLink(UrlHelper::absolutizeLink(UrlHelper::createUrl('consultation/index')));
$feed->setFeedLink(UrlHelper::absolutizeLink(UrlHelper::createUrl('consultation/feedmotions')));
foreach ($newest as $motion) {
$motion->addToFeed($feed);
}
return new BinaryFileResponse(
BinaryFileResponse::TYPE_XML,
$feed->getFeed(),
false,
'feed.xml',
$this->layoutParams->isRobotsIndex($this->action)
);
}
public function actionFeedamendments(): BinaryFileResponse
{
$newest = Amendment::getNewestByConsultation($this->consultation, 20);
$feed = new RSSExporter();
if ($this->consultation->getSettings()->logoUrl) {
$feed->setImage($this->consultation->getSettings()->logoUrl);
} else {
$feed->setImage('/img/logo.png');
}
$feed->setTitle($this->consultation->title . ': ' . \Yii::t('con', 'feed_amendments'));
$feed->setBaseLink(UrlHelper::absolutizeLink(UrlHelper::createUrl('consultation/index')));
$feed->setFeedLink(UrlHelper::absolutizeLink(UrlHelper::createUrl('consultation/feedamendments')));
foreach ($newest as $amend) {
$amend->addToFeed($feed);
}
return new BinaryFileResponse(
BinaryFileResponse::TYPE_XML,
$feed->getFeed(),
false,
'feed.xml',
$this->layoutParams->isRobotsIndex($this->action)
);
}
public function actionFeedcomments(): BinaryFileResponse
{
$newest = IComment::getNewestByConsultation($this->consultation, 20);
$feed = new RSSExporter();
if ($this->consultation->getSettings()->logoUrl) {
$feed->setImage($this->consultation->getSettings()->logoUrl);
} else {
$feed->setImage('/img/logo.png');
}
$feed->setTitle($this->consultation->title . ': ' . \Yii::t('con', 'feed_comments'));
$feed->setBaseLink(UrlHelper::absolutizeLink(UrlHelper::createUrl('consultation/index')));
$feed->setFeedLink(UrlHelper::absolutizeLink(UrlHelper::createUrl('consultation/feedcomments')));
foreach ($newest as $comm) {
$comm->addToFeed($feed);
}
return new BinaryFileResponse(
BinaryFileResponse::TYPE_XML,
$feed->getFeed(),
false,
'feed.xml',
$this->layoutParams->isRobotsIndex($this->action)
);
}
public function actionFeedall(): BinaryFileResponse
{
$items = array_merge(
MotionRepository::getNewestByConsultation($this->consultation, 20),
Amendment::getNewestByConsultation($this->consultation, 20),
MotionComment::getNewestByConsultation($this->consultation, 20),
AmendmentComment::getNewestByConsultation($this->consultation, 20)
);
usort($items, function (IRSSItem $item1, IRSSItem $item2) {
return -1 * Tools::compareSqlTimes($item1->getDate(), $item2->getDate());
});
$items = array_slice($items, 0, 20);
$feed = new RSSExporter();
if ($this->consultation->getSettings()->logoUrl) {
$feed->setImage($this->consultation->getSettings()->logoUrl);
} else {
$feed->setImage('/img/logo.png');
}
$feed->setTitle($this->consultation->title . ': ' . \Yii::t('con', 'feed_all'));
$feed->setBaseLink(UrlHelper::absolutizeLink(UrlHelper::createUrl('consultation/index')));
$feed->setFeedLink(UrlHelper::absolutizeLink(UrlHelper::createUrl('consultation/feedall')));
foreach ($items as $item) {
/** @var IRSSItem $item */
$item->addToFeed($feed);
}
return new BinaryFileResponse(
BinaryFileResponse::TYPE_XML,
$feed->getFeed(),
false,
'feed.xml',
$this->layoutParams->isRobotsIndex($this->action)
);
}
public function actionNotifications(): HtmlResponse
{
$this->forceLogin();
$user = User::getCurrentUser();
$con = $this->consultation;
if ($this->isPostSet('save')) {
$newNotis = $this->getPostValue('notifications', []);
if (isset($newNotis['motion'])) {
UserNotification::addNotification($user, $con, UserNotification::NOTIFICATION_NEW_MOTION);
} else {
UserNotification::removeNotification($user, $con, UserNotification::NOTIFICATION_NEW_MOTION);
}
if (isset($newNotis['amendment'])) {
if (isset($newNotis['amendmentsettings']) && $newNotis['amendmentsettings'] == 1) {
UserNotification::addNotification($user, $con, UserNotification::NOTIFICATION_NEW_AMENDMENT);
} else {
UserNotification::addNotification($user, $con, UserNotification::NOTIFICATION_AMENDMENT_MY_MOTION);
}
} else {
UserNotification::removeNotification($user, $con, UserNotification::NOTIFICATION_NEW_AMENDMENT);
UserNotification::removeNotification($user, $con, UserNotification::NOTIFICATION_AMENDMENT_MY_MOTION);
}
if (isset($newNotis['comment'])) {
if (isset($newNotis['commentsetting'])) {
$commentSetting = IntVal($newNotis['commentsetting']);
} else {
$commentSetting = UserNotification::COMMENT_SETTINGS[0];
}
UserNotification::addCommentNotification($user, $con, $commentSetting);
} else {
UserNotification::removeNotification($user, $con, UserNotification::NOTIFICATION_NEW_COMMENT);
}
$this->getHttpSession()->setFlash('success', \Yii::t('base', 'saved'));
}
$notifications = UserNotification::getUserConsultationNotis($user, $this->consultation);
return new HtmlResponse($this->render('user_notifications', ['user' => $user, 'notifications' => $notifications]));
}
private function consultationSidebar(Consultation $consultation): void
{
$newestAmendments = Amendment::getNewestByConsultation($consultation, 5);
$newestMotions = MotionRepository::getNewestByConsultation($consultation, 3);
$newestComments = IComment::getNewestByConsultation($consultation, 3);
$this->renderPartial(
$consultation->getSettings()->getConsultationSidebar(),
[
'newestMotions' => $newestMotions,
'newestAmendments' => $newestAmendments,
'newestComments' => $newestComments,
]
);
}
public function actionHome(): ResponseInterface
{
foreach (AntragsgruenApp::getActivePlugins() as $plugin) {
if ($plugin::hasSiteHomePage()) {
return $plugin::getSiteHomePage();
}
}
return $this->actionIndex();
}
public function actionMotions(): ResponseInterface
{
return new HtmlResponse($this->render('motions', []));
}
public function actionResolutions(): ResponseInterface
{
return new HtmlResponse($this->render('resolutions', []));
}
public function actionIndex(): ResponseInterface
{
if (!$this->consultation) {
return new HtmlErrorResponse(500, 'The page was not found. This might be due to a misconfiguration of the installation.');
}
if ($this->consultation->getSettings()->maintenanceMode && !User::havePrivilege($this->consultation, Privileges::PRIVILEGE_CONSULTATION_SETTINGS, null)) {
return $this->renderContentPage('maintenance');
}
foreach (AntragsgruenApp::getActivePlugins() as $plugin) {
$pluginHome = $plugin::getConsultationHomePage($this->consultation);
if ($pluginHome !== null) {
return $pluginHome;
}
}
if ($this->consultation->getForcedMotion()) {
return new RedirectResponse(UrlHelper::createMotionUrl($this->consultation->getForcedMotion()));
}
$cache = LayoutHelper::getHomePageCache($this->consultation);
if ($cache->isSkipCache() || !$cache->cacheIsFilled()) {
$this->consultation->preloadAllMotionData(Consultation::PRELOAD_ONLY_AMENDMENTS);
}
$this->layout = 'column2';
$this->consultationSidebar($this->consultation);
$myself = User::getCurrentUser();
return new HtmlResponse($this->render('index', [
'cache' => $cache,
'consultation' => $this->consultation,
'myself' => $myself,
'myMotionComments' => MotionComment::getPrivatelyCommentedByConsultation($myself, $this->consultation),
'myAmendmentComments' => AmendmentComment::getPrivatelyCommentedByConsultation($myself, $this->consultation),
]));
}
public function actionActivitylog(string $page = "0", ?string $showAll = null): ResponseInterface
{
$this->layout = 'column2';
$this->consultationSidebar($this->consultation);
$isUserAdmin = User::havePrivilege($this->consultation, Privileges::PRIVILEGE_CONSULTATION_SETTINGS, null);
$motion = null;
$amendment = null;
$user = null;
$userGroup = null;
$form = new ConsultationActivityFilterForm($this->consultation);
$form->setPage(intval($page));
if ($this->getHttpRequest()->get('amendmentId')) {
$amendment = $this->consultation->getAmendment((int)$this->getHttpRequest()->get('amendmentId'));
if (!$amendment) {
return new HtmlErrorResponse(404, 'Amendment not found');
}
$form->setFilterForAmendmentId($amendment->id);
} elseif ($this->getHttpRequest()->get('motionId')) {
$motion = $this->consultation->getMotion((string)$this->getHttpRequest()->get('motionId'));
if (!$motion) {
return new HtmlErrorResponse(404, 'Motion not found');
}
$form->setFilterForMotionId($motion->id);
} elseif ($isUserAdmin && $this->getHttpRequest()->get('userId')) {
$user = User::getCachedUser((int)$this->getHttpRequest()->get('userId'));
if (!$user) {
return new HtmlErrorResponse(404, 'User not found');
}
$form->setFilterForUserId($user->id);
} elseif ($isUserAdmin && $this->getHttpRequest()->get('userGroupId')) {
$userGroup = $this->consultation->getUserGroupById((int)$this->getHttpRequest()->get('userGroupId'), true);
if (!$userGroup) {
return new HtmlErrorResponse(404, 'User group not found');
}
$form->setFilterForUserGroupId($userGroup->id);
}
$showInvisible = false;
if ($showAll && User::havePrivilege($this->consultation, Privileges::PRIVILEGE_CONTENT_EDIT, null)) {
$form->setShowUserInvisibleEvents(true);
$showInvisible = true;
}
return new HtmlResponse($this->render('activity_log', [
'form' => $form,
'motion' => $motion,
'amendment' => $amendment,
'user' => $user,
'userGroup' => $userGroup,
'showInvisible' => $showInvisible,
]));
}
public function actionProposedProcedure(): HtmlResponse
{
$this->consultation->preloadAllMotionData(Consultation::PRELOAD_ONLY_AMENDMENTS);
$this->layout = 'column1';
$proposalFactory = new Factory($this->consultation, false);
return new HtmlResponse($this->render('proposed_procedure', [
'proposedAgenda' => $proposalFactory->create(),
]));
}
public function actionProposedProcedureRest(): RestApiResponse
{
$this->handleRestHeaders(['GET']);
$this->consultation->preloadAllMotionData(Consultation::PRELOAD_ONLY_AMENDMENTS);
$proposalFactory = new Factory($this->consultation, false);
return new RestApiResponse(200, null, $this->renderPartial('proposed_procedure_rest_get', [
'proposedAgenda' => $proposalFactory->create(),
]));
}
public function actionProposedProcedureAjax(): JsonResponse
{
$proposalFactory = new Factory($this->consultation, false);
$html = $this->renderPartial('_proposed_procedure_content', [
'proposedAgenda' => $proposalFactory->create(),
]);
return new JsonResponse([
'success' => true,
'html' => $html,
'date' => date('H:i:s'),
]);
}
public function actionCollecting(): ResponseInterface
{
if (!$this->consultation->getSettings()->collectingPage) {
return new HtmlErrorResponse(404, 'This site is not available');
}
$this->layout = 'column2';
$this->consultationSidebar($this->consultation);
return new HtmlResponse($this->render('collecting'));
}
public function actionRest(): RestApiResponse
{
$this->handleRestHeaders(['GET']);
$this->consultation->preloadAllMotionData(Consultation::PRELOAD_ONLY_AMENDMENTS);
return new RestApiResponse(200, null, $this->renderPartial('rest_get', ['consultation' => $this->consultation]));
}
public function actionRestSite(): RestApiResponse
{
$this->handleRestHeaders(['GET']);
return new RestApiResponse(200, null, $this->renderPartial('rest_site_get', ['site' => $this->site]));
}
public function actionDebugbarAjax(): JsonResponse
{
switch ($this->getPostValue('action', '')) {
case 'close':
DateTools::setDeadlineDebugMode($this->consultation, false);
return new JsonResponse(['success' => true]);
case 'setTime':
try {
$time = Tools::dateBootstraptime2sql($this->getPostValue('time'));
} catch (Internal $e) {
$time = null;
}
DateTools::setDeadlineTime($this->consultation, $time);
return new JsonResponse(['success' => true]);
default:
return new JsonResponse(['success' => false, 'error' => 'No operation given']);
}
}
public function actionTodo(): HtmlResponse
{
if (!User::havePrivilege($this->consultation, Privileges::PRIVILEGE_ANY, PrivilegeQueryContext::anyRestriction())) {
throw new ResponseException(new HtmlErrorResponse(403, \Yii::t('admin', 'no_access')));
}
$todo = AdminTodoItem::getConsultationTodos($this->consultation, true);
return new HtmlResponse($this->render('todo', ['todo' => $todo]));
}
public function actionTodoCount(): JsonResponse
{
if (!User::havePrivilege($this->consultation, Privileges::PRIVILEGE_ANY, PrivilegeQueryContext::anyRestriction())) {
throw new ResponseException(new HtmlErrorResponse(403, \Yii::t('admin', 'no_access')));
}
$todo = AdminTodoItem::getConsultationTodoCount($this->consultation, false);
return new JsonResponse(['count' => $todo]);
}
private function getUnassignedQueueOrCreate(): SpeechQueue
{
foreach ($this->consultation->speechQueues as $queue) {
if ($queue->motionId === null && $queue->amendmentId === null && $queue->agendaItemId === null) {
return $queue;
}
}
$unassignedQueue = SpeechQueue::createWithSubqueues($this->consultation, false);
$unassignedQueue->save();
return $unassignedQueue;
}
public function actionAdminSpeech(?string $queue = null): ResponseInterface
{
$this->layout = 'column2';
$user = User::getCurrentUser();
if (!$user || !$user->hasPrivilege($this->consultation, Privileges::PRIVILEGE_SPEECH_QUEUES, null)) {
$this->getHttpSession()->setFlash('error', \Yii::t('motion', 'err_edit_permission'));
return new RedirectResponse(UrlHelper::homeUrl());
}
$foundQueue = null;
if ($queue) {
$queueId = intval($queue);
foreach ($this->consultation->speechQueues as $speechQueue) {
if ($speechQueue->id === $queueId) {
$foundQueue = $speechQueue;
}
}
} else {
$foundQueue = $this->getUnassignedQueueOrCreate();
}
if (!$foundQueue) {
return new HtmlErrorResponse(404, 'Speaking list not found');
}
return new HtmlResponse($this->render('@app/views/speech/admin-singlepage', ['queue' => $foundQueue]));
}
public function actionSpeech(): HtmlResponse
{
$this->layout = 'column2';
$queue = null;
foreach ($this->consultation->speechQueues as $speechQueue) {
if ($speechQueue->isActive) {
$queue = $speechQueue;
}
}
if (!$queue) {
$queue = $this->getUnassignedQueueOrCreate();
}
return new HtmlResponse($this->render('@app/views/speech/index-singlepage', ['queue' => $queue]));
}
public function actionAdminVotings(): ResponseInterface
{
$this->layout = 'column2';
$user = User::getCurrentUser();
if (!$user || !$user->hasPrivilege($this->consultation, Privileges::PRIVILEGE_VOTINGS, null)) {
$this->getHttpSession()->setFlash('error', \Yii::t('motion', 'err_edit_permission'));
return new RedirectResponse(UrlHelper::homeUrl());
}
return new HtmlResponse($this->render('@app/views/voting/admin-votings'));
}
public function actionVotings(): ResponseInterface
{
$this->forceLogin();
$this->layout = 'column2';
return new HtmlResponse($this->render('@app/views/voting/votings'));
}
public function actionVotingResults(): ResponseInterface
{
$this->forceLogin();
$this->layout = 'column2';
return new HtmlResponse($this->render('@app/views/voting/voting-results'));
}
private function tagMotionResolutionList(int $tagId, bool $isResolutionList): ResponseInterface
{
$tag = $this->consultation->getTagById($tagId);
if (!$tag) {
return new HtmlErrorResponse(404, 'Tag not found');
}
$myself = User::getCurrentUser();
return new HtmlResponse($this->render('tag_motion_list', [
'tag' => $tag,
'cache' => LayoutHelper::getTagMotionListCache($this->consultation, $tag, $isResolutionList),
'isResolutionList' => $isResolutionList,
'myMotionComments' => MotionComment::getPrivatelyCommentedByConsultation($myself, $this->consultation),
'myAmendmentComments' => AmendmentComment::getPrivatelyCommentedByConsultation($myself, $this->consultation),
]));
}
public function actionTagsMotions(int $tagId): ResponseInterface
{
return $this->tagMotionResolutionList($tagId, false);
}
public function actionTagsResolutions(int $tagId): ResponseInterface
{
return $this->tagMotionResolutionList($tagId, true);
}
}