<?php
namespace Cms\TenantBundle\Controller\Dashboard;
use App\Contracts\Service\CustomServiceSubscriberTrait;
use Cms\ContainerBundle\Controller\DashboardController;
use Cms\CoreBundle\Model\Scenes\DashboardScenes\DocumentScene;
use Cms\CoreBundle\Model\View;
use Cms\CoreBundle\Service\TeamworkPm;
use Cms\CoreBundle\Util\Controller;
use Cms\TenantBundle\Entity\Tenant;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\HttpFoundation\RedirectResponse;
/**
* Class ProcessController
* @package Cms\TenantBundle\Controller\Dashboard
*/
final class ProcessController extends Controller
{
const ROUTES__INDEX = 'campussuite.cms.tenant.dashboard.process.index';
const ROUTES__COMMENT = 'campussuite.cms.tenant.dashboard.process.comment';
const ROUTES__CLOSE = 'campussuite.cms.tenant.dashboard.process.close';
const ROUTES__REOPEN = 'campussuite.cms.tenant.dashboard.process.reopen';
const TASKS_PHASES = array(
self::TASKS_PHASES__BUILD,
self::TASKS_PHASES__LAUNCH,
self::TASKS_PHASES__DESIGN,
);
const TASKS_PHASES__BUILD = 'BUILD';
const TASKS_PHASES__LAUNCH = 'LAUNCH';
const TASKS_PHASES__DESIGN = 'DESIGN';
const TASK_STATUSES__NEW = 'new';
const TASK_STATUSES__COMPLETED = 'completed';
const TASK_STATUSES__REOPENED = 'reopened';
const TASK_STATUSES__DELETED = 'deleted';
/**
* @return TeamworkPm|object
*/
private function getTeamworkPm(): TeamworkPm
{
return $this->get(__METHOD__);
}
/**
* @return DocumentScene|View|RedirectResponse
*
* @Route(
* "/",
* name = ProcessController::ROUTES__INDEX
* )
*/
public function indexAction()
{
// AUDIT
$this->denyAccessUnlessGranted('campussuite.cms.process.manage');
// make sure the tenant isn't live yet
if ($this->getGlobalContext()->getTenant()->getStage() === Tenant::STAGE__LIVE) {
return $this->redirectToRoute(DashboardController::ROUTES__INDEX);
}
// initialize holders in proper order
$issues = array(
self::TASKS_PHASES__DESIGN => array(
'icon' => 'paint-brush',
'description' => '
Planning and Assessment: Questionnaire completed, template selected and assets provided (including logo and colors.)
SEO strategy complete and ready for integration into your site architecture.',
'complete' => null,
'steps' => [],
),
self::TASKS_PHASES__BUILD => array(
'icon' => 'wrench',
'description' => '
Strategy and Production: Having the theme selected, your brand incorporated, and the architecture published,
we\'re set to produce pages and create a site that will align your web presence
with your other marketing tactics while maintaining a user-focused web experience.',
'complete' => null,
'steps' => [],
),
self::TASKS_PHASES__LAUNCH => array(
'icon' => 'rocket',
'description' => '
The final phase of website development.
SchoolStatus Sites & Apps will provide basic publisher training and prepare your website for deployment
by going through our comprehensive pre- and post-deployment QA checklists.
We\'ll also make sure you have traffic analytics properly installed and tracking.',
'complete' => null,
'steps' => [],
),
);
$tasklist = $this->getTeamworkPm()->getTasklist(
$this->getGlobalContext()->getTenant()->getTeamworkTasklist()
);
if ( ! is_null($tasklist)) {
$tasks = $this->getTeamworkPm()->getAllTasks(
$tasklist['id'],
self::TASKS_PHASES
);
// loop over results and process them appropriately
foreach ($tasks as $task) {
// parse task title to get an estimation
if (preg_match('#\\(([^\\(\\)]*)\\)$#', trim($task['content']), $match)) {
$task['estimation'] = $match[1];
$task['content'] = trim(preg_replace('#(\\([^\\(\\)]+\\))$#', '', $task['content']));
}
// parse task title to get who is responsible
if (preg_match('#^([\w\s]+):\s.*#', trim($task['content']), $match)) {
$task['responsibility'] = $match[1];
$task['content'] = trim(preg_replace('#^([\w\s]+:\s)#', '', $task['content']));
}
foreach ($task['tags'] as $tag) {
$tag = $tag['name'];
// need to check and ensure that tag is one we are looking for,
// might be possible to have tags outside what we expect
if (in_array($tag, self::TASKS_PHASES)) {
// fix completion
$issues[$tag]['complete'] =
(is_null($issues[$tag]['complete']) ?: $issues[$tag]['complete']) && $task['completed'];
$issues[$tag]['steps'][] = $task;
}
}
}
}
return $this->view(
array(
'tasklist' => $tasklist,
'issues' => $issues,
)
);
}
/**
* @param int $taskId
* @return View|RedirectResponse
* @Route(
* "/{taskId}/comment",
* name = ProcessController::ROUTES__COMMENT,
* )
*/
public function commentAction($taskId)
{
// AUDIT
$this->denyAccessUnlessGranted('campussuite.cms.process.manage');
// make sure the tenant isn't live yet
if ($this->getGlobalContext()->getTenant()->getStage() === Tenant::STAGE__LIVE) {
return $this->redirectToRoute(DashboardController::ROUTES__INDEX);
}
// generate form
$form = $this->createFormBuilder(array(), array(
'csrf_protection' => false,
))
->add('body', TextareaType::class)
->getForm();
// check for submission
if ($this->handleForm($form)) {
// add comment
$this->addComment(
$taskId,
$this->getGlobalContext()->getEffectiveAccount()->getEmail(),
$form->getData()['body']
);
// TODO: log
}
// back to index
return $this->redirectToRoute(self::ROUTES__INDEX);
}
/**
* @param int $taskId
* @return View|RedirectResponse
* @throws \Exception
*
* @Route(
* "/{taskId}/close",
* name = ProcessController::ROUTES__CLOSE,
* requirements = {
* "taskId" = "[1-9]\d*"
* }
* )
*/
public function closeAction($taskId)
{
// AUDIT
$this->denyAccessUnlessGranted('campussuite.cms.process.manage');
// make sure the tenant isn't live yet
if ($this->getGlobalContext()->getTenant()->getStage() === Tenant::STAGE__LIVE) {
return $this->redirectToRoute(DashboardController::ROUTES__INDEX);
}
return $this->transitIssue($taskId, self::TASK_STATUSES__COMPLETED);
}
/**
* @param int $taskId
* @return View|RedirectResponse
* @throws \Exception
*
* @Route(
* "/{taskId}/reopen",
* name = ProcessController::ROUTES__REOPEN,
* requirements = {
* "taskId" = "[1-9]\d*"
* }
* )
*/
public function reopenAction($taskId)
{
// AUDIT
$this->denyAccessUnlessGranted('campussuite.cms.process.manage');
// make sure the tenant isn't live yet
if ($this->getGlobalContext()->getTenant()->getStage() === Tenant::STAGE__LIVE) {
return $this->redirectToRoute(DashboardController::ROUTES__INDEX);
}
return $this->transitIssue($taskId, self::TASK_STATUSES__REOPENED);
}
/**
* Provided handling of a transition request
* 1. Sends comment to issue
* 2. Making transition call depending on given $transitionId
*
* @param int $taskId
* @param string $status
* @return RedirectResponse
* @throws \Exception
*/
private function transitIssue($taskId, $status)
{
if ( ! in_array($status, array_keys($this->getTransitTaskCallback()))) {
throw new \Exception('Unexpected transition task status');
}
// make form
$form = $this->createFormBuilder(
[],
array('csrf_protection' => false,)
)
->add('body', TextareaType::class)
->getForm();
// check for submission
if ($this->handleForm($form)) {
// add comment
$this->addComment(
$taskId,
$this->getGlobalContext()->getEffectiveAccount()->getEmail(),
$form->getData()['body']
);
$this->getTeamworkPm()->{$this->getTransitTaskCallback()[$status]}($taskId);
// TODO: log
// back to listing
return $this->redirectToRoute(self::ROUTES__INDEX);
}
// back to index
return $this->redirectToRoute(self::ROUTES__INDEX);
}
/**
* Returns array of available transition statuses for TeamworkPM as keys
* and methods of TeamworkPM service as values
*
* @return array
*/
private function getTransitTaskCallback()
{
return array(
self::TASK_STATUSES__COMPLETED => 'markTaskComplete',
self::TASK_STATUSES__REOPENED => 'markTaskUncomplete',
);
}
/**
* @param int $taskId
* @param string $email
* @param string $body
* @return array
*/
private function addComment($taskId, $email, $body)
{
return $this->getTeamworkPm()->addComment(
$taskId,
array(
'body' => sprintf(
"%s\n\n\n%s",
$email,
$body
)
)
);
}
}