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"