interdiff impossible; taking evasive action
reverted:
--- b/core/modules/editor/editor.module
+++ a/core/modules/editor/editor.module
@@ -16,8 +16,6 @@
use Drupal\Core\Entity\EntityInterface;
use Drupal\filter\FilterFormatInterface;
use Drupal\filter\Plugin\FilterInterface;
-use Drupal\Core\Entity\EntityStorageInterface;
-use Drupal\file\FileInterface;
/**
* Implements hook_help().
@@ -456,132 +454,6 @@
}
/**
- * Implements hook_file_download().
- *
- * @see file_file_download()
- * @see file_get_file_references()
- */
-function editor_file_download($uri) {
- // Get the file record based on the URI. If not in the database just return.
- /** @var \Drupal\file\FileInterface[] $files */
- $files = entity_load_multiple_by_properties('file', array('uri' => $uri));
- if (count($files)) {
- foreach ($files as $item) {
- // Since some database servers sometimes use a case-insensitive comparison
- // by default, double check that the filename is an exact match.
- if ($item->getFileUri() === $uri) {
- $file = $item;
- break;
- }
- }
- }
- if (!isset($file)) {
- return;
- }
-
- // Temporary files are handled by file_file_download(), so nothing to do here
- // about them.
-
- // Find out which (if any) fields of this type contain the file.
- $references = editor_get_file_references($file, EntityStorageInterface::FIELD_LOAD_CURRENT);
-
- // Stop processing if there are no references in order to avoid returning
- // headers for files controlled by other modules. Make an exception for
- // temporary files where the host entity has not yet been saved (for example,
- // an image preview on a node/add form) in which case, allow download by the
- // file's owner.
- if (empty($references) && ($file->isPermanent() || $file->getOwnerId() != \Drupal::currentUser()->id())) {
- return;
- }
-
- // Editor.module MUST NOT call $file->access() here (like file_file_download()
- // does) as checking the 'download' access to a file entity would end up in
- // FileAccessControlHandler->checkAccess() and ->getFileReferences(), which
- // calls file_get_file_references() again. This latter one would allow
- // downloading files only handled by the file.module, which is exactly not the
- // case right here.
-// if (!($return = $file->access('download', NULL, TRUE))) {
-// return -1;
-// }
-
- // Access is granted.
- $headers = file_get_content_headers($file);
- return $headers;
-}
-
-/**
- * Retrieves a list of references to a file uploaded via EditorImageDialog.
- *
- * @param \Drupal\file\FileInterface $file
- * A file entity.
- * @param int $age
- * (optional) A constant that specifies which references to count. Use
- * EntityStorageInterface::FIELD_LOAD_REVISION (the default) to retrieve all
- * references within all revisions or
- * EntityStorageInterface::FIELD_LOAD_CURRENT to retrieve references only in
- * the current revisions of all entities that have references to this file.
- *
- * @return array
- * A multidimensional array. The keys are field_name, entity_type,
- * entity_id and the value is an entity referencing this file.
- *
- * @ingroup file
- * @see file_get_file_references()
- */
-function editor_get_file_references(FileInterface $file, $age = EntityStorageInterface::FIELD_LOAD_REVISION) {
- $references = &drupal_static(__FUNCTION__, array());
- $field_columns = &drupal_static(__FUNCTION__ . ':field_columns', array());
-
- // Fill the static cache, disregard $field and $field_type for now.
- if (!isset($references[$file->id()][$age])) {
- $references[$file->id()][$age] = array();
- $usage_list = \Drupal::service('file.usage')->listUsage($file);
- $file_usage_list = isset($usage_list['editor']) ? $usage_list['editor'] : array();
- foreach ($file_usage_list as $entity_type_id => $entity_ids) {
- $entities = entity_load_multiple($entity_type_id, array_keys($entity_ids));
- /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
- foreach ($entities as $entity) {
- $bundle = $entity->bundle();
-
- // We need to find editor fields for this entity type and bundle.
- if (!isset($editor_fields[$entity_type_id][$bundle])) {
- $editor_fields[$entity_type_id][$bundle] = _editor_get_formatted_text_fields($entity);
- }
- foreach ($editor_fields[$entity_type_id][$bundle] as $field_name) {
- // Iterate over the field items to find the referenced file and field
- // name. This will fail if the usage checked is in a non-current
- // revision because field items are from the current
- // revision.
- // We also iterate over all translations because a file can be linked
- // to a language other than the default.
- foreach ($entity->getTranslationLanguages() as $langcode => $language) {
- /** @var \Drupal\text\Plugin\Field\FieldType\TextItemBase $item */
- foreach ($entity->getTranslation($langcode)->get($field_name) as $item) {
- // Check if the file is referenced in either
- // $item->values['value'] or $item->values['summary'] (if the
- // latter exists at all).
- $value = $item->getValue();
- foreach (['summary', 'value'] as $subfield) {
- if (isset($value[$subfield])) {
- $xpath = new \DOMXPath(Html::load($value['value']));
- /** @var DOMNodeList $query */
- $query = $xpath->query('//*[@data-entity-type="file" and @data-entity-uuid="' . $file->uuid() . '"]');
- if ($query->length) {
- $references[$file->id()][$age][$field_name][$entity_type_id][$entity->id()] = $entity;
- break;
- }
- }
- }
- }
- }
- }
- }
- }
- }
- return $references[$file->id()][$age];
-}
-
-/**
* Finds all files referenced (data-entity-uuid) by formatted text fields.
*
* @param EntityInterface $entity
unchanged:
--- a/core/modules/editor/editor.module
+++ b/core/modules/editor/editor.module
@@ -16,6 +16,8 @@
use Drupal\Core\Entity\EntityInterface;
use Drupal\filter\FilterFormatInterface;
use Drupal\filter\Plugin\FilterInterface;
+use Drupal\Core\Entity\EntityStorageInterface;
+use Drupal\file\FileInterface;
/**
* Implements hook_help().
@@ -468,6 +470,131 @@ function _editor_delete_file_usage(array $uuids, EntityInterface $entity, $count
}
/**
+ * Implements hook_file_download().
+ *
+ * @see file_file_download()
+ * @see file_get_file_references()
+ */
+function editor_file_download($uri) {
+ // Get the file record based on the URI. If not in the database just return.
+ /** @var \Drupal\file\FileInterface[] $files */
+ $files = entity_load_multiple_by_properties('file', array('uri' => $uri));
+ if (count($files)) {
+ foreach ($files as $item) {
+ // Since some database servers sometimes use a case-insensitive comparison
+ // by default, double check that the filename is an exact match.
+ if ($item->getFileUri() === $uri) {
+ $file = $item;
+ break;
+ }
+ }
+ }
+ if (!isset($file)) {
+ return;
+ }
+
+ // Temporary files are handled by file_file_download(), so nothing to do here
+ // about them.
+
+ // Find out which (if any) fields of this type contain the file.
+ $references = editor_get_file_references($file, EntityStorageInterface::FIELD_LOAD_CURRENT);
+
+ // Stop processing if there are no references in order to avoid returning
+ // headers for files controlled by other modules. Make an exception for
+ // temporary files where the host entity has not yet been saved (for example,
+ // an image preview on a node/add form) in which case, allow download by the
+ // file's owner.
+ if (empty($references) && ($file->isPermanent() || $file->getOwnerId() != \Drupal::currentUser()->id())) {
+ return;
+ }
+
+ // Editor.module MUST NOT call $file->access() here (like file_file_download()
+ // does) as checking the 'download' access to a file entity would end up in
+ // FileAccessControlHandler->checkAccess() and ->getFileReferences(), which
+ // calls file_get_file_references() again. This latter one would allow
+ // downloading files only handled by the file.module, which is exactly not the
+ // case right here.
+// if (!($return = $file->access('download', NULL, TRUE))) {
+// return -1;
+// }
+
+ // Access is granted.
+ $headers = file_get_content_headers($file);
+ return $headers;
+}
+
+/**
+ * Retrieves a list of references to a file uploaded via EditorImageDialog.
+ *
+ * @param \Drupal\file\FileInterface $file
+ * A file entity.
+ * @param int $age
+ * (optional) A constant that specifies which references to count. Use
+ * EntityStorageInterface::FIELD_LOAD_REVISION (the default) to retrieve all
+ * references within all revisions or
+ * EntityStorageInterface::FIELD_LOAD_CURRENT to retrieve references only in
+ * the current revisions of all entities that have references to this file.
+ *
+ * @return array
+ * A multidimensional array. The keys are field_name, entity_type,
+ * entity_id and the value is an entity referencing this file.
+ *
+ * @ingroup file
+ * @see file_get_file_references()
+ */
+function editor_get_file_references(FileInterface $file, $age = EntityStorageInterface::FIELD_LOAD_REVISION) {
+ $references = &drupal_static(__FUNCTION__, array());
+
+ // Fill the static cache, disregard $field and $field_type for now.
+ if (!isset($references[$file->id()][$age])) {
+ $references[$file->id()][$age] = array();
+ $usage_list = \Drupal::service('file.usage')->listUsage($file);
+ $file_usage_list = isset($usage_list['editor']) ? $usage_list['editor'] : array();
+ foreach ($file_usage_list as $entity_type_id => $entity_ids) {
+ $entities = entity_load_multiple($entity_type_id, array_keys($entity_ids));
+ /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
+ foreach ($entities as $entity) {
+ $bundle = $entity->bundle();
+
+ // We need to find editor fields for this entity type and bundle.
+ if (!isset($editor_fields[$entity_type_id][$bundle])) {
+ $editor_fields[$entity_type_id][$bundle] = _editor_get_formatted_text_fields($entity);
+ }
+ foreach ($editor_fields[$entity_type_id][$bundle] as $field_name) {
+ // Iterate over the field items to find the referenced file and field
+ // name. This will fail if the usage checked is in a non-current
+ // revision because field items are from the current
+ // revision.
+ // We also iterate over all translations because a file can be linked
+ // to a language other than the default.
+ foreach ($entity->getTranslationLanguages() as $langcode => $language) {
+ /** @var \Drupal\text\Plugin\Field\FieldType\TextItemBase $item */
+ foreach ($entity->getTranslation($langcode)->get($field_name) as $item) {
+ // Check if the file is referenced in either
+ // $item->values['value'] or $item->values['summary'] (if the
+ // latter exists at all).
+ $value = $item->getValue();
+ foreach (['summary', 'value'] as $subfield) {
+ if (isset($value[$subfield])) {
+ $xpath = new \DOMXPath(Html::load($value['value']));
+ /** @var DOMNodeList $query */
+ $query = $xpath->query('//*[@data-entity-type="file" and @data-entity-uuid="' . $file->uuid() . '"]');
+ if ($query->length) {
+ $references[$file->id()][$age][$field_name][$entity_type_id][$entity->id()] = $entity;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return $references[$file->id()][$age];
+}
+
+/**
* Finds all files referenced (data-entity-uuid) by formatted text fields.
*
* @param EntityInterface $entity
only in patch2:
unchanged:
--- /dev/null
+++ b/core/modules/editor/src/Tests/EditorPrivateFileReferenceFilterTest.php
@@ -0,0 +1,70 @@
+drupalCreateContentType(array('type' => 'page', 'name' => 'Basic page'));
+
+ // Create a file in the 'private:// ' stream.
+ $filename = 'test.png';
+ $src = '/system/files/' . $filename;
+ /** @var FileInterface $file */
+ $file = File::create([
+ 'uri' => 'private://' . $filename,
+ 'status' => FILE_STATUS_PERMANENT,
+ ]);
+ // Create the file itself.
+ file_put_contents($file->getFileUri(), $this->randomString());
+ $file->save();
+
+ // Create a node with its body field properly pointing to the just-created
+ // file.
+ $node = $this->drupalCreateNode([
+ 'type' => 'page',
+ 'body' => [
+ 'value' => '
',
+ 'format' => 'private_images',
+ ],
+ ]);
+ $this->drupalGet('/node/' . $node->id());
+
+ // Do the actual test. The image should be visible for anonymous.
+ $this->drupalGet($src);
+ $this->assertResponse(200, 'Image is downloadable as anonymous.');
+ }
+
+}
only in patch2:
unchanged:
--- /dev/null
+++ b/core/modules/editor/tests/editor_private_test/config/install/editor.editor.private_images.yml
@@ -0,0 +1,34 @@
+format: private_images
+status: true
+langcode: en
+editor: ckeditor
+settings:
+ toolbar:
+ rows:
+ -
+ -
+ name: Media
+ items:
+ - DrupalImage
+ -
+ name: Tools
+ items:
+ - Source
+ plugins:
+ language:
+ language_list: un
+ stylescombo:
+ styles: ''
+image_upload:
+ status: true
+ scheme: private
+ directory: ''
+ max_size: ''
+ max_dimensions:
+ width: null
+ height: null
+dependencies:
+ config:
+ - filter.format.private_images
+ module:
+ - ckeditor
only in patch2:
unchanged:
--- /dev/null
+++ b/core/modules/editor/tests/editor_private_test/config/install/filter.format.private_images.yml
@@ -0,0 +1,23 @@
+format: private_images
+name: 'Private images'
+status: true
+langcode: en
+filters:
+ editor_file_reference:
+ id: editor_file_reference
+ provider: editor
+ status: true
+ weight: 0
+ settings: { }
+ filter_html:
+ id: filter_html
+ provider: filter
+ status: false
+ weight: -10
+ settings:
+ allowed_html: '
'
+ filter_html_help: true
+ filter_html_nofollow: false
+dependencies:
+ module:
+ - editor
only in patch2:
unchanged:
--- /dev/null
+++ b/core/modules/editor/tests/editor_private_test/editor_private_test.info.yml
@@ -0,0 +1,9 @@
+name: 'Text Editor Private test'
+type: module
+description: 'Support module for the Text Editor Private module tests.'
+core: 8.x
+package: Testing
+version: VERSION
+dependencies:
+ - filter
+ - ckeditor