Skip to content

Commit bf9b32c

Browse files
Merge pull request #715 from City-of-Helsinki/ASU-1733-delete-application
Asu 1733 delete application
2 parents a94c77d + 745bd0c commit bf9b32c

10 files changed

Lines changed: 229 additions & 12 deletions

File tree

public/modules/custom/asu_api/src/Api/BackendApi/BackendApi.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use Drupal\Core\TempStore\PrivateTempStore;
66
use Drupal\Core\TempStore\PrivateTempStoreFactory;
77
use Drupal\asu_api\Api\BackendApi\Request\AuthenticationRequest;
8+
use Drupal\asu_api\Api\BackendApi\Request\DeleteApplicationRequest;
89
use Drupal\asu_api\Api\Request;
910
use Drupal\asu_api\Api\Response;
1011
use Drupal\asu_api\Exception\IllegalApplicationException;
@@ -63,6 +64,12 @@ public function __construct(Client $client, LoggerInterface $logger, PrivateTemp
6364
$this->store = $storeFactory->get('customer');
6465
}
6566

67+
public function deleteApplication(UserInterface $sender, string $applicationId): void {
68+
$request = new DeleteApplicationRequest($sender, $applicationId);
69+
$this->send($request);
70+
}
71+
72+
6673
/**
6774
* Send request.
6875
*
@@ -79,6 +86,7 @@ public function __construct(Client $client, LoggerInterface $logger, PrivateTemp
7986
*/
8087
public function send(Request $request, array $options = []): ?Response {
8188
$options['headers'] = $options['headers'] ?? [];
89+
$sender = $request->getSender();
8290
if ($request->requiresAuthentication()) {
8391
if ($token = $this->handleAuthentication($request->getSender())) {
8492
$options['headers']['Authorization'] = sprintf("Bearer %s", $token);
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?php
2+
3+
namespace Drupal\asu_api\Api\BackendApi\Request;
4+
5+
use Drupal\asu_api\Api\Request;
6+
use Drupal\asu_api\Api\BackendApi\Response\DeleteApplicationResponse;
7+
use Drupal\user\UserInterface;
8+
use GuzzleHttp\Exception\GuzzleException;
9+
use GuzzleHttp\Client;
10+
use Psr\Http\Message\ResponseInterface;
11+
12+
class DeleteApplicationRequest extends Request {
13+
14+
protected const AUTHENTICATED = TRUE;
15+
protected string $method = 'DELETE';
16+
17+
protected string $applicationId;
18+
protected array $payload;
19+
20+
public function __construct(
21+
?UserInterface $sender,
22+
string $applicationId,
23+
array $payload = []
24+
) {
25+
if ($sender) {
26+
$this->setSender($sender);
27+
}
28+
$this->applicationId = $applicationId;
29+
$this->payload = $payload ?: [
30+
'comment' => 'Cancelled by user',
31+
'cancellation_reason' => 'terminated',
32+
];
33+
}
34+
35+
public function getPath(): string {
36+
return "/v1/applications/delete/{$this->applicationId}/";
37+
}
38+
39+
public static function getResponse(ResponseInterface $response): DeleteApplicationResponse {
40+
return DeleteApplicationResponse::createFromHttpResponse($response);
41+
}
42+
43+
public function getPayload(): array {
44+
return $this->payload;
45+
}
46+
47+
public function toArray(): array {
48+
return $this->getPayload();
49+
}
50+
51+
public function getMethod(): string {
52+
return 'DELETE';
53+
}
54+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
namespace Drupal\asu_api\Api\BackendApi\Response;
4+
5+
use Drupal\asu_api\Api\Response;
6+
use Psr\Http\Message\ResponseInterface;
7+
8+
/**
9+
* Response for DeleteApplicationRequest.
10+
*/
11+
class DeleteApplicationResponse extends Response {
12+
13+
/**
14+
* Create a new response from HTTP response.
15+
*/
16+
public static function createFromHttpResponse(ResponseInterface $response): self {
17+
return new self($response);
18+
}
19+
20+
}

public/modules/custom/asu_application/asu_application.install

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,3 +194,17 @@ function asu_application_update_9009() : void {
194194
->condition('field_locked', 1)
195195
->execute();
196196
}
197+
198+
/**
199+
* Add backend_id field to asu_application entity.
200+
*/
201+
function asu_application_update_9011() {
202+
$field = BaseFieldDefinition::create('string')
203+
->setLabel(t('Backend application ID'))
204+
->setDescription(t('UUID returned from Django backend when creating application.'))
205+
->setRequired(FALSE)
206+
->setReadOnly(FALSE);
207+
208+
$definition_update_manager = \Drupal::entityDefinitionUpdateManager();
209+
$definition_update_manager->installFieldStorageDefinition('field_backend_application_id', 'asu_application', 'asu_application', $field);
210+
}

public/modules/custom/asu_application/asu_application.module

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ function template_preprocess_asu_application(array &$variables) {
130130
$application_teaser_values = [
131131
'#teaser_values' => [
132132
'application_id' => $application->id->value,
133+
'backend_application_id' => $application->get('field_backend_id')->value,
133134
'project_uuid' => $project->uuid(),
134135
'language' => \Drupal::languageManager()->getCurrentLanguage()->getId(),
135136
'project_name' => $project->field_housing_company->value,
@@ -291,3 +292,22 @@ function asu_application_send_apply_for_free_apartment(array &$message, array $p
291292
$message['subject'] = $params['subject'];
292293
$message['body'][] = $params['message'];
293294
}
295+
296+
297+
/**
298+
* Implements hook_preprocess_HOOK() for asu_application templates.
299+
*/
300+
function asu_application_preprocess_asu_application(array &$variables) {
301+
$view_mode = $variables['elements']['#view_mode'] ?? NULL;
302+
if ($view_mode !== 'submitted_teaser') {
303+
return;
304+
}
305+
306+
$teaser_values = &$variables['content']['#teaser_values'];
307+
308+
if (!empty($teaser_values['application_id'])) {
309+
$app_id = $teaser_values['application_id'];
310+
$token = \Drupal::service('csrf_token')->get("delete_application_{$app_id}");
311+
$teaser_values['delete_url'] = "/asu/application/delete/{$app_id}/{$token}";
312+
}
313+
}
Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,42 @@
11
asu_application.admin_create_application:
2-
path: '/application/create/{user_id}'
2+
path: "/application/create/{user_id}"
33
defaults:
4-
_title: 'Create application'
4+
_title: "Create application"
55
_form: '\Drupal\asu_application\Form\SalespersonApplicationForm'
66
requirements:
7-
_permission: 'administer applications'
7+
_permission: "administer applications"
88

99
asu_application.application_results:
10-
path: '/user/application/results'
10+
path: "/user/application/results"
1111
defaults:
1212
_controller: '\Drupal\asu_application\Controller\ResultController::getResults'
1313
methods: [POST]
1414
requirements:
15-
_permission: 'create application'
15+
_permission: "create application"
1616

1717
asu_application.lottery_start:
18-
path: '/lottery/start/{project_uuid}'
18+
path: "/lottery/start/{project_uuid}"
1919
defaults:
2020
_controller: '\Drupal\asu_application\Controller\ResultController::startLottery'
2121
methods: [GET]
2222
requirements:
23-
_permission: 'administer applications'
23+
_permission: "administer applications"
2424

2525
asu_application.apply_for_free_apartment:
26-
path: '/contact/apply_for_free_apartment'
26+
path: "/contact/apply_for_free_apartment"
2727
defaults:
2828
_form: 'Drupal\asu_application\Form\ReservedApartmentContactForm'
29-
_title: 'Apply for an apartment'
29+
_title: "Apply for an apartment"
3030
requirements:
31-
_permission: 'access content'
31+
_permission: "access content"
32+
33+
asu_application.delete_secure:
34+
path: "/asu/application/delete/{application}/{token}"
35+
defaults:
36+
_controller: '\Drupal\asu_application\Controller\AsuApplicationDeleteController::delete'
37+
_title: "Delete application"
38+
requirements:
39+
_permission: "access content"
40+
application: '\d+'
41+
token: "[A-Za-z0-9-_]+"
42+
methods: ["POST"]
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<?php
2+
3+
namespace Drupal\asu_application\Controller;
4+
5+
use Drupal\Core\Controller\ControllerBase;
6+
use Drupal\Core\Entity\EntityTypeManagerInterface;
7+
use Symfony\Component\HttpFoundation\Request;
8+
use Symfony\Component\HttpFoundation\RedirectResponse;
9+
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
10+
use Symfony\Component\DependencyInjection\ContainerInterface;
11+
use Drupal\asu_api\Api\BackendApi\BackendApi;
12+
use Drupal\Core\Session\AccountProxyInterface;
13+
14+
class AsuApplicationDeleteController extends ControllerBase {
15+
16+
protected $entityTypeManager;
17+
protected BackendApi $backendApi;
18+
protected $currentUser;
19+
20+
public function __construct(
21+
EntityTypeManagerInterface $entityTypeManager,
22+
BackendApi $backendApi,
23+
AccountProxyInterface $currentUser
24+
) {
25+
$this->entityTypeManager = $entityTypeManager;
26+
$this->backendApi = $backendApi;
27+
$this->currentUser = $currentUser;
28+
}
29+
30+
public static function create(ContainerInterface $container): self {
31+
return new static(
32+
$container->get('entity_type.manager'),
33+
$container->get('asu_api.backendapi'),
34+
$container->get('current_user')
35+
);
36+
}
37+
38+
public function delete($application, $token): RedirectResponse {
39+
40+
$storage = $this->entityTypeManager->getStorage('asu_application');
41+
$entity = $storage->load($application);
42+
foreach ($entity->getFields() as $name => $field) {
43+
$value = $field->isEmpty() ? 'EMPTY' : $field->first()->getValue();
44+
}
45+
if (!$entity || $entity->getOwnerId() !== $this->currentUser->id()) {
46+
$this->messenger()->addError($this->t('Application not found or access denied.'));
47+
return new RedirectResponse('/user/applications');
48+
}
49+
50+
$externalId = $entity->get('field_backend_id')->value;
51+
if (!$externalId) {
52+
$this->messenger()->addError($this->t('Missing application ID.'));
53+
return new RedirectResponse('/user/applications');
54+
}
55+
56+
$sender = \Drupal::entityTypeManager()->getStorage('user')->load($this->currentUser->id());
57+
58+
try {
59+
$user = \Drupal::entityTypeManager()->getStorage('user')->load($this->currentUser->id());
60+
$this->backendApi->deleteApplication($user, $externalId);
61+
}
62+
catch (\Exception $e) {
63+
$this->messenger()->addError($this->t('Failed to delete application from backend.'));
64+
return new RedirectResponse('/user/applications');
65+
}
66+
67+
// Удаление из Drupal
68+
$entity->delete();
69+
70+
$this->messenger()->addStatus($this->t('Your application has been successfully deleted.'));
71+
return new RedirectResponse('/user/applications');
72+
}
73+
}

public/modules/custom/asu_application/src/Entity/Application.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,12 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
274274
'settings' => [],
275275
]);
276276

277+
$fields['field_backend_id'] = BaseFieldDefinition::create('string')
278+
->setLabel(t('Backend application ID'))
279+
->setDescription(t('Application UUID returned from Django backend.'))
280+
->setReadOnly(TRUE)
281+
->setDefaultValue(NULL);
282+
277283
$fields['main_applicant'] = BaseFieldDefinition::create('asu_main_applicant')
278284
->setLabel(t('Basic information'))
279285
->setDescription(t('Basic information of the people who are applying'))

public/modules/custom/asu_application/src/EventSubscriber/ApplicationSubscriber.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,8 @@ public function sendApplicationToBackend(ApplicationEvent $applicationEvent) {
153153
);
154154

155155
$request->setSender($user);
156-
$this->backendApi->send($request);
156+
$response = $this->backendApi->send($request);
157+
$application->set('field_backend_id', $response->getContent()['application_uuid'] ?? NULL);
157158

158159
$application->set('field_locked', 1);
159160
$application->set('error', NULL);
@@ -259,7 +260,8 @@ public function salesSendApplicationToBackend(SalesApplicationEvent $application
259260

260261
$request->setSender($sender);
261262

262-
$this->backendApi->send($request);
263+
$response = $this->backendApi->send($request);
264+
$application->set('field_backend_id', $response->getContent()['application_uuid'] ?? NULL);
263265
$this->logger->notice(
264266
'Sales sent application to backend successfully'
265267
);

public/modules/custom/asu_application/templates/asu-application.html.twig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,15 @@
106106
</span>
107107
</a>
108108
</div>
109+
{% if content['#teaser_values'].is_application_period and content['#teaser_values'].delete_url %}
110+
<div class="application--action">
111+
<form method="post" action="{{ content['#teaser_values'].delete_url }}">
112+
<button type="submit" class="hds-button hds-button--secondary">
113+
<span class="hds-button__label">{% trans %}Delete{% endtrans %}</span>
114+
</button>
115+
</form>
116+
</div>
117+
{% endif %}
109118
</div>
110119
</div>
111120
</article>

0 commit comments

Comments
 (0)