<?php
namespace ApplicationBundle\Modules\Notice\Controller;
use ApplicationBundle\Controller\GenericController;
use ApplicationBundle\Interfaces\SessionCheckInterface;
use ApplicationBundle\Modules\Authentication\Constants\UserConstants;
use ApplicationBundle\Modules\Notice\Service\NoticeService;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\JsonResponse;
/**
* Notice / Announcement module — cp-shell admin panel + user-facing board + JSON feed
* for the header bell / dashboard widget / post-login banner. Regular authenticated
* access (SessionCheckInterface), NOT SystemInterface, so it never triggers the
* super-admin / subscription gate.
*/
class NoticeController extends GenericController implements SessionCheckInterface
{
/** Resolve the caller's tenant + employee context. */
private function ctx(Request $request)
{
$session = $request->getSession();
$appId = (int) $this->getLoggedUserAppId($request);
$companyId = (int) $this->getLoggedUserCompanyId($request);
$userId = (int) $session->get(UserConstants::USER_ID, 0);
$loginId = (int) $session->get(UserConstants::USER_LOGIN_ID, 0);
$employeeId = (int) $session->get(UserConstants::USER_EMPLOYEE_ID, 0);
$deptId = 0; $desigId = 0;
if ($employeeId) {
try {
$emp = $this->getDoctrine()->getManager()
->getRepository('ApplicationBundle\\Entity\\Employee')->find($employeeId);
if ($emp) {
$deptId = (int) $emp->getDepartmentId();
$desigId = (int) $emp->getPositionId();
}
} catch (\Throwable $e) { /* ignore */ }
}
return compact('appId', 'companyId', 'userId', 'loginId', 'employeeId', 'deptId', 'desigId');
}
/* ---------------------------------------------------------- admin panel */
public function adminListAction(Request $request)
{
$c = $this->ctx($request);
$em = $this->getDoctrine()->getManager();
$filters = [
'status' => $request->query->get('status', ''),
'type' => $request->query->get('type', ''),
];
$notices = NoticeService::listForAdmin($em, $c['appId'], $filters);
return $this->render('@Notice/pages/admin_list.html.twig', [
'page_title' => 'Notices',
'notices' => $notices,
'filters' => $filters,
'active' => 'list',
]);
}
public function formAction(Request $request, $id = 0)
{
$c = $this->ctx($request);
$em = $this->getDoctrine()->getManager();
$notice = $id ? NoticeService::find($em, $id, $c['appId']) : null;
if ($id && !$notice) {
return $this->redirectToRoute('notice_admin_list');
}
return $this->render('@Notice/pages/notice_form.html.twig', [
'page_title' => $id ? 'Edit Notice' : 'New Notice',
'notice' => $notice,
'types' => NoticeService::TYPES,
'audiences' => NoticeService::AUDIENCES,
'priorities' => NoticeService::PRIORITIES,
'active' => 'new',
]);
}
public function saveAction(Request $request)
{
$c = $this->ctx($request);
$em = $this->getDoctrine()->getManager();
$data = [
'id' => $request->request->get('id', 0),
'title' => $request->request->get('title', ''),
'body' => $request->request->get('body', ''),
'type' => $request->request->get('type', 'general'),
'audience' => $request->request->get('audience', 'all'),
'audienceRefId' => $request->request->get('audienceRefId', 0),
'priority' => $request->request->get('priority', 'normal'),
'pinned' => $request->request->get('pinned', 0),
'requireAck' => $request->request->get('requireAck', 0),
'effectiveFrom' => $request->request->get('effectiveFrom', ''),
'effectiveTo' => $request->request->get('effectiveTo', ''),
'status' => $request->request->get('status', 'draft'),
];
if (trim($data['title']) === '') {
$this->addFlash('error', 'Notice title is required.');
return $this->redirectToRoute('notice_new');
}
$n = NoticeService::save($em, $data, $c['appId'], $c['companyId'], $c['loginId']);
$this->addFlash('success', 'Notice saved.');
return $this->redirectToRoute('notice_edit', ['id' => $n->getId()]);
}
public function setStatusAction(Request $request)
{
$c = $this->ctx($request);
$em = $this->getDoctrine()->getManager();
$ok = NoticeService::setStatus($em, $request->request->get('id', 0), $c['appId'], $request->request->get('status', ''));
return new JsonResponse(['success' => $ok]);
}
public function deleteAction(Request $request)
{
$c = $this->ctx($request);
$em = $this->getDoctrine()->getManager();
$ok = NoticeService::softDelete($em, $request->request->get('id', 0), $c['appId']);
return new JsonResponse(['success' => $ok]);
}
public function ackRosterAction(Request $request, $id)
{
$c = $this->ctx($request);
$em = $this->getDoctrine()->getManager();
$notice = NoticeService::find($em, $id, $c['appId']);
$roster = $notice ? NoticeService::acknowledgementRoster($em, $c['appId'], $id) : [];
return $this->render('@Notice/pages/ack_roster.html.twig', [
'page_title' => 'Acknowledgements',
'notice' => $notice,
'roster' => $roster,
'active' => 'list',
]);
}
/* ------------------------------------------------------- user-facing */
public function boardAction(Request $request)
{
$c = $this->ctx($request);
$em = $this->getDoctrine()->getManager();
$notices = NoticeService::getActiveNoticesForUser($em, $c['appId'], $c['companyId'], $c['employeeId'], $c['deptId'], $c['desigId']);
$seen = array_flip(NoticeService::seenNoticeIds($em, $c['appId'], $c['userId']));
$acked = array_flip(NoticeService::acknowledgedNoticeIds($em, $c['appId'], $c['userId']));
$pendingAck = 0;
foreach ($notices as $n) {
if ($n['id'] > 0 && !empty($n['requireAck']) && !isset($acked[$n['id']])) {
$pendingAck++;
}
}
return $this->render('@Notice/pages/board.html.twig', [
'page_title' => 'Notice Board',
'notices' => $notices,
'seenIds' => $seen,
'ackedIds' => $acked,
'pendingAck' => $pendingAck,
'active' => 'board',
]);
}
/* ------------------------------------------------------- JSON feed */
public function feedApiAction(Request $request)
{
$c = $this->ctx($request);
$em = $this->getDoctrine()->getManager();
$notices = NoticeService::getActiveNoticesForUser($em, $c['appId'], $c['companyId'], $c['employeeId'], $c['deptId'], $c['desigId']);
$seen = array_flip(NoticeService::seenNoticeIds($em, $c['appId'], $c['userId']));
$acked = array_flip(NoticeService::acknowledgedNoticeIds($em, $c['appId'], $c['userId']));
$out = [];
foreach ($notices as $n) {
$out[] = [
'id' => $n['id'],
'title' => $n['title'],
'body' => mb_substr(strip_tags((string) $n['body']), 0, 240),
'type' => $n['type'],
'priority' => $n['priority'],
'pinned' => $n['pinned'],
'requireAck' => $n['requireAck'],
'source' => $n['source'],
'date' => $n['date'] ? $n['date']->format('Y-m-d') : '',
'unread' => ($n['id'] > 0 && !isset($seen[$n['id']])) ? 1 : 0,
'acked' => ($n['id'] > 0 && isset($acked[$n['id']])) ? 1 : 0,
];
}
return new JsonResponse(['success' => true, 'notices' => $out]);
}
public function countApiAction(Request $request)
{
$c = $this->ctx($request);
$em = $this->getDoctrine()->getManager();
$counts = NoticeService::attentionCounts($em, $c['appId'], $c['companyId'], $c['userId'], $c['employeeId'], $c['deptId'], $c['desigId']);
return new JsonResponse([
'success' => true,
'count' => $counts['unread'], // badge: unread only — reading a notice clears it
'unread' => $counts['unread'],
'ackPending' => $counts['ackPending'],
]);
}
public function markSeenAction(Request $request)
{
$c = $this->ctx($request);
$em = $this->getDoctrine()->getManager();
$ids = $request->request->get('ids', $request->request->get('id', ''));
if (!is_array($ids)) $ids = array_filter(array_map('trim', explode(',', (string) $ids)));
foreach ($ids as $nid) {
NoticeService::markSeen($em, $c['appId'], (int) $nid, $c['userId'], $c['employeeId']);
}
return new JsonResponse(['success' => true]);
}
public function ackAction(Request $request)
{
$c = $this->ctx($request);
$em = $this->getDoctrine()->getManager();
$ok = NoticeService::markAcknowledged($em, $c['appId'], $request->request->get('id', 0), $c['userId'], $c['employeeId']);
return new JsonResponse(['success' => $ok]);
}
}