Skip to content
This repository has been archived by the owner on Jul 27, 2022. It is now read-only.

Commit

Permalink
Merge pull request #2436 from ec-europa/ISAICP-6139
Browse files Browse the repository at this point in the history
ISAICP-6139: Show parent of distributions in download report.
  • Loading branch information
pfrenssen authored Apr 13, 2021
2 parents 2404260 + 0ed2d91 commit 776f52c
Show file tree
Hide file tree
Showing 13 changed files with 358 additions and 26 deletions.
130 changes: 130 additions & 0 deletions config/sync/views.view.asset_distribution_downloads.yml
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,136 @@ display:
hide_alter_empty: true
link_to_entity: true
plugin_id: entity_label
parent_entity_type:
id: parent_entity_type
table: joinup_download_event
field: parent_entity_type
relationship: none
group_type: group
admin_label: ''
label: ''
exclude: true
alter:
alter_text: false
text: ''
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: false
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
click_sort_column: value
type: string
settings:
link_to_entity: false
group_column: value
group_columns: { }
group_rows: true
delta_limit: 0
delta_offset: 0
delta_reversed: false
delta_first_last: false
multi_type: separator
separator: ', '
field_api_classes: false
entity_type: download_event
entity_field: parent_entity_type
plugin_id: field
parent_entity_id:
id: parent_entity_id
table: joinup_download_event
field: parent_entity_id
relationship: none
group_type: group
admin_label: ''
label: 'Parent'
exclude: false
alter:
alter_text: false
text: ''
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: true
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
click_sort_column: value
type: string
settings:
link_to_entity: false
group_column: value
group_columns: { }
group_rows: true
delta_limit: 0
delta_offset: 0
delta_reversed: false
delta_first_last: false
multi_type: separator
separator: ', '
field_api_classes: false
entity_type: download_event
entity_field: parent_entity_id
plugin_id: field
created:
id: created
table: joinup_download_event
Expand Down
27 changes: 16 additions & 11 deletions tests/features/asset_distribution/track_download.feature
Original file line number Diff line number Diff line change
Expand Up @@ -110,19 +110,23 @@ Feature: Asset distribution editing.
When I go to the "Winter of 95" release
Then I should see the link "External" in the "i386" tile

Scenario: Tests the CSV download.
Scenario: Download a CSV export of the distributions download report.
Given users:
| Username | E-mail |
| user1 | user1@example.com |
| user2 | user2@example.com |
And the following solution:
| title | Solution |
| state | validated |
And the following release:
| title | Release 1 |
| is version of | Solution |
| state | validated |
And the following distributions:
| title | parent | access url |
| Distribution 1 | Solution | text.pdf |
| Distribution 2 | Solution | test.zip |
| Distribution 3 | Solution | test1.zip |
| title | parent | access url |
| Distribution 1 | Release 1 | text.pdf |
| Distribution 2 | Solution | test.zip |
| Distribution 3 | Solution | test1.zip |
And the following distribution download events:
| distribution | user |
| Distribution 1 | visitor@example.com |
Expand All @@ -139,9 +143,10 @@ Feature: Asset distribution editing.
Then I should see the success message "Export complete. Download the file here if file is not automatically downloaded."
And I should see the link "here"
And the file downloaded from the "here" link contains the following strings:
| ID,User,Email,"File name",Distribution,Created |
| ,"Anonymous (not verified)",visitor@example.com,text.pdf,"Distribution 1", |
| ,user1,user1@example.com,text.pdf,"Distribution 1", |
| ,user2,user2@example.com,test.zip,"Distribution 2", |
| ,"Anonymous (not verified)",anon@example.com,test1.zip,"Distribution 3", |
| ,user1,user1@example.com,test1.zip,"Distribution 3", |
| ID,User,Email,"File name",Distribution,Parent,Created |
# The %title% variable will translate the title to the entity ID since that is what is exported in the CSV file.
| ,"Anonymous (not verified)",visitor@example.com,text.pdf,"Distribution 1",%Release 1%, |
| ,user1,user1@example.com,text.pdf,"Distribution 1",%Release 1%, |
| ,user2,user2@example.com,test.zip,"Distribution 2",%Solution%, |
| ,"Anonymous (not verified)",anon@example.com,test1.zip,"Distribution 3",%Solution%, |
| ,user1,user1@example.com,test1.zip,"Distribution 3",%Solution%, |
17 changes: 13 additions & 4 deletions tests/features/bootstrap/FeatureContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -2006,11 +2006,20 @@ public function assertDownloadedFileContainsStrings(string $link_label, TableNod
throw new \Exception("The downloaded file has no content.");
}

$not_found = array_filter($strings_table->getColumn(0), function (string $text) use ($content): bool {
return strpos($content, $text) === FALSE;
});
$not_found = [];
foreach ($strings_table->getColumn(0) as $text) {
$matches = [];
if (preg_match('/^.*%(.*?)%.*$/', $text, $matches)) {
$entity = $this->getEntityByLabel('rdf_entity', $matches[1]);
$text = str_replace("%{$matches[1]}%", $entity->id(), $text);
}

if ($not_found) {
if (strpos($content, $text) === FALSE) {
$not_found[] = $text;
}
}

if (!empty($not_found)) {
throw new ExpectationFailedException("Following strings were not found in the downloaded file:\n- " . implode("\n- ", $not_found));
}
}
Expand Down
6 changes: 5 additions & 1 deletion tests/src/Context/AssetDistributionContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -483,15 +483,19 @@ public function clickLinkInCompactDistribution(string $link, string $heading): v
*/
public function downloadEvents(TableNode $table): void {
foreach ($table->getColumnsHash() as $row) {
/** @var \Drupal\asset_distribution\Entity\AssetDistributionInterface $distribution */
$distribution = $this->getRdfEntityByLabel($row['distribution'], 'asset_distribution');
/** @var \Drupal\file\FileInterface $file */
$file = FileUrlHandler::urlToFile($distribution->get('field_ad_access_url')->target_id);
$account = \Drupal::service('email.validator')->isValid($row['user']) ? new AnonymousUserSession() : user_load_by_name($row['user']);
$mail = $account->isAnonymous() ? $row['user'] : $account->getEmail();

$parent = $distribution->getParent();
$entity = DownloadEvent::create([
'uid' => $account->id(),
'mail' => $mail,
'file' => $file->id(),
'parent_entity_type' => $parent->getEntityTypeId(),
'parent_entity_id' => $parent->id(),
]);
$entity->save();
$this->entities['download_event'][$entity->id()] = $entity;
Expand Down
35 changes: 35 additions & 0 deletions web/modules/custom/asset_distribution/asset_distribution.module
Original file line number Diff line number Diff line change
Expand Up @@ -309,3 +309,38 @@ function asset_distribution_rdf_entity_delete(RdfInterface $distribution) {
}

}

/**
* Implements hook_preprocess_HOOK().
*/
function asset_distribution_preprocess_views_view_field(&$variables) {
$view = $variables['view'];
$field = $variables['field'];
if ($view->storage->id() !== 'asset_distribution_downloads' || $view->current_display !== 'page' || $field->field !== 'parent_entity_id') {
return;
}

/** @var \Drupal\asset_distribution\Entity\DownloadEvent $download_event */
$download_event = $variables['row']->_entity;
if ($download_event->get('parent_entity_type')->isEmpty() || $download_event->get('parent_entity_id')->isEmpty()) {
return;
}

// Notice: This code works for any entity type, though it is only meant for
// distributions so it might be too much.
$entity_type = $download_event->get('parent_entity_type')->value;
$entity_id = $download_event->get('parent_entity_id')->value;

try {
$storage = $storages[$entity_type] ?? \Drupal::entityTypeManager()->getStorage($entity_type);
}
catch (Exception $exception) {
return;
}

$storages[$entity_type] = $storage;
$entity = $storage->load($entity_id);
if (!empty($entity)) {
$variables['output'] = $entity->toLink($entity->label());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
use Drupal\Core\Session\AccountInterface;
use Drupal\asset_distribution\Form\AnonymousDownloadForm;
use Drupal\file\FileInterface;
use Drupal\file\FileUsage\FileUsageInterface;
use Drupal\file_url\FileUrlHandler;
use Drupal\sparql_entity_storage\SparqlEntityStorageInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Response;

Expand Down Expand Up @@ -49,10 +51,17 @@ class DownloadTrackingController extends ControllerBase {
*/
protected $sparqlStorage;

/**
* The file usage service.
*
* @var \Drupal\file\FileUsage\FileUsageInterface
*/
protected $fileUsage;

/**
* Instantiates a new DownloadTrackingController object.
*
* @param \Drupal\Core\Entity\ContentEntityStorageInterface $sparql_storage
* @param \Drupal\sparql_entity_storage\SparqlEntityStorageInterface $sparql_storage
* The RDF entity storage.
* @param \Drupal\Core\Entity\ContentEntityStorageInterface $event_storage
* The download event entity storage.
Expand All @@ -62,13 +71,16 @@ class DownloadTrackingController extends ControllerBase {
* The form builder.
* @param \Drupal\Core\Session\AccountInterface $current_user
* The current logged in user.
* @param \Drupal\file\FileUsage\FileUsageInterface $file_usage
* The file usage service.
*/
public function __construct(ContentEntityStorageInterface $sparql_storage, ContentEntityStorageInterface $event_storage, FileUrlHandler $file_url_handler, FormBuilderInterface $form_builder, AccountInterface $current_user) {
public function __construct(SparqlEntityStorageInterface $sparql_storage, ContentEntityStorageInterface $event_storage, FileUrlHandler $file_url_handler, FormBuilderInterface $form_builder, AccountInterface $current_user, FileUsageInterface $file_usage) {
$this->sparqlStorage = $sparql_storage;
$this->eventStorage = $event_storage;
$this->fileUrlHandler = $file_url_handler;
$this->formBuilder = $form_builder;
$this->currentUser = $current_user;
$this->fileUsage = $file_usage;
}

/**
Expand All @@ -80,7 +92,8 @@ public static function create(ContainerInterface $container) {
$container->get('entity_type.manager')->getStorage('download_event'),
$container->get('file_url.handler'),
$container->get('form_builder'),
$container->get('current_user')
$container->get('current_user'),
$container->get('file.usage')
);
}

Expand Down Expand Up @@ -124,9 +137,26 @@ protected function trackAnonymousDownload(FileInterface $file) {
* The generated response.
*/
protected function trackAuthenticatedDownload(FileInterface $file) {
$usages = $this->fileUsage->listUsage($file);

// Normally, only one distribution is allowed to use a file and only
// distributions call this code.
if (empty($usages['file']['rdf_entity'])) {
throw new \RuntimeException('No distributions were found using the file with ID ' . $file->id());
}
if (count($usages['file']['rdf_entity']) > 1) {
throw new \RuntimeException('More than one distributions were found for the file with ID ' . $file->id());
}
$distribution = $this->sparqlStorage->load(key($usages['file']['rdf_entity']));

/** @var \Drupal\solution\Entity\SolutionInterface|\Drupal\asset_release\Entity\AssetReleaseInterface $parent */
$parent = $distribution->getParent();

$event = $this->eventStorage->create([
'uid' => $this->currentUser->id(),
'file' => $file->id(),
'parent_entity_type' => $parent->getEntityTypeId(),
'parent_entity_id' => $parent->id(),
]);
$event->save();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@ class AssetDistribution extends Rdf implements AssetDistributionInterface {
/**
* {@inheritdoc}
*/
public function getParent() {
public function getParent(): DistributionsParentInterface {
/** @var \Drupal\asset_distribution\DistributionParentFieldItemList $field */
$field = $this->get('parent');

/** @var \Drupal\asset_release\Entity\AssetReleaseInterface|\Drupal\solution\Entity\SolutionInterface $parent */
if ($field->isEmpty() || !($parent = $field->entity)) {
if ($field->isEmpty() || !($parent = $field->entity) || !$parent instanceof DistributionsParentInterface) {
// During normal operation every distribution should have a parent entity,
// so the only way a parent can be missing is because of an unexpected
// condition occurring at runtime, for example if a data store goes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ interface AssetDistributionInterface extends RdfInterface, CollectionContentInte
/**
* Return the distribution's parent, either a release or a solution.
*
* @return \Drupal\asset_release\Entity\AssetReleaseInterface|\Drupal\solution\Entity\SolutionInterface
* @return \Drupal\asset_distribution\Entity\DistributionsParentInterface
* The parent entity, either a release or a solution.
*
* @throws \Drupal\asset_distribution\Exception\MissingDistributionParentException
Expand All @@ -26,7 +26,7 @@ interface AssetDistributionInterface extends RdfInterface, CollectionContentInte
* need to catch this exception. This will only be thrown in case something
* is seriously wrong, e.g. if the database is down.
*/
public function getParent();
public function getParent(): DistributionsParentInterface;

/**
* Checks whether the distribution parent is a solution rather than a release.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@

namespace Drupal\asset_distribution\Entity;

use Drupal\Core\Entity\ContentEntityInterface;

/**
* Bundle class for content having distributions as children.
*/
interface DistributionsParentInterface {
interface DistributionsParentInterface extends ContentEntityInterface {

/**
* Returns the child distribution IDs.
Expand Down
Loading

0 comments on commit 776f52c

Please sign in to comment.