paragraphs_gantt-1.0.x-dev/src/Controller/ParagraphsGanttController.php
src/Controller/ParagraphsGanttController.php
<?php
namespace Drupal\paragraphs_gantt\Controller;
use Drupal\Component\Serialization\Json;
use Drupal\Core\Cache\CacheTagsInvalidatorInterface;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Datetime\DrupalDateTime;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Queue\QueueFactory;
use Drupal\Core\Queue\QueueWorkerManagerInterface;
use Drupal\Core\Url;
use Drupal\paragraphs\Entity\Paragraph;
use Drupal\paragraphs\ParagraphsTypeInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
/**
* Class definition for ComponentFormController.
*/
class ParagraphsGanttController extends ControllerBase {
/**
* Query POST.
*
* @var mixed
*/
protected $post;
public function __construct(protected CacheTagsInvalidatorInterface $cacheTags, protected QueueFactory $queueFactory, protected QueueWorkerManagerInterface $queueWorkerManager) {
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('cache_tags.invalidator'),
$container->get('queue'),
$container->get('plugin.manager.queue_worker'),
);
}
/**
* Add new / Edit / Delete Paragraphs.
*/
public function ajax(Request $request, ParagraphsTypeInterface $paragraph_type, $view_mode, $entity_type, $entity_field, $entity_id) {
$entity = $this->entityTypeManager()
->getStorage($entity_type)
->load($entity_id);
$bundle = $entity->bundle();
$loadViewDisplay = $this->entityTypeManager()
->getStorage('entity_view_display')
->load($entity_type . '.' . $bundle . '.' . $view_mode);
$fieldSetting = $loadViewDisplay->getRenderer($entity_field)->getSettings();
$action = $request->request->get("!nativeeditor_status") ?? FALSE;
$output = [];
if (!$action) {
return new JsonResponse($output);
}
$this->post = $request->request->all();
$mode = $request->query->get('gantt_mode');
if ($mode == 'tasks') {
if (!empty($fieldSetting['action_queue']) && $action == 'updated') {
$output = $this->actionTaskQueue($action, $entity, $paragraph_type, $entity_field, $fieldSetting);
}
else {
$output = $this->actionTask($action, $entity, $paragraph_type, $entity_field, $fieldSetting);
}
}
if ($mode == 'links') {
$output = $this->actionLinks($action, $entity, $paragraph_type, $entity_field, $fieldSetting);
}
return new JsonResponse($output);
}
/**
* Import file MPP.
*
* {@inheritdoc}
*/
public function import(Request $request, ParagraphsTypeInterface $paragraph_type, $view_mode, $entity_type, $entity_field, $entity_id) {
$action = 'inserted';
$entity = $this->entityTypeManager()
->getStorage($entity_type)
->load($entity_id);
$mapping_data = [];
$mapping_link = [];
$bundle = $entity->bundle();
$loadViewDisplay = $this->entityTypeManager()
->getStorage('entity_view_display')
->load($entity_type . '.' . $bundle . '.' . $view_mode);
$fieldSetting = $loadViewDisplay->getRenderer($entity_field)->getSettings();
if (!empty($request->get("data"))) {
$rawData = $request->get("data");
$rawLinks = $request->get("links");
$this->post = [];
foreach ($rawData as $data) {
$start = new DrupalDateTime(preg_replace('/\s*\(.*\)$/', '', $data['start_date']));
$end = new DrupalDateTime(preg_replace('/\s*\(.*\)$/', '', $data['end_date']));
$this->post = [
'text' => $data['text'],
'duration' => $data['duration'],
'progress' => $data['progress'],
'open' => $data['open'],
'parent' => $data['parent'],
'type' => $data['type'] ?? 'task',
'start_date' => $start->format('Y-m-d H:i'),
'end_date' => $end->format('Y-m-d H:i'),
];
if (!empty($mapping_data[$data['parent']]['id'])) {
$this->post['parent'] = $mapping_data[$data['parent']]['id'];
}
$output = $this->actionTask($action, $entity, $paragraph_type, $entity_field, $fieldSetting);
$mapping_data[$data['id']] = ['id' => $output['tid'], 'parent' => $mapping_data[$data['parent']]['id']];
}
foreach ($rawLinks as $data) {
$this->post = [
'source' => $mapping_data[$data['source']]['id'],
'target' => $mapping_data[$data['target']]['id'],
'type' => $data['type'],
'lag' => $data['lag'],
];
$output = $this->actionLinks($action, $entity, $paragraph_type, $entity_field, $fieldSetting);
$mapping_link[$data['id']] = [
'id' => $output['tid'],
'source' => $mapping_data[$data['source']]['id'],
'target' => $mapping_data[$data['target']]['id'],
];
}
}
return new JsonResponse(['data' => $mapping_data, 'links' => $mapping_link]);
}
/**
* Delete paragraphs.
*
* {@inheritDoc}
*/
protected function deleteParagraph($entity, $entity_field, $paragraph) {
$getField = $entity->get($entity_field);
$array_of_referenced_items = $getField->getValue();
$index_to_remove = array_search($this->post['id'], array_column($array_of_referenced_items, 'target_id'));
if (is_numeric($index_to_remove) && $index_to_remove >= 0) {
$getField->removeItem($index_to_remove);
$entity->save();
$paragraph->delete();
$storage = $this->entityTypeManager()
->getStorage($entity->getEntityTypeId());
$storage->resetCache([$entity->id()]);
}
return $paragraph->id();
}
/**
* Update paragraphs.
*
* {@inheritDoc}
*/
protected function updateParagraphOrder($field_settings, $paragraph) {
$managerParagraph = $this->entityTypeManager()->getStorage('paragraph');
$listOrder = Json::decode($this->post['listOrder']);
if (!empty($listOrder)) {
foreach ($listOrder as $item) {
if ($item['id'] == $paragraph->id()) {
continue;
}
$entity_order = $managerParagraph->load($item['id']);
if ($entity_order) {
$entity_order->set($field_settings['order'], $item['order']);
$entity_order->save();
}
}
}
}
/**
* Update schedule planned.
*
* {@inheritDoc}
*/
protected function updateSchedulePlanned($field_settings, $type_date, $time_timezone, $utc_timezone) {
$managerParagraph = $this->entityTypeManager()->getStorage('paragraph');
$listUpdate = Json::decode($this->post['listSchedulePlanned']);
if (!empty($listUpdate)) {
foreach ($listUpdate as $item_planned) {
$entity_update = $managerParagraph->load($item_planned['id']);
if ($entity_update) {
$planned_date = $this->processDate('planned_start', 'planned_end', $type_date, $field_settings, $time_timezone, $utc_timezone, $item_planned['planned_start'], $item_planned['planned_end']);
if ($type_date['start_date_planned']['field_type'] == 'daterange') {
$entity_update->set($field_settings['planned_date'], $planned_date);
}
else {
if (!empty($field_settings['planned_date'])) {
$entity_update->set($field_settings['planned_date'], $planned_date['value']);
}
if (!empty($field_settings['planned_end_date'])) {
$entity_update->set($field_settings['planned_end_date'], $planned_date['end_value']);
}
}
$entity_update->save();
}
}
}
}
/**
* Process Dates.
*
* {@inheritDoc}
*/
protected function processDate($start_key, $end_key, $type_date, $field_settings, $time_timezone, $utc_timezone, $start = NULL, $end = NULL) {
$type_key_start = 'start_date_planned';
$type_key_end = 'end_date_planned';
if ($start_key == 'start_date') {
$type_key_start = 'start_date_actually';
$type_key_end = 'end_date_actually';
}
$dateFormatStart = $dateFormatEnd = 'Y-m-d\TH:i:s';
if ($type_date[$type_key_start]['field_type'] == 'daterange' && $type_date[$type_key_start]['date_type'] == 'date') {
$dateFormatStart = $dateFormatEnd = 'Y-m-d';
}
elseif ($type_date[$type_key_end]['date_type'] == 'date') {
$dateFormatEnd = 'Y-m-d';
}
if (empty($start)) {
$start = $this->post[$start_key] ?? NULL;
}
if (empty($end)) {
$end = $this->post[$end_key] ?? NULL;
}
if (!empty($start) && $start != 'undefined') {
$start_date = new DrupalDateTime($start, $time_timezone);
$end_date = new DrupalDateTime($end, $time_timezone);
if ($type_date[$type_key_start]['field_type'] == 'daterange' && $type_date[$type_key_start]['date_type'] == 'datetime') {
$start_date->setTimezone($utc_timezone);
$end_date->setTimezone($utc_timezone);
}
elseif ($type_date[$type_key_start]['date_type'] == 'datetime') {
$start_date->setTimezone($utc_timezone);
if (!empty($type_date[$type_key_end]['date_type']) && $type_date[$type_key_end]['date_type'] == 'datetime') {
$end_date->setTimezone($utc_timezone);
}
}
$type = $type_date[$type_key_start]["field_type"] == 'daterange' ? $type_date[$type_key_start]["date_type"] : $type_date[$type_key_end]["date_type"];
if ($field_settings['last_of_the_day'] && $type == 'date') {
$end_date->modify("-1 day");
}
return [
'value' => $start_date->format($dateFormatStart),
'end_value' => $end_date->format($dateFormatEnd),
];
}
return [
'value' => NULL,
'end_value' => NULL,
];
}
/**
* Process Task.
*/
protected function actionTask($action, $entity, $paragraph_type, $entity_field, $field_settings) {
$output = ['action' => $action];
if (!empty($this->post['isImport'])) {
return $output;
}
$paragraph = $action == 'inserted'
? Paragraph::create(['type' => $paragraph_type->id()])
: Paragraph::load($this->post["id"]);
if (empty($paragraph)) {
return $output;
}
if ($action == 'deleted') {
$output['tid'] = $this->deleteParagraph($entity, $entity_field, $paragraph);
return $output;
}
$this->setFieldValue($paragraph, $field_settings, $action);
if ($action == 'inserted') {
$paragraph->isNew();
}
$paragraph->save();
$output['tid'] = $paragraph->id();
if ($action == 'inserted') {
$destination = $this->post['destination'] ?? '';
$destination = str_replace('destination=', '', $destination);
$destination = ['destination' => $destination];
$output["link_detail"] = Url::fromRoute('paragraphs_gantt.edit', ['paragraph' => $paragraph->id()])
->setOptions(['query' => $destination])->toString();
$entity->get($entity_field)->appendItem([
'target_id' => $paragraph->id(),
'target_revision_id' => $paragraph->getRevisionId(),
]);
$entity->save();
}
else {
$this->cacheTags->invalidateTags($entity->getCacheTags());
}
return $output;
}
/**
* {@inheritDoc}
*/
protected function actionLinks(string $action, EntityInterface $entity, ParagraphsTypeInterface $paragraph_type, string $entity_field, array $field_settings) {
$output = ['action' => $action];
$source = !empty($this->post['source']) ? $this->post['source'] : FALSE;
$target = !empty($this->post['target']) ? $this->post['target'] : FALSE;
$type = !empty($this->post['type']) ? $this->post['type'] : '0';
if ($action == 'deleted') {
[$source, $target] = str_contains($this->post['id'], '-')
? explode('-', $this->post['id'])
: [$this->post['id'], NULL];
}
$paragraph = Paragraph::load($source);
if (empty($paragraph)) {
return $output;
}
// Schedule planned.
if (!empty($this->post['listSchedulePlanned'])) {
$fieldDefinitions = $paragraph->getFieldDefinitions();
// Timezone current user.
$user_timezone = $this->currentUser()->getTimeZone() ?? "UTC";
$time_timezone = new \DateTimeZone($user_timezone);
$utc_timezone = $user_timezone == 'UTC' ? $time_timezone : new \DateTimeZone('UTC');
// Type date actually, planned.
$type_date = $this->getTypeDateField($fieldDefinitions, $field_settings);
// Set plan date.
$this->updateSchedulePlanned($field_settings, $type_date, $time_timezone, $utc_timezone);
}
$linkValues = [];
if (!$paragraph?->get($field_settings['links'])?->isEmpty()) {
$linkValues = $paragraph->get($field_settings['links'])->getValue();
}
$lag = !empty($this->post['lag']) ? $this->post['lag'] : NULL;
if ($action == 'inserted' || !empty($this->post['isImport'])) {
$output['tid'] = "$source-$target";
if ($paragraph->getFieldDefinition($field_settings['links']) && $paragraph->getFieldDefinition($field_settings['links'])->getType() == 'triples_field') {
$linkValues[] = ['first' => $target, 'second' => $type, 'third' => $lag];
}
else {
$linkValues[] = ['first' => $target, 'second' => $type];
}
}
if ($action == 'updated') {
foreach ($linkValues as $delta => $linkValue) {
if ($linkValue['first'] == $target) {
if ($paragraph->getFieldDefinition($field_settings['links']) && $paragraph->getFieldDefinition($field_settings['links'])->getType() == 'triples_field') {
$linkValues[$delta] = [
'first' => $target,
'second' => $type,
'third' => $lag,
];
}
else {
$linkValues[$delta] = [
'first' => $target,
'second' => $type,
];
}
}
}
}
if ($action == 'deleted') {
foreach ($linkValues as $delta => $linkValue) {
if ($linkValue['first'] == $target) {
unset($linkValues[$delta]);
}
}
}
$paragraph->set($field_settings['links'], $linkValues);
$paragraph->save();
$this->cacheTags->invalidateTags($entity->getCacheTags());
return $output;
}
/**
* {@inheritDoc}
*/
public function getTypeDateField($fieldDefinitions, $setting) {
// Get type date field.
$type_date = [
'start_date_actually' => ['date_type' => '', 'field_type' => ''],
'end_date_actually' => ['date_type' => '', 'field_type' => ''],
'start_date_planned' => ['date_type' => '', 'field_type' => ''],
'end_date_planned' => ['date_type' => '', 'field_type' => ''],
];
$populateDateType = function ($settingKey, $typeKey) use (&$type_date, $fieldDefinitions, $setting) {
if (!empty($setting[$settingKey]) && !empty($fieldDefinitions[$setting[$settingKey]])) {
$dateSettings = $fieldDefinitions[$setting[$settingKey]]->getSettings();
$type_date[$typeKey]['date_type'] = $dateSettings["datetime_type"];
$type_date[$typeKey]['field_type'] = $fieldDefinitions[$setting[$settingKey]]->getType();
}
};
$populateDateType('start_date', 'start_date_actually');
$populateDateType('end_date', 'end_date_actually');
$populateDateType('planned_date', 'start_date_planned');
$populateDateType('planned_end_date', 'end_date_planned');
return $type_date;
}
/**
* {@inheritDoc}
*/
protected function actionTaskQueue($action, $entity, $paragraph_type, $entity_field, $field_settings) {
$output = ['action' => $action];
$queue = $this->queueFactory->get('queue_task_paragraphs_gantt');
$queue_worker = $this->queueWorkerManager->createInstance('queue_task_paragraphs_gantt');
$data = [
'current_user' => $this->currentUser()->id(),
'data' => $this->post,
'action' => $action,
'entity' => $entity,
'paragraph_type' => $paragraph_type,
'entity_field' => $entity_field,
'field_settings' => $field_settings,
];
if ($queue->createItem($data)) {
$output['tid'] = $this->post['tid'];
}
while ($item = $queue->claimItem()) {
$queue_worker->processItem($item->data);
$queue->deleteItem($item);
}
return $output;
}
/**
* {@inheritDoc}
*/
protected function setFieldValue(&$paragraph, $field_settings, $action) {
$fieldDefinitions = $paragraph->getFieldDefinitions();
// Timezone current user.
$user_timezone = $this->currentUser()->getTimeZone() ?? "UTC";
$time_timezone = new \DateTimeZone($user_timezone);
$utc_timezone = $user_timezone == 'UTC' ? $time_timezone : new \DateTimeZone('UTC');
// Type date actually, planned.
$type_date = $this->getTypeDateField($fieldDefinitions, $field_settings);
// Set start date.
$actual_date = $this->processDate('start_date', 'end_date', $type_date, $field_settings, $time_timezone, $utc_timezone);
// Set plan date.
$planned_date = $this->processDate('planned_start', 'planned_end', $type_date, $field_settings, $time_timezone, $utc_timezone);
// Set actual date and plan date.
if (!empty($actual_date)) {
if ($type_date['start_date_actually']['field_type'] == 'daterange') {
$paragraph->set($field_settings['start_date'], $actual_date);
}
else {
if (!empty($field_settings['start_date'])) {
$paragraph->set($field_settings['start_date'], $actual_date['value']);
}
if (!empty($field_settings['end_date'])) {
$paragraph->set($field_settings['end_date'], $actual_date['end_value']);
}
}
if (!empty($field_settings['planned_date'])) {
if ($type_date['start_date_planned']['field_type'] == 'daterange') {
$paragraph->set($field_settings['planned_date'], $planned_date);
}
else {
if (!empty($field_settings['planned_date'])) {
$paragraph->set($field_settings['planned_date'], $planned_date['value']);
}
if (!empty($field_settings['planned_end_date'])) {
$paragraph->set($field_settings['planned_end_date'], $planned_date['end_value']);
}
}
}
}
// Set field option.
$options = [
'type',
'duration',
'progress',
'priority',
'planned_duration',
'custom_field',
];
foreach ($options as $option) {
$value = $this->post[$option] ?? NULL;
if (!empty($field_settings[$option]) && !empty($fieldDefinitions[$field_settings[$option]])) {
$paragraph->set($field_settings[$option], $value);
}
}
// Set text.
if (!empty($field_settings['text']) && !empty($fieldDefinitions[$field_settings['text']])) {
$paragraph->set($field_settings['text'], $this->post['text'] ?? 'Undefined');
}
// Set parent.
if (!empty($field_settings['parent']) && !empty($fieldDefinitions[$field_settings['parent']])) {
$paragraph->set($field_settings['parent'], $this->post['parent'] ?? NULL);
}
// Set open.
if (!empty($field_settings['open']) && !empty($fieldDefinitions[$field_settings['open']])) {
$paragraph->set($field_settings['open'], $this->post['open'] ?? 0);
}
// Set order.
if (!empty($field_settings['order']) && !empty($fieldDefinitions[$field_settings['order']])) {
$paragraph->set($field_settings['order'], $this->post['order'] ?? 0);
if ($action == 'order' && !empty($this->post['listOrder'])) {
$this->updateParagraphOrder($field_settings, $paragraph);
}
}
// Set resource.
if (!empty($field_settings["custom_resource"])) {
foreach ($field_settings["custom_resource"] as $field_resource) {
if ($field_settings["creator"] == $field_resource || empty($fieldDefinitions[$field_resource])) {
continue;
}
$resource = [];
$this->post[$field_resource] = !empty($this->post[$field_resource]) ? json_decode($this->post[$field_resource]) : [];
if (is_array($this->post[$field_resource])) {
foreach ($this->post[$field_resource] as $resource_id) {
if (!empty($resource_id)) {
$resource[] = ['target_id' => $resource_id];
}
}
}
else {
$resource = [['target_id' => $this->post[$field_resource]]];
}
$paragraph->set($field_resource, $resource);
}
}
// Set creator.
if (!empty($field_settings["creator"]) && $action == 'inserted' && !empty($fieldDefinitions[$field_settings['creator']])) {
$paragraph->set($field_settings["creator"], $this->currentUser()->id());
}
// Set constraint.
if (!empty($field_settings["constraint"]) && !empty($this->post['constraint_type']) && !empty($fieldDefinitions[$field_settings['constraint']])) {
$paragraph->set($field_settings["constraint"], [
'first' => $this->post['constraint_type'] ?? NULL,
'second' => $this->post['constraint_date'] ?? NULL,
]);
}
// Update schedule planned.
if (!empty($this->post['listSchedulePlanned'])) {
$this->updateSchedulePlanned($field_settings, $type_date, $time_timezone, $utc_timezone);
}
}
}
