├── .travis-before-script.sh ├── .travis.yml ├── composer.json ├── config ├── install │ ├── core.entity_form_display.file.image.default.yml │ ├── core.entity_view_display.file.audio.thumbnail.yml │ ├── core.entity_view_display.file.document.thumbnail.yml │ ├── core.entity_view_display.file.image.default.yml │ ├── core.entity_view_display.file.image.thumbnail.yml │ ├── core.entity_view_display.file.video.thumbnail.yml │ ├── core.entity_view_mode.file.thumbnail.yml │ ├── field.field.file.image.field_image_alt_text.yml │ ├── field.field.file.image.field_image_title_text.yml │ ├── field.storage.file.field_image_alt_text.yml │ ├── field.storage.file.field_image_title_text.yml │ ├── file_entity.settings.yml │ ├── file_entity.type.audio.yml │ ├── file_entity.type.document.yml │ ├── file_entity.type.image.yml │ ├── file_entity.type.video.yml │ ├── system.action.file_delete_action.yml │ ├── system.action.file_permanent_action.yml │ ├── system.action.file_temporary_action.yml │ └── views.view.file_entity_files.yml └── schema │ └── file_entity.schema.yml ├── file_entity.api.php ├── file_entity.file.inc ├── file_entity.info.yml ├── file_entity.install ├── file_entity.links.action.yml ├── file_entity.links.menu.yml ├── file_entity.links.task.yml ├── file_entity.module ├── file_entity.permissions.yml ├── file_entity.routing.yml ├── file_entity.services.yml ├── file_entity.theme.inc ├── file_entity.tokens.inc ├── file_entity.views.inc ├── src ├── Controller │ └── FileController.php ├── Entity │ ├── FileEntity.php │ ├── FileEntityViewBuilder.php │ └── FileType.php ├── FileEntityAccessControlHandler.php ├── FileEntityInterface.php ├── FileEntityPermissions.php ├── FileEntityServiceProvider.php ├── FileEntityStorageSchema.php ├── FileTypeInterface.php ├── FileTypeListBuilder.php ├── Form │ ├── FileAddArchiveForm.php │ ├── FileAddForm.php │ ├── FileDeleteMultipleForm.php │ ├── FileEditForm.php │ ├── FileInlineEditForm.php │ ├── FileSettingsForm.php │ ├── FileTypeDisableForm.php │ ├── FileTypeEnableForm.php │ └── FileTypeForm.php ├── Mimetypes.php ├── Normalizer │ ├── FileEntityNormalizer.php │ └── FileItemNormalizer.php ├── Plugin │ ├── Action │ │ ├── FileDelete.php │ │ ├── FileSetPermanent.php │ │ └── FileSetTemporary.php │ ├── Field │ │ ├── FieldFormatter │ │ │ ├── FileAudioFormatter.php │ │ │ ├── FileDownloadLinkFormatter.php │ │ │ ├── FileImageFormatter.php │ │ │ ├── FileSizeFormatter.php │ │ │ └── FileVideoFormatter.php │ │ └── FieldWidget │ │ │ └── FileEditableWidget.php │ ├── QueueWorker │ │ └── FileDetermineType.php │ └── views │ │ ├── argument │ │ └── Type.php │ │ ├── field │ │ ├── FileName.php │ │ ├── Link.php │ │ ├── LinkDelete.php │ │ ├── LinkDownload.php │ │ ├── LinkEdit.php │ │ └── Type.php │ │ └── filter │ │ ├── SchemeType.php │ │ └── Type.php ├── Routing │ └── RouteSubscriber.php ├── Tests │ ├── FileEntityAccessTest.php │ ├── FileEntityAdminTest.php │ ├── FileEntityCacheTagsTest.php │ ├── FileEntityCreationTest.php │ ├── FileEntityEditTest.php │ ├── FileEntityFileTypeClassificationTest.php │ ├── FileEntityPathautoTest.php │ ├── FileEntityReplaceTest.php │ ├── FileEntityServicesTest.php │ ├── FileEntitySettingsTest.php │ ├── FileEntityTestBase.php │ ├── FileEntityTokenTest.php │ ├── FileEntityTypeTest.php │ └── FileEntityUnitTest.php └── UploadValidatorsTrait.php ├── templates ├── file-entity-audio.html.twig ├── file-entity-download-link.html.twig ├── file-entity-video.html.twig └── file.html.twig ├── tests └── src │ └── Kernel │ └── FileEntityNormalizerTest.php └── views ├── views_handler_field_file_rendered.inc └── views_plugin_row_file_rss.inc /.travis-before-script.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e $DRUPAL_TI_DEBUG 4 | 5 | # Ensure the right Drupal version is installed. 6 | # Note: This function is re-entrant. 7 | drupal_ti_ensure_drupal 8 | 9 | # Add needed dependencies. 10 | cd "$DRUPAL_TI_DRUPAL_DIR" 11 | 12 | # These variables come from environments/drupal-*.sh 13 | mkdir -p "$DRUPAL_TI_MODULES_PATH" 14 | cd "$DRUPAL_TI_MODULES_PATH" 15 | 16 | # Download Pathauto 8.x-1.x 17 | git clone --depth 1 --branch 8.x-1.x http://git.drupal.org/project/pathauto.git 18 | git clone --depth 1 --branch 8.x-1.x http://git.drupal.org/project/token.git 19 | git clone --depth 1 --branch 8.x-3.x http://git.drupal.org/project/ctools.git 20 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # @file 2 | # .travis.yml - Drupal for Travis CI Integration 3 | # 4 | # Template provided by https://github.com/LionsAd/drupal_ti. 5 | # 6 | # Based for simpletest upon: 7 | # https://github.com/sonnym/travis-ci-drupal-module-example 8 | 9 | language: php 10 | 11 | sudo: false 12 | 13 | php: 14 | - 5.5 15 | - 5.6 16 | - 7 17 | - hhvm 18 | 19 | matrix: 20 | fast_finish: true 21 | allow_failures: 22 | - php: 7 23 | - php: hhvm 24 | 25 | env: 26 | global: 27 | # add composer's global bin directory to the path 28 | # see: https://github.com/drush-ops/drush#install---composer 29 | - PATH="$PATH:$HOME/.composer/vendor/bin" 30 | 31 | # Configuration variables. 32 | - DRUPAL_TI_MODULE_NAME="file_entity" 33 | - DRUPAL_TI_SIMPLETEST_GROUP="file_entity" 34 | 35 | # Define runners and environment vars to include before and after the 36 | # main runners / environment vars. 37 | #- DRUPAL_TI_SCRIPT_DIR_BEFORE="./.drupal_ti/before" 38 | #- DRUPAL_TI_SCRIPT_DIR_AFTER="./drupal_ti/after" 39 | 40 | # The environment to use, supported are: drupal-7, drupal-8 41 | - DRUPAL_TI_ENVIRONMENT="drupal-8" 42 | - DRUPAL_TI_CORE_BRANCH="8.2.x" 43 | 44 | # Drupal specific variables. 45 | - DRUPAL_TI_DB="drupal_travis_db" 46 | - DRUPAL_TI_DB_URL="mysql://root:@127.0.0.1/drupal_travis_db" 47 | # Note: Do not add a trailing slash here. 48 | - DRUPAL_TI_WEBSERVER_URL="http://127.0.0.1" 49 | - DRUPAL_TI_WEBSERVER_PORT="8080" 50 | 51 | # Simpletest specific commandline arguments, the DRUPAL_TI_SIMPLETEST_GROUP is appended at the end. 52 | - DRUPAL_TI_SIMPLETEST_ARGS="--verbose --color --concurrency 4 --url $DRUPAL_TI_WEBSERVER_URL:$DRUPAL_TI_WEBSERVER_PORT" 53 | 54 | # === Behat specific variables. 55 | # This is relative to $TRAVIS_BUILD_DIR 56 | - DRUPAL_TI_BEHAT_DIR="./tests/behat" 57 | # These arguments are passed to the bin/behat command. 58 | - DRUPAL_TI_BEHAT_ARGS="" 59 | # Specify the filename of the behat.yml with the $DRUPAL_TI_DRUPAL_DIR variables. 60 | - DRUPAL_TI_BEHAT_YML="behat.yml.dist" 61 | # This is used to setup Xvfb. 62 | - DRUPAL_TI_BEHAT_SCREENSIZE_COLOR="1280x1024x16" 63 | # The version of seleniumthat should be used. 64 | - DRUPAL_TI_BEHAT_SELENIUM_VERSION="2.44" 65 | # Set DRUPAL_TI_BEHAT_DRIVER to "selenium" to use "firefox" or "chrome" here. 66 | - DRUPAL_TI_BEHAT_DRIVER="phantomjs" 67 | - DRUPAL_TI_BEHAT_BROWSER="firefox" 68 | 69 | # PHPUnit specific commandline arguments. 70 | - DRUPAL_TI_PHPUNIT_ARGS="--verbose --debug" 71 | # Specifying the phpunit-core src/ directory is useful when e.g. a vendor/ 72 | # directory is present in the module directory, which phpunit would then 73 | # try to find tests in. This option is relative to $TRAVIS_BUILD_DIR. 74 | #- DRUPAL_TI_PHPUNIT_CORE_SRC_DIRECTORY="./tests/src" 75 | 76 | # Code coverage via coveralls.io 77 | - DRUPAL_TI_COVERAGE="satooshi/php-coveralls:0.6.*" 78 | # This needs to match your .coveralls.yml file. 79 | - DRUPAL_TI_COVERAGE_FILE="build/logs/clover.xml" 80 | 81 | # Debug options 82 | #- DRUPAL_TI_DEBUG="-x -v" 83 | # Set to "all" to output all files, set to e.g. "xvfb selenium" or "selenium", 84 | # etc. to only output those channels. 85 | #- DRUPAL_TI_DEBUG_FILE_OUTPUT="selenium xvfb webserver" 86 | 87 | matrix: 88 | # [[[ SELECT ANY OR MORE OPTIONS ]]] 89 | #- DRUPAL_TI_RUNNERS="phpunit" 90 | #- DRUPAL_TI_RUNNERS="simpletest" 91 | #- DRUPAL_TI_RUNNERS="behat" 92 | - DRUPAL_TI_RUNNERS="phpunit-core simpletest" 93 | 94 | # This will create the database 95 | mysql: 96 | database: drupal_travis_db 97 | username: root 98 | encoding: utf8 99 | 100 | # To be able to run a webbrowser 101 | # If we need anything more powerful 102 | # than e.g. phantomjs 103 | before_install: 104 | - composer self-update 105 | - composer global require "lionsad/drupal_ti:1.*" 106 | - drupal-ti before_install 107 | 108 | install: 109 | - drupal-ti install 110 | 111 | before_script: 112 | - drupal-ti --include .travis-before-script.sh 113 | - drupal-ti before_script 114 | 115 | script: 116 | - drupal-ti script 117 | 118 | after_script: 119 | - drupal-ti after_script 120 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "drupal/file_entity", 3 | "description": "Extends Drupal file entities to be fieldable and viewable.", 4 | "type": "drupal-module", 5 | "license": "GPL-2.0+", 6 | "minimum-stability": "dev", 7 | "require": { } 8 | } 9 | -------------------------------------------------------------------------------- /config/install/core.entity_form_display.file.image.default.yml: -------------------------------------------------------------------------------- 1 | langcode: en 2 | status: true 3 | dependencies: 4 | config: 5 | - field.field.file.image.field_image_alt_text 6 | - field.field.file.image.field_image_title_text 7 | - file_entity.type.image 8 | id: file.image.default 9 | targetEntityType: file 10 | bundle: image 11 | mode: default 12 | content: 13 | field_image_title_text: 14 | type: string_textfield 15 | weight: 0 16 | settings: 17 | size: 60 18 | placeholder: '' 19 | third_party_settings: { } 20 | field_image_alt_text: 21 | type: string_textfield 22 | weight: 0 23 | settings: 24 | size: 60 25 | placeholder: '' 26 | third_party_settings: { } 27 | hidden: { } 28 | -------------------------------------------------------------------------------- /config/install/core.entity_view_display.file.audio.thumbnail.yml: -------------------------------------------------------------------------------- 1 | langcode: en 2 | status: true 3 | dependencies: 4 | config: 5 | - core.entity_view_mode.file.thumbnail 6 | - file_entity.type.audio 7 | id: file.audio.thumbnail 8 | targetEntityType: file 9 | bundle: audio 10 | mode: thumbnail 11 | content: 12 | filename: 13 | type: string 14 | weight: 0 15 | settings: 16 | link_to_entity: false 17 | third_party_settings: { } 18 | label: hidden 19 | hidden: 20 | filemime: true 21 | filesize: true 22 | uid: true 23 | uri: true 24 | -------------------------------------------------------------------------------- /config/install/core.entity_view_display.file.document.thumbnail.yml: -------------------------------------------------------------------------------- 1 | langcode: en 2 | status: true 3 | dependencies: 4 | config: 5 | - core.entity_view_mode.file.thumbnail 6 | - file_entity.type.document 7 | id: file.document.thumbnail 8 | targetEntityType: file 9 | bundle: document 10 | mode: thumbnail 11 | content: 12 | filename: 13 | type: string 14 | weight: 0 15 | settings: 16 | link_to_entity: false 17 | third_party_settings: { } 18 | label: hidden 19 | hidden: 20 | filemime: true 21 | filesize: true 22 | uid: true 23 | uri: true 24 | -------------------------------------------------------------------------------- /config/install/core.entity_view_display.file.image.default.yml: -------------------------------------------------------------------------------- 1 | langcode: en 2 | status: true 3 | dependencies: 4 | config: 5 | - field.field.file.image.field_image_alt_text 6 | - field.field.file.image.field_image_title_text 7 | - file_entity.type.image 8 | id: file.image.default 9 | label: null 10 | targetEntityType: file 11 | bundle: image 12 | mode: default 13 | content: 14 | field_image_alt_text: 15 | type: string 16 | weight: 0 17 | settings: { } 18 | third_party_settings: { } 19 | label: above 20 | field_image_title_text: 21 | type: string 22 | weight: 0 23 | settings: { } 24 | third_party_settings: { } 25 | label: above 26 | hidden: { } 27 | -------------------------------------------------------------------------------- /config/install/core.entity_view_display.file.image.thumbnail.yml: -------------------------------------------------------------------------------- 1 | langcode: en 2 | status: true 3 | dependencies: 4 | config: 5 | - core.entity_view_mode.file.thumbnail 6 | - field.field.file.image.field_image_alt_text 7 | - field.field.file.image.field_image_title_text 8 | - file_entity.type.image 9 | module: 10 | - file_entity 11 | id: file.image.thumbnail 12 | targetEntityType: file 13 | bundle: image 14 | mode: thumbnail 15 | content: 16 | filename: 17 | type: string 18 | weight: 1 19 | label: visually_hidden 20 | settings: 21 | link_to_entity: false 22 | third_party_settings: { } 23 | uri: 24 | type: file_image 25 | label: hidden 26 | weight: 0 27 | settings: 28 | image_style: thumbnail 29 | image_link: '' 30 | third_party_settings: { } 31 | hidden: 32 | field_image_alt_text: true 33 | field_image_title_text: true 34 | filemime: true 35 | filesize: true 36 | uid: true 37 | -------------------------------------------------------------------------------- /config/install/core.entity_view_display.file.video.thumbnail.yml: -------------------------------------------------------------------------------- 1 | langcode: en 2 | status: true 3 | dependencies: 4 | config: 5 | - core.entity_view_mode.file.thumbnail 6 | - file_entity.type.video 7 | id: file.video.thumbnail 8 | targetEntityType: file 9 | bundle: video 10 | mode: thumbnail 11 | content: 12 | filename: 13 | type: string 14 | weight: 1 15 | label: hidden 16 | settings: 17 | link_to_entity: false 18 | third_party_settings: { } 19 | hidden: 20 | filemime: true 21 | filesize: true 22 | uid: true 23 | uri: true 24 | -------------------------------------------------------------------------------- /config/install/core.entity_view_mode.file.thumbnail.yml: -------------------------------------------------------------------------------- 1 | langcode: en 2 | status: true 3 | dependencies: 4 | module: 5 | - file 6 | - file_entity 7 | id: file.thumbnail 8 | label: Thumbnail 9 | targetEntityType: file 10 | cache: true 11 | -------------------------------------------------------------------------------- /config/install/field.field.file.image.field_image_alt_text.yml: -------------------------------------------------------------------------------- 1 | langcode: en 2 | status: true 3 | dependencies: 4 | enforced: 5 | module: 6 | - file_entity 7 | config: 8 | - field.storage.file.field_image_alt_text 9 | - file_entity.type.image 10 | id: file.image.field_image_alt_text 11 | label: 'Alt Text' 12 | field_name: field_image_alt_text 13 | entity_type: file 14 | bundle: image 15 | description: 'Alternative text is used by screen readers, search engines, and when the image cannot be loaded. By adding alt text you improve accessibility and search engine optimization.' 16 | required: false 17 | translatable: false 18 | default_value: { } 19 | default_value_function: '' 20 | settings: { } 21 | field_type: string 22 | -------------------------------------------------------------------------------- /config/install/field.field.file.image.field_image_title_text.yml: -------------------------------------------------------------------------------- 1 | langcode: en 2 | status: true 3 | dependencies: 4 | enforced: 5 | module: 6 | - file_entity 7 | config: 8 | - field.storage.file.field_image_title_text 9 | - file_entity.type.image 10 | id: file.image.field_image_title_text 11 | label: 'Title Text' 12 | field_name: field_image_title_text 13 | entity_type: file 14 | bundle: image 15 | description: 'Title text is used in the tool tip when a user hovers their mouse over the image. Adding title text makes it easier to understand the context of an image and improves usability.' 16 | required: false 17 | translatable: false 18 | default_value: { } 19 | default_value_function: '' 20 | settings: { } 21 | field_type: string 22 | -------------------------------------------------------------------------------- /config/install/field.storage.file.field_image_alt_text.yml: -------------------------------------------------------------------------------- 1 | langcode: en 2 | status: true 3 | dependencies: 4 | enforced: 5 | module: 6 | - file_entity 7 | module: 8 | - file 9 | id: file.field_image_alt_text 10 | field_name: field_image_alt_text 11 | entity_type: file 12 | type: string 13 | settings: 14 | max_length: 255 15 | module: text 16 | locked: false 17 | cardinality: 1 18 | translatable: true 19 | indexes: { } 20 | -------------------------------------------------------------------------------- /config/install/field.storage.file.field_image_title_text.yml: -------------------------------------------------------------------------------- 1 | langcode: en 2 | status: true 3 | dependencies: 4 | enforced: 5 | module: 6 | - file_entity 7 | module: 8 | - file 9 | id: file.field_image_title_text 10 | field_name: field_image_title_text 11 | entity_type: file 12 | type: string 13 | settings: 14 | max_length: 255 15 | module: text 16 | locked: false 17 | cardinality: 1 18 | translatable: true 19 | indexes: { } 20 | -------------------------------------------------------------------------------- /config/install/file_entity.settings.yml: -------------------------------------------------------------------------------- 1 | max_filesize: 0 2 | default_allowed_extensions: 'jpg jpeg gif png txt doc docx xls xlsx pdf ppt pptx pps ppsx odt ods odp mp3 mov mp4 m4a m4v mpeg avi ogg oga ogv weba webp webm' 3 | alt: '[file:field_file_image_alt_text:value]' 4 | title: '[file:field_file_image_title_text:value]' 5 | wizard_skip_file_type: false 6 | wizard_skip_scheme: false 7 | wizard_skip_fields: false 8 | allow_insecure_download: false 9 | -------------------------------------------------------------------------------- /config/install/file_entity.type.audio.yml: -------------------------------------------------------------------------------- 1 | id: audio 2 | label: Audio 3 | description: 'An Audio file is a sound recording.' 4 | status: true 5 | mimetypes: 6 | - 'audio/*' 7 | -------------------------------------------------------------------------------- /config/install/file_entity.type.document.yml: -------------------------------------------------------------------------------- 1 | id: document 2 | label: Document 3 | description: 'A Document file is written information.' 4 | status: true 5 | mimetypes: 6 | - 'text/plain' 7 | - 'application/msword' 8 | - 'application/vnd.ms-excel' 9 | - 'application/pdf' 10 | - 'application/vnd.ms-powerpoint' 11 | - 'application/vnd.oasis.opendocument.text' 12 | - 'application/vnd.oasis.opendocument.spreadsheet' 13 | - 'application/vnd.oasis.opendocument.presentation' 14 | - 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' 15 | - 'application/vnd.openxmlformats-officedocument.presentationml.presentation' 16 | - 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' 17 | -------------------------------------------------------------------------------- /config/install/file_entity.type.image.yml: -------------------------------------------------------------------------------- 1 | id: image 2 | label: Image 3 | description: 'An Image file is a still visual.' 4 | status: true 5 | mimetypes: 6 | - 'image/*' 7 | -------------------------------------------------------------------------------- /config/install/file_entity.type.video.yml: -------------------------------------------------------------------------------- 1 | id: video 2 | label: Video 3 | description: 'A Video file is a moving visual recording.' 4 | status: true 5 | mimetypes: 6 | - 'video/*' 7 | -------------------------------------------------------------------------------- /config/install/system.action.file_delete_action.yml: -------------------------------------------------------------------------------- 1 | id: file_delete_action 2 | label: 'Delete file' 3 | status: true 4 | langcode: en 5 | type: file 6 | plugin: file_delete_action 7 | -------------------------------------------------------------------------------- /config/install/system.action.file_permanent_action.yml: -------------------------------------------------------------------------------- 1 | id: file_permanent_action 2 | label: 'Set file status to permanent' 3 | status: true 4 | langcode: en 5 | type: file 6 | plugin: file_permanent_action 7 | -------------------------------------------------------------------------------- /config/install/system.action.file_temporary_action.yml: -------------------------------------------------------------------------------- 1 | id: file_temporary_action 2 | label: 'Set file status to temporary' 3 | status: true 4 | langcode: en 5 | type: file 6 | plugin: file_temporary_action 7 | -------------------------------------------------------------------------------- /config/schema/file_entity.schema.yml: -------------------------------------------------------------------------------- 1 | file_entity.type.*: 2 | type: config_entity 3 | label: 'File type' 4 | mapping: 5 | id: 6 | type: string 7 | label: 'ID' 8 | label: 9 | type: label 10 | label: 'Label' 11 | description: 12 | type: text 13 | label: 'Description' 14 | mimetypes: 15 | type: sequence 16 | label: 'MIME types' 17 | sequence: 18 | - type: string 19 | label: 'MIME type' 20 | 21 | file_entity.settings: 22 | type: config_object 23 | label: 'File Entity settings' 24 | mapping: 25 | max_filesize: 26 | type: integer 27 | label: 'Maximum upload size' 28 | default_allowed_extensions: 29 | type: string 30 | label: 'Default allowed file extensions' 31 | alt: 32 | type: string 33 | label: 'Alt attribute' 34 | title: 35 | type: string 36 | label: 'Title attribute' 37 | wizard_skip_file_type: 38 | type: boolean 39 | label: 'Skip filetype selection' 40 | wizard_skip_scheme: 41 | type: boolean 42 | label: 'Skip scheme selection' 43 | wizard_skip_fields: 44 | type: boolean 45 | label: 'Skip available fields' 46 | allow_insecure_download: 47 | type: boolean 48 | label: Allow insecure downloads 49 | 50 | field.formatter.settings.file_image: 51 | type: field.formatter.settings.image 52 | label: 'Image formatter for file entity settings' 53 | mapping: 54 | title: 55 | type: string 56 | label: 'Title field' 57 | alt: 58 | type: string 59 | label: 'Alt field' 60 | 61 | field.formatter.settings.file_size: 62 | type: mapping 63 | 64 | field.formatter.settings.file_download_link: 65 | type: mapping 66 | label: 'File download link display format settings' 67 | mapping: 68 | access_message: 69 | type: string 70 | label: 'Access message' 71 | text: 72 | type: string 73 | label: 'Link text' 74 | 75 | field.formatter.settings.file_audio: 76 | type: mapping 77 | label: 'File audio display format settings' 78 | mapping: 79 | controls: 80 | type: boolean 81 | label: 'Show audio controls' 82 | autoplay: 83 | type: boolean 84 | label: 'Autoplay' 85 | loop: 86 | type: boolean 87 | label: 'Loop' 88 | multiple_file_behavior: 89 | type: string 90 | label: 'Display of multiple files' 91 | 92 | field.formatter.settings.file_video: 93 | type: mapping 94 | label: 'File video display format settings' 95 | mapping: 96 | controls: 97 | type: boolean 98 | label: 'Show audio controls' 99 | autoplay: 100 | type: boolean 101 | label: 'Autoplay' 102 | loop: 103 | type: boolean 104 | label: 'Loop' 105 | muted: 106 | type: boolean 107 | label: 'Muted' 108 | width: 109 | type: integer 110 | label: 'Width' 111 | height: 112 | type: integer 113 | label: 'Height' 114 | multiple_file_behavior: 115 | type: string 116 | label: 'Display of multiple files' 117 | 118 | action.configuration.file_delete_action: 119 | type: action_configuration_default 120 | label: 'Delete file configuration' 121 | 122 | action.configuration.file_permanent_action: 123 | type: action_configuration_default 124 | label: 'Set file to permanent configuration' 125 | 126 | action.configuration.file_temporary_action: 127 | type: action_configuration_default 128 | label: 'Set file to temporary configuration' 129 | 130 | field.widget.settings.file_editable: 131 | type: field.widget.settings.file_generic 132 | label: 'File format settings' 133 | -------------------------------------------------------------------------------- /file_entity.api.php: -------------------------------------------------------------------------------- 1 | condition('timestamp', REQUEST_TIME - 3600, '<='); 21 | } 22 | 23 | /** 24 | * Alter file download headers. 25 | * 26 | * @param array $headers 27 | * Array of download headers. 28 | * @param object $file 29 | * File object. 30 | */ 31 | function hook_file_download_headers_alter(array &$headers, $file) { 32 | // Instead of being powered by PHP, tell the world this resource was powered 33 | // by your custom module! 34 | $headers['X-Powered-By'] = 'My Module'; 35 | } 36 | 37 | /** 38 | * React to a file being downloaded. 39 | */ 40 | function hook_file_transfer($uri, array $headers) { 41 | // Redirect a download for an S3 file to the actual location. 42 | if (file_uri_scheme($uri) == 's3') { 43 | $url = file_create_url($uri); 44 | drupal_goto($url); 45 | } 46 | } 47 | 48 | /** 49 | * Decides which file type (bundle) should be assigned to a file entity. 50 | * 51 | * @param object $file 52 | * File object. 53 | * 54 | * @return array 55 | * Array of file type machine names that can be assigned to a given file type. 56 | * If there are more proposed file types the one, that was returned the first, 57 | * wil be chosen. This can be, however, changed in alter hook. 58 | * 59 | * @see hook_file_type_alter() 60 | */ 61 | function hook_file_type($file) { 62 | // Assign all files uploaded by anonymous users to a special file type. 63 | if (user_is_anonymous()) { 64 | return array('untrusted_files'); 65 | } 66 | } 67 | 68 | /** 69 | * Alters list of file types that can be assigned to a file. 70 | * 71 | * @param array $types 72 | * List of proposed types. 73 | * @param object $file 74 | * File object. 75 | */ 76 | function hook_file_type_alter(&$types, $file) { 77 | // Choose a specific, non-first, file type. 78 | $types = array($types[4]); 79 | } 80 | 81 | /** 82 | * Provides metadata information. 83 | * 84 | * @todo Add documentation. 85 | * 86 | * @return array 87 | * An array of metadata information. 88 | */ 89 | function hook_file_metadata_info() { 90 | 91 | } 92 | 93 | /** 94 | * Alters metadata information. 95 | * 96 | * @todo Add documentation. 97 | * 98 | * @return array 99 | * an array of metadata information. 100 | */ 101 | function hook_file_metadata_info_alter() { 102 | 103 | } 104 | -------------------------------------------------------------------------------- /file_entity.file.inc: -------------------------------------------------------------------------------- 1 | getMimeTypes(), $file->getMimeType())) { 18 | $types[] = $type->id(); 19 | } 20 | } 21 | 22 | return $types; 23 | } 24 | 25 | /** 26 | * Implements hook_file_metadata_info(). 27 | */ 28 | function file_entity_file_metadata_info() { 29 | $info['width'] = array('label' => t('Width'), 'type' => 'integer'); 30 | $info['height'] = array('label' => t('Height'), 'type' => 'integer'); 31 | return $info; 32 | } 33 | -------------------------------------------------------------------------------- /file_entity.info.yml: -------------------------------------------------------------------------------- 1 | type: module 2 | name: File entity 3 | description: "Extends Drupal file entities to be fieldable and viewable." 4 | package: Media 5 | core: 8.x 6 | dependencies: 7 | - drupal:file 8 | - drupal:text 9 | - drupal:views 10 | - drupal:image 11 | - token:token 12 | configure: file_entity.settings 13 | test_dependencies: 14 | - pathauto:pathauto 15 | -------------------------------------------------------------------------------- /file_entity.install: -------------------------------------------------------------------------------- 1 | 'Cache images dimensions.', 17 | 'fields' => array( 18 | 'fid' => array( 19 | 'description' => 'The {file_managed}.fid of the metadata.', 20 | 'type' => 'int', 21 | 'unsigned' => TRUE, 22 | 'not null' => TRUE, 23 | 'default' => 0, 24 | ), 25 | 'name' => array( 26 | 'description' => "The name of the metadata (e.g. 'width').", 27 | 'type' => 'varchar', 28 | 'length' => '128', 29 | 'not null' => TRUE, 30 | ), 31 | 'value' => array( 32 | 'description' => "The value of the metadata (e.g. '200px').", 33 | 'type' => 'blob', 34 | 'not null' => FALSE, 35 | 'size' => 'big', 36 | 'serialize' => TRUE, 37 | ), 38 | ), 39 | 'primary key' => array('fid', 'name'), 40 | 'foreign keys' => array( 41 | 'file_managed' => array( 42 | 'table' => 'file_managed', 43 | 'columns' => array('fid' => 'fid'), 44 | ), 45 | ), 46 | ); 47 | return $schema; 48 | } 49 | 50 | /** 51 | * Implements hook_install(). 52 | */ 53 | function file_entity_install() { 54 | $type_storage_definition = \Drupal::service('entity_field.manager')->getFieldStorageDefinitions('file')['type']; 55 | \Drupal::entityDefinitionUpdateManager()->installFieldStorageDefinition('type', 'file', 'file_entity', $type_storage_definition); 56 | // Set permissions. 57 | $roles = user_roles(); 58 | foreach ($roles as $rid => $role) { 59 | user_role_grant_permissions($rid, array('view files')); 60 | } 61 | 62 | // Classify existing files according to the currently defined file types. 63 | // Queue all files to be classified during cron runs using the Queue API. 64 | $queue = \Drupal::queue('file_entity_type_determine'); 65 | $ids = \Drupal::entityQuery('file') 66 | ->execute(); 67 | foreach ($ids as $id) { 68 | $queue->createItem($id); 69 | } 70 | 71 | // Warn users that existing files will not have a file type until the queue 72 | // has been processed. 73 | if ($queue->numberOfItems()) { 74 | drupal_set_message(t('Existing files must be classified according to the currently defined file types. These files have been queued for processing and will have their file type determined during cron runs.')); 75 | } 76 | 77 | // Disable the core files view. 78 | if ($view = View::load('files')) { 79 | $view->set('status', FALSE); 80 | $view->save(); 81 | } 82 | } 83 | 84 | /** 85 | * Implements hook_uninstall(). 86 | */ 87 | function file_entity_uninstall() { 88 | // Remove the added column to the core {file_managed} table. 89 | db_drop_field('file_managed', 'type'); 90 | } 91 | 92 | /** 93 | * Update existing bogus entries for File types Document. 94 | */ 95 | function file_entity_update_8002(&$sandbox) { 96 | foreach (\Drupal::configFactory()->listAll('file_entity') as $config_name) { 97 | $config = \Drupal::configFactory()->getEditable($config_name); 98 | $config->set('mimetypes', str_replace("\r", "", $config->get('mimetypes'))); 99 | $config->save(); 100 | } 101 | } 102 | 103 | /** 104 | * Update entity form displays to use the new entity browser widget. 105 | */ 106 | function file_entity_update_8003() { 107 | 108 | /** @var \Drupal\Core\Entity\Entity\EntityFormDisplay $form_display */ 109 | foreach (EntityFormDisplay::loadMultiple() as $form_display) { 110 | $components = $form_display->getComponents(); 111 | 112 | $changed = FALSE; 113 | foreach ($components as $name => $options) { 114 | if (isset($options['type']) && $options['type'] == 'file_entity_browser') { 115 | $options['type'] = 'entity_browser_file'; 116 | $form_display->setComponent($name, $options); 117 | $changed = TRUE; 118 | } 119 | } 120 | 121 | if ($changed) { 122 | $form_display->save(); 123 | } 124 | } 125 | 126 | } 127 | -------------------------------------------------------------------------------- /file_entity.links.action.yml: -------------------------------------------------------------------------------- 1 | file_entity.file_type_add: 2 | route_name: entity.file_type.add_form 3 | title: 'Add file type' 4 | appears_on: 5 | - entity.file_type.collection 6 | 7 | entity.file.add_form: 8 | route_name: entity.file.add_form 9 | title: 'Add file' 10 | appears_on: 11 | - entity.file.collection 12 | -------------------------------------------------------------------------------- /file_entity.links.menu.yml: -------------------------------------------------------------------------------- 1 | file_entity.types: 2 | title: 'File types' 3 | description: 'Manage settings for the type of files used on your site.' 4 | route_name: entity.file_type.collection 5 | parent: system.admin_structure 6 | 7 | 8 | file_entity.settings: 9 | title: File settings 10 | description: 'Configure allowed file extensions, default alt and title sources, and the file upload wizard.' 11 | route_name: file_entity.settings 12 | parent: system.admin_config_media 13 | weight: 10 14 | -------------------------------------------------------------------------------- /file_entity.links.task.yml: -------------------------------------------------------------------------------- 1 | entity.file_type.edit_form: 2 | route_name: entity.file_type.edit_form 3 | title: 'Edit' 4 | base_route: entity.file_type.edit_form 5 | 6 | file_entity.file: 7 | route_name: entity.file.canonical 8 | title: 'View' 9 | base_route: entity.file.canonical 10 | weight: -5 11 | 12 | entity.file.edit_form: 13 | route_name: entity.file.edit_form 14 | title: 'Edit' 15 | base_route: entity.file.canonical 16 | weight: 0 17 | 18 | entity.file.collection: 19 | route_name: entity.file.collection 20 | base_route: system.admin_content 21 | title: 'Files' 22 | description: 'Manage files for your site.' 23 | 24 | entity.file.add_form: 25 | route_name: entity.file.add_form 26 | base_route: entity.file.add_form 27 | title: 'File' 28 | 29 | file_entity.file_add_archive_form: 30 | route_name: file_entity.file_add_archive_form 31 | title: 'Archive' 32 | base_route: entity.file.add_form 33 | -------------------------------------------------------------------------------- /file_entity.permissions.yml: -------------------------------------------------------------------------------- 1 | bypass file access: 2 | title: 'Bypass file access control' 3 | description: 'View, edit and delete all files regardless of permission restrictions.' 4 | restrict access: true 5 | administer file types: 6 | title: 'Administer file types' 7 | restrict access: true 8 | administer files: 9 | title: 'Administer files' 10 | restrict access: true 11 | create files: 12 | title: 'Add and upload new files' 13 | view own private files: 14 | title: 'View own private files' 15 | view own files: 16 | title: 'View own files' 17 | view private files: 18 | title: 'View private files' 19 | restrict access: true 20 | view files: 21 | title: 'View files' 22 | 23 | permission_callbacks: 24 | - \Drupal\file_entity\FileEntityPermissions::extendPermissionDetails 25 | - \Drupal\file_entity\FileEntityPermissions::fileTypePermissions 26 | -------------------------------------------------------------------------------- /file_entity.routing.yml: -------------------------------------------------------------------------------- 1 | # @todo Move this back to admin/config/media/file-types in Drupal 8 if MenuTreeStorage::MAX_DEPTH is increased to a value higher than 9. 2 | entity.file_type.collection: 3 | path: /admin/structure/file-types 4 | defaults: 5 | _entity_list: 'file_type' 6 | _title: 'File types' 7 | requirements: 8 | _permission: 'administer file types' 9 | 10 | entity.file_type.add_form: 11 | path: /admin/structure/file-types/add 12 | defaults: 13 | _entity_form: 'file_type.default' 14 | _title: 'Add file type' 15 | requirements: 16 | _permission: 'administer file types' 17 | 18 | entity.file_type.edit_form: 19 | path: /admin/structure/file-types/manage/{file_type}/edit 20 | defaults: 21 | _entity_form: 'file_type.default' 22 | _title: 'Manage file types' 23 | requirements: 24 | _permission: 'administer file types' 25 | 26 | entity.file_type.enable: 27 | path: /admin/structure/file-types/manage/{file_type}/enable 28 | defaults: 29 | _entity_form: 'file_type.enable' 30 | _title: 'Enable file type' 31 | requirements: 32 | _permission: 'administer file types' 33 | 34 | entity.file_type.disable: 35 | path: /admin/structure/file-types/manage/{file_type}/disable 36 | defaults: 37 | _entity_form: 'file_type.disable' 38 | _title: 'Disable file type' 39 | requirements: 40 | _permission: 'administer file types' 41 | 42 | entity.file_type.delete_form: 43 | path: /admin/structure/file-types/manage/{file_type}/delete 44 | defaults: 45 | _entity_form: 'file_type.delete' 46 | _title: 'Delete file type' 47 | requirements: 48 | _permission: 'administer file types' 49 | 50 | entity.file.add_form: 51 | path: /file/add 52 | defaults: 53 | _form: '\Drupal\file_entity\Form\FileAddForm' 54 | _title: 'Add file' 55 | requirements: 56 | _entity_create_access: 'file' 57 | options: 58 | _admin_route: TRUE 59 | 60 | file_entity.file_add_upload: 61 | path: /file/add/upload 62 | defaults: 63 | _controller: '\Drupal\file_entity\Controller\FileController::FileAddUpload' 64 | _title: 'Upload' 65 | requirements: 66 | _permission: 'access content' 67 | options: 68 | _admin_route: TRUE 69 | 70 | file_entity.file_add_upload_file: 71 | path: /file/add/upload/file 72 | defaults: 73 | _controller: '\Drupal\file_entity\Controller\FileController::FileAddUploadFile' 74 | _title: 'File' 75 | requirements: 76 | _permission: 'access content' 77 | options: 78 | _admin_route: TRUE 79 | 80 | entity.file.canonical: 81 | path: /file/{file} 82 | defaults: 83 | _entity_view: 'file.full' 84 | _title: 'File' 85 | requirements: 86 | _entity_access: 'file.view' 87 | 88 | file_entity.file_download: 89 | path: /file/{file}/download 90 | defaults: 91 | _controller: '\Drupal\file_entity\Controller\FileController::download' 92 | _title: 'Download file' 93 | requirements: 94 | _entity_access: 'file.download' 95 | 96 | entity.file.edit_form: 97 | path: /file/{file}/edit 98 | defaults: 99 | _entity_form: 'file.edit' 100 | _title: 'Edit' 101 | requirements: 102 | _entity_access: 'file.update' 103 | options: 104 | _admin_route: TRUE 105 | 106 | entity.file.inline_edit_form: 107 | path: /file/{file}/inline-edit 108 | defaults: 109 | _controller: '\Drupal\file_entity\Controller\FileController::inlineEdit' 110 | requirements: 111 | _entity_access: 'file.update' 112 | 113 | entity.file.delete_form: 114 | path: /file/{file}/delete 115 | defaults: 116 | _entity_form: 'file.delete' 117 | _title: 'Delete file' 118 | requirements: 119 | _entity_access: 'file.delete' 120 | options: 121 | _admin_route: TRUE 122 | 123 | file_entity.multiple_delete_confirm: 124 | path: /admin/content/files/delete 125 | defaults: 126 | _form: '\Drupal\file_entity\Form\FileDeleteMultipleForm' 127 | requirements: 128 | _permission: 'administer files' 129 | 130 | file_entity.settings: 131 | path: /admin/config/media/file-settings 132 | defaults: 133 | _form: '\Drupal\file_entity\Form\FileSettingsForm' 134 | _title: 'File settings' 135 | requirements: 136 | _permission: 'administer files' 137 | 138 | file_entity.file_add_archive_form: 139 | path: /admin/content/files/archive 140 | defaults: 141 | _form: '\Drupal\file_entity\Form\FileAddArchiveForm' 142 | _title: 'Upload archive' 143 | requirements: 144 | _permission: 'administer files' 145 | options: 146 | _admin_route: TRUE 147 | 148 | -------------------------------------------------------------------------------- /file_entity.services.yml: -------------------------------------------------------------------------------- 1 | services: 2 | file_entity.route_subscriber: 3 | class: Drupal\file_entity\Routing\RouteSubscriber 4 | tags: 5 | - { name: event_subscriber } 6 | -------------------------------------------------------------------------------- /file_entity.theme.inc: -------------------------------------------------------------------------------- 1 | fid; 18 | $icon = theme('file_icon', array('file' => $file, 'icon_directory' => $icon_directory)); 19 | 20 | // Set options as per anchor format described at 21 | // http://microformats.org/wiki/file-format-examples 22 | $options = array( 23 | 'attributes' => array( 24 | 'type' => $file->filemime . '; length=' . $file->filesize, 25 | ), 26 | ); 27 | 28 | // Use the description as the link text if available. 29 | if (empty($file->description)) { 30 | $link_text = $file->filename; 31 | } 32 | else { 33 | $link_text = $file->description; 34 | $options['attributes']['title'] = check_plain($file->filename); 35 | } 36 | 37 | return '' . $icon . ' ' . l($link_text, $url, $options) . ''; 38 | } 39 | -------------------------------------------------------------------------------- /file_entity.tokens.inc: -------------------------------------------------------------------------------- 1 | t('File type'), 19 | 'description' => t('Tokens associated with file types.'), 20 | 'needs-data' => 'file_type', 21 | ); 22 | $info['tokens']['file-type']['name'] = array( 23 | 'name' => t('Name'), 24 | 'description' => t('The name of the file type.'), 25 | ); 26 | $info['tokens']['file-type']['machine-name'] = array( 27 | 'name' => t('Machine-readable name'), 28 | 'description' => t('The unique machine-readable name of the file type.'), 29 | ); 30 | $info['tokens']['file-type']['count'] = array( 31 | 'name' => t('File count'), 32 | 'description' => t('The number of files belonging to the file type.'), 33 | ); 34 | $info['tokens']['file-type']['edit-url'] = array( 35 | 'name' => t('Edit URL'), 36 | 'description' => t("The URL of the file type's edit page."), 37 | ); 38 | 39 | // File tokens. 40 | $info['tokens']['file']['type'] = array( 41 | 'name' => t('File type'), 42 | 'description' => t('The file type of the file.'), 43 | 'type' => 'file-type', 44 | ); 45 | $info['tokens']['file']['download-url'] = array( 46 | 'name' => t('Download URL'), 47 | 'description' => t('The URL to download the file directly.'), 48 | 'type' => 'url', 49 | ); 50 | 51 | return $info; 52 | } 53 | 54 | /** 55 | * Implements hook_token_info_alter(). 56 | */ 57 | function file_entity_token_info_alter(&$info) { 58 | $info['tokens']['file']['name']['description'] = t('The name of the file.'); 59 | } 60 | 61 | /** 62 | * Implements hook_tokens(). 63 | */ 64 | function file_entity_tokens($type, $tokens, array $data, array $options, BubbleableMetadata $bubbleable_metadata) { 65 | $replacements = array(); 66 | 67 | $url_options = array('absolute' => TRUE); 68 | if (isset($options['langcode'])) { 69 | $langcode = $options['langcode']; 70 | $url_options['language'] = \Drupal::languageManager()->getLanguage($langcode); 71 | } 72 | else { 73 | $langcode = NULL; 74 | } 75 | 76 | $sanitize = !empty($options['sanitize']); 77 | 78 | // File tokens. 79 | if ($type == 'file' && !empty($data['file'])) { 80 | $file = $data['file']; 81 | 82 | foreach ($tokens as $name => $original) { 83 | switch ($name) { 84 | case 'type': 85 | if ($file_type = FileType::load($file->bundle())) { 86 | $bubbleable_metadata->addCacheableDependency($file_type); 87 | $replacements[$original] = $sanitize ? SafeMarkup::checkPlain($file_type->label()) : $file_type->label(); 88 | } 89 | break; 90 | 91 | case 'download-url': 92 | $replacements[$original] = $file->downloadUrl($url_options)->toString(); 93 | break; 94 | } 95 | } 96 | 97 | // Chained token relationships. 98 | $token_service = \Drupal::service('token'); 99 | if (($file_type_tokens = $token_service->findWithPrefix($tokens, 'type')) && $file_type = FileType::load($file->bundle())) { 100 | $replacements += $token_service->generate('file-type', $file_type_tokens, array('file_type' => $file_type), $options, $bubbleable_metadata); 101 | } 102 | if ($download_url_tokens = $token_service->findWithPrefix($tokens, 'download-url')) { 103 | $replacements += $token_service->generate('url', $download_url_tokens, $file->downloadUrl()->toString(), $options, $bubbleable_metadata); 104 | } 105 | } 106 | 107 | // File type tokens. 108 | if ($type == 'file-type' && !empty($data['file_type'])) { 109 | $file_type = $data['file_type']; 110 | 111 | foreach ($tokens as $name => $original) { 112 | switch ($name) { 113 | case 'name': 114 | $replacements[$original] = $sanitize ? SafeMarkup::checkPlain($file_type->label()) : $file_type->label(); 115 | break; 116 | 117 | case 'machine-name': 118 | // This is a machine name so does not ever need to be sanitized. 119 | $replacements[$original] = $file_type->id(); 120 | break; 121 | 122 | case 'count': 123 | $query = db_select('file_managed'); 124 | $query->condition('type', $file_type->id()); 125 | $query->addTag('file_type_file_count'); 126 | $count = $query->countQuery()->execute()->fetchField(); 127 | $replacements[$original] = (int) $count; 128 | break; 129 | 130 | case 'edit-url': 131 | $replacements[$original] = Url::fromUri('admin/structure/file-types/manage/' . $file_type->type . '/fields', $url_options)->toString(); 132 | break; 133 | } 134 | } 135 | } 136 | 137 | return $replacements; 138 | } 139 | -------------------------------------------------------------------------------- /file_entity.views.inc: -------------------------------------------------------------------------------- 1 | t('Filename'), 22 | 'help' => t('File name with optional link to view.'), 23 | 'field' => array( 24 | 'id' => 'file_name', 25 | ), 26 | 'sort' => array( 27 | 'id' => 'standard', 28 | ), 29 | 'filter' => array( 30 | 'id' => 'string', 31 | ), 32 | 'argument' => array( 33 | 'id' => 'string', 34 | ), 35 | ); 36 | 37 | // File type. 38 | $data['file_managed']['type'] = array( 39 | 'title' => t('Type'), 40 | 'help' => t('The type of the file (for example, "audio", "image", "video", etc).'), 41 | 'field' => array( 42 | 'id' => 'file_entity_type', 43 | ), 44 | 'sort' => array( 45 | 'id' => 'standard', 46 | ), 47 | 'filter' => array( 48 | 'id' => 'file_entity_type', 49 | ), 50 | ); 51 | 52 | // File schema type. 53 | $data['file_managed']['schema_type'] = array( 54 | 'title' => t('Schema type'), 55 | 'help' => t('Filter files by schema, such as public or private.'), 56 | 'filter' => array( 57 | 'handler' => 'views_handler_filter_schema_type', 58 | ), 59 | ); 60 | 61 | // Rendered file. 62 | $data['file_managed']['rendered'] = array( 63 | 'title' => t('Rendered'), 64 | 'help' => t('Display the file in a specific view mode.'), 65 | 'field' => array( 66 | 'handler' => 'views_handler_field_file_rendered', 67 | 'click sortable' => TRUE, 68 | 'real field' => 'fid', 69 | 'additional fields' => array( 70 | 'fid', 71 | ), 72 | ), 73 | ); 74 | 75 | // View link. 76 | $data['file_managed']['link'] = array( 77 | 'title' => t('Link'), 78 | 'help' => t('Provide a simple link to the file entity.'), 79 | 'field' => array( 80 | 'handler' => 'views_handler_field_file_link', 81 | 'real field' => 'fid', 82 | 'additional fields' => array( 83 | 'fid', 84 | ), 85 | ), 86 | ); 87 | 88 | // View link. 89 | $data['file_managed']['view'] = array( 90 | 'field' => array( 91 | 'title' => t('Link to file'), 92 | 'help' => t('Provide a simple link to the file.'), 93 | 'id' => 'file_entity_link', 94 | ), 95 | ); 96 | 97 | // Edit link. 98 | $data['file_managed']['edit'] = array( 99 | 'title' => t('Edit link'), 100 | 'help' => t('Provide a simple link to edit the file entity.'), 101 | 'field' => array( 102 | 'id' => 'file_entity_link_edit', 103 | 'real field' => 'fid', 104 | ), 105 | ); 106 | 107 | // Delete link. 108 | $data['file_managed']['delete'] = array( 109 | 'title' => t('Delete link'), 110 | 'help' => t('Provide a simple link to delete the file entity.'), 111 | 'field' => array( 112 | 'id' => 'file_entity_link_delete', 113 | 'real field' => 'fid', 114 | ), 115 | ); 116 | 117 | // Download link. 118 | $data['file_managed']['download'] = array( 119 | 'title' => t('Download link'), 120 | 'help' => t('Provide a simple link to download the file entity.'), 121 | 'field' => array( 122 | 'id' => 'file_entity_link_download', 123 | 'real field' => 'fid', 124 | ), 125 | ); 126 | 127 | // Usage link. 128 | $data['file_managed']['usage'] = array( 129 | 'title' => t('Usage link'), 130 | 'help' => t('Provide a simple link to view the usage of the file entity.'), 131 | 'field' => array( 132 | 'handler' => 'views_handler_field_file_link_usage', 133 | 'click sortable' => TRUE, 134 | 'real field' => 'fid', 135 | 'additional fields' => array( 136 | 'fid', 137 | ), 138 | ), 139 | 'sort' => array( 140 | 'handler' => 'views_handler_sort', 141 | ), 142 | ); 143 | 144 | $data['file_managed']['bulk_form'] = array( 145 | 'title' => 'File operations bulk form', 146 | 'help' => 'Form elements to perform operations on multiple files at once.', 147 | 'field' => array( 148 | 'id' => 'bulk_form', 149 | ), 150 | ); 151 | 152 | // @todo This should really be added in file.views.inc 153 | $data['file_usage']['table']['join'] = array( 154 | 'file_managed' => array( 155 | 'field' => 'fid', 156 | 'left_field' => 'fid', 157 | ), 158 | ); 159 | } 160 | -------------------------------------------------------------------------------- /src/Controller/FileController.php: -------------------------------------------------------------------------------- 1 | config('file_entity.settings')->get('allow_insecure_download')) { 60 | if (!isset($_GET['token']) || $_GET['token'] !== $file->getDownloadToken()) { 61 | return new Response(t('Access to file @url denied', array('@url' => $file->getFileUri())), 403); 62 | } 63 | } 64 | 65 | $headers = array( 66 | 'Content-Type' => Unicode::mimeHeaderEncode($file->getMimeType()), 67 | 'Content-Disposition' => 'attachment; filename="' . Unicode::mimeHeaderEncode(drupal_basename($file->getFileUri())) . '"', 68 | 'Content-Length' => $file->getSize(), 69 | 'Content-Transfer-Encoding' => 'binary', 70 | 'Pragma' => 'no-cache', 71 | 'Cache-Control' => 'must-revalidate, post-check=0, pre-check=0', 72 | 'Expires' => '0', 73 | ); 74 | 75 | // Let other modules alter the download headers. 76 | \Drupal::moduleHandler()->alter('file_download_headers', $headers, $file); 77 | 78 | // Let other modules know the file is being downloaded. 79 | \Drupal::moduleHandler()->invokeAll('file_transfer', array($file->getFileUri(), $headers)); 80 | 81 | try { 82 | return new BinaryFileResponse($file->getFileUri(), 200, $headers); 83 | } 84 | catch (FileNotFoundException $e) { 85 | return new Response(t('File @uri not found', array('@uri' =>$file->getFileUri())), 404); 86 | } 87 | } 88 | 89 | /** 90 | * Return an Ajax dialog command for editing a file inline. 91 | * 92 | * @param \Drupal\file\FileInterface $file 93 | * The file being edited. 94 | * 95 | * @return \Drupal\Core\Ajax\AjaxResponse 96 | * An Ajax response with a command for opening or closing the a dialog 97 | * containing the edit form. 98 | */ 99 | public function inlineEdit(FileInterface $file) { 100 | // Build the file edit form. 101 | $form_object = $this->entityManager()->getFormObject('file', 'inline_edit'); 102 | $form_object->setEntity($file); 103 | $form_state = (new FormState()) 104 | ->setFormObject($form_object) 105 | ->disableRedirect(); 106 | // Building the form also submits. 107 | $form = $this->formBuilder()->buildForm($form_object, $form_state); 108 | 109 | // Return a response, depending on whether it's successfully submitted. 110 | if (!$form_state->isExecuted()) { 111 | // Return the form as a modal dialog. 112 | $form['#attached']['library'][] = 'core/drupal.dialog.ajax'; 113 | $title = $this->t('Edit file @file', ['@file' => $file->label()]); 114 | $response = AjaxResponse::create()->addCommand(new OpenModalDialogCommand($title, $form, ['width' => 800])); 115 | return $response; 116 | } 117 | else { 118 | // Return command for closing the modal. 119 | return AjaxResponse::create()->addCommand(new CloseModalDialogCommand()); 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/Entity/FileEntityViewBuilder.php: -------------------------------------------------------------------------------- 1 | listUsage($entity) as $module => $module_references) { 33 | foreach ($module_references as $type => $ids) { 34 | if (\Drupal::entityManager()->hasDefinition($type)) { 35 | $build['#cache']['tags'] = Cache::mergeTags($build['#cache']['tags'], array($type . ':' . key($ids))); 36 | break 2; 37 | } 38 | } 39 | } 40 | return $build; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Entity/FileType.php: -------------------------------------------------------------------------------- 1 | description; 89 | } 90 | 91 | /** 92 | * {@inheritdoc} 93 | */ 94 | public function getMimeTypes() { 95 | return $this->mimetypes ?: array(); 96 | } 97 | 98 | /** 99 | * {@inheritdoc} 100 | */ 101 | public function setLabel($label) { 102 | $this->label = $label; 103 | } 104 | 105 | /** 106 | * {@inheritdoc} 107 | */ 108 | public function setDescription($description) { 109 | $this->description = $description; 110 | } 111 | 112 | /** 113 | * {@inheritdoc} 114 | */ 115 | public function setMimeTypes($mimetypes) { 116 | $this->mimetypes = array_values($mimetypes); 117 | } 118 | 119 | /** 120 | * {@inheritdoc} 121 | */ 122 | public static function loadEnabled($status = TRUE) { 123 | $types = array(); 124 | foreach (self::loadMultiple() as $id => $type) { 125 | if ($type->status == $status) { 126 | $types[$id] = $type; 127 | } 128 | } 129 | return $types; 130 | } 131 | 132 | /** 133 | * {@inheritdoc} 134 | */ 135 | public static function sort(ConfigEntityInterface $a, ConfigEntityInterface $b) { 136 | // Sort primarily by status, secondarily by label. 137 | if ($a->status() == $b->status()) { 138 | return strnatcasecmp($a->label(), $b->label()); 139 | } 140 | return ($b->status() - $a->status()); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/FileEntityAccessControlHandler.php: -------------------------------------------------------------------------------- 1 | prepareUser($account); 27 | $result = AccessResult::allowedIfHasPermission($account, 'bypass file access') 28 | ->orIf(parent::access($entity, $operation, $account, TRUE)); 29 | return $return_as_object ? $result : $result->isAllowed(); 30 | } 31 | 32 | /** 33 | * {@inheritdoc} 34 | */ 35 | public function createAccess($entity_bundle = NULL, AccountInterface $account = NULL, array $context = array(), $return_as_object = FALSE) { 36 | $account = $this->prepareUser($account); 37 | $result = AccessResult::allowedIfHasPermission($account, 'bypass file access') 38 | ->orIf(parent::createAccess($entity_bundle, $account, $context, TRUE)); 39 | return $return_as_object ? $result : $result->isAllowed(); 40 | } 41 | 42 | /** 43 | * {@inheritdoc} 44 | */ 45 | protected function checkCreateAccess(AccountInterface $account, array $context, $entity_bundle = NULL) { 46 | return AccessResult::allowedIfHasPermission($account, 'create files'); 47 | } 48 | 49 | /** 50 | * {@inheritdoc} 51 | */ 52 | protected function checkAccess(EntityInterface $entity, $operation, AccountInterface $account) { 53 | /** @var FileEntity $entity */ 54 | $is_owner = $entity->getOwnerId() === $account->id(); 55 | 56 | if ($operation == 'view') { 57 | $schemes = file_entity_get_public_and_private_stream_wrapper_names(); 58 | if (isset($schemes['private'][file_uri_scheme($entity->getFileUri())])) { 59 | return AccessResult::allowedIfHasPermission($account, 'view private files') 60 | ->orIf(AccessResult::allowedIf($account->isAuthenticated() && $is_owner)->addCacheableDependency($entity) 61 | ->andIf(AccessResult::allowedIfHasPermission($account, 'view own private files'))); 62 | } 63 | elseif ($entity->isPermanent()) { 64 | return AccessResult::allowedIfHasPermission($account, 'view files') 65 | ->orIf(AccessResult::allowedIf($is_owner)->addCacheableDependency($entity) 66 | ->andIf(AccessResult::allowedIfHasPermission($account, 'view own files'))); 67 | } 68 | } 69 | 70 | // User can perform these operations if they have the "any" permission or if 71 | // they own it and have the "own" permission. 72 | if (in_array($operation, array('download', 'update', 'delete'))) { 73 | $permission_action = $operation == 'update' ? 'edit' : $operation; 74 | $type = $entity->get('type')->target_id; 75 | return AccessResult::allowedIfHasPermission($account, "$permission_action any $type files") 76 | ->orIf(AccessResult::allowedIf($is_owner)->addCacheableDependency($entity) 77 | ->andIf(AccessResult::allowedIfHasPermission($account, "$permission_action own $type files"))); 78 | } 79 | 80 | // Fall back to the parent implementation so that file uploads work. 81 | // @todo Merge that in here somehow? 82 | return parent::checkAccess($entity, $operation, $account); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/FileEntityInterface.php: -------------------------------------------------------------------------------- 1 | array($this->t('None')), 28 | 'private' => array($this->t('None')), 29 | ); 30 | 31 | $permissions = array(); 32 | $permissions['view files']['description'] = $this->t('Includes the following stream wrappers: %wrappers.', array('%wrappers' => implode(', ', $wrappers['public']))); 33 | $permissions['view own private files']['description'] = $this->t('Includes the following stream wrappers: %wrappers.', array('%wrappers' => implode(', ', $wrappers['private']))); 34 | return $permissions; 35 | } 36 | 37 | /** 38 | * Generates standard file permissions for all applicable file types. 39 | * 40 | * @return array 41 | * File type permissions. 42 | */ 43 | public function fileTypePermissions() { 44 | // Generate standard file permissions for all applicable file types. 45 | $permissions = array(); 46 | foreach (FileType::loadEnabled() as $type) { 47 | /** @var \Drupal\file_entity\Entity\FileType $type */ 48 | $id = $type->id(); 49 | $permissions += array( 50 | "edit own $id files" => array( 51 | 'title' => $this->t('%type_name: Edit own files', array('%type_name' => $type->label())), 52 | ), 53 | "edit any $id files" => array( 54 | 'title' => $this->t('%type_name: Edit any files', array('%type_name' => $type->label())), 55 | ), 56 | "delete own $id files" => array( 57 | 'title' => $this->t('%type_name: Delete own files', array('%type_name' => $type->label())), 58 | ), 59 | "delete any $id files" => array( 60 | 'title' => $this->t('%type_name: Delete any files', array('%type_name' => $type->label())), 61 | ), 62 | "download own $id files" => array( 63 | 'title' => $this->t('%type_name: Download own files', array('%type_name' => $type->label())), 64 | ), 65 | "download any $id files" => array( 66 | 'title' => $this->t('%type_name: Download any files', array('%type_name' => $type->label())), 67 | ), 68 | ); 69 | } 70 | return $permissions; 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/FileEntityServiceProvider.php: -------------------------------------------------------------------------------- 1 | getParameter('container.modules'); 24 | // Check for installed REST and HAL modules. HAL does not require REST 25 | // anymore in 8.3 and later. 26 | if (isset($modules['hal']) && isset($modules['rest'])) { 27 | // Add a normalizer service for file entities. 28 | $service_definition = new Definition('Drupal\file_entity\Normalizer\FileEntityNormalizer', array( 29 | new Reference('rest.link_manager'), 30 | new Reference('entity.manager'), 31 | new Reference('module_handler'), 32 | )); 33 | // The priority must be higher than that of 34 | // serializer.normalizer.file_entity.hal in hal.services.yml 35 | $service_definition->addTag('normalizer', array('priority' => 30)); 36 | $container->setDefinition('serializer.normalizer.entity.file_entity', $service_definition); 37 | 38 | // Add a normalizer service for file fields. 39 | $service_definition = new Definition('Drupal\file_entity\Normalizer\FileItemNormalizer', array( 40 | new Reference('rest.link_manager'), 41 | new Reference('serializer.entity_resolver'), 42 | )); 43 | // Supersede EntityReferenceItemNormalizer. 44 | $service_definition->addTag('normalizer', array('priority' => 20)); 45 | $container->setDefinition('serializer.normalizer.entity_reference_item.file_entity', $service_definition); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/FileEntityStorageSchema.php: -------------------------------------------------------------------------------- 1 | getName() == 'type') { 24 | $schema['fields']['type']['initial'] = FILE_TYPE_NONE; 25 | } 26 | return $schema; 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/FileTypeInterface.php: -------------------------------------------------------------------------------- 1 | t('Description'), 28 | 'class' => array(RESPONSIVE_PRIORITY_MEDIUM), 29 | ); 30 | $header['status'] = t('Status'); 31 | return $header + parent::buildHeader(); 32 | } 33 | 34 | /** 35 | * {@inheritdoc} 36 | */ 37 | public function buildRow(EntityInterface $entity) { 38 | /** @var FileType $entity */ 39 | $row['label'] = array( 40 | 'data' => $this->getLabel($entity), 41 | 'class' => array('menu-label'), 42 | ); 43 | $row['description']['data'] = ['#markup' => $entity->getDescription()]; 44 | $row['status'] = $entity->status() ? t('Enabled') : t('Disabled'); 45 | return $row + parent::buildRow($entity); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Form/FileAddArchiveForm.php: -------------------------------------------------------------------------------- 1 | archiver_get_extensions(), 36 | ]; 37 | $options = $form_state->get('options') ? $form_state->get('options') : $options; 38 | $validators = $this->getUploadValidators($options); 39 | 40 | $form['upload'] = array( 41 | '#type' => 'managed_file', 42 | '#title' => $this->t('Upload an archive file'), 43 | '#upload_location' => 'public://', 44 | '#progress_indicator' => 'bar', 45 | '#default_value' => $form_state->has('file') ? array($form_state->get('file')->id()) : NULL, 46 | '#required' => TRUE, 47 | '#description' => $this->t('Files must be less than %valid_size
Allowed file types: %valid_extension', array('%valid_size' => format_size($validators['file_validate_size'][0]), '%valid_extension' => $validators['file_validate_extensions'][0])), 48 | '#upload_validators' => $validators, 49 | ); 50 | 51 | $form['pattern'] = array( 52 | '#type' => 'textfield', 53 | '#title' => $this->t('Pattern'), 54 | '#description' => $this->t('Only files matching this pattern will be imported. For example, to import all jpg and gif files, the pattern would be .*jpg|.*gif. Use .* to extract all files in the archive.'), 55 | '#default_value' => '.*', 56 | '#required' => TRUE, 57 | ); 58 | 59 | $form['remove_archive'] = array( 60 | '#type' => 'checkbox', 61 | '#title' => $this->t('Remove archive'), 62 | '#description' => $this->t('Removes archive after extraction.'), 63 | '#default_value' => FALSE, 64 | ); 65 | 66 | $form['actions'] = array('#type' => 'actions'); 67 | $form['actions']['submit'] = array( 68 | '#type' => 'submit', 69 | '#value' => t('Submit'), 70 | ); 71 | 72 | return $form; 73 | } 74 | 75 | /** 76 | * {@inheritdoc} 77 | */ 78 | public function submitForm(array &$form, FormStateInterface $form_state) { 79 | if ($archive = File::load($form_state->getValue('upload')[0])) { 80 | if ($archiver = archiver_get_archiver($archive->getFileUri())) { 81 | 82 | $extract_dir = file_default_scheme() . '://' . pathinfo($archive->getFilename(), PATHINFO_FILENAME); 83 | $extract_dir = file_destination($extract_dir, FILE_EXISTS_RENAME); 84 | if (!file_prepare_directory($extract_dir, FILE_MODIFY_PERMISSIONS | FILE_CREATE_DIRECTORY)) { 85 | throw new \Exception(t('Unable to prepare, the directory %dir for extraction.', array('%dir' => $extract_dir))); 86 | } 87 | $archiver->extract($extract_dir); 88 | $pattern = '/' . $form_state->getValue('pattern') . '/'; 89 | if ($files = file_scan_directory($extract_dir, $pattern)) { 90 | foreach ($files as $file) { 91 | $file = File::create([ 92 | 'uri' => $file->uri, 93 | 'filename' => $file->filename, 94 | 'status' => FILE_STATUS_PERMANENT, 95 | ]); 96 | $file->save(); 97 | } 98 | $all_files = file_scan_directory($extract_dir, '/.*/'); 99 | // Get all files that don't match the pattern so we can remove them. 100 | $remainig_files = array_diff_key($all_files, $files); 101 | foreach ($remainig_files as $file) { 102 | drupal_unlink($file->uri); 103 | } 104 | } 105 | drupal_set_message($this->t('Extracted %file and added @count new files.', array('%file' => $archive->getFilename(), '@count' => count($files)))); 106 | if ($form_state->getValue('remove_archive')) { 107 | drupal_set_message($this->t('Archive %name was removed from the system.', array('%name' => $archive->getFilename()))); 108 | $archive->delete(); 109 | } 110 | else { 111 | $archive->setPermanent(); 112 | $archive->save(); 113 | } 114 | } 115 | else { 116 | $form_state->setErrorByName('', $this->t('Cannot extract %file, not a valid archive.', array('%file' => $archive->getFileUri()))); 117 | } 118 | } 119 | $this->redirect('entity.file.collection')->send(); 120 | } 121 | 122 | } 123 | -------------------------------------------------------------------------------- /src/Form/FileDeleteMultipleForm.php: -------------------------------------------------------------------------------- 1 | tempStore = $temp_store_factory->get('file_multiple_delete_confirm'); 57 | $this->storage = $manager->getStorage('file'); 58 | } 59 | 60 | /** 61 | * {@inheritdoc} 62 | */ 63 | public static function create(ContainerInterface $container) { 64 | return new static( 65 | $container->get('user.private_tempstore'), 66 | $container->get('entity.manager') 67 | ); 68 | } 69 | 70 | /** 71 | * {@inheritdoc} 72 | */ 73 | public function getFormId() { 74 | return 'file_multiple_delete_confirm'; 75 | } 76 | 77 | /** 78 | * {@inheritdoc} 79 | */ 80 | public function getQuestion() { 81 | return \Drupal::translation()->formatPlural( 82 | count($this->files), 83 | 'Are you sure you want to delete this file?', 84 | 'Are you sure you want to delete these files?' 85 | ); 86 | } 87 | 88 | /** 89 | * {@inheritdoc} 90 | */ 91 | public function getCancelUrl() { 92 | return new Url('entity.file.collection'); 93 | } 94 | 95 | /** 96 | * {@inheritdoc} 97 | */ 98 | public function getConfirmText() { 99 | return t('Delete'); 100 | } 101 | 102 | /** 103 | * {@inheritdoc} 104 | */ 105 | public function buildForm(array $form, FormStateInterface $form_state) { 106 | $this->files = $this->tempStore->get('delete'); 107 | if (empty($this->files)) { 108 | $form_state->setRedirect('entity.file.collection'); 109 | } 110 | 111 | $form['files'] = array( 112 | '#theme' => 'item_list', 113 | '#items' => array_map(function (FileInterface $file) { 114 | return SafeMarkup::checkPlain($file->label()); 115 | }, $this->files), 116 | ); 117 | $form = parent::buildForm($form, $form_state); 118 | 119 | return $form; 120 | } 121 | 122 | /** 123 | * {@inheritdoc} 124 | */ 125 | public function submitForm(array &$form, FormStateInterface $form_state) { 126 | if ($form_state->getValue('confirm') && !empty($this->files)) { 127 | $this->storage->delete($this->files); 128 | $this->tempStore->delete('delete'); 129 | } 130 | $form_state->setRedirect('entity.file.collection'); 131 | } 132 | 133 | } 134 | -------------------------------------------------------------------------------- /src/Form/FileEditForm.php: -------------------------------------------------------------------------------- 1 | entity->bundle() == FILE_TYPE_NONE) { 26 | $this->entity->updateBundle(); 27 | } 28 | } 29 | 30 | /** 31 | * {@inheritdoc} 32 | */ 33 | public function form(array $form, FormStateInterface $form_state) { 34 | /** @var FileInterface $file */ 35 | $file = $this->entity; 36 | 37 | if ($this->operation == 'edit') { 38 | if ($file->bundle() == 'undefined') { 39 | $type = $this->t('file'); 40 | } 41 | else { 42 | $type = FileType::load($file->bundle())->label(); 43 | } 44 | 45 | $form['#title'] = $this->t('Edit @type "@title"', array( 46 | '@type' => $type, 47 | '@title' => $file->label(), 48 | )); 49 | 50 | // Add a 'replace this file' upload field if the file is writeable. 51 | if ($file->isWritable()) { 52 | // Set up replacement file validation. 53 | $replacement_options = array(); 54 | // Replacement file must have the same extension as the original file. 55 | $replacement_options['file_extensions'] = pathinfo($file->getFilename(), PATHINFO_EXTENSION); 56 | 57 | $form['replace_upload'] = array( 58 | '#type' => 'managed_file', 59 | '#title' => $this->t('Replace file'), 60 | '#upload_validators' => $this->getUploadValidators($replacement_options), 61 | ); 62 | 63 | $file_upload_help = array( 64 | '#theme' => 'file_upload_help', 65 | '#description' => $this->t('This file will replace the existing file. This action cannot be undone.'), 66 | '#upload_validators' => $form['replace_upload']['#upload_validators'], 67 | ); 68 | $form['replace_upload']['#description'] = drupal_render($file_upload_help); 69 | } 70 | } 71 | 72 | return parent::form($form, $form_state, $file); 73 | } 74 | 75 | /** 76 | * {@inheritdoc} 77 | */ 78 | public function save(array $form, FormStateInterface $form_state) { 79 | $file = $this->entity; 80 | $insert = $file->isNew(); 81 | $file->save(); 82 | 83 | $t_args = array('%title' => $file->label()); 84 | 85 | if ($insert) { 86 | drupal_set_message(t('%title has been created.', $t_args)); 87 | } 88 | else { 89 | drupal_set_message(t('%title has been updated.', $t_args)); 90 | } 91 | 92 | // Check if file ID exists. 93 | if ($file->id()) { 94 | $form_state->setRedirectUrl($file->urlInfo()); 95 | } 96 | else { 97 | // In the unlikely case something went wrong on save, the file will be 98 | // rebuilt and file form redisplayed the same way as in preview. 99 | drupal_set_message(t('The post could not be saved.'), 'error'); 100 | $form_state->setRebuild(); 101 | } 102 | } 103 | 104 | /** 105 | * {@inheritdoc} 106 | */ 107 | public function validateForm(array &$form, FormStateInterface $form_state) { 108 | // Handle the replacement file if uploaded. 109 | if ($form_state->getValue('replace_upload')) { 110 | // Save the file as a temporary file. 111 | $file = file_save_upload('replace_upload', $form['replace_upload']['#upload_validators']); 112 | if (!empty($file)) { 113 | // Put the temporary file in form_state so we can save it on submit. 114 | $form_state->setValue('replace_upload', $file); 115 | } 116 | elseif ($file === FALSE) { 117 | // File uploaded failed. 118 | $form_state->setError($form['replace_upload'], t('The replacement file could not be uploaded.')); 119 | } 120 | } 121 | parent::validateForm($form, $form_state); 122 | } 123 | 124 | /** 125 | * {@inheritdoc} 126 | */ 127 | public function submitForm(array &$form, FormStateInterface $form_state) { 128 | // Check if a replacement file has been uploaded. 129 | if ($form_state->getValue('replace_upload')) { 130 | $replacement = $form_state->getValue('replace_upload')[0]; 131 | if ($replacement instanceof FileEntity) { 132 | $entity_replacement = $replacement; 133 | } else { 134 | $entity_replacement = File::load($replacement); 135 | } 136 | $log_args = array('@old' => $this->entity->getFilename(), '@new' => $entity_replacement->getFileName()); 137 | // Move file from temp to permanent home. 138 | if (file_unmanaged_copy($entity_replacement->getFileUri(), $this->entity->getFileUri(), FILE_EXISTS_REPLACE)) { 139 | $entity_replacement->delete(); 140 | \Drupal::logger('file_entity')->info('File @old was replaced by @new', $log_args); 141 | } 142 | else { 143 | \Drupal::logger('file_entity')->notice('File @old failed to be replaced by @new', $log_args); 144 | } 145 | } 146 | parent::submitForm($form, $form_state); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/Form/FileInlineEditForm.php: -------------------------------------------------------------------------------- 1 | getEntity()->id() . '/inline-edit'; 21 | return $form; 22 | } 23 | 24 | /** 25 | * {@inheritdoc} 26 | */ 27 | protected function actionsElement(array $form, FormStateInterface $form_state) { 28 | $elements = parent::actionsElement($form, $form_state); 29 | // Let's allow the save button only. 30 | foreach (Element::children($elements) as $key) { 31 | if ($key != 'submit') { 32 | $elements[$key]['#access'] = FALSE; 33 | } 34 | } 35 | // Use Ajax. 36 | $elements['submit']['#ajax'] = [ 37 | 'url' => Url::fromRoute('entity.file.inline_edit_form', ['file' => $this->getEntity()->id()]), 38 | ]; 39 | return $elements; 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/Form/FileSettingsForm.php: -------------------------------------------------------------------------------- 1 | 'textfield', 39 | '#title' => t('Maximum upload size'), 40 | '#default_value' => \Drupal::config('file_entity.settings')->get('max_filesize'), 41 | '#description' => t('Enter a value like "512" (bytes), "80 KB" (kilobytes) or "50 MB" (megabytes) in order to restrict the allowed file size. If left empty the file sizes will be limited only by PHP\'s maximum post and file upload sizes (current max limit %limit).', array('%limit' => format_size(file_upload_max_size()))), 42 | '#element_validate' => ['\Drupal\file\Plugin\Field\FieldType\FileItem::validateMaxFilesize'], 43 | '#size' => 10, 44 | ); 45 | 46 | $form['default_allowed_extensions'] = array( 47 | '#type' => 'textfield', 48 | '#title' => t('Default allowed file extensions'), 49 | '#default_value' => \Drupal::config('file_entity.settings')->get('default_allowed_extensions'), 50 | '#description' => t('Separate extensions with a space or comma and do not include the leading dot.'), 51 | '#element_validate' => ['\Drupal\file\Plugin\Field\FieldType\FileItem::validateExtensions'], 52 | '#maxlength' => NULL, 53 | ); 54 | 55 | $form['file_entity_alt'] = array( 56 | '#type' => 'textfield', 57 | '#title' => t('Alt attribute'), 58 | '#description' => t('The text to use as value for the img tag alt attribute.'), 59 | '#default_value' => \Drupal::config('file_entity.settings')->get('alt'), 60 | ); 61 | 62 | $form['file_entity_title'] = array( 63 | '#type' => 'textfield', 64 | '#title' => t('Title attribute'), 65 | '#description' => t('The text to use as value for the img tag title attribute.'), 66 | '#default_value' => \Drupal::config('file_entity.settings')->get('title'), 67 | ); 68 | 69 | // Provide default token values. 70 | if (\Drupal::moduleHandler()->moduleExists('token')) { 71 | $form['token_help'] = array( 72 | '#theme' => 'token_tree_link', 73 | '#token_types' => array('file'), 74 | ); 75 | $form['file_entity_alt']['#description'] .= t('This field supports tokens. Default tokens depend on the Token module to work correctly. The ":value" version of the token (just raw value, no markup) should be used for performance and to avoid theme issues.', [':token' => 'https://drupal.org/project/token']); 76 | $form['file_entity_title']['#description'] .= t('This field supports tokens. Default tokens depend on the Token module to work correctly. The ":value" version of the token (just raw value, no markup) should be used for performance and to avoid theme issues', [':token' => 'https://drupal.org/project/token']); 77 | } 78 | 79 | $form['file_upload_wizard'] = array( 80 | '#type' => 'fieldset', 81 | '#title' => t('File upload wizard'), 82 | '#collapsible' => TRUE, 83 | '#collapsed' => FALSE, 84 | '#description' => t('Configure the steps available when uploading a new file.'), 85 | ); 86 | 87 | $form['file_upload_wizard']['wizard_skip_file_type'] = array( 88 | '#type' => 'checkbox', 89 | '#title' => t('Skip filetype selection.'), 90 | '#default_value' => \Drupal::config('file_entity.settings')->get('wizard_skip_file_type'), 91 | '#description' => t('The file type selection step is only available if the uploaded file falls into two or more file types. If this step is skipped, files with no available file type or two or more file types will not be assigned a file type.'), 92 | ); 93 | 94 | $form['file_upload_wizard']['wizard_skip_scheme'] = array( 95 | '#type' => 'checkbox', 96 | '#title' => t('Skip scheme selection.'), 97 | '#default_value' => \Drupal::config('file_entity.settings')->get('wizard_skip_scheme'), 98 | '#description' => t('The scheme selection step is only available if two or more file destinations, such as public local files served by the webserver and private local files served by Drupal, are available. If this step is skipped, files will automatically be saved using the default download method.'), 99 | ); 100 | 101 | $form['file_upload_wizard']['wizard_skip_fields'] = array( 102 | '#type' => 'checkbox', 103 | '#title' => t('Skip available fields.'), 104 | '#default_value' => \Drupal::config('file_entity.settings')->get('wizard_skip_fields'), 105 | '#description' => t('The field selection step is only available if the file type the file belongs to has any available fields. If this step is skipped, any fields on the file will be left blank.'), 106 | ); 107 | 108 | $form['actions']['submit'] = array( 109 | '#type' => 'submit', 110 | '#value' => t('Save'), 111 | ); 112 | 113 | return $form; 114 | } 115 | 116 | /** 117 | * {@inheritdoc} 118 | */ 119 | public function validateForm(array &$form, FormStateInterface $form_state) { 120 | // @TODO: Validation? 121 | } 122 | 123 | /** 124 | * {@inheritdoc} 125 | */ 126 | public function submitForm(array &$form, FormStateInterface $form_state) { 127 | $this->config('file_entity.settings') 128 | ->set('max_filesize', $form_state->getValue('max_filesize')) 129 | ->set('default_allowed_extensions', $form_state->getValue('default_allowed_extensions')) 130 | ->set('alt', $form_state->getValue('file_entity_alt')) 131 | ->set('title', $form_state->getValue('file_entity_title')) 132 | ->set('wizard_skip_file_type', $form_state->getValue('wizard_skip_file_type')) 133 | ->set('wizard_skip_scheme', $form_state->getValue('wizard_skip_scheme')) 134 | ->set('wizard_skip_fields', $form_state->getValue('wizard_skip_fields')) 135 | ->save(); 136 | 137 | drupal_set_message(t('File Settings have been succesfully saved.')); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/Form/FileTypeDisableForm.php: -------------------------------------------------------------------------------- 1 | $this->entity->label()) 27 | ); 28 | } 29 | 30 | /** 31 | * {@inheritdoc} 32 | */ 33 | public function getConfirmText() { 34 | return t('Disable'); 35 | } 36 | 37 | /** 38 | * {@inheritdoc} 39 | */ 40 | public function submitForm(array &$form, FormStateInterface $form_state) { 41 | /** @var FileType $type */ 42 | $type = $this->entity; 43 | $type->disable()->save(); 44 | drupal_set_message(t( 45 | 'The file type %label has been disabled.', 46 | array('%label' => $type->label()) 47 | )); 48 | $form_state->setRedirect('entity.file_type.collection'); 49 | } 50 | 51 | /** 52 | * {@inheritdoc} 53 | */ 54 | public function getCancelUrl() { 55 | return new Url('entity.file_type.collection'); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Form/FileTypeEnableForm.php: -------------------------------------------------------------------------------- 1 | $this->entity->label()) 27 | ); 28 | } 29 | 30 | /** 31 | * {@inheritdoc} 32 | */ 33 | public function getConfirmText() { 34 | return t('Enable'); 35 | } 36 | 37 | /** 38 | * {@inheritdoc} 39 | */ 40 | public function submitForm(array &$form, FormStateInterface $form_state) { 41 | /** @var FileType $type */ 42 | $type = $this->entity; 43 | $type->enable()->save(); 44 | drupal_set_message(t( 45 | 'The file type %label has been enabled.', 46 | array('%label' => $type->label()) 47 | )); 48 | $form_state->setRedirect('entity.file_type.collection'); 49 | } 50 | 51 | /** 52 | * {@inheritdoc} 53 | */ 54 | public function getCancelUrl() { 55 | return new Url('entity.file_type.collection'); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Form/FileTypeForm.php: -------------------------------------------------------------------------------- 1 | entity; 30 | 31 | $form['label'] = array( 32 | '#title' => t('Label'), 33 | '#type' => 'textfield', 34 | '#default_value' => $type->label(), 35 | '#description' => t('The human-readable name of the file type.'), 36 | '#required' => TRUE, 37 | '#size' => 30, 38 | ); 39 | 40 | $form['id'] = array( 41 | '#type' => 'machine_name', 42 | '#default_value' => $type->id(), 43 | '#maxlength' => EntityTypeInterface::BUNDLE_MAX_LENGTH, 44 | '#machine_name' => array( 45 | 'exists' => 'Drupal\file_entity\Entity\FileType::load', 46 | 'source' => array('label'), 47 | ), 48 | '#description' => t('A unique machine-readable name for this file type. It must only contain lowercase letters, numbers, and underscores.'), 49 | ); 50 | 51 | $form['description'] = array( 52 | '#title' => t('Description'), 53 | '#type' => 'textarea', 54 | '#default_value' => $type->getDescription(), 55 | '#description' => t('A brief description of this file type.'), 56 | ); 57 | 58 | $form['mimetypes'] = array( 59 | '#type' => 'textarea', 60 | '#title' => t('MIME types'), 61 | '#description' => t('Enter one MIME type per line.'), 62 | '#default_value' => implode("\n", $type->getMimeTypes()), 63 | ); 64 | 65 | $mimetypes = new Mimetypes(\Drupal::moduleHandler()); 66 | 67 | $form['mimetype_list'] = array( 68 | '#type' => 'details', 69 | '#title' => t('Known MIME types'), 70 | '#collapsed' => TRUE, 71 | ); 72 | $form['mimetype_list']['list'] = array( 73 | '#theme' => 'item_list', 74 | '#items' => $mimetypes->get(), 75 | ); 76 | 77 | $form['actions'] = array('#type' => 'actions'); 78 | 79 | $form['actions']['submit'] = array( 80 | '#type' => 'submit', 81 | '#value' => t('Save'), 82 | ); 83 | // Arbitrary expressions in empty() allowed in PHP 5.5 only. 84 | $id = $type->id(); 85 | if (!empty($id)) { 86 | $form['actions']['delete'] = array( 87 | '#type' => 'submit', 88 | '#value' => t('Delete'), 89 | ); 90 | } 91 | 92 | return $form; 93 | } 94 | 95 | /** 96 | * {@inheritdoc} 97 | */ 98 | public function validateForm(array &$form, FormStateInterface $form_state) { 99 | parent::validateForm($form, $form_state); 100 | 101 | $id = trim($form_state->getValue('id')); 102 | // '0' is invalid, since elsewhere we check it using empty(). 103 | if ($id == '0') { 104 | $form_state->setError($form['id'], $this->t("Invalid machine-readable name. Enter a name other than %invalid.", array('%invalid' => $id))); 105 | } 106 | } 107 | 108 | /** 109 | * {@inheritdoc} 110 | */ 111 | public function save(array $form, FormStateInterface $form_state) { 112 | $status = $this->entity->save(); 113 | 114 | $t_args = array('%name' => $this->entity->label()); 115 | 116 | if ($status == SAVED_UPDATED) { 117 | drupal_set_message(t('The file type %name has been updated.', $t_args)); 118 | } 119 | elseif ($status == SAVED_NEW) { 120 | drupal_set_message(t('The file type %name has been added.', $t_args)); 121 | \Drupal::logger('file_entity')->notice(t('Added file type %name.', $t_args)); 122 | } 123 | 124 | $form_state->setRedirect('entity.file_type.collection'); 125 | } 126 | 127 | /** 128 | * {@inheritdoc} 129 | */ 130 | protected function copyFormValuesToEntity(EntityInterface $entity, array $form, FormStateInterface $form_state) { 131 | // Convert multi-line string to array before copying. 132 | // This may be called multiple times and exectued only if it is a string. 133 | if (is_string($form_state->getValue('mimetypes'))) { 134 | $form_state->setValue('mimetypes', explode("\n", str_replace("\r", "", $form_state->getValue('mimetypes')))); 135 | } 136 | parent::copyFormValuesToEntity($entity, $form, $form_state); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /src/Mimetypes.php: -------------------------------------------------------------------------------- 1 | mapping === NULL) { 26 | $mapping = $this->defaultMapping; 27 | // Allow modules to alter the default mapping. 28 | $this->moduleHandler->alter('file_mimetype_mapping', $mapping); 29 | $this->mapping = $mapping; 30 | } 31 | return $this->mapping['mimetypes']; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Normalizer/FileEntityNormalizer.php: -------------------------------------------------------------------------------- 1 | getFileUri())); 30 | $data += array( 31 | 'data' => array(array('value' => $file_data)), 32 | ); 33 | } 34 | return $data; 35 | } 36 | 37 | /** 38 | * {@inheritdoc} 39 | */ 40 | public function denormalize($data, $class, $format = NULL, array $context = array()) { 41 | // Avoid 'data' being treated as a field. 42 | $file_data = $data['data'][0]['value']; 43 | unset($data['data']); 44 | // Decode and save to file. 45 | $file_contents = base64_decode($file_data); 46 | $entity = parent::denormalize($data, $class, $format, $context); 47 | $dirname = drupal_dirname($entity->getFileUri()); 48 | file_prepare_directory($dirname, FILE_CREATE_DIRECTORY); 49 | if ($uri = file_unmanaged_save_data($file_contents, $entity->getFileUri())) { 50 | $entity->setFileUri($uri); 51 | } 52 | else { 53 | throw new \RuntimeException(SafeMarkup::format('Failed to write @filename.', array('@filename' => $entity->getFilename()))); 54 | } 55 | return $entity; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Normalizer/FileItemNormalizer.php: -------------------------------------------------------------------------------- 1 | getParent()->getName(); 45 | $entity = $field_item->getEntity(); 46 | $field_uri = $this->linkManager->getRelationUri($entity->getEntityTypeId(), $entity->bundle(), $field_name); 47 | 48 | // Add any field-specific data. 49 | $data['_embedded'][$field_uri][0] += $field_item->getValue(); 50 | unset($data['_embedded'][$field_uri][0]['target_id']); 51 | 52 | return $data; 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/Plugin/Action/FileDelete.php: -------------------------------------------------------------------------------- 1 | tempStore = $temp_store_factory->get('file_multiple_delete_confirm'); 43 | } 44 | 45 | /** 46 | * {@inheritdoc} 47 | */ 48 | public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { 49 | return new static($configuration, $plugin_id, $plugin_definition, $container->get('user.private_tempstore')); 50 | } 51 | 52 | /** 53 | * {@inheritdoc} 54 | */ 55 | public function execute($entity = NULL) { 56 | $this->executeMultiple(array($entity)); 57 | } 58 | 59 | /** 60 | * {@inheritdoc} 61 | */ 62 | public function executeMultiple(array $entities) { 63 | // @todo Make translation-aware, similar to node. 64 | $entities_by_id = []; 65 | foreach ($entities as $entity) { 66 | $entities_by_id[$entity->id()] = $entity; 67 | } 68 | // Just save in temp store for now, delete after confirmation. 69 | $this->tempStore->set('delete', $entities_by_id); 70 | } 71 | 72 | /** 73 | * {@inheritdoc} 74 | */ 75 | public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) { 76 | $result = AccessResult::allowedIf($object instanceof FileInterface)->andIf(AccessResult::allowedIf($object->access('delete'))); 77 | return $return_as_object ? $result : $result->isAllowed(); 78 | } 79 | 80 | 81 | } 82 | -------------------------------------------------------------------------------- /src/Plugin/Action/FileSetPermanent.php: -------------------------------------------------------------------------------- 1 | setPermanent(); 33 | $entity->save(); 34 | } 35 | 36 | /** 37 | * {@inheritdoc} 38 | */ 39 | public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) { 40 | $result = AccessResult::allowedIf($object instanceof FileInterface)->andIf(AccessResult::allowedIf($object->access('update'))); 41 | return $return_as_object ? $result : $result->isAllowed(); 42 | } 43 | 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/Plugin/Action/FileSetTemporary.php: -------------------------------------------------------------------------------- 1 | setTemporary(); 33 | $entity->save(); 34 | } 35 | 36 | /** 37 | * {@inheritdoc} 38 | */ 39 | public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) { 40 | $result = AccessResult::allowedIf($object instanceof FileInterface)->andIf(AccessResult::allowedIf($object->access('delete'))); 41 | return $return_as_object ? $result : $result->isAllowed(); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/Plugin/Field/FieldFormatter/FileAudioFormatter.php: -------------------------------------------------------------------------------- 1 | renderer = $renderer; 66 | } 67 | 68 | /** 69 | * {@inheritdoc} 70 | */ 71 | public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { 72 | return new static( 73 | $plugin_id, 74 | $plugin_definition, 75 | $configuration['field_definition'], 76 | $configuration['settings'], 77 | $configuration['label'], 78 | $configuration['view_mode'], 79 | $configuration['third_party_settings'], 80 | $container->get('renderer') 81 | ); 82 | } 83 | 84 | /** 85 | * {@inheritdoc} 86 | */ 87 | public static function defaultSettings() { 88 | return array( 89 | 'controls' => TRUE, 90 | 'autoplay' => FALSE, 91 | 'loop' => FALSE, 92 | 'multiple_file_behavior' => 'tags', 93 | ) + parent::defaultSettings(); 94 | } 95 | 96 | /** 97 | * {@inheritdoc} 98 | */ 99 | public function settingsForm(array $form, FormStateInterface $form_state) { 100 | $element['controls'] = array( 101 | '#title' => t('Show audio controls'), 102 | '#type' => 'checkbox', 103 | '#default_value' => $this->getSetting('controls'), 104 | ); 105 | $element['autoplay'] = array( 106 | '#title' => t('Autoplay'), 107 | '#type' => 'checkbox', 108 | '#default_value' => $this->getSetting('autoplay'), 109 | ); 110 | $element['loop'] = array( 111 | '#title' => t('Loop'), 112 | '#type' => 'checkbox', 113 | '#default_value' => $this->getSetting('loop'), 114 | ); 115 | $element['multiple_file_behavior'] = array( 116 | '#title' => t('Display of multiple files'), 117 | '#type' => 'radios', 118 | '#options' => array( 119 | 'tags' => t('Use multiple @tag tags, each with a single source.', array('@tag' => '