Skip to content

Commit

Permalink
Afform - Enable file downloads
Browse files Browse the repository at this point in the history
Fixes dev/core#5179
Users with 'access uploaded files' permission (or afform entities with permission checks disables)
will see uploaded files as a link which can be clicked to download the file
  • Loading branch information
colemanw committed Nov 12, 2024
1 parent 9695738 commit 8bc6bf7
Show file tree
Hide file tree
Showing 6 changed files with 111 additions and 9 deletions.
6 changes: 3 additions & 3 deletions Civi/Api4/Service/Spec/Provider/FileGetSpecProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,10 @@ public static function renderFileUrl(array $idField, Api4SelectQuery $query): st
}
}
if (isset($entityIdField)) {
return "CONCAT('civicrm/file?reset=1&id=', $idField[sql_name], '&eid=', $entityIdField[sql_name])";
return "CONCAT('civicrm/file?reset=1&id=', {$idField['sql_name']}, '&eid=', {$entityIdField['sql_name']})";
}
// Guess we couldn't find an `entity_id` in the query. This function could probably be improved.
return "NULL";
// Couldn't find an `entity_id` in the query so add a subquery instead.
return "CONCAT('civicrm/file?reset=1&id=', {$idField['sql_name']}, '&eid=', (SELECT `entity_id` FROM `civicrm_entity_file` WHERE `file_id` = {$idField['sql_name']} LIMIT 1))";
}

public static function renderFileIsImage(array $mimeTypeField, Api4SelectQuery $query): string {
Expand Down
13 changes: 11 additions & 2 deletions ext/afform/core/Civi/Api4/Action/Afform/AbstractProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ public function getJoinResult(array $afEntity, string $joinEntity, array $join,
}

/**
* Delegated by loadEntity to call API.get and fill in additioal info
* Delegated by loadEntity to call API.get and fill in additional info
*
* @param string $afEntityName
* e.g. Individual1
Expand All @@ -343,10 +343,14 @@ private function apiGet($afEntityName, $apiEntityName, $entityFields, string $ke
// Fill additional info about file fields
$fileFields = $this->getFileFields($apiEntityName, $entityFields);
foreach ($fileFields as $fieldName => $fieldDefn) {
$select = ['file_name', 'icon'];
if ($this->canViewFileAttachments($afEntityName)) {
$select[] = 'url';
}
foreach ($result as &$item) {
if (!empty($item[$fieldName])) {
$fileInfo = File::get(FALSE)
->addSelect('file_name', 'icon')
->setSelect($select)
->addWhere('id', '=', $item[$fieldName])
->execute()->first();
$item[$fieldName] = $fileInfo;
Expand All @@ -356,6 +360,11 @@ private function apiGet($afEntityName, $apiEntityName, $entityFields, string $ke
return $result;
}

private function canViewFileAttachments(string $afEntityName): bool {
$afEntity = $this->_formDataModel->getEntity($afEntityName);
return ($afEntity['security'] === 'FBAC' || \CRM_Core_Permission::check('access uploaded files'));
}

protected static function getFileFields($entityName, $entityFields): array {
if (!$entityFields) {
return [];
Expand Down
2 changes: 1 addition & 1 deletion ext/afform/core/Civi/Api4/Action/Afform/Prefill.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ private function formatViewValues(array $afformEntity, array &$valueSets): void
}

private function replaceViewValue(string $entityType, string $fieldName, array &$values, $originalValues) {
if (isset($values[$fieldName])) {
if (isset($values[$fieldName]) && !isset($values[$fieldName]['file_name'])) {
$fieldInfo = $this->_formDataModel->getField($entityType, $fieldName, 'create', $originalValues);
$values[$fieldName] = \Civi\Afform\Utils::formatViewValue($fieldName, $fieldInfo, $originalValues);
}
Expand Down
14 changes: 13 additions & 1 deletion ext/afform/core/ang/af/fields/DisplayOnly.html
Original file line number Diff line number Diff line change
@@ -1 +1,13 @@
{{dataProvider.getFieldData()[$ctrl.fieldName]}}
<span ng-if="dataProvider.getFieldData()[$ctrl.fieldName].file_name">
<a ng-if="dataProvider.getFieldData()[$ctrl.fieldName].url" ng-href="{{:: dataProvider.getFieldData()[$ctrl.fieldName].url }}">
<i class="crm-i {{ dataProvider.getFieldData()[$ctrl.fieldName].icon }}"></i>
{{ dataProvider.getFieldData()[$ctrl.fieldName].file_name }}
</a>
<span ng-if="!dataProvider.getFieldData()[$ctrl.fieldName].url">
<i class="crm-i {{ dataProvider.getFieldData()[$ctrl.fieldName].icon }}"></i>
{{ dataProvider.getFieldData()[$ctrl.fieldName].file_name }}
</span>
</span>
<span ng-if="!dataProvider.getFieldData()[$ctrl.fieldName].file_name">
{{ dataProvider.getFieldData()[$ctrl.fieldName] }}
</span>
10 changes: 8 additions & 2 deletions ext/afform/core/ang/af/fields/File.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
<span ng-if="dataProvider.getFieldData()[$ctrl.fieldName].file_name">
<i class="crm-i {{ dataProvider.getFieldData()[$ctrl.fieldName].icon }}"></i>
{{ dataProvider.getFieldData()[$ctrl.fieldName].file_name }}
<a ng-if="dataProvider.getFieldData()[$ctrl.fieldName].url" ng-href="{{:: dataProvider.getFieldData()[$ctrl.fieldName].url }}">
<i class="crm-i {{ dataProvider.getFieldData()[$ctrl.fieldName].icon }}"></i>
{{ dataProvider.getFieldData()[$ctrl.fieldName].file_name }}
</a>
<span ng-if="!dataProvider.getFieldData()[$ctrl.fieldName].url">
<i class="crm-i {{ dataProvider.getFieldData()[$ctrl.fieldName].icon }}"></i>
{{ dataProvider.getFieldData()[$ctrl.fieldName].file_name }}
</span>
<a class="crm-hover-button" title="{{:: ts('Replace') }}" ng-click="dataProvider.getFieldData()[$ctrl.fieldName].file_name = null">
<i class="crm-i fa-times" aria-hidden="true"></i>
</a>
Expand Down
75 changes: 75 additions & 0 deletions tests/phpunit/api/v4/Custom/CustomFileTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?php

/*
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC. All rights reserved. |
| |
| This work is published under the GNU AGPLv3 license with some |
| permitted exceptions and without any warranty. For full license |
| and copyright information, see https://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/

/**
* @package CRM
* @copyright CiviCRM LLC https://civicrm.org/licensing
*/

namespace Civi\tests\phpunit\api\v4\Custom;

use api\v4\Custom\CustomTestBase;
use Civi\Api4\CustomGroup;
use Civi\Api4\CustomField;
use Civi\Api4\File;

/**
* @group headless
*/
class CustomFileTest extends CustomTestBase {

/**
*/
public function testCustomFileField(): void {
$group = CustomGroup::create()->setValues([
'title' => 'FileFields',
'extends' => 'Individual',
])->execute()->single();
$field = CustomField::create()->setValues([
'label' => 'TestMyFile',
'custom_group_id.name' => 'FileFields',
'html_type' => 'File',
'data_type' => 'File',
])->execute()->single();

$fieldName = 'FileFields.TestMyFile';

$contact = $this->createTestRecord('Individual');

// FIXME: Use Api4 when available
$file = civicrm_api3('Attachment', 'create', [
// The mismatch between entity id and entity table feels very wrong but that's how core does it for now
'entity_id' => $contact['id'],
'entity_table' => $group['table_name'],
'mime_type' => 'text/plain',
'name' => 'test123.txt',
'content' => 'Hello World 123',
]);

civicrm_api4('Individual', 'update', [
'values' => [
'id' => $contact['id'],
$fieldName => $file['id'],
],
'checkPermissions' => FALSE,
]);

$file = File::get(FALSE)
->addSelect('id', 'file_name', 'url')
->addWhere('id', '=', $file['id'])
->execute()->single();

$this->assertEquals('test123.txt', $file['file_name']);
$this->assertStringContainsString("id={$file['id']}&eid={$contact['id']}&fcs=", $file['url']);
}

}

0 comments on commit 8bc6bf7

Please sign in to comment.