dmstr / yii2-filefly-module
FlySystem API for Filemanager
Package info
github.com/dmstr/yii2-filefly-module
Type:yii2-extension
pkg:composer/dmstr/yii2-filefly-module
Requires
- creocoder/yii2-flysystem: ^1.0.0
- dmstr/yii2-active-record-permissions: ^1.0.0
- yiisoft/yii2: ~2.0.13
Requires (Dev)
- enshrined/svg-sanitize: ^1.1
- phpunit/phpunit: ^9.6
Suggests
- dmstr/yii2-filemanager-widgets: Useful widget for working with files via the filefly api
- enshrined/svg-sanitize: Required for ContentValidator::$sanitizeSvg (^1.1)
- league/flysystem-sftp: SFTP adapter (~1.0)
- league/flysystem-webdav: WebDAV adapter (~1.0)
Provides
None
Conflicts
None
Replaces
None
- dev-master
- 1.8.0
- 1.7.5
- 1.7.4
- 1.7.3
- 1.7.2
- 1.7.1
- 1.7.0
- 1.7.0-rc3
- 1.7.0-rc2
- 1.7.0-rc1
- 1.6.x-dev
- 1.6.2
- 1.6.0
- 1.5.1
- 1.5.0
- 1.4.2
- 1.4.1
- 1.4.0
- 1.4.0-beta3
- 1.4.0-beta2
- 1.4.0-beta1
- 1.3.0-rc1
- 1.3.0-beta4
- 1.3.0-beta3
- 1.3.0-beta2
- 1.3.0-beta1
- 1.2.0-rc2
- 1.2.0-rc1
- 1.1.0-rc6
- 1.1.0-rc5
- 1.1.0-rc4
- 1.1.0-rc3
- 1.1.0-rc1
- 1.0.0
- 1.0.0-alpha2
- 1.0.0-alpha1
- 0.7.0-beta2
- 0.7.0-beta1
- 0.6.20
- 0.6.19
- 0.6.18
- 0.6.17
- 0.6.16
- 0.6.15
- 0.6.14
- 0.6.13
- 0.6.12
- 0.6.11
- 0.6.11-rc7
- 0.6.11-rc6
- 0.6.11-rc5
- 0.6.11-rc4
- 0.6.11-rc3
- 0.6.11-rc2
- 0.6.11-rc1
- 0.6.10
- 0.6.9.1
- 0.6.9
- 0.6.8
- 0.6.7
- 0.6.6
- 0.6.5
- 0.6.4
- 0.6.3
- 0.6.2
- 0.6.1
- 0.6.0
- 0.5.5
- 0.5.5-rc5
- 0.5.5-rc4
- 0.5.5-rc3
- 0.5.5-rc2
- 0.5.5-rc1
- 0.5.4
- 0.5.3
- 0.5.2
- 0.5.1
- 0.5.0
- 0.4.0
- 0.3.0
- 0.2.2
- 0.2.1
- 0.2.0
- 0.1.0
- dev-feature/upload-content-validation
- dev-feature/upload-extension-validation
- dev-feature/improve-rule
- dev-dev/api-search
- dev-feature/fix-search-permission-checks
- dev-feature/filemanager-widget-options
- dev-feature/rm-bootstrap
- dev-feature/preview-callback
- dev-feature/url-callback
- dev-feature/file-events
- dev-feature/postgres-support
- dev-feature/wip-improve-permissions
- dev-feature/search-action
This package is auto-updated.
Last update: 2026-11-02 11:32:47 UTC
README
FlySystem API for dmstr/yii2-filemanager-widgets
Installation
ENV variables
| Variable | Value | Required |
|---|---|---|
| AFM_FILESYSTEM | yii component name | yes |
| AFM_REPAIR | default: true | no |
| AFM_SLUG_NAMES | default: true | no |
| AFM_DELETE_RECURSIVE | default: false | no |
i.e. AFM_FILESYSTEM=fsLocal
❓ How to configure a filesystem component Filesystem docs
Yii config
'filefly' => [ 'class' => 'hrzg\filefly\Module', 'layout' => '@backend/views/layouts/main', 'filesystem' => getenv('AFM_FILESYSTEM'), 'slugNames' => (getenv('AFM_SLUG_NAMES')) ? getenv('AFM_SLUG_NAMES') : true, 'repair' => (getenv('AFM_REPAIR')) ? getenv('AFM_REPAIR') : true, 'deleteRecursive' => (getenv('AFM_DELETE_RECURSIVE')) ? getenv('AFM_DELETE_RECURSIVE') : false, 'streamExpireOffset' => 604800 # Offset (in seconds) for Expires Header in stream action 'defaultPermissions' => [ \hrzg\filefly\Module::ACCESS_OWNER => 1, \hrzg\filefly\Module::ACCESS_READ => \hrzg\filefly\models\FileflyHashmap::$_all, \hrzg\filefly\Module::ACCESS_UPDATE => \hrzg\filefly\models\FileflyHashmap::$_all, \hrzg\filefly\Module::ACCESS_DELETE => \hrzg\filefly\models\FileflyHashmap::$_all, ], # the urlCallbck property can be used to provide customized urls for each file item which (if defined) will overwrite # the default handler URLs 'urlCallback' => function($item) { $urls = []; $isImageFileExtList = ['jpg', 'jpeg', 'gif', 'tiff', 'tif', 'svg', 'png', 'bmp'] ; if ($item['type'] === 'file') { if (in_array(strtolower($item['extension']), $isImageFileExtList)) { $urls['image url'] = \dmstr\willnorrisImageproxy\Url::image($item['path']); } else { $urls['download url'] = implode('/', ['/img/download', ltrim($item['path'], '/')]) . ',p1'; } } return $urls; }, // previewCallback can be used to overwrite the default downloadUrl for preview URLs within filemanagerApp 'previewCallback' => function($item) { $isImageFileExtList = ['jpg', 'jpeg', 'gif', 'tiff', 'tif', 'png', 'bmp'] ; if ($item['type'] === 'file') { if (in_array(strtolower($item['extension']), $isImageFileExtList)) { return \dmstr\willnorrisImageproxy\Url::image($item['path'], '500x'); } } return ''; } ],
Upload validation
Uploaded files are validated on the server before they are written to the filesystem. The same extension rules are applied to the new file name on rename and copy.
All rules are disabled by default, so existing installations keep their behavior. File names with control characters, null bytes or path separators are always rejected.
| Property | Default | Description |
|---|---|---|
| allowedExtensions | null |
allowed extensions (case-insensitive), checked against the last extension, null allows all |
| deniedExtensions | [] |
denied extensions, checked against every extension of the file name, so shell.php.jpg is rejected if php is listed |
| denyMultipleExtensions | false |
reject file names with more than one extension like demo.pdf.jpg (note that this also rejects names like archive.tar.gz) |
| maxFileSize | null |
max size of an uploaded file in bytes or as shorthand like 50M |
Both the original file name and the name after slugging (slugNames) are checked. Trailing dots and spaces are ignored when the extensions are determined, so shell.php. is treated like shell.php.
Recommended configuration:
'filefly' => [ // ... 'allowedExtensions' => ['jpg', 'jpeg', 'png', 'gif', 'webp', 'pdf', 'docx', 'xlsx', 'zip'], 'deniedExtensions' => \hrzg\filefly\components\UploadValidator::DANGEROUS_EXTENSIONS, 'denyMultipleExtensions' => true, 'maxFileSize' => '50M', ],
maxFileSize only works within the limits of PHP (upload_max_filesize, post_max_size) and the web server or proxy (e.g. client_max_body_size in nginx), which should be set accordingly.
RBAC
Prosa
-
FileflyAdminfull module access including debug information -
FileflyDefaultmanagement access (eg. POST request for upload, change, delete) -
FileflyPermissionsassigned users can set or unset roles or permissions which the user himself has assigned -
access_ownerpermission beforeaccess_read,access_update,access_delete -
If no permission is set, it will check if any inherited permission can be granted
So if inherited access_* permissions should be used, set defaultPermissions to null NOT to '*'.
'defaultPermissions' => [
\hrzg\filefly\Module::ACCESS_OWNER => 1,
\hrzg\filefly\Module::ACCESS_READ => null,
\hrzg\filefly\Module::ACCESS_UPDATE => null,
\hrzg\filefly\Module::ACCESS_DELETE => null,
],
ActiveRecord: FileflyHashmap
- uses
dmstr\activeRecordPermissions\ActiveRecordAccessTraitwith$activeAccessTrait = false - access checks will be done for each permission type explicitly,
hasPermission($action) - uses a
pathValidatorrule to ensure thepathsyntax on active record operations
Roles
-
FileflyAdmin
- filefly
-
FileflyDefault
- filefly_default_index
-
FileflyApi
- filefly_api_index
-
FileflyPermissions
Permissions
- filefly
- filefly_default_index
- filefly_api_index
RBAC Plugins
Permission checks will ever come after file or folder operation
GrantPermission
Granted or deny permission
1. access field empty (is owner, true or continue)
2. access field set (permission granted, true)
access field set (is access owner, true, permission denied, false)
SetPermission
Create or update permission
1. Add new hash records
2. Update hash records (recursive option)
- Multi create and update option
RemovePermission
Remove permission
1. Deletes file or folder records
- Multi delete option
CLI
Configure
'controllerMap' => [
'fs' => [
'class' => '\hrzg\filefly\commands\FsController',
'filesystemComponents' => [
'local' => 'fs',
's3' => 'fsS3',
'storage' => 'fsFtp',
],
],
]
Widget
see https://github.com/dmstr/yii2-filemanager-widgets
{{ use ('hrzg/filemanager/widgets') }}
{{ file_manager_widget_widget(
{
"handlerUrl": "/#{app.language}/filefly/api"
}
) }}
Options for the yii2-filemanager-widgets Widget can be injected via the Module property fileManagerWidgetOptions
example:
'filefly' => [ 'filesystemComponents' => [ 'ftp' => 'fsFtp', 'ftpcrud' => 'fsFtpCrud', ], // ... // other configs // ... // Options for the angular fileManagerWidget 'fileManagerWidgetOptions' => [ 'searchForm' => false, 'allowedActions' => [ 'move' => false, 'upload' => false, 'createFolder' => false, ], ], ],
Controller action in iFrame
Modal button
<a type="button" data-toggle="modal" data-target="#modal-filemanager" id="filefly-iframe-toggle">
<i class="fa fa-folder-open"></i>
</a>
Load event listener
<script>
window.addEventListener('load', function () {
var fileflyIframeToggle = document.querySelector('#filefly-iframe-toggle');
var iframe = document.querySelector('#filefly-iframe');
fileflyIframeToggle.addEventListener('click', function () {
iframe.src = "/filefly/default/filemanager-full-screen";
});
});
</script>
Iframe
{{ use ('yii/bootstrap') }}
{{ modal_begin(
{
'id': 'modal-filemanager',
'size': 'modal-lg'
}) }}
<iframe id="filefly-iframe" width="100%" height="100%" frameborder="0" src=""></iframe>
{{ modal_end() }}'
Helper
| Description | Method call | Example output |
|---|---|---|
| Total size for all filesystems | FileflyHashmap::getTotalSize() |
202.82 MiB |
| Total size for all filesystems (raw bytes) | FileflyHashmap::getTotalSize(true) |
212670464 |
Total size for local filesystems |
FileflyHashmap::getTotalSize(false, 'local') |
48.32 MiB |
Total size for s3 filesystems (raw bytes) |
FileflyHashmap::getTotalSize(true, 's3') |
166546843 |
Content security
Delivery of active content
The stream and download actions deliver files from the same origin as the application. SVG, HTML, XML and script files could run embedded JavaScript in the context of the application when a browser opens them directly. These files are therefore delivered with Content-Disposition: attachment and a sandbox Content-Security-Policy, and every delivered file gets X-Content-Type-Options: nosniff. Embedding SVG files with <img> keeps working. This is enabled by default.
'deliveryPolicy' => [ // 'enabled' => true, // MIME types delivered as attachment, shell wildcards are supported // 'activeContentMimeTypes' => ['text/html', 'image/svg+xml', '*+xml', ...], // 'contentSecurityPolicy' => "default-src 'none'; style-src 'unsafe-inline'; sandbox", // 'noSniff' => true, ],
The type is checked for the MIME type reported by the filesystem and for the type derived from the file extension. The sandbox CSP is added after Response::EVENT_BEFORE_SEND handlers from the application config, so an application wide CSP header does not replace it.
Upload content validation
Uploaded files are checked by hrzg\filefly\components\ContentValidator before they are written. The MIME type is detected from the file content (fileinfo), not taken from the client. By default only PHP files are rejected.
| Property | Default | Description |
|---|---|---|
enabled |
true |
Enable content checks |
blockedMimeTypes |
PHP types | Detected MIME types that are rejected, shell wildcards are supported |
checkMimeTypeMatchesExtension |
false |
Reject files whose content does not match the extension, e.g. a script named image.jpg |
mimeTypeAliases |
[] |
Additional accepted MIME types per extension, e.g. ['dwg' => ['application/octet-stream']] |
sanitizeSvg |
false |
Remove scripts, event handlers and remote references from SVG files, requires enshrined/svg-sanitize |
The existing mime-whitelist setting (filefly section) is still checked after the content validation.
Virus scanner and own checks
The validator triggers ContentValidator::EVENT_VALIDATE_FILE for every file that passed the built-in checks. Set $event->isValid = false to reject the file. The module has no dependency on a scanner, e.g. ClamAV can be connected by calling clamdscan:
'contentValidator' => [ 'on validateFile' => function (\hrzg\filefly\events\UploadValidationEvent $event) { exec('clamdscan --no-summary --fdpass ' . escapeshellarg($event->tempName), $output, $exitCode); if ($exitCode !== 0) { $event->isValid = false; // optional message key from messages/*.json, default: file_content_rejected // $event->errorMessage = 'file_content_rejected'; } }, ],
Archives
The API has no compress and extract actions, so archives are never unpacked on the server. The filemanager view of this module hides both actions in the widget. If the widget is rendered elsewhere, disable them via 'allowedActions' => ['compress' => false, 'extract' => false].
Recommended configuration
'filefly' => [ // ... 'contentValidator' => [ 'blockedMimeTypes' => \hrzg\filefly\components\ContentValidator::RECOMMENDED_BLOCKED_MIME_TYPES, 'checkMimeTypeMatchesExtension' => true, // 'sanitizeSvg' => true, // 'on validateFile' => function ($event) { /* virus scan */ }, ], ],
RECOMMENDED_BLOCKED_MIME_TYPES blocks server side scripts (PHP, shell, Perl, Python, ...), executables and HTML/JavaScript files.