diff --git a/civiremote_funding.module b/civiremote_funding.module index 0cf6044..db2ed0b 100644 --- a/civiremote_funding.module +++ b/civiremote_funding.module @@ -18,15 +18,31 @@ declare(strict_types=1); +use Drupal\civiremote_funding\Api\FundingApi; use Drupal\civiremote_funding\Breadcrumb\BreadcrumbRouteAnalyzer; use Drupal\civiremote_funding\File\FundingFileDownloadHook; use Drupal\civiremote_funding\File\FundingFileManager; use Drupal\civiremote_funding\Install\DashboardBlockInstaller; +use Drupal\civiremote_funding\Views\ApplicationProcessDropButton; use Drupal\civiremote_funding\ViewTranslator; use Drupal\Core\Breadcrumb\Breadcrumb; use Drupal\Core\Cache\CacheableMetadata; use Drupal\Core\Routing\RouteMatchInterface; use Drupal\file\FileInterface; +use Drupal\views\Plugin\views\field\Dropbutton; +use Drupal\views\ViewExecutable; + +function civiremote_funding_views_pre_build(ViewExecutable $view): void { + if (!in_array($view->id(), ['civiremote_funding_application_list', 'civiremote_funding_combined_application_process_list'], TRUE)) { + return; + } + + if (($view->field['dropbutton'] ?? NULL) instanceof Dropbutton) { + /** @var \Drupal\civiremote_funding\Api\FundingApi $fundingApi */ + $fundingApi = \Drupal::service(FundingApi::class); + $view->field['dropbutton'] = new ApplicationProcessDropButton($fundingApi, $view->field['dropbutton']); + } +} /** * Implements hook_rebuild(). diff --git a/civiremote_funding.routing.yml b/civiremote_funding.routing.yml index 6cf38df..90e2369 100644 --- a/civiremote_funding.routing.yml +++ b/civiremote_funding.routing.yml @@ -56,6 +56,20 @@ civiremote_funding.application_history: requirements: _permission: 'civiremote_funding: access' +civiremote_funding.application_template_render: + path: '/civiremote/funding/application/{applicationProcessId}/template/{templateId}/render' + defaults: + _controller: 'Drupal\civiremote_funding\Controller\ApplicationTemplateRenderController:render' + options: + no_cache: TRUE + parameters: + applicationProcessId: + type: int + templateId: + type: int + requirements: + _permission: 'civiremote_funding: access' + civiremote_funding.case: path: '/civiremote/funding/case/{fundingCaseId}' defaults: diff --git a/civiremote_funding.services.yml b/civiremote_funding.services.yml index 1abf60e..4310a90 100644 --- a/civiremote_funding.services.yml +++ b/civiremote_funding.services.yml @@ -177,6 +177,10 @@ services: class: Drupal\civiremote_funding\Controller\ApplicationHistoryController public: true + Drupal\civiremote_funding\Controller\ApplicationTemplateRenderController: + class: Drupal\civiremote_funding\Controller\ApplicationTemplateRenderController + public: true + Drupal\civiremote_funding\Controller\TransferContractDownloadController: class: Drupal\civiremote_funding\Controller\TransferContractDownloadController public: true diff --git a/phpcs.xml.dist b/phpcs.xml.dist index 4a1ccfc..082a8e4 100644 --- a/phpcs.xml.dist +++ b/phpcs.xml.dist @@ -26,6 +26,9 @@ + + + diff --git a/src/Api/DTO/ApplicationProcessTemplate.php b/src/Api/DTO/ApplicationProcessTemplate.php new file mode 100644 index 0000000..cf06715 --- /dev/null +++ b/src/Api/DTO/ApplicationProcessTemplate.php @@ -0,0 +1,43 @@ +. + */ + +declare(strict_types=1); + +namespace Drupal\civiremote_funding\Api\DTO; + +/** + * @phpstan-type templateT array{ + * id: int, + * label: string, + * } + * + * @phpstan-extends AbstractDTO + * + * @codeCoverageIgnore + */ +final class ApplicationProcessTemplate extends AbstractDTO { + + public function getId(): int { + return $this->values['id']; + } + + public function getLabel(): string { + return $this->values['label']; + } + +} diff --git a/src/Api/FundingApi.php b/src/Api/FundingApi.php index c40d821..71fce45 100644 --- a/src/Api/FundingApi.php +++ b/src/Api/FundingApi.php @@ -22,6 +22,7 @@ use Drupal\civiremote_funding\Access\RemoteContactIdProviderInterface; use Drupal\civiremote_funding\Api\DTO\ApplicationProcessActivity; +use Drupal\civiremote_funding\Api\DTO\ApplicationProcessTemplate; use Drupal\civiremote_funding\Api\DTO\FundingCase; use Drupal\civiremote_funding\Api\DTO\FundingCaseInfo; use Drupal\civiremote_funding\Api\DTO\FundingCaseType; @@ -183,6 +184,31 @@ public function getApplicationActivities(int $applicationProcessId): array { return ApplicationProcessActivity::allFromArrays($result['values']); } + public function getApplicationTemplateRenderUri(int $applicationProcessId, int $templateId): string { + $result = $this->apiClient->executeV4('RemoteFundingApplicationProcess', 'getTemplateRenderUri', [ + 'remoteContactId' => $this->remoteContactIdProvider->getRemoteContactId(), + 'applicationProcessId' => $applicationProcessId, + 'templateId' => $templateId, + ]); + + // @phpstan-ignore-next-line + return $result['values']['renderUri']; + } + + /** + * @phpstan-return list + * + * @throws \Drupal\civiremote_funding\Api\Exception\ApiCallFailedException + */ + public function getApplicationTemplates(int $applicationProcessId): array { + $result = $this->apiClient->executeV4('RemoteFundingApplicationProcess', 'getTemplates', [ + 'remoteContactId' => $this->remoteContactIdProvider->getRemoteContactId(), + 'applicationProcessId' => $applicationProcessId, + ]); + + return ApplicationProcessTemplate::allFromArrays($result['values']); + } + /** * @phpstan-return array * Options with option ID as key. diff --git a/src/Controller/ApplicationTemplateRenderController.php b/src/Controller/ApplicationTemplateRenderController.php new file mode 100644 index 0000000..69a5643 --- /dev/null +++ b/src/Controller/ApplicationTemplateRenderController.php @@ -0,0 +1,53 @@ +. + */ + +declare(strict_types=1); + +namespace Drupal\civiremote_funding\Controller; + +use Drupal\civiremote_funding\Api\FundingApi; +use Drupal\civiremote_funding\RemotePage\RemotePageProxy; +use Drupal\Core\Controller\ControllerBase; +use Symfony\Component\HttpFoundation\Response; + +final class ApplicationTemplateRenderController extends ControllerBase { + + private FundingApi $fundingApi; + + private RemotePageProxy $remotePageProxy; + + public function __construct( + FundingApi $fundingApi, + RemotePageProxy $remotePageProxy + ) { + $this->fundingApi = $fundingApi; + $this->remotePageProxy = $remotePageProxy; + } + + public function render(int $applicationProcessId, int $templateId): Response { + $uri = $this->getDownloadUri($applicationProcessId, $templateId); + + return $this->remotePageProxy->get($uri); + } + + private function getDownloadUri(int $applicationProcessId, int $templateId): string { + return $this->fundingApi->getApplicationTemplateRenderUri($applicationProcessId, $templateId); + } + +} diff --git a/src/RemotePage/RemotePageClient.php b/src/RemotePage/RemotePageClient.php index e2bc6f6..02dd8b6 100644 --- a/src/RemotePage/RemotePageClient.php +++ b/src/RemotePage/RemotePageClient.php @@ -34,7 +34,9 @@ */ class RemotePageClient { - private const DEFAULT_TIMEOUT = 3.0; + private const DEFAULT_CONNECT_TIMEOUT = 3.0; + + private const DEFAULT_TIMEOUT = 7.0; private string $apiKey; @@ -83,6 +85,7 @@ public function __construct( public function request(string $method, string $uri, array $options = []): ResponseInterface { // @phpstan-ignore-next-line $options['headers'] = array_merge($options['headers'] ?? [], $this->buildHeaders()); + $options['connect_timeout'] ??= self::DEFAULT_CONNECT_TIMEOUT; $options['timeout'] ??= self::DEFAULT_TIMEOUT; $options['http_errors'] ??= FALSE; diff --git a/src/Views/ApplicationProcessDropButton.php b/src/Views/ApplicationProcessDropButton.php new file mode 100644 index 0000000..7a6e5b7 --- /dev/null +++ b/src/Views/ApplicationProcessDropButton.php @@ -0,0 +1,99 @@ +. + */ + +declare(strict_types=1); + +namespace Drupal\civiremote_funding\Views; + +use Drupal\civiremote_funding\Api\FundingApi; +use Drupal\Core\Url; +use Drupal\views\Plugin\views\field\Dropbutton; +use Drupal\views\ResultRow; + +/** + * Acts like a decorator (a real decorator is not possible) to add application + * process document creation links. + */ +final class ApplicationProcessDropButton extends Dropbutton { + + private FundingApi $fundingApi; + + private ?int $applicationProcessId = NULL; + + public function __construct(FundingApi $fundingApi, Dropbutton $dropbutton) { + $this->fundingApi = $fundingApi; + parent::__construct($dropbutton->configuration, $dropbutton->getPluginId(), $dropbutton->getPluginDefinition()); + // @phpstan-ignore-next-line + if (NULL !== $dropbutton->view) { + $this->init($dropbutton->view, $dropbutton->view->getDisplay(), $dropbutton->options); + } + } + + /** + * {@inheritDoc} + */ + public function render(ResultRow $values) { + // @phpstan-ignore-next-line + foreach ($values as $key => $value) { + if (str_ends_with($key, '_application_process_id')) { + $this->applicationProcessId = $value; + + break; + } + } + + try { + return parent::render($values); + } + finally { + $this->applicationProcessId = NULL; + } + } + + /** + * {@inheritDoc} + * + * @phpstan-return array + * + * @throws \Drupal\civiremote_funding\Api\Exception\ApiCallFailedException + */ + protected function getLinks(): array { + $links = parent::getLinks(); + if (NULL === $this->applicationProcessId) { + // Should not happen. + return $links; + } + + foreach ($this->fundingApi->getApplicationTemplates($this->applicationProcessId) as $template) { + $links[] = [ + 'url' => Url::fromRoute('civiremote_funding.application_template_render', [ + 'applicationProcessId' => $this->applicationProcessId, + 'templateId' => $template->getId(), + ]), + 'title' => $this->t('Create: @label', ['@label' => $template->getLabel()]), + 'attributes' => [ + 'target' => '_blank', + ], + ]; + } + + return $links; + } + +} diff --git a/translations/de.po b/translations/de.po index f174882..c1c0f66 100644 --- a/translations/de.po +++ b/translations/de.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: civiremote_funding\n" -"POT-Creation-Date: 2023-10-19 16:21+0200\n" +"POT-Creation-Date: 2024-02-22 16:04+0100\n" "PO-Revision-Date: \n" "Last-Translator: \n" "Language-Team: \n" @@ -79,7 +79,7 @@ msgid "Download" msgstr "Herunterladen" #: config/optional/views.view.civiremote_funding_application_list.yml -#: src/Plugin/Block/CiviremoteFundingDashboardGroupApplications.php:40 +#: src/Plugin/Block/CiviremoteFundingDashboardGroupApplications.php:42 msgid "Funding Applications" msgstr "Förderanträge" @@ -204,7 +204,7 @@ msgid "CiviCRM funding transfer contract via remote API" msgstr "" #: config/optional/views.view.civiremote_funding_drawdown_list.yml -#: src/Form/NewDrawdownForm.php:95 +#: src/Form/NewDrawdownForm.php:96 msgid "Amount" msgstr "Betrag" @@ -230,7 +230,7 @@ msgid "List of CiviCRM funding applications (non-combined) via remote API" msgstr "Liste der CiviCRM Förderanträge (keine Sammelanträge) via Remote API" #: config/optional/views.view.civiremote_funding_transfer_contract.yml -#: src/Form/NewDrawdownForm.php:90 +#: src/Form/NewDrawdownForm.php:91 msgid "Transfer Contract" msgstr "Weiterleitungsvertrag" @@ -299,7 +299,7 @@ msgid "Creation Date" msgstr "Erstellungsdatum" #: config/optional/views.view.civiremote_funding_transfer_contract_list.yml -#: src/Plugin/Block/CiviremoteFundingDashboardGroupApprovements.php:45 +#: src/Plugin/Block/CiviremoteFundingDashboardGroupApprovements.php:47 msgid "Transfer Contracts" msgstr "Weiterleitungsverträge" @@ -391,11 +391,11 @@ msgstr "" msgid "Loading form failed: @error" msgstr "Laden des Formulars fehlgeschlagen: @error" -#: src/Form/AbstractFundingJsonFormsForm.php:100 +#: src/Form/AbstractFundingJsonFormsForm.php:105 msgid "Error validating form: @error" msgstr "Fehler bei der Formularvalidierung: @error" -#: src/Form/AbstractFundingJsonFormsForm.php:124 +#: src/Form/AbstractFundingJsonFormsForm.php:129 msgid "Submitting form failed: @error" msgstr "Senden des Formulars fehlgeschlagen: @error" @@ -419,19 +419,19 @@ msgstr "Kein Förderfalltyp im gewählten Förderprogramm verfügbar." msgid "API request failed: @error" msgstr "API-Anfrage fehlgeschlagen: @error" -#: src/Form/NewDrawdownForm.php:87 +#: src/Form/NewDrawdownForm.php:88 msgid "Create Drawdown" msgstr "Mittelabruf erstellen" -#: src/Form/NewDrawdownForm.php:104 +#: src/Form/NewDrawdownForm.php:105 msgid "Submit" msgstr "Absenden" -#: src/Form/NewDrawdownForm.php:117 +#: src/Form/NewDrawdownForm.php:118 msgid "Drawdown created." msgstr "Mittelabruf erstellt." -#: src/Form/NewDrawdownForm.php:124 +#: src/Form/NewDrawdownForm.php:125 msgid "Failed to create drawdown: @error" msgstr "Erstellung des Mittelabrufs fehlgeschlagen: @error" @@ -439,34 +439,38 @@ msgstr "Erstellung des Mittelabrufs fehlgeschlagen: @error" msgid "Validation failed." msgstr "Validierung fehlgeschlagen." -#: src/Plugin/Block/CiviremoteFundingDashboardGroupApplications.php:45 +#: src/Plugin/Block/CiviremoteFundingDashboardGroupApplications.php:47 msgid "My Applications" msgstr "Meine Förderanträge" -#: src/Plugin/Block/CiviremoteFundingDashboardGroupApplications.php:48 +#: src/Plugin/Block/CiviremoteFundingDashboardGroupApplications.php:50 msgid "Manage current funding processes" msgstr "Laufende Antragsverfahren verwalten" -#: src/Plugin/Block/CiviremoteFundingDashboardGroupApplications.php:53 +#: src/Plugin/Block/CiviremoteFundingDashboardGroupApplications.php:55 msgid "My Combined Applications" msgstr "Meine Sammelanträge" -#: src/Plugin/Block/CiviremoteFundingDashboardGroupApplications.php:56 +#: src/Plugin/Block/CiviremoteFundingDashboardGroupApplications.php:58 msgid "Manage current combined applications" msgstr "Laufende Sammelanträge verwalten" -#: src/Plugin/Block/CiviremoteFundingDashboardGroupApplications.php:61 +#: src/Plugin/Block/CiviremoteFundingDashboardGroupApplications.php:63 msgid "New Application" msgstr "Neuer Antrag" -#: src/Plugin/Block/CiviremoteFundingDashboardGroupApplications.php:64 +#: src/Plugin/Block/CiviremoteFundingDashboardGroupApplications.php:66 msgid "Place a new application" msgstr "Neuen Antrag stellen" -#: src/Plugin/Block/CiviremoteFundingDashboardGroupApprovements.php:40 +#: src/Plugin/Block/CiviremoteFundingDashboardGroupApprovements.php:42 msgid "Approvements" msgstr "Bewilligungen" -#: src/Plugin/Block/CiviremoteFundingDashboardGroupApprovements.php:48 +#: src/Plugin/Block/CiviremoteFundingDashboardGroupApprovements.php:50 msgid "Download transfer contracts and manage drawdowns" msgstr "Weiterleitungsverträge herunterladen und Mittelabrufe verwalten" + +#: src/Views/ApplicationProcessDropButton.php:89 +msgid "Create: @label" +msgstr "Erstellen: @label"