Booking, QI Content, Trees, Media

This commit is contained in:
Kevin Adametz 2019-10-02 20:03:55 +02:00
parent 1f340e96fa
commit 7fbac395a9
260 changed files with 27160 additions and 3773 deletions

View file

@ -0,0 +1,10 @@
Please check the following instructions before submitting a bug :
* Make sure you are using the latest version.
* Make sure you read [installation](http://unisharp.github.io/laravel-filemanager/installation), [integration](http://unisharp.github.io/laravel-filemanager/integration), and [upgrade](http://unisharp.github.io/laravel-filemanager/upgrade) document.
And provide the followings :
* Operating system :
* Laravel version :
* Package version :
* Steps to reproduce your issue :
* Screenshots of browser console :

View file

@ -0,0 +1,16 @@
---
name: Bug report
about: Create a report to help us improve
---
Please check the following instructions before submitting a bug :
* Make sure you are using the latest version.
* Make sure you read [installation](http://unisharp.github.io/laravel-filemanager/installation), [integration](http://unisharp.github.io/laravel-filemanager/integration), and [upgrade](http://unisharp.github.io/laravel-filemanager/upgrade) document.
And provide the followings :
* Operating system :
* Laravel version :
* Package version :
* Steps to reproduce your issue :
* Screenshots of browser console :

View file

@ -0,0 +1,17 @@
---
name: Feature request
about: Suggest an idea for this project
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View file

@ -0,0 +1,4 @@
/vendor
composer.lock
/docs/Gemfile.lock
/docs/_site

View file

@ -0,0 +1 @@
preset: psr2

View file

@ -0,0 +1,19 @@
language: php
php:
- 7.2
- 7.1
- 7.0
- 5.6
matrix:
fast_finish: true
allow_failures:
- php: 5.6
before_script:
- travis_retry composer self-update
- travis_retry composer install --no-interaction --prefer-source
script:
- vendor/bin/phpunit --verbose

View file

@ -0,0 +1,76 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at service@unisharp.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq

View file

@ -0,0 +1,13 @@
## Steps to contribute
1. Fork [unisharp/laravel-filemanager](https://github.com/UniSharp/laravel-filemanager) from GitHub.
1. Run commands below:
```
git clone git@github.com:UniSharp/laravel-filemanager-example-5.3.git
cd laravel-filemanager-example-5.3
composer require unisharp/laravel-filemanager:dev-master
make init
```
1. Edit codes in `vendor/unisharp/laravel-filemanager`
1. Push your changes to your fork.
1. Send a pull request to [unisharp/laravel-filemanager](https://github.com/UniSharp/laravel-filemanager).

View file

@ -0,0 +1,23 @@
The MIT License (MIT)
Copyright (c) 2015 Trevor Sawler <https://github.com/tsawler>
Copyright (c) 2015-2017 All contributors from GitHub
Copyright (c) 2015-2017 UniSharp
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,3 @@
test:
vendor/bin/phpunit --coverage-text
vendor/bin/phpcs --version && echo && vendor/bin/phpcs -p --standard=PSR2 --ignore=src/lang/,src/views/ src tests

View file

@ -0,0 +1,2 @@
#### (optional) Issue number:
#### Summary of the change:

View file

@ -0,0 +1,110 @@
<p align="center"><img src="https://unisharp.github.io/laravel-filemanager/images/logo_vertical_colored.png"></p>
<p align="center">
<a target="_blank" href="https://travis-ci.org/UniSharp/laravel-filemanager"><img src="https://img.shields.io/travis/UniSharp/laravel-filemanager.svg"></a>
<a target="_blank" href="https://packagist.org/packages/unisharp/laravel-filemanager"><img src="https://poser.pugx.org/unisharp/laravel-filemanager/downloads"></a>
<a target="_blank" href="https://packagist.org/packages/unisharp/laravel-filemanager"><img src="https://img.shields.io/badge/unstable-v2.0.0--alpha8-orange.svg"></a>
<a target="_blank" href="https://packagist.org/packages/unisharp/laravel-filemanager"><img src="https://poser.pugx.org/unisharp/laravel-filemanager/v/stable"></a>
<a target="_blank" href="https://packagist.org/packages/unisharp/laravel-filemanager"><img src="https://poser.pugx.org/unisharp/laravel-filemanager/license"></a>
</p>
<p align="center">
<a href="http://unisharp.github.io/laravel-filemanager/">Documents</a>
<a href="http://unisharp.github.io/laravel-filemanager/installation">Installation</a>
<a href="http://unisharp.github.io/laravel-filemanager/integration">Integration</a>
<a href="http://unisharp.github.io/laravel-filemanager/config">Config</a>
<a href="http://unisharp.github.io/laravel-filemanager/customization">Customization</a>
<a href="http://unisharp.github.io/laravel-filemanager/events">Events</a>
<a href="http://unisharp.github.io/laravel-filemanager/upgrade">Upgrade</a>
<a href="https://github.com/UniSharp/laravel-filemanager-example-5.3">Demo</a>
<a href="https://github.com/UniSharp/laravel-filemanager/wiki">FAQ</a>
</p>
## Installing alpha version
The alpha version of `v2.0` contains support of cloud storage and fresh new UI with RWD.
* Run `composer require unisharp/laravel-filemanager:dev-master` to get the latest code.
* Run `composer require unisharp/laravel-filemanager:v2.0.0-alpha8` to get the latest release of alpha version.
## v2.0 progress
* [x] (done) Unit test
* [x] (done) Integrate with Laravel Storage
* [x] (done) Multiple selection
* [x] (done) Responsive design
* [x] (done) Config refactoring
* [x] (done) JSON APIs
* [x] (done) Move to folder function
* [x] (done) Applying MIME icon generator
* [x] (done) Refactor floating action buttons
* [x] (done) Configurable disk of storage
* [x] (done) Bootstrap 4 support
* [x] (done) Remove bootbox
* [ ] Documents for v2.0
* [x] (done) Resize function RWD refactor
* [ ] ConfigHandler should overwrite most configs
* [ ] Events should pass object instead of only file path
* [ ] Add more events for files and folders manipulation
## Documents of V1
https://github.com/UniSharp/laravel-filemanager/tree/v1/docs
## Errors with namespace
We have changed namespace from `Unisharp` to `UniSharp`, and change the first character of every namespace into capital.
If you are updating this package and encounter any errors like `Class not found`, please remove this package entirely and reinstall again.
## v1.8 released
* Please follow the intructions in [upgrade document](https://unisharp.github.io/laravel-filemanager/upgrade).
* Important changes :
* Fix Windows compatibility (utf-8 file names and folder names).
* New feature : Copy & Crop. Thanks [gwleuverink](https://github.com/gwleuverink).
* [Config document](https://unisharp.github.io/laravel-filemanager/config) is refactored.
## Security
It is important to note that if you use your own routes **you must protect your routes to Laravel-Filemanager in order to prevent unauthorized uploads to your server**. Fortunately, Laravel makes this very easy.
If, for example, you want to ensure that only logged in users have the ability to access the Laravel-Filemanager, simply wrap the routes in a group, perhaps like this:
```php
Route::group(['middleware' => 'auth'], function () {
Route::get('/laravel-filemanager', '\UniSharp\LaravelFilemanager\Controllers\LfmController@show');
Route::post('/laravel-filemanager/upload', '\UniSharp\LaravelFilemanager\Controllers\UploadController@upload');
// list all lfm routes here...
});
```
This approach ensures that only authenticated users have access to the Laravel-Filemanager. If you are using Middleware or some other approach to enforce security, modify as needed.
**If you use the laravel-filemanager default route, make sure the `auth` middleware (set in config/lfm.php) is enabled and functional**.
## Contributors & Credits
### Developers / Maintainers
* [Stream](https://github.com/g0110280)
* [@gwleuverink](https://github.com/gwleuverink)
* All [@UniSharp](https://github.com/UniSharp) members
### Contributors
* [All contibutors](https://github.com/UniSharp/laravel-filemanager/graphs/contributors) from GitHub. (issues / PR)
* [@taswler](https://github.com/tsawler) the original author of this package.
* Nathan for providing security suggestions.
* [@mdnazmulhasan27771](https://github.com/mdnazmulhasan27771) the designer of our logo. (Licensed CC BY 4.0)
### Credits
* [@olivervogel](https://github.com/olivervogel) for the awesome [image library](https://github.com/Intervention/image).
* SVG Loaders by [Sam](http://samherbert.net/svg-loaders/) (Licensed MIT)
* Articles and videos which helped promoting this package.
* All users and you.

View file

@ -0,0 +1,61 @@
{
"name": "iqcontent/laravel-filemanager",
"description": "A file upload/editor intended for use with Laravel 5 and CKEditor / TinyMCE",
"license": "MIT",
"keywords": [
"filemanager",
"laravel",
"ckeditor",
"tinymce",
"upload",
"file",
"manager",
"image"
],
"authors": [
{
"name": "Kevin Adametz",
"email": "info@adametz.media"
}
],
"require": {
"php": ">=7.1.3",
"ext-exif": "*",
"ext-fileinfo": "*",
"intervention/image": "2.*",
"illuminate/config": "5.4.* || 5.5.* || 5.6.* || 5.7.* || 5.8.*",
"illuminate/filesystem": "5.4.* || 5.5.* || 5.6.* || 5.7.* || 5.8.*",
"illuminate/support": "5.4.* || 5.5.* || 5.6.* || 5.7.* || 5.8.*",
"illuminate/http": "5.4.* || 5.5.* || 5.6.* || 5.7.* || 5.8.*",
"illuminate/container": "5.4.* || 5.5.* || 5.6.* || 5.7.* || 5.8.*",
"cviebrock/eloquent-sluggable": "*"
},
"require-dev": {
"phpunit/phpunit": "^6.2",
"mockery/mockery": "^0.9.9",
"squizlabs/php_codesniffer": "^3.1"
},
"suggest": {
"ext-gd": "to use GD library based image processing.",
"ext-imagick": "to use Imagick based image processing."
},
"autoload": {
"psr-4": {
"IqContent\\LaravelFilemanager\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Tests\\": "tests"
}
},
"extra": {
"laravel": {
"providers": [
"IqContent\\LaravelFilemanager\\LaravelFilemanagerServiceProvider"
],
"aliases": {
}
}
}
}

View file

@ -0,0 +1,51 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateIQContentFoldersTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('i_q_content_folders', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('folder_id')->nullable()->index();
$table->string('name')->index();
$table->string('slug')->unique()->index();
$table->string('identifier')->nullable();
$table->string('path')->nullable();
$table->unsignedTinyInteger('color')->default(0);
$table->unsignedTinyInteger('pos')->default(0);
$table->boolean('active')->default(true);
$table->timestamps();
$table->foreign('folder_id')
->references('id')
->on('i_q_content_folders')
->onDelete('cascade');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('i_q_content_folders');
}
}

View file

@ -0,0 +1,55 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateIQContentFilesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('i_q_content_files', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('folder_id')->nullable()->index();
$table->string('name')->index();
$table->string('identifier')->nullable();
$table->string('slug')->unique()->index();
$table->string('ext', 10)->nullable();
$table->string('mine', 100)->nullable();
$table->unsignedInteger('size')->unsigned();
$table->string('dimensions', 100)->nullable();
$table->text('content')->nullable();
$table->unsignedTinyInteger('color')->default(0);
$table->unsignedTinyInteger('pos')->default(0);
$table->boolean('active')->default(true);
$table->timestamps();
$table->foreign('folder_id')
->references('id')
->on('i_q_content_folders')
->onDelete('cascade');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('i_q_content_files');
}
}

View file

@ -0,0 +1,39 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateIQContentCategoriesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('i_q_content_categories', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name')->index();
$table->string('slug')->unique()->index();
$table->unsignedTinyInteger('pos')->default(0);
$table->boolean('active')->default(true);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('i_q_content_categories');
}
}

View file

@ -0,0 +1,45 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateIQContentTagsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('i_q_content_tags', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('category_id')->index();
$table->string('name')->index();
$table->string('slug')->unique()->index();
$table->unsignedTinyInteger('pos')->default(0);
$table->boolean('active')->default(true);
$table->timestamps();
$table->foreign('category_id')
->references('id')
->on('i_q_content_categories')
->onDelete('cascade');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('i_q_content_tags');
}
}

View file

@ -0,0 +1,49 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateIQContentFileTagsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('i_q_content_file_tags', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('file_id')->index();
$table->unsignedBigInteger('tag_id')->index();
$table->timestamps();
$table->foreign('file_id')
->references('id')
->on('i_q_content_files')
->onDelete('cascade');
$table->foreign('tag_id')
->references('id')
->on('i_q_content_tags')
->onDelete('cascade');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('i_q_content_file_tags');
}
}

View file

@ -0,0 +1,2 @@
source 'https://rubygems.org'
gem 'github-pages', group: :jekyll_plugins

View file

@ -0,0 +1,11 @@
theme: jekyll-theme-cayman
# Setup
title: Laravel File Manager
description: It's like Dropbox for your Laravel app.
google_analytics: UA-92410814-4
# About/contact
author:
name: UniSharp
url: https://github.com/UniSharp

View file

@ -0,0 +1,140 @@
<!DOCTYPE html>
<html lang="en-us">
<head>
<meta charset="UTF-8">
<title>{{ page.title | default: site.title }}</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#157878">
<link href='https://fonts.googleapis.com/css?family=Open+Sans:400,700' rel='stylesheet' type='text/css'>
<link rel="stylesheet" href="{{ '/assets/css/style.css?v=' | append: site.github.build_revision | relative_url }}">
<style>
h1.project-name {
margin-top: 100px;
}
h2.project-tagline {
margin-bottom: 5rem;
}
#nav > span > a {
color: #dddddd;
}
#nav > span:not(:last-child)::after {
content: ' |';
color: #dddddd;
}
.button-group {
display: flex;
justify-content: center;
margin-bottom: 30px;
}
@media screen and (max-width: 42em) {
a.btn {
width: 50%;
margin: 0;
}
a.btn + a.btn {
margin-top: 0;
margin-left: 15px;
}
}
.main-content {
padding: 1rem !important;
}
#carbonads {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", Helvetica, Arial, sans-serif;
overflow: hidden;
background-color: rgba(0, 0, 0, .05);
font-size: 14px;
padding: 15px 15px 15px 160px;
}
#carbonads a {
text-decoration: none;
}
#carbonads a:hover {
color: inherit;
}
.carbon-img {
float: left;
margin-left: -145px;
width: 130px;
height: 100px;
}
.carbon-img img {
width: 130px;
height: 100px;
}
.carbon-text {
color: #333;
}
.carbon-poweredby {
display: block;
color: #777;
}
@media only screen and (min-width: 400px) {
#carbonads {
max-width: 330px;
}
}
</style>
</head>
<body>
<section class="page-header">
<h1 class="project-name">{{ site.title | default: site.github.repository_name }}</h1>
<h2 class="project-tagline">{{ site.description | default: site.github.project_tagline }}</h2>
<div class="button-group">
<a class="btn" href="https://github.com/UniSharp/laravel-filemanager-example-5.3" target="_blank">Demo</a>
{% if site.github.is_project_page %}
<a href="{{ site.github.repository_url }}" class="btn">View on GitHub</a>
{% endif %}
{% if site.show_downloads %}
<a href="{{ site.github.zip_url }}" class="btn">Download .zip</a>
<a href="{{ site.github.tar_url }}" class="btn">Download .tar.gz</a>
{% endif %}
</div>
<nav id="nav">
<span><a href="/laravel-filemanager">Home</a></span>
<span><a href="installation">Installation</a></span>
<span><a href="integration">Integration</a></span>
<span><a href="config">Config</a></span>
<span><a href="customization">Customization</a></span>
<span><a href="events">Events</a></span>
<span><a href="upgrade">Upgrade</a></span>
<span><a href="contribution">Contribution</a></span>
</nav>
</section>
<section class="main-content">
<script async type="text/javascript" src="//cdn.carbonads.com/carbon.js?serve=CK7DE2JL&placement=unisharpgithubio" id="_carbonads_js"></script>
{{ content }}
<footer class="site-footer">
{% if site.github.is_project_page %}
<span class="site-footer-owner"><a href="{{ site.github.repository_url }}">{{ site.github.repository_name }}</a> is maintained by <a href="{{ site.github.owner_url }}">{{ site.github.owner_name }}</a>.</span>
{% endif %}
<span class="site-footer-credits">This page was generated by <a href="https://pages.github.com">GitHub Pages</a>.</span>
</footer>
</section>
{% if site.google_analytics %}
<script type="text/javascript">
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', '{{ site.google_analytics }}', 'auto');
ga('send', 'pageview');
</script>
{% endif %}
</body>
</html>

View file

@ -0,0 +1,103 @@
**The config is in `config/lfm.php`.**
## Routing:
| Key | Type | Description |
|----------------------|---------|--------------------------------------------------------------------------------------------------------------|
| use\_package\_routes | boolean | Use routes from package or not. If false, you will need to define routes to all controllers of this package. |
| middlewares | array | Middlewares to be applied to default routes. For laravel 5.1 and before, remove 'web' from the array. |
| url_prefix | string | The url prefix to this package. Change it if necessary. |
## Multi-User Mode:
| Key | Type | Description |
|----------------------|---------|------------------------------------------------------------------------------------------------|
| allow\_multi\_user | boolean | If true, private folders will be created for each signed-in user. |
| allow\_share\_folder | boolean | If true, share folder will be created. |
| user_field | string | Private folders will be named by this. Can receive column name of `users` table or class name. |
### If you want to name private folders other than columns of users table, follow these steps:
1. Run `php artisan vendor:publish --tag=lfm_handler`.
2. Fill `App\Handler\ConfigHandler::class` into `user_field`.
3. Edit `userField()` in the `App\Handler\ConfigHandler`
## Working Directory:
| Key | Type | Description |
|----------------------|--------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| base_directory | string | Which folder to store files in project, fill in 'public', 'resources', 'storage' and so on. Does not support path relative path like `../public_html` or `public/upload/user/`. |
| images\_folder\_name | string | Does not support path relative path like `../public_html` or `public/upload/user/`. |
| files\_folder\_name | string | Does not support path relative path like `../public_html` or `public/upload/user/`. |
| shared\_folder\_name | string | Does not support path relative path like `../public_html` or `public/upload/user/`. |
| thumb\_folder\_name | string | Does not support path relative path like `../public_html` or `public/upload/user/`. |
## Startup Views:
| Key | Type | Description |
|-----------------------|--------|-----------------------------------------------------------------|
| images\_startup\_view | string | The default display type for images. Supported: "grid", "list". |
| files\_startup\_view | string | The default display type for files. Supported: "grid", "list". |
## Upload / Validation:
| Key | Type | Description |
|----------------------------|---------|---------------------------------------------------------------------------|
| disk (Alpha version only) | string | Correspond to `disks` section in `config/filesystems.php`. |
| rename_file | string | If true, the uploaded file will be renamed to uniqid() + file extension. |
| alphanumeric_filename | string | If true, non-alphanumeric file name will be replaced with `_`. |
| alphanumeric_directory | boolean | If true, non-alphanumeric folder name will be rejected. |
| should\_validate\_size | boolean | If true, the size of uploading file will be verified. |
| max\_image\_size | int | Specify max size of uploading image. |
| max\_file\_size | int | Specify max size of uploading file. |
| should\_validate\_mime | boolean | If true, the mime type of uploading file will be verified. |
| valid\_image\_mimetypes | array | Array of mime types. Available since v1.3.0 . |
| should\_create\_thumbnails | boolean | If true, thumbnails will be created for faster loading. |
| raster\_mimetypes | array | Array of mime types. Thumbnails will be created only for these mimetypes. |
| create\_folder\_mode | int | Permission setting for folders created by this package. |
| create\_file\_mode | int | Permission setting for files uploaded to this package. |
| should\_change\_file\_mode | boolean | If true, it will attempt to chmod the file after upload |
| valid\_file\_mimetypes | array | Array of mime types. Available since v1.3.0 . |
##### Appendix:
* [full mime types list](http://docs.w3cub.com/http/basics_of_http/mime_types/complete_list_of_mime_types/)
* [Laravel File Storage](https://laravel.com/docs/master/filesystem)
## Thumbnail dimensions:
| Key | Type | Description |
|--------------------|--------|--------------------------------------------------|
| thumb\_img\_width | string | Width of thumbnail made when image is uploaded. |
| thumb\_img\_height | string | Height of thumbnail made when image is uploaded. |
## File Extension Information
| Key | Type | Description |
|-------------------|-------|---------------------------------------------|
| file\_type\_array | array | Map file extension with display names. |
| file\_icon\_array | array | Map file extension with icons(font-awsome). |
## php.ini override
| Key | Type | Description |
|---------------------|------------------|-----------------------------------------------------------------------------------------------------------------------------------|
| php\_ini\_overrides | array or boolean | These values override your php.ini settings before uploading files. Set these to false to ingnore and apply your php.ini settings |
### Caveats
The php\_ini\_overrides are applied on every request the filemanager does and are reset once the script has finished executing.
This has one drawback: any ini settings that you might want to change that apply to the request itself will not work.
For example, overriding these settings will not work:
* upload\_max\_filesize
* post\_max\_size
**Why this is expected behaviour:**
upload\_max\_filesize and post\_max\_size will get set but uploaded files are already passed to your PHP script before the settings are changed.

View file

@ -0,0 +1,13 @@
## Steps to contribute
1. Fork [unisharp/laravel-filemanager](https://github.com/UniSharp/laravel-filemanager) from GitHub.
1. Run commands below:
```
git clone git@github.com:UniSharp/laravel-filemanager-example-5.3.git
cd laravel-filemanager-example-5.3
composer require unisharp/laravel-filemanager:dev-master
make init
```
1. Edit codes in `vendor/unisharp/laravel-filemanager`
1. Push your changes to your fork.
1. Send a pull request to [unisharp/laravel-filemanager](https://github.com/UniSharp/laravel-filemanager).

View file

@ -0,0 +1,47 @@
## Routes
1. Edit `routes/web.php` :
Create route group to wrap package routes.
```php
Route::group(['prefix' => 'laravel-filemanager', 'middleware' => ['web', 'auth']], function () {
\UniSharp\LaravelFilemanager\Lfm::routes();
});
```
Make sure `auth` middleware is present to :
1. prevent unauthorized uploads
1. work properly with multi-user mode
1. Make sure urls below is correspond to your route (remember to include type parameter `?type=Images` or `?type=Files`) :
* CKEditor
```javascript
CKEDITOR.replace('editor', {
filebrowserImageBrowseUrl: '/your-custom-route?type=Images',
filebrowserBrowseUrl: '/your-custom-route?type=Files'
});
```
* TinyMCE
```javascript
...
var cmsURL = editor_config.path_absolute + 'your-custom-route?field_name='+field_name+'&lang='+ tinymce.settings.language;
if (type == 'image') {
cmsURL = cmsURL + "&type=Images";
} else {
cmsURL = cmsURL + "&type=Files";
}
...
```
## Views
Copy views to `resources/views/vendor/unisharp/laravel-filemanager/` :
```bash
php artisan vendor:publish --tag=lfm_view
```
## Translations
1. Copy `vendor/unisharp/laravel-filemanager/src/lang/en` to `/resources/lang/vendor/laravel-filemanager/<YOUR LANGUAGE>/lfm.php`.
1. Edit the file as you please.

View file

@ -0,0 +1,97 @@
## List of events
* UniSharp\LaravelFilemanager\Events\ImageIsUploading
* UniSharp\LaravelFilemanager\Events\ImageWasUploaded
* UniSharp\LaravelFilemanager\Events\ImageIsRenaming
* UniSharp\LaravelFilemanager\Events\ImageWasRenamed
* UniSharp\LaravelFilemanager\Events\ImageIsDeleting
* UniSharp\LaravelFilemanager\Events\ImageWasDeleted
* UniSharp\LaravelFilemanager\Events\FolderIsRenaming
* UniSharp\LaravelFilemanager\Events\FolderWasRenamed
* UniSharp\LaravelFilemanager\Events\ImageIsResizing
* UniSharp\LaravelFilemanager\Events\ImageWasResized
* UniSharp\LaravelFilemanager\Events\ImageIsCropping
* UniSharp\LaravelFilemanager\Events\ImageWasCropped
## How to use
* Sample code : [laravel-filemanager-demo-events](https://github.com/UniSharp/laravel-filemanager-demo-events)
* To use events you can add a listener to listen to the events.
Snippet for `EventServiceProvider`
```php
protected $listen = [
ImageWasUploaded::class => [
UploadListener::class,
],
];
```
The `UploadListener` will look like:
```php
class UploadListener
{
public function handle($event)
{
$method = 'on'.class_basename($event);
if (method_exists($this, $method)) {
call_user_func([$this, $method], $event);
}
}
public function onImageWasUploaded(ImageWasUploaded $event)
{
$path = $event->path();
//your code, for example resizing and cropping
}
}
```
* Or by using Event Subscribers
Snippet for `EventServiceProvider`
```php
protected $subscribe = [
UploadListener::class
];
```
The `UploadListener` will look like:
```php
public function subscribe($events)
{
$events->listen('*', UploadListener::class);
}
public function handle($event)
{
$method = 'on'.class_basename($event);
if (method_exists($this, $method)) {
call_user_func([$this, $method], $event);
}
}
public function onImageWasUploaded(ImageWasUploaded $event)
{
$path = $event->path();
// your code, for example resizing and cropping
}
public function onImageWasRenamed(ImageWasRenamed $event)
{
// image was renamed
}
public function onImageWasDeleted(ImageWasDeleted $event)
{
// image was deleted
}
public function onFolderWasRenamed(FolderWasRenamed $event)
{
// folder was renamed
}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 966 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -0,0 +1,29 @@
[![Latest Stable Version](https://poser.pugx.org/unisharp/laravel-filemanager/v/stable)](https://packagist.org/packages/unisharp/laravel-filemanager)
[![Total Downloads](https://poser.pugx.org/unisharp/laravel-filemanager/downloads)](https://packagist.org/packages/unisharp/laravel-filemanager)
[![License](https://poser.pugx.org/unisharp/laravel-filemanager/license)](https://packagist.org/packages/unisharp/laravel-filemanager)
## Features
* CKEditor and TinyMCE integration
* Standalone button
* Uploading validation
* Cropping and resizing of images
* Public and private folders for multi users
* Customizable routes, middlewares, views, and folder path
* Supports two types : files and images. Each type works in different directory.
* Supported locales : ar, bg, de, el, en, es, fa, fr, it, he, hu, nl, pl, pt-BR, pt_PT, ro, ru, sv, tr, zh-CN, zh-TW
PR is welcome!
## Screenshots
> Standalone button :
![Standalone button demo](https://unisharp.github.io/laravel-filemanager/images/lfm01.png)
> Grid view :
![Grid view demo](https://unisharp.github.io/laravel-filemanager/images/lfm02.png)
> List view :
![List view demo](https://unisharp.github.io/laravel-filemanager/images/lfm03.png)

View file

@ -0,0 +1,64 @@
## Requirements
* php >= 5.4
* exif extension
* fileinfo extension
* GD Library >=2.0 or Imagick PHP extension >=6.5.7
* Laravel 5
* requires [intervention/image](https://github.com/Intervention/image) (to make thumbs, crop and resize images).
## Installation
1. Install package
```bash
composer require unisharp/laravel-filemanager:~1.8
```
1. (optional) Edit `config/app.php` :
\* *For Laravel 5.5 and up, skip to step 3. All service providers and facades are automatically discovered.*
Add service providers
```php
UniSharp\LaravelFilemanager\LaravelFilemanagerServiceProvider::class,
Intervention\Image\ImageServiceProvider::class,
```
And add class aliases
```php
'Image' => Intervention\Image\Facades\Image::class,
```
Code above is for Laravel 5.1.
In Laravel 5.0 should leave only quoted class names.
1. Publish the package's config and assets :
```bash
php artisan vendor:publish --tag=lfm_config
php artisan vendor:publish --tag=lfm_public
```
1. Run commands to clear cache :
```bash
php artisan route:clear
php artisan config:clear
```
1. Ensure that the files & images directories (in `config/lfm.php`) are writable by your web server (run commands like `chown` or `chmod`).
1. Create symbolic link :
```bash
php artisan storage:link
```
1. Edit `APP_URL` in `.env`.
## What's next
1. Check the [integration document](http://unisharp.github.io/laravel-filemanager/integration) to see how to apply this package.
1. Check the [config document](http://unisharp.github.io/laravel-filemanager/config) to discover the flexibility of this package.

View file

@ -0,0 +1,205 @@
## Note
Check `vendor/unisharp/laravel-filemanager/src/views/demo.blade.php`, which already integrated all options from below.
## WYSIWYG Editor Integration:
### Option 1: CKEditor
```html
<textarea id="my-editor" name="content" class="form-control">{!! old('content', 'test editor content') !!}</textarea>
<script src="//cdn.ckeditor.com/4.6.2/standard/ckeditor.js"></script>
<script>
var options = {
filebrowserImageBrowseUrl: '/laravel-filemanager?type=Images',
filebrowserImageUploadUrl: '/laravel-filemanager/upload?type=Images&_token={{csrf_token()}}',
filebrowserBrowseUrl: '/laravel-filemanager?type=Files',
filebrowserUploadUrl: '/laravel-filemanager/upload?type=Files&_token={{csrf_token()}}'
};
</script>
```
* Sample 1 - Replace by ID:
```html
<script>
CKEDITOR.replace('my-editor', options);
</script>
```
* Sample 2 - With JQuery Selector:
```html
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script src="/vendor/unisharp/laravel-ckeditor/adapters/jquery.js"></script>
<script>
$('textarea.my-editor').ckeditor(options);
</script>
```
### Option 2: TinyMCE4
```html
<script src="//cdn.tinymce.com/4/tinymce.min.js"></script>
<textarea name="content" class="form-control my-editor">{!! old('content', $content) !!}</textarea>
<script>
var editor_config = {
path_absolute : "/",
selector: "textarea.my-editor",
plugins: [
"advlist autolink lists link image charmap print preview hr anchor pagebreak",
"searchreplace wordcount visualblocks visualchars code fullscreen",
"insertdatetime media nonbreaking save table contextmenu directionality",
"emoticons template paste textcolor colorpicker textpattern"
],
toolbar: "insertfile undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image media",
relative_urls: false,
file_browser_callback : function(field_name, url, type, win) {
var x = window.innerWidth || document.documentElement.clientWidth || document.getElementsByTagName('body')[0].clientWidth;
var y = window.innerHeight|| document.documentElement.clientHeight|| document.getElementsByTagName('body')[0].clientHeight;
var cmsURL = editor_config.path_absolute + 'laravel-filemanager?field_name=' + field_name;
if (type == 'image') {
cmsURL = cmsURL + "&type=Images";
} else {
cmsURL = cmsURL + "&type=Files";
}
tinyMCE.activeEditor.windowManager.open({
file : cmsURL,
title : 'Filemanager',
width : x * 0.8,
height : y * 0.8,
resizable : "yes",
close_previous : "no"
});
}
};
tinymce.init(editor_config);
</script>
```
### Option 3: Summernote
```html
<!-- dependencies (Summernote depends on Bootstrap & jQuery) -->
<link href="http://netdna.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.css" rel="stylesheet">
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.js"></script>
<script src="http://netdna.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.js"></script>
<link href="http://cdnjs.cloudflare.com/ajax/libs/summernote/0.8.3/summernote.css" rel="stylesheet">
<script src="http://cdnjs.cloudflare.com/ajax/libs/summernote/0.8.3/summernote.js"></script>
<!-- markup -->
<textarea id="summernote-editor" name="content">{!! old('content', $content) !!}</textarea>
<!-- summernote config -->
<script>
$(document).ready(function(){
// Define function to open filemanager window
var lfm = function(options, cb) {
var route_prefix = (options && options.prefix) ? options.prefix : '/laravel-filemanager';
window.open(route_prefix + '?type=' + options.type || 'file', 'FileManager', 'width=900,height=600');
window.SetUrl = cb;
};
// Define LFM summernote button
var LFMButton = function(context) {
var ui = $.summernote.ui;
var button = ui.button({
contents: '<i class="note-icon-picture"></i> ',
tooltip: 'Insert image with filemanager',
click: function() {
lfm({type: 'image', prefix: '/laravel-filemanager'}, function(lfmItems, path) {
lfmItems.forEach(function (lfmItem) {
context.invoke('insertImage', lfmItem.url);
});
});
}
});
return button.render();
};
// Initialize summernote with LFM button in the popover button group
// Please note that you can add this button to any other button group you'd like
$('#summernote-editor').summernote({
toolbar: [
['popovers', ['lfm']],
],
buttons: {
lfm: LFMButton
}
})
});
</script>
```
## Standalone button
If you are going to use filemanager independently, meaning set the value of an input to selected photo/file url, follow this structure:
1. Create a button, input, and image preview holder if you are going to choose images.
Specify the id to the input and image preview by `data-input` and `data-preview`.
```html
<div class="input-group">
<span class="input-group-btn">
<a id="lfm" data-input="thumbnail" data-preview="holder" class="btn btn-primary">
<i class="fa fa-picture-o"></i> Choose
</a>
</span>
<input id="thumbnail" class="form-control" type="text" name="filepath">
</div>
<img id="holder" style="margin-top:15px;max-height:100px;">
```
1. Import lfm.js(run `php artisan vendor:publish` if you need).
```html
<script src="/vendor/laravel-filemanager/js/stand-alone-button.js"></script>
```
1. Init filemanager with type. (requires jQuery)
```javascript
$('#lfm').filemanager('image');
```
or
```javascript
$('#lfm').filemanager('file');
```
Domain can be specified in the second parameter(optional, but will be required when developing on Windows mechines) :
```javascript
var domain = "{{ url() }}";
$('#lfm').filemanager('image', {prefix: domain});
```
## JavaScript integration
In case you are developing javascript application and you want dynamically to trigger filemanager popup, you can create function like this. It doesn't rely on jQuery.
```javascript
var lfm = function(options, cb) {
var route_prefix = (options && options.prefix) ? options.prefix : '/laravel-filemanager';
window.open(route_prefix + '?type=' + options.type || 'file', 'FileManager', 'width=900,height=600');
window.SetUrl = cb;
}
```
And use it like this:
```javascript
lfm({type: 'image', prefix: 'prefix'}, function(url, path) {
});
```
## Embed file manager
```html
<iframe src="/laravel-filemanager" style="width: 100%; height: 500px; overflow: hidden; border: none;"></iframe>
```

View file

@ -0,0 +1,23 @@
## Upgrade instructions
1. Please backup your own `config/lfm.php` before upgrading.
1. Run commands:
```bash
composer update unisharp/laravel-filemanager
php artisan vendor:publish --tag=lfm_view --force
php artisan vendor:publish --tag=lfm_public --force
php artisan vendor:publish --tag=lfm_config --force
php artisan route:clear
php artisan config:clear
```
1. Clear browser cache if page is broken after upgrading.
## Errors with namespace
We have changed namespace from `Unisharp` to `UniSharp`, and change the first character of every namespace into capital.
If you are updating this package and encounter any errors like `Class not found`, please remove this package entirely and reinstall again.

View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
bootstrap="vendor/autoload.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false"
>
<testsuites>
<testsuite name="Package Test Suite">
<directory suffix=".php">./tests/</directory>
</testsuite>
</testsuites>
</phpunit>

View file

@ -0,0 +1,9 @@
/*!
* Cropper v0.9.1
* https://github.com/fengyuanchen/cropper
*
* Copyright (c) 2014-2015 Fengyuan Chen and contributors
* Released under the MIT license
*
* Date: 2015-03-21T04:58:27.265Z
*/.cropper-container{position:relative;overflow:hidden;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent;-webkit-touch-callout:none}.cropper-container img{display:block;width:100%;min-width:0!important;max-width:none!important;height:100%;min-height:0!important;max-height:none!important;image-orientation:0deg!important}.cropper-canvas,.cropper-crop-box,.cropper-drag-box,.cropper-modal{position:absolute;top:0;right:0;bottom:0;left:0}.cropper-drag-box{background-color:#fff;filter:alpha(opacity=0);opacity:0}.cropper-modal{background-color:#000;filter:alpha(opacity=50);opacity:.5}.cropper-view-box{display:block;width:100%;height:100%;overflow:hidden;outline:#69f solid 1px;outline-color:rgba(102,153,255,.75)}.cropper-dashed{position:absolute;display:block;filter:alpha(opacity=50);border:0 dashed #fff;opacity:.5}.cropper-dashed.dashed-h{top:33.33333333%;left:0;width:100%;height:33.33333333%;border-top-width:1px;border-bottom-width:1px}.cropper-dashed.dashed-v{top:0;left:33.33333333%;width:33.33333333%;height:100%;border-right-width:1px;border-left-width:1px}.cropper-face,.cropper-line,.cropper-point{position:absolute;display:block;width:100%;height:100%;filter:alpha(opacity=10);opacity:.1}.cropper-face{top:0;left:0;cursor:move;background-color:#fff}.cropper-line{background-color:#69f}.cropper-line.line-e{top:0;right:-3px;width:5px;cursor:e-resize}.cropper-line.line-n{top:-3px;left:0;height:5px;cursor:n-resize}.cropper-line.line-w{top:0;left:-3px;width:5px;cursor:w-resize}.cropper-line.line-s{bottom:-3px;left:0;height:5px;cursor:s-resize}.cropper-point{width:5px;height:5px;background-color:#69f;filter:alpha(opacity=75);opacity:.75}.cropper-point.point-e{top:50%;right:-3px;margin-top:-3px;cursor:e-resize}.cropper-point.point-n{top:-3px;left:50%;margin-left:-3px;cursor:n-resize}.cropper-point.point-w{top:50%;left:-3px;margin-top:-3px;cursor:w-resize}.cropper-point.point-s{bottom:-3px;left:50%;margin-left:-3px;cursor:s-resize}.cropper-point.point-ne{top:-3px;right:-3px;cursor:ne-resize}.cropper-point.point-nw{top:-3px;left:-3px;cursor:nw-resize}.cropper-point.point-sw{bottom:-3px;left:-3px;cursor:sw-resize}.cropper-point.point-se{right:-3px;bottom:-3px;width:20px;height:20px;cursor:se-resize;filter:alpha(opacity=100);opacity:1}.cropper-point.point-se:before{position:absolute;right:-50%;bottom:-50%;display:block;width:200%;height:200%;content:" ";background-color:#69f;filter:alpha(opacity=0);opacity:0}@media (min-width:768px){.cropper-point.point-se{width:15px;height:15px}}@media (min-width:992px){.cropper-point.point-se{width:10px;height:10px}}@media (min-width:1200px){.cropper-point.point-se{width:5px;height:5px;filter:alpha(opacity=75);opacity:.75}}.cropper-bg{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC)}.cropper-invisible{filter:alpha(opacity=0);opacity:0}.cropper-hide{position:fixed;top:0;left:0;z-index:-1;width:auto!important;min-width:0!important;max-width:none!important;height:auto!important;min-height:0!important;max-height:none!important;filter:alpha(opacity=0);opacity:0}.cropper-hidden{display:none!important}.cropper-move{cursor:move}.cropper-crop{cursor:crosshair}.cropper-disabled .cropper-canvas,.cropper-disabled .cropper-face,.cropper-disabled .cropper-line,.cropper-disabled .cropper-point{cursor:not-allowed}

View file

@ -0,0 +1,532 @@
@-webkit-keyframes passing-through {
0% {
opacity: 0;
-webkit-transform: translateY(40px);
-moz-transform: translateY(40px);
-ms-transform: translateY(40px);
-o-transform: translateY(40px);
transform: translateY(40px)
}
30%, 70% {
opacity: 1;
-webkit-transform: translateY(0px);
-moz-transform: translateY(0px);
-ms-transform: translateY(0px);
-o-transform: translateY(0px);
transform: translateY(0px)
}
100% {
opacity: 0;
-webkit-transform: translateY(-40px);
-moz-transform: translateY(-40px);
-ms-transform: translateY(-40px);
-o-transform: translateY(-40px);
transform: translateY(-40px)
}
}
@-moz-keyframes passing-through {
0% {
opacity: 0;
-webkit-transform: translateY(40px);
-moz-transform: translateY(40px);
-ms-transform: translateY(40px);
-o-transform: translateY(40px);
transform: translateY(40px)
}
30%, 70% {
opacity: 1;
-webkit-transform: translateY(0px);
-moz-transform: translateY(0px);
-ms-transform: translateY(0px);
-o-transform: translateY(0px);
transform: translateY(0px)
}
100% {
opacity: 0;
-webkit-transform: translateY(-40px);
-moz-transform: translateY(-40px);
-ms-transform: translateY(-40px);
-o-transform: translateY(-40px);
transform: translateY(-40px)
}
}
@keyframes passing-through {
0% {
opacity: 0;
-webkit-transform: translateY(40px);
-moz-transform: translateY(40px);
-ms-transform: translateY(40px);
-o-transform: translateY(40px);
transform: translateY(40px)
}
30%, 70% {
opacity: 1;
-webkit-transform: translateY(0px);
-moz-transform: translateY(0px);
-ms-transform: translateY(0px);
-o-transform: translateY(0px);
transform: translateY(0px)
}
100% {
opacity: 0;
-webkit-transform: translateY(-40px);
-moz-transform: translateY(-40px);
-ms-transform: translateY(-40px);
-o-transform: translateY(-40px);
transform: translateY(-40px)
}
}
@-webkit-keyframes slide-in {
0% {
opacity: 0;
-webkit-transform: translateY(40px);
-moz-transform: translateY(40px);
-ms-transform: translateY(40px);
-o-transform: translateY(40px);
transform: translateY(40px)
}
30% {
opacity: 1;
-webkit-transform: translateY(0px);
-moz-transform: translateY(0px);
-ms-transform: translateY(0px);
-o-transform: translateY(0px);
transform: translateY(0px)
}
}
@-moz-keyframes slide-in {
0% {
opacity: 0;
-webkit-transform: translateY(40px);
-moz-transform: translateY(40px);
-ms-transform: translateY(40px);
-o-transform: translateY(40px);
transform: translateY(40px)
}
30% {
opacity: 1;
-webkit-transform: translateY(0px);
-moz-transform: translateY(0px);
-ms-transform: translateY(0px);
-o-transform: translateY(0px);
transform: translateY(0px)
}
}
@keyframes slide-in {
0% {
opacity: 0;
-webkit-transform: translateY(40px);
-moz-transform: translateY(40px);
-ms-transform: translateY(40px);
-o-transform: translateY(40px);
transform: translateY(40px)
}
30% {
opacity: 1;
-webkit-transform: translateY(0px);
-moz-transform: translateY(0px);
-ms-transform: translateY(0px);
-o-transform: translateY(0px);
transform: translateY(0px)
}
}
@-webkit-keyframes pulse {
0% {
-webkit-transform: scale(1);
-moz-transform: scale(1);
-ms-transform: scale(1);
-o-transform: scale(1);
transform: scale(1)
}
10% {
-webkit-transform: scale(1.1);
-moz-transform: scale(1.1);
-ms-transform: scale(1.1);
-o-transform: scale(1.1);
transform: scale(1.1)
}
20% {
-webkit-transform: scale(1);
-moz-transform: scale(1);
-ms-transform: scale(1);
-o-transform: scale(1);
transform: scale(1)
}
}
@-moz-keyframes pulse {
0% {
-webkit-transform: scale(1);
-moz-transform: scale(1);
-ms-transform: scale(1);
-o-transform: scale(1);
transform: scale(1)
}
10% {
-webkit-transform: scale(1.1);
-moz-transform: scale(1.1);
-ms-transform: scale(1.1);
-o-transform: scale(1.1);
transform: scale(1.1)
}
20% {
-webkit-transform: scale(1);
-moz-transform: scale(1);
-ms-transform: scale(1);
-o-transform: scale(1);
transform: scale(1)
}
}
@keyframes pulse {
0% {
-webkit-transform: scale(1);
-moz-transform: scale(1);
-ms-transform: scale(1);
-o-transform: scale(1);
transform: scale(1)
}
10% {
-webkit-transform: scale(1.1);
-moz-transform: scale(1.1);
-ms-transform: scale(1.1);
-o-transform: scale(1.1);
transform: scale(1.1)
}
20% {
-webkit-transform: scale(1);
-moz-transform: scale(1);
-ms-transform: scale(1);
-o-transform: scale(1);
transform: scale(1)
}
}
.dropzone, .dropzone * {
box-sizing: border-box
}
.dropzone {
min-height: 150px;
border: 2px solid rgba(0, 0, 0, 0.3);
background: white;
padding: 20px 20px
}
.dropzone.dz-clickable {
cursor: pointer
}
.dropzone.dz-clickable * {
cursor: default
}
.dropzone.dz-clickable .dz-message, .dropzone.dz-clickable .dz-message * {
cursor: pointer
}
.dropzone.dz-started .dz-message {
display: none
}
.dropzone.dz-drag-hover {
border-style: solid
}
.dropzone.dz-drag-hover .dz-message {
opacity: 0.5
}
.dropzone .dz-message {
text-align: center;
margin: 2em 0
}
.dropzone .dz-preview {
position: relative;
display: inline-block;
vertical-align: top;
margin: 16px;
min-height: 100px
}
.dropzone .dz-preview:hover {
z-index: 1000
}
.dropzone .dz-preview:hover .dz-details {
opacity: 1
}
.dropzone .dz-preview.dz-file-preview .dz-image {
border-radius: 20px;
background: #999;
background: linear-gradient(to bottom, #eee, #ddd)
}
.dropzone .dz-preview.dz-file-preview .dz-details {
opacity: 1
}
.dropzone .dz-preview.dz-image-preview {
background: white
}
.dropzone .dz-preview.dz-image-preview .dz-details {
-webkit-transition: opacity 0.2s linear;
-moz-transition: opacity 0.2s linear;
-ms-transition: opacity 0.2s linear;
-o-transition: opacity 0.2s linear;
transition: opacity 0.2s linear
}
.dropzone .dz-preview .dz-remove {
font-size: 14px;
text-align: center;
display: block;
cursor: pointer;
border: none
}
.dropzone .dz-preview .dz-remove:hover {
text-decoration: underline
}
.dropzone .dz-preview:hover .dz-details {
opacity: 1
}
.dropzone .dz-preview .dz-details {
z-index: 20;
position: absolute;
top: 0;
left: 0;
opacity: 0;
font-size: 13px;
min-width: 100%;
max-width: 100%;
padding: 2em 1em;
text-align: center;
color: rgba(0, 0, 0, 0.9);
line-height: 150%
}
.dropzone .dz-preview .dz-details .dz-size {
margin-bottom: 1em;
font-size: 16px
}
.dropzone .dz-preview .dz-details .dz-filename {
white-space: nowrap
}
.dropzone .dz-preview .dz-details .dz-filename:hover span {
border: 1px solid rgba(200, 200, 200, 0.8);
background-color: rgba(255, 255, 255, 0.8)
}
.dropzone .dz-preview .dz-details .dz-filename:not(:hover) {
overflow: hidden;
text-overflow: ellipsis
}
.dropzone .dz-preview .dz-details .dz-filename:not(:hover) span {
border: 1px solid transparent
}
.dropzone .dz-preview .dz-details .dz-filename span, .dropzone .dz-preview .dz-details .dz-size span {
background-color: rgba(255, 255, 255, 0.4);
padding: 0 0.4em;
border-radius: 3px
}
.dropzone .dz-preview:hover .dz-image img {
-webkit-transform: scale(1.05, 1.05);
-moz-transform: scale(1.05, 1.05);
-ms-transform: scale(1.05, 1.05);
-o-transform: scale(1.05, 1.05);
transform: scale(1.05, 1.05);
-webkit-filter: blur(8px);
filter: blur(8px)
}
.dropzone .dz-preview .dz-image {
border-radius: 20px;
overflow: hidden;
width: 120px;
height: 120px;
position: relative;
display: block;
z-index: 10
}
.dropzone .dz-preview .dz-image img {
display: block
}
.dropzone .dz-preview.dz-success .dz-success-mark {
-webkit-animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1);
-moz-animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1);
-ms-animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1);
-o-animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1);
animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1)
}
.dropzone .dz-preview.dz-error .dz-error-mark {
opacity: 1;
-webkit-animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1);
-moz-animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1);
-ms-animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1);
-o-animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1);
animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1)
}
.dropzone .dz-preview .dz-success-mark, .dropzone .dz-preview .dz-error-mark {
pointer-events: none;
opacity: 0;
z-index: 500;
position: absolute;
display: block;
top: 50%;
left: 50%;
margin-left: -27px;
margin-top: -27px
}
.dropzone .dz-preview .dz-success-mark svg, .dropzone .dz-preview .dz-error-mark svg {
display: block;
width: 54px;
height: 54px
}
.dropzone .dz-preview.dz-processing .dz-progress {
opacity: 1;
-webkit-transition: all 0.2s linear;
-moz-transition: all 0.2s linear;
-ms-transition: all 0.2s linear;
-o-transition: all 0.2s linear;
transition: all 0.2s linear
}
.dropzone .dz-preview.dz-complete .dz-progress {
opacity: 0;
-webkit-transition: opacity 0.4s ease-in;
-moz-transition: opacity 0.4s ease-in;
-ms-transition: opacity 0.4s ease-in;
-o-transition: opacity 0.4s ease-in;
transition: opacity 0.4s ease-in
}
.dropzone .dz-preview:not(.dz-processing) .dz-progress {
-webkit-animation: pulse 6s ease infinite;
-moz-animation: pulse 6s ease infinite;
-ms-animation: pulse 6s ease infinite;
-o-animation: pulse 6s ease infinite;
animation: pulse 6s ease infinite
}
.dropzone .dz-preview .dz-progress {
opacity: 1;
z-index: 1000;
pointer-events: none;
position: absolute;
height: 16px;
left: 50%;
top: 50%;
margin-top: -8px;
width: 80px;
margin-left: -40px;
background: rgba(255, 255, 255, 0.9);
-webkit-transform: scale(1);
border-radius: 8px;
overflow: hidden
}
.dropzone .dz-preview .dz-progress .dz-upload {
background: #333;
background: linear-gradient(to bottom, #666, #444);
position: absolute;
top: 0;
left: 0;
bottom: 0;
width: 0;
-webkit-transition: width 300ms ease-in-out;
-moz-transition: width 300ms ease-in-out;
-ms-transition: width 300ms ease-in-out;
-o-transition: width 300ms ease-in-out;
transition: width 300ms ease-in-out
}
.dropzone .dz-preview.dz-error .dz-error-message {
display: block
}
.dropzone .dz-preview.dz-error:hover .dz-error-message {
transform: scale(1.5);
pointer-events: auto
}
.dropzone .dz-preview .dz-error-message {
pointer-events: none;
z-index: 1000;
position: absolute;
display: block;
display: none;
opacity: 1;
-webkit-transition: all 0.3s ease;
-moz-transition: all 0.3s ease;
-ms-transition: all 0.3s ease;
-o-transition: all 0.3s ease;
transition: all 0.3s ease;
border-radius: 8px;
font-size: 13px;
top: 130px;
left: -10px;
width: 140px;
background: #be2626;
background: linear-gradient(to bottom, #be2626, #a92222);
padding: 0.5em 1.2em;
color: white
}
.dropzone .dz-preview .dz-error-message:after {
content: '';
position: absolute;
top: -6px;
left: 64px;
width: 0;
height: 0;
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-bottom: 6px solid #be2626
}

View file

@ -0,0 +1,374 @@
/* General */
a {
color: #333844;
text-decoration: none !important;
cursor: pointer;
}
#nav a, #fab a {
color: white;
}
#nav, #nav .dropdown-menu, .bg-main {
background-color: #333844;
}
#nav .dropdown-menu > a:hover {
color: #333844;
}
#actions {
display: flex;
}
#actions > a {
display: inline-block;
line-height: 4rem;
text-align: center;
width: 100%;
font-size: 1.25rem;
}
#actions > a > i {
margin-right: .25rem;
}
#actions > a + a {
border-left: 1px solid #dee2e6;
}
#multi_selection_toggle > i {
font-size: 20px;
}
.breadcrumb-item:not(.active) {
transition: .2s color;
}
.breadcrumb-item:not(.active):hover {
cursor: pointer;
color: #75C7C3;
}
#main {
width: 100%;
}
@media screen and (min-width: 992px) {
#main {
width: calc(100% - 300px);
/*margin-left: 1rem;*/
padding: 1rem;
}
.invisible-lg {
visibility: hidden;
}
}
#tree {
background-color: white;
width: 300px;
}
@media screen and (max-width: 991px) {
#tree {
position: absolute;
z-index: 999;
left: 0;
transform: translateX(-100%);
transition: 1s transform;
}
#tree.in {
transform: translateX(0);
}
}
#empty {
height: 60vh;
color: #333844;
}
#empty:not(.d-none) {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
#empty > i {
font-size: 10rem;
}
.carousel-image {
height: 50vh;
background-position: center;
background-size: contain;
background-repeat: no-repeat;
margin: 0 auto;
}
.carousel-indicators {
bottom: 0;
}
.carousel-label, .carousel-label:hover {
position: absolute;
bottom: 0;
background: linear-gradient(transparent 10px, rgba(0, 0, 0, .4), rgba(0, 0, 0, .5));
padding: 40px 20px 30px;
width: 100%;
color: white;
word-break: break-word;
text-align: center;
}
.carousel-control-background {
border-radius: 50%;
width: 25px;
height: 25px;
box-shadow: 0 0 10px #666;
background-color: #666;
}
#uploadForm > .dz-default.dz-message {
border: 2px dashed #ccc;
border-radius: 5px;
color: #aaa;
margin: 0;
padding: 3rem 0;
}
/* Loader */
#lfm-loader {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: gray;
opacity: 0.7;
z-index: 9999;
text-align: center;
}
#lfm-loader:before {
content: "";
display: inline-block;
vertical-align: middle;
height: 100%;
}
#lfm-loader img {
width: 100px;
margin: 0 auto;
display: inline-block;
vertical-align: middle;
}
/* Sidebar */
.nav-pills > .nav-item > .nav-link {
height: 5rem;
display: flex;
align-items: center;
}
.nav-pills > .sub-item > .nav-link {
height: 3rem;
padding-left: 3rem;
}
.nav-pills > li.active > a, .nav-pills > li:hover > a {
background-color: #ddd;
border-radius: 0;
color: #333844;
}
/* Items */
#content.preserve_actions_space {
padding-bottom: 4rem; /* preserve space for main actions */
}
.square {
cursor: pointer;
position: relative;
display: flex;
align-items: center;
justify-content: center;
padding: 5px;
}
.grid {
display: flex;
flex-wrap: wrap;
padding: .5rem;
justify-content: center;
}
.grid a {
margin: .5rem;
display: flex;
flex-direction: column;
}
.list a {
border-top: 1px solid rgb(221, 221, 221);
padding: 5px;
margin-top: 0;
display: flex;
flex-direction: row;
}
.list a:last-child {
border-bottom: 1px solid rgb(221, 221, 221);
}
.grid .square {
border: 1px solid rgb(221, 221, 221);
width: 135px;
height: 135px;
}
.list .square {
margin-right: 1rem;
width: 70px;
height: 70px;
}
.square > div {
width: 100%;
height: 100%;
background-size: cover;
background-position: center;
background-repeat: no-repeat;
}
.square > i {
color: #333844;
}
.grid .square > i {
padding: 20px;
font-size: 80px;
}
.list .square > i {
padding: 10px;
font-size: 50px;
}
.grid .square.selected {
border: 5px solid #75C7C3;
}
.list .square.selected {
border: 4px solid #75C7C3;
}
.square.selected {
padding: 1px;
}
.grid .item_name {
border: 1px solid rgb(221, 221, 221);
border-top: none;
margin-top: -1px;
padding: 10px;
text-align: center;
max-width: calc(135px);
}
.list .item_name {
font-size: 1.25rem;
padding: 5px 0 5px;
}
time {
font-size: .9rem;
}
.grid time {
display: none;
}
.info > * {
max-width: calc(100vw - 70px - 60px);
}
/* Mime icon generator overwrite */
.grid .mime-icon:before {
top: calc(45% - 1rem);
font-size: 2rem;
}
.list .mime-icon:before {
top: calc(45% - .5rem);
font-size: 1rem;
}
/* Floating action buttons */
.fab-wrapper {
margin: 1.5rem;
right: 0;
bottom: 0;
position: fixed;
}
.fab-wrapper .fab-button {
position: relative;
background-color: #333844;
width: 3.5rem;
height: 3.5rem;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.25rem;
box-shadow: 0 0 4px rgba(0, 0, 0, 0.14), 0 4px 8px rgba(0, 0, 0, 0.28);
}
.fab-wrapper .fab-toggle {
z-index: 1;
}
.fab-wrapper .fab-toggle i {
-webkit-transform: scale(1) rotate(0deg);
transform: scale(1) rotate(0deg);
-webkit-transition: -webkit-transform 350ms;
transition: transform 350ms;
}
.fab-wrapper.fab-expand .fab-toggle i {
-webkit-transform: scale(1) rotate(-225deg);
transform: scale(1) rotate(-225deg);
-webkit-transition: -webkit-transform 350ms;
transition: transform 350ms;
}
.fab-wrapper .fab-action {
z-index: -1;
margin-bottom: -3.5rem;
opacity: 0;
transition: margin-bottom 350ms, opacity 350ms;
}
.fab-wrapper.fab-expand .fab-action {
margin-bottom: 1rem;
opacity: 1;
transition: margin-bottom 350ms, opacity 350ms;
}
.fab-wrapper .fab-action:before {
position: absolute;
right: 4rem;
padding: .15rem .75rem;
border-radius: .25rem;
background-color: rgba(0, 0, 0, .4);
color: rgba(255, 255, 255, .8);
text-align: right;
font-size: .9rem;
white-space: nowrap;
content: attr(data-label);
}

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 966 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

View file

@ -0,0 +1,33 @@
<!-- By Sam Herbert (@sherb), for everyone. More @ http://goo.gl/7AJzbL -->
<svg width="120" height="30" viewBox="0 0 120 30" xmlns="http://www.w3.org/2000/svg" fill="#fff">
<circle cx="15" cy="15" r="15">
<animate attributeName="r" from="15" to="15"
begin="0s" dur="0.8s"
values="15;9;15" calcMode="linear"
repeatCount="indefinite" />
<animate attributeName="fill-opacity" from="1" to="1"
begin="0s" dur="0.8s"
values="1;.5;1" calcMode="linear"
repeatCount="indefinite" />
</circle>
<circle cx="60" cy="15" r="9" fill-opacity="0.3">
<animate attributeName="r" from="9" to="9"
begin="0s" dur="0.8s"
values="9;15;9" calcMode="linear"
repeatCount="indefinite" />
<animate attributeName="fill-opacity" from="0.5" to="0.5"
begin="0s" dur="0.8s"
values=".5;1;.5" calcMode="linear"
repeatCount="indefinite" />
</circle>
<circle cx="105" cy="15" r="15">
<animate attributeName="r" from="15" to="15"
begin="0s" dur="0.8s"
values="15;9;15" calcMode="linear"
repeatCount="indefinite" />
<animate attributeName="fill-opacity" from="1" to="1"
begin="0s" dur="0.8s"
values="1;.5;1" calcMode="linear"
repeatCount="indefinite" />
</circle>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,670 @@
var lfm_route = location.origin + location.pathname;
var show_list;
var sort_type = 'alphabetic';
var multi_selection_enabled = false;
var selected = [];
var items = [];
$.fn.fab = function (options) {
var menu = this;
menu.addClass('fab-wrapper');
var toggler = $('<a>')
.addClass('fab-button fab-toggle')
.append($('<i>').addClass('fas fa-plus'))
.click(function () {
menu.toggleClass('fab-expand');
});
menu.append(toggler);
options.buttons.forEach(function (button) {
toggler.before(
$('<a>').addClass('fab-button fab-action')
.attr('data-label', button.label)
.attr('id', button.attrs.id)
.append($('<i>').addClass(button.icon))
.click(function () {
menu.removeClass('fab-expand');
})
);
});
};
$(document).ready(function () {
$('#fab').fab({
buttons: [
{
icon: 'fas fa-upload',
label: lang['nav-upload'],
attrs: {id: 'upload'}
},
{
icon: 'fas fa-folder',
label: lang['nav-new'],
attrs: {id: 'add-folder'}
}
]
});
actions.reverse().forEach(function (action) {
$('#nav-buttons > ul').prepend(
$('<li>').addClass('nav-item').append(
$('<a>').addClass('nav-link d-none')
.attr('data-action', action.name)
.attr('data-multiple', action.multiple)
.append($('<i>').addClass('fas fa-fw fa-' + action.icon))
.append($('<span>').text(action.label))
)
);
});
sortings.forEach(function (sort) {
$('#nav-buttons .dropdown-menu').append(
$('<a>').addClass('dropdown-item').attr('data-sortby', sort.by)
.append($('<i>').addClass('fas fa-fw fa-' + sort.icon))
.append($('<span>').text(sort.label))
.click(function() {
sort_type = sort.by;
loadItems();
})
);
});
loadFolders();
performLfmRequest('errors')
.done(function (response) {
JSON.parse(response).forEach(function (message) {
$('#alerts').append(
$('<div>').addClass('alert alert-warning')
.append($('<i>').addClass('fas fa-exclamation-circle'))
.append(' ' + message)
);
});
});
$(window).on('dragenter', function(){
$('#uploadModal').modal('show');
});
if (usingWysiwygEditor()) {
$('#multi_selection_toggle').hide();
}
});
// ======================
// == Navbar actions ==
// ======================
$('#multi_selection_toggle').click(function () {
multi_selection_enabled = !multi_selection_enabled;
$('#multi_selection_toggle i')
.toggleClass('fa-times', multi_selection_enabled)
.toggleClass('fa-check-double', !multi_selection_enabled);
if (!multi_selection_enabled) {
clearSelected();
}
});
$('#to-previous').click(function () {
var previous_dir = getPreviousDir();
if (previous_dir == '') return;
goTo(previous_dir);
});
function toggleMobileTree(should_display) {
if (should_display === undefined) {
should_display = !$('#tree').hasClass('in');
}
$('#tree').toggleClass('in', should_display);
}
$('#show_tree').click(function (e) {
toggleMobileTree();
});
$('#main').click(function (e) {
if ($('#tree').hasClass('in')) {
toggleMobileTree(false);
}
});
$(document).on('click', '#add-folder', function () {
dialog(lang['message-name'], '', createFolder);
});
$(document).on('click', '#upload', function () {
$('#uploadModal').modal('show');
});
$(document).on('click', '[data-display]', function() {
show_list = $(this).data('display');
loadItems();
});
$(document).on('click', '[data-action]', function() {
window[$(this).data('action')]($(this).data('multiple') ? getSelectedItems() : getOneSelectedElement());
});
// ==========================
// == Multiple Selection ==
// ==========================
function toggleSelected (e) {
if (!multi_selection_enabled) {
selected = [];
}
var sequence = $(e.target).closest('a').data('id');
var element_index = selected.indexOf(sequence);
if (element_index === -1) {
selected.push(sequence);
} else {
selected.splice(element_index, 1);
}
updateSelectedStyle();
}
function clearSelected () {
selected = [];
multi_selection_enabled = false;
updateSelectedStyle();
}
function updateSelectedStyle() {
items.forEach(function (item, index) {
$('[data-id=' + index + ']')
.find('.square')
.toggleClass('selected', selected.indexOf(index) > -1);
});
toggleActions();
}
function getOneSelectedElement(orderOfItem) {
var index = orderOfItem !== undefined ? orderOfItem : selected[0];
return items[index];
}
function getSelectedItems() {
return selected.reduce(function (arr_objects, id) {
arr_objects.push(getOneSelectedElement(id));
return arr_objects
}, []);
}
function toggleActions() {
var one_selected = selected.length === 1;
var many_selected = selected.length >= 1;
var only_image = getSelectedItems()
.filter(function (item) { return !item.is_image; })
.length === 0;
var only_file = getSelectedItems()
.filter(function (item) { return !item.is_file; })
.length === 0;
$('[data-action=use]').toggleClass('d-none', !(many_selected && only_file));
$('[data-action=rename]').toggleClass('d-none', !one_selected);
$('[data-action=preview]').toggleClass('d-none', !(many_selected && only_file));
$('[data-action=move]').toggleClass('d-none', !many_selected);
$('[data-action=download]').toggleClass('d-none', !(many_selected && only_file));
$('[data-action=resize]').toggleClass('d-none', !(one_selected && only_image));
$('[data-action=crop]').toggleClass('d-none', !(one_selected && only_image));
$('[data-action=trash]').toggleClass('d-none', !many_selected);
$('[data-action=open]').toggleClass('d-none', !one_selected || only_file);
$('#multi_selection_toggle').toggleClass('d-none', usingWysiwygEditor() || !many_selected);
$('#actions').toggleClass('d-none', selected.length === 0);
$('#fab').toggleClass('d-none', selected.length !== 0);
}
// ======================
// == Folder actions ==
// ======================
$(document).on('click', '#tree a', function (e) {
goTo($(e.target).closest('a').data('path'));
toggleMobileTree(false);
});
function goTo(new_dir) {
$('#working_dir').val(new_dir);
loadItems();
}
function getPreviousDir() {
var working_dir = $('#working_dir').val();
return working_dir.substring(0, working_dir.lastIndexOf('/'));
}
function setOpenFolders() {
$('#tree [data-path]').each(function (index, folder) {
// close folders that are not parent
var should_open = ($('#working_dir').val() + '/').startsWith($(folder).data('path') + '/');
$(folder).children('i')
.toggleClass('fa-folder-open', should_open)
.toggleClass('fa-folder', !should_open);
});
$('#tree .nav-item').removeClass('active');
$('#tree [data-path="' + $('#working_dir').val() + '"]').parent('.nav-item').addClass('active');
}
// ====================
// == Ajax actions ==
// ====================
function performLfmRequest(url, parameter, type) {
var data = defaultParameters();
if (parameter != null) {
$.each(parameter, function (key, value) {
data[key] = value;
});
}
return $.ajax({
type: 'GET',
beforeSend: function(request) {
var token = getUrlParam('token');
if (token !== null) {
request.setRequestHeader("Authorization", 'Bearer ' + token);
}
},
dataType: type || 'text',
url: lfm_route + '/' + url,
data: data,
cache: false
}).fail(function (jqXHR, textStatus, errorThrown) {
displayErrorResponse(jqXHR);
});
}
function displayErrorResponse(jqXHR) {
notify('<div style="max-height:50vh;overflow: scroll;">' + jqXHR.responseText + '</div>');
}
var refreshFoldersAndItems = function (data) {
loadFolders();
if (data != 'OK') {
data = Array.isArray(data) ? data.join('<br/>') : data;
notify(data);
}
};
var hideNavAndShowEditor = function (data) {
$('#nav-buttons > ul').addClass('d-none');
$('#content').html(data).removeClass('preserve_actions_space');
clearSelected();
}
function loadFolders() {
performLfmRequest('folders', {}, 'html')
.done(function (data) {
$('#tree').html(data);
loadItems();
});
}
function loadItems() {
loading(true);
performLfmRequest('jsonitems', {show_list: show_list, sort_type: sort_type}, 'html')
.done(function (data) {
selected = [];
var response = JSON.parse(data);
var working_dir = response.working_dir;
items = response.items;
var hasItems = items.length !== 0;
$('#empty').toggleClass('d-none', hasItems);
$('#content').html('').removeAttr('class');
if (hasItems) {
$('#content').addClass(response.display).addClass('preserve_actions_space');
items.forEach(function (item, index) {
var template = $('#item-template').clone()
.removeAttr('id class')
.attr('data-id', index)
.click(toggleSelected)
.dblclick(function (e) {
if (item.is_file) {
use(getSelectedItems());
} else {
goTo(item.url);
}
});
if (item.thumb_url) {
var image = $('<div>').css('background-image', 'url("' + item.thumb_url + '?timestamp=' + item.time + '")');
} else {
var image = $('<div>').addClass('mime-icon ico-' + item.icon);
}
template.find('.square').append(image);
template.find('.item_name').text(item.name);
template.find('time').text((new Date(item.time * 1000)).toLocaleString());
$('#content').append(template);
});
}
$('#nav-buttons > ul').removeClass('d-none');
$('#working_dir').val(working_dir);
console.log('Current working_dir : ' + working_dir);
var breadcrumbs = [];
var validSegments = working_dir.split('/').filter(function (e) { return e; });
validSegments.forEach(function (segment, index) {
if (index === 0) {
// set root folder name as the first breadcrumb
breadcrumbs.push($("[data-path='/" + segment + "']").text());
} else {
breadcrumbs.push(segment);
}
});
$('#current_folder').text(breadcrumbs[breadcrumbs.length - 1]);
$('#breadcrumbs > ol').html('');
breadcrumbs.forEach(function (breadcrumb, index) {
var li = $('<li>').addClass('breadcrumb-item').text(breadcrumb);
if (index === breadcrumbs.length - 1) {
li.addClass('active').attr('aria-current', 'page');
} else {
li.click(function () {
// go to corresponding path
goTo('/' + validSegments.slice(0, 1 + index).join('/'));
});
}
$('#breadcrumbs > ol').append(li);
});
var atRootFolder = getPreviousDir() == '';
$('#to-previous').toggleClass('d-none invisible-lg', atRootFolder);
$('#show_tree').toggleClass('d-none', !atRootFolder).toggleClass('d-block', atRootFolder);
setOpenFolders();
loading(false);
toggleActions();
});
}
function loading(show_loading) {
$('#loading').toggleClass('d-none', !show_loading);
}
function createFolder(folder_name) {
performLfmRequest('newfolder', {name: folder_name})
.done(refreshFoldersAndItems);
}
// ==================================
// == File Actions ==
// ==================================
function rename(item) {
dialog(lang['message-rename'], item.name, function (new_name) {
performLfmRequest('rename', {
file: item.name,
new_name: new_name
}).done(refreshFoldersAndItems);
});
}
function trash(items) {
notify(lang['message-delete'], function () {
performLfmRequest('delete', {
items: items.map(function (item) { return item.name; })
}).done(refreshFoldersAndItems)
});
}
function crop(item) {
performLfmRequest('crop', {img: item.name})
.done(hideNavAndShowEditor);
}
function resize(item) {
performLfmRequest('resize', {img: item.name})
.done(hideNavAndShowEditor);
}
function download(items) {
items.forEach(function (item, index) {
var data = defaultParameters();
data['file'] = item.name;
var token = getUrlParam('token');
if (token) {
data['token'] = token;
}
setTimeout(function () {
location.href = lfm_route + '/download?' + $.param(data);
}, index * 100);
});
}
function open(item) {
goTo(item.url);
}
function preview(items) {
var carousel = $('#carouselTemplate').clone().attr('id', 'previewCarousel').removeClass('d-none');
var imageTemplate = carousel.find('.carousel-item').clone().removeClass('active');
var indicatorTemplate = carousel.find('.carousel-indicators > li').clone().removeClass('active');
carousel.children('.carousel-inner').html('');
carousel.children('.carousel-indicators').html('');
carousel.children('.carousel-indicators,.carousel-control-prev,.carousel-control-next').toggle(items.length > 1);
items.forEach(function (item, index) {
var carouselItem = imageTemplate.clone()
.addClass(index === 0 ? 'active' : '');
if (item.thumb_url) {
carouselItem.find('.carousel-image').css('background-image', 'url(\'' + item.url + '?timestamp=' + item.time + '\')');
} else {
carouselItem.find('.carousel-image').css('width', '50vh').append($('<div>').addClass('mime-icon ico-' + item.icon));
}
carouselItem.find('.carousel-label').attr('target', '_blank').attr('href', item.url)
.append(item.name)
.append($('<i class="fas fa-external-link-alt ml-2"></i>'));
carousel.children('.carousel-inner').append(carouselItem);
var carouselIndicator = indicatorTemplate.clone()
.addClass(index === 0 ? 'active' : '')
.attr('data-slide-to', index);
carousel.children('.carousel-indicators').append(carouselIndicator);
});
// carousel swipe control
var touchStartX = null;
carousel.on('touchstart', function (event) {
var e = event.originalEvent;
if (e.touches.length == 1) {
var touch = e.touches[0];
touchStartX = touch.pageX;
}
}).on('touchmove', function (event) {
var e = event.originalEvent;
if (touchStartX != null) {
var touchCurrentX = e.changedTouches[0].pageX;
if ((touchCurrentX - touchStartX) > 60) {
touchStartX = null;
carousel.carousel('prev');
} else if ((touchStartX - touchCurrentX) > 60) {
touchStartX = null;
carousel.carousel('next');
}
}
}).on('touchend', function () {
touchStartX = null;
});
// end carousel swipe control
notify(carousel);
}
function move(items) {
performLfmRequest('move', { items: items.map(function (item) { return item.name; }) })
.done(refreshFoldersAndItems);
}
function getUrlParam(paramName) {
var reParam = new RegExp('(?:[\?&]|&)' + paramName + '=([^&]+)', 'i');
var match = window.location.search.match(reParam);
return ( match && match.length > 1 ) ? match[1] : null;
}
function use(items) {
function useTinymce3(url) {
if (!usingTinymce3()) { return; }
var win = tinyMCEPopup.getWindowArg("window");
win.document.getElementById(tinyMCEPopup.getWindowArg("input")).value = url;
if (typeof(win.ImageDialog) != "undefined") {
// Update image dimensions
if (win.ImageDialog.getImageData) {
win.ImageDialog.getImageData();
}
// Preview if necessary
if (win.ImageDialog.showPreviewImage) {
win.ImageDialog.showPreviewImage(url);
}
}
tinyMCEPopup.close();
}
function useTinymce4AndColorbox(url) {
if (!usingTinymce4AndColorbox()) { return; }
parent.document.getElementById(getUrlParam('field_name')).value = url;
if(typeof parent.tinyMCE !== "undefined") {
parent.tinyMCE.activeEditor.windowManager.close();
}
if(typeof parent.$.fn.colorbox !== "undefined") {
parent.$.fn.colorbox.close();
}
}
function useCkeditor3(url) {
if (!usingCkeditor3()) { return; }
if (window.opener) {
// Popup
window.opener.CKEDITOR.tools.callFunction(getUrlParam('CKEditorFuncNum'), url);
} else {
// Modal (in iframe)
parent.CKEDITOR.tools.callFunction(getUrlParam('CKEditorFuncNum'), url);
parent.CKEDITOR.tools.callFunction(getUrlParam('CKEditorCleanUpFuncNum'));
}
}
function useFckeditor2(url) {
if (!usingFckeditor2()) { return; }
var p = url;
var w = data['Properties']['Width'];
var h = data['Properties']['Height'];
window.opener.SetUrl(p,w,h);
}
var url = items[0].url;
var callback = getUrlParam('callback');
var useFileSucceeded = true;
if (usingWysiwygEditor()) {
useTinymce3(url);
useTinymce4AndColorbox(url);
useCkeditor3(url);
useFckeditor2(url);
} else if (callback && window[callback]) {
window[callback](getSelectedItems());
} else if (callback && parent[callback]) {
parent[callback](getSelecteditems());
} else if (window.opener) { // standalone button or other situations
window.opener.SetUrl(getSelectedItems());
} else {
useFileSucceeded = false;
}
if (useFileSucceeded) {
if (window.opener) {
window.close();
}
} else {
console.log('window.opener not found');
// No editor found, open/download file using browser's default method
window.open(url);
}
}
//end useFile
// ==================================
// == WYSIWYG Editors Check ==
// ==================================
function usingTinymce3() {
return !!window.tinyMCEPopup;
}
function usingTinymce4AndColorbox() {
return !!getUrlParam('field_name');
}
function usingCkeditor3() {
return !!getUrlParam('CKEditor') || !!getUrlParam('CKEditorCleanUpFuncNum');
}
function usingFckeditor2() {
return window.opener && typeof data != 'undefined' && data['Properties']['Width'] != '';
}
function usingWysiwygEditor() {
return usingTinymce3() || usingTinymce4AndColorbox() || usingCkeditor3() || usingFckeditor2();
}
// ==================================
// == Others ==
// ==================================
function defaultParameters() {
return {
working_dir: $('#working_dir').val(),
type: $('#type').val()
};
}
function notImp() {
notify('Not yet implemented!');
}
function notify(body, callback) {
$('#notify').find('.btn-primary').toggle(callback !== undefined);
$('#notify').find('.btn-primary').unbind().click(callback);
$('#notify').modal('show').find('.modal-body').html(body);
}
function dialog(title, value, callback) {
$('#dialog').find('input').val(value);
$('#dialog').on('shown.bs.modal', function () {
$('#dialog').find('input').focus();
});
$('#dialog').find('.btn-primary').unbind().click(function (e) {
callback($('#dialog').find('input').val());
});
$('#dialog').modal('show').find('.modal-title').text(title);
}

View file

@ -0,0 +1,36 @@
(function( $ ){
$.fn.filemanager = function(type, options) {
type = type || 'file';
this.on('click', function(e) {
var route_prefix = (options && options.prefix) ? options.prefix : '/laravel-filemanager';
var target_input = $('#' + $(this).data('input'));
var target_preview = $('#' + $(this).data('preview'));
window.open(route_prefix + '?type=' + type, 'FileManager', 'width=900,height=600');
window.SetUrl = function (items) {
var file_path = items.map(function (item) {
return item.url;
}).join(',');
// set the value of the desired input to image url
target_input.val('').val(file_path).trigger('change');
// clear previous preview
target_preview.html('');
// set or change the preview image src
items.forEach(function (item) {
target_preview.append(
$('<img>').css('height', '5rem').attr('src', item.thumb_url)
);
});
// trigger change event
target_preview.trigger('change');
};
return false;
});
}
})(jQuery);

View file

@ -0,0 +1,12 @@
<?php
namespace IqContent\LaravelFilemanager\Controllers;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Foundation\Validation\ValidatesRequests;
abstract class Controller extends BaseController
{
use DispatchesJobs, ValidatesRequests;
}

View file

@ -0,0 +1,59 @@
<?php
namespace IqContent\LaravelFilemanager\Controllers;
use Intervention\Image\Facades\Image;
use IqContent\LaravelFilemanager\Events\ImageIsCropping;
use IqContent\LaravelFilemanager\Events\ImageWasCropped;
class CropController extends LfmController
{
/**
* Show crop page.
*
* @return mixed
*/
public function getCrop()
{
return view('laravel-filemanager::crop')
->with([
'working_dir' => request('working_dir'),
'img' => $this->lfm->pretty(request('img'))
]);
}
/**
* Crop the image (called via ajax).
*/
public function getCropimage($overWrite = true)
{
$image_name = request('img');
$image_path = $this->lfm->setName($image_name)->path('absolute');
$crop_path = $image_path;
if (! $overWrite) {
$fileParts = explode('.', $image_name);
$fileParts[count($fileParts) - 2] = $fileParts[count($fileParts) - 2] . '_cropped_' . time();
$crop_path = $this->lfm->setName(implode('.', $fileParts))->path('absolute');
}
event(new ImageIsCropping($image_path));
$crop_info = request()->only('dataWidth', 'dataHeight', 'dataX', 'dataY');
// crop image
Image::make($image_path)
->crop(...array_values($crop_info))
->save($crop_path);
// make new thumbnail
$this->lfm->makeThumbnail($image_name);
event(new ImageWasCropped($image_path));
}
public function getNewCropimage()
{
$this->getCropimage(false);
}
}

View file

@ -0,0 +1,58 @@
<?php
namespace IqContent\LaravelFilemanager\Controllers;
use IqContent\LaravelFilemanager\Events\ImageIsDeleting;
use IqContent\LaravelFilemanager\Events\ImageWasDeleted;
class DeleteController extends LfmController
{
/**
* Delete image and associated thumbnail.
*
* @return mixed
*/
public function getDelete()
{
$item_names = request('items');
$errors = [];
foreach ($item_names as $name_to_delete) {
$file_to_delete = $this->lfm->pretty($name_to_delete);
$file_path = $file_to_delete->path();
event(new ImageIsDeleting($file_path));
if (is_null($name_to_delete)) {
return $this->response('error', parent::getError('folder-name'));
continue;
}
if (! $this->lfm->setName($name_to_delete)->exists()) {
return $this->response('error', parent::getError('folder-not-found', ['folder' => $file_path]));
}
if ($this->lfm->setName($name_to_delete)->isDirectory()) {
if (! $this->lfm->setName($name_to_delete)->directoryIsEmpty()) {
return $this->response('error', parent::getError('delete-folder'));
}
} else {
if ($file_to_delete->isImage()) {
$this->lfm->setName($name_to_delete)->thumb()->delete();
}
}
$file_to_delete->deleteModel();
$this->lfm->setName($name_to_delete)->delete();
event(new ImageWasDeleted($file_path));
}
if (count($errors) > 0) {
return $this->response('error', $errors);
}
return parent::$success_response;
}
}

View file

@ -0,0 +1,11 @@
<?php
namespace IqContent\LaravelFilemanager\Controllers;
class DemoController extends LfmController
{
public function index()
{
return view('laravel-filemanager::demo');
}
}

View file

@ -0,0 +1,11 @@
<?php
namespace IqContent\LaravelFilemanager\Controllers;
class DownloadController extends LfmController
{
public function getDownload()
{
return response()->download($this->lfm->setName(request('file'))->path('absolute'));
}
}

View file

@ -0,0 +1,61 @@
<?php
namespace IqContent\LaravelFilemanager\Controllers;
class FolderController extends LfmController
{
/**
* Get list of folders as json to populate treeview.
*
* @return mixed
*/
public function getFolders()
{
$folder_types = array_filter(['user', 'share'], function ($type) {
return $this->helper->allowFolderType($type);
});
return view('laravel-filemanager::tree')
->with([
'root_folders' => array_map(function ($type) use ($folder_types) {
$path = $this->lfm->dir($this->helper->getRootFolder($type));
return (object) [
'name' => trans('laravel-filemanager::lfm.title-' . $type),
'url' => $path->path('working_dir'),
'children' => $path->folders(),
'has_next' => ! ($type == end($folder_types)),
];
}, $folder_types),
]);
}
/**
* Add a new folder.
*
* @return mixed
*/
public function getAddfolder()
{
$folder_name = $this->helper->input('name');
if(config('lfm.alphanumeric_directory')){
$folder_name = $this->helper->sanitize($folder_name);
}
try {
if (empty($folder_name)) {
return $this->helper->error('folder-name');
} elseif ($this->lfm->setName($folder_name)->exists()) {
return $this->helper->error('folder-exist');
} elseif (config('lfm.alphanumeric_directory') && preg_match('/[^\w-]/i', $folder_name)) {
return $this->helper->error('folder-alnum');
} else {
$this->lfm->setName($folder_name)->createFolder();
}
} catch (\Exception $e) {
return $e->getMessage();
}
return parent::$success_response;
}
}

View file

@ -0,0 +1,79 @@
<?php
namespace IqContent\LaravelFilemanager\Controllers;
use IqContent\LaravelFilemanager\Events\FileIsMoving;
use IqContent\LaravelFilemanager\Events\FileWasMoving;
use IqContent\LaravelFilemanager\Events\FolderIsMoving;
use IqContent\LaravelFilemanager\Events\FolderWasMoving;
class ItemsController extends LfmController
{
/**
* Get the images to load for a selected folder.
*
* @return mixed
*/
public function getItems()
{
return [
'items' => array_map(function ($item) {
return $item->fill()->attributes;
}, array_merge($this->lfm->folders(), $this->lfm->files())),
'display' => $this->helper->getDisplayMode(),
'working_dir' => $this->lfm->path('working_dir'),
];
}
public function move()
{
$items = request('items');
$folder_types = array_filter(['user', 'share'], function ($type) {
return $this->helper->allowFolderType($type);
});
return view('laravel-filemanager::move')
->with([
'root_folders' => array_map(function ($type) use ($folder_types) {
$path = $this->lfm->dir($this->helper->getRootFolder($type));
return (object) [
'name' => trans('laravel-filemanager::lfm.title-' . $type),
'url' => $path->path('working_dir'),
'children' => $path->folders(),
'has_next' => ! ($type == end($folder_types)),
];
}, $folder_types),
])
->with('items', $items);
}
public function domove()
{
$target = $this->helper->input('goToFolder');
$items = $this->helper->input('items');
foreach ($items as $item) {
$old_file = $this->lfm->pretty($item);
$is_directory = $old_file->isDirectory();
if ($old_file->hasThumb()) {
$new_file = $this->lfm->setName($item)->thumb()->dir($target);
if ($is_directory) {
event(new FolderIsMoving($old_file->path(), $new_file->path()));
} else {
event(new FileIsMoving($old_file->path(), $new_file->path()));
}
$this->lfm->setName($item)->thumb()->move($new_file);
}
$new_file = $this->lfm->setName($item)->dir($target);
$this->lfm->setName($item)->move($new_file);
if ($is_directory) {
event(new FolderWasMoving($old_file->path(), $new_file->path()));
} else {
event(new FileWasMoving($old_file->path(), $new_file->path()));
}
};
return parent::$success_response;
}
}

View file

@ -0,0 +1,108 @@
<?php
namespace IqContent\LaravelFilemanager\Controllers;
use IqContent\LaravelFilemanager\Lfm;
use IqContent\LaravelFilemanager\LfmPath;
class LfmController extends Controller
{
protected static $success_response = 'OK';
public function __construct()
{
$this->applyIniOverrides();
}
/**
* Set up needed functions.
*
* @return object|null
*/
public function __get($var_name)
{
if ($var_name === 'lfm') {
return app(LfmPath::class);
} elseif ($var_name === 'helper') {
return app(Lfm::class);
}
}
/**
* Show the filemanager.
*
* @return mixed
*/
public function show()
{
return view('laravel-filemanager::index')
->withHelper($this->helper);
}
/**
* Check if any extension or config is missing.
*
* @return array
*/
public function getErrors()
{
$arr_errors = [];
if (! extension_loaded('gd') && ! extension_loaded('imagick')) {
array_push($arr_errors, trans('laravel-filemanager::lfm.message-extension_not_found'));
}
if (! extension_loaded('exif')) {
array_push($arr_errors, 'EXIF extension not found.');
}
if (! extension_loaded('fileinfo')) {
array_push($arr_errors, 'Fileinfo extension not found.');
}
$mine_config_key = 'lfm.folder_categories.'
. $this->helper->currentLfmType()
. '.valid_mime';
if (! is_array(config($mine_config_key))) {
array_push($arr_errors, 'Config : ' . $mine_config_key . ' is not a valid array.');
}
return $arr_errors;
}
public function error($error_type, $variables = [])
{
return $this->helper->error($error_type, $variables);
}
public function getError($error_type, $variables = [])
{
return $this->helper->getError($error_type, $variables);
}
public function response($error_type, $variables = [])
{
return $this->helper->response($error_type, $variables);
}
/**
* Overrides settings in php.ini.
*
* @return null
*/
public function applyIniOverrides()
{
$overrides = config('lfm.php_ini_overrides');
if ($overrides && is_array($overrides) && count($overrides) === 0) {
return;
}
foreach ($overrides as $key => $value) {
if ($value && $value != 'false') {
ini_set($key, $value);
}
}
}
}

View file

@ -0,0 +1,20 @@
<?php
namespace IqContent\LaravelFilemanager\Controllers;
use Illuminate\Support\Facades\Storage;
class RedirectController extends LfmController
{
public function showFile($file_path)
{
$storage = Storage::disk($this->helper->config('disk'));
if (! $storage->exists($file_path)) {
abort(404);
}
return response($storage->get($file_path))
->header('Content-Type', $storage->mimeType($file_path));
}
}

View file

@ -0,0 +1,91 @@
<?php
namespace IqContent\LaravelFilemanager\Controllers;
use IqContent\LaravelFilemanager\Events\ImageIsRenaming;
use IqContent\LaravelFilemanager\Events\ImageWasRenamed;
use IqContent\LaravelFilemanager\Events\FolderIsRenaming;
use IqContent\LaravelFilemanager\Events\FolderWasRenamed;
class RenameController extends LfmController
{
public function getRename()
{
$old_name = $this->helper->input('file');
$new_name = $this->helper->input('new_name');
$old_file = $this->lfm->pretty($old_name);
return $old_file->path('working_dir');
$is_directory = $old_file->isDirectory();
if (empty($new_name)) {
if ($is_directory) {
return $this->response('error', parent::getError('folder-name'));
} else {
return $this->response('error', parent::getError('file-name'));
}
}
if ($is_directory) {
if(config('lfm.alphanumeric_directory')){
$new_name = $this->helper->sanitize($new_name);
if(preg_match('/[^\w-]/i', $new_name)){
return $this->response('error', parent::getError('folder-alnum'));
}
}
}else {
if (config('lfm.alphanumeric_filename')) {
$extension = $old_file->extension();
if ($extension) {
$new_name = str_replace('.' . $extension, '', $new_name);
$new_name = $this->helper->sanitize($new_name) . '.' . $extension;
}
}
}
if($this->lfm->setName($new_name)->exists()) {
return $this->response('error', parent::getError('rename'));
}
if (! $is_directory) {
$extension = $old_file->extension();
if ($extension) {
$new_name = str_replace('.' . $extension, '', $new_name) . '.' . $extension;
}
}
$new_file = $this->lfm->setName($new_name)->path('absolute');
if ($is_directory) {
event(new FolderIsRenaming($old_file->path(), $new_file));
} else {
event(new ImageIsRenaming($old_file->path(), $new_file));
}
if ($old_file->hasThumb()) {
$this->lfm->setName($old_name)->thumb()
->move($this->lfm->setName($new_name)->thumb());
}
$this->lfm->setName($old_name)
->move($this->lfm->setName($new_name));
//->renameModel($new_name)
if ($is_directory) {
event(new FolderWasRenamed($old_file->path(), $new_file));
} else {
event(new ImageWasRenamed($old_file->path(), $new_file));
}
return parent::$success_response;
}
}

View file

@ -0,0 +1,65 @@
<?php
namespace IqContent\LaravelFilemanager\Controllers;
use Intervention\Image\Facades\Image;
use IqContent\LaravelFilemanager\Events\ImageIsResizing;
use IqContent\LaravelFilemanager\Events\ImageWasResized;
class ResizeController extends LfmController
{
/**
* Dipsplay image for resizing.
*
* @return mixed
*/
public function getResize()
{
$ratio = 1.0;
$image = request('img');
$original_image = Image::make($this->lfm->setName($image)->path('absolute'));
$original_width = $original_image->width();
$original_height = $original_image->height();
$scaled = false;
// FIXME size should be configurable
if ($original_width > 600) {
$ratio = 600 / $original_width;
$width = $original_width * $ratio;
$height = $original_height * $ratio;
$scaled = true;
} else {
$width = $original_width;
$height = $original_height;
}
if ($height > 400) {
$ratio = 400 / $original_height;
$width = $original_width * $ratio;
$height = $original_height * $ratio;
$scaled = true;
}
return view('laravel-filemanager::resize')
->with('img', $this->lfm->pretty($image))
->with('height', number_format($height, 0))
->with('width', $width)
->with('original_height', $original_height)
->with('original_width', $original_width)
->with('scaled', $scaled)
->with('ratio', $ratio);
}
public function performResize()
{
$image_path = $this->lfm->setName(request('img'))->path('absolute');
event(new ImageIsResizing($image_path));
Image::make($image_path)->resize(request('dataWidth'), request('dataHeight'))->save();
event(new ImageWasResized($image_path));
return parent::$success_response;
}
}

View file

@ -0,0 +1,58 @@
<?php
namespace IqContent\LaravelFilemanager\Controllers;
use Illuminate\Support\Facades\Log;
use IqContent\LaravelFilemanager\Events\ImageIsUploading;
use IqContent\LaravelFilemanager\Events\ImageWasUploaded;
use IqContent\LaravelFilemanager\Lfm;
class UploadController extends LfmController
{
protected $errors;
public function __construct()
{
parent::__construct();
$this->errors = [];
}
/**
* Upload files
*
* @param void
* @return string
*/
public function upload()
{
$uploaded_files = request()->file('upload');
$error_bag = [];
$new_filename = null;
foreach (is_array($uploaded_files) ? $uploaded_files : [$uploaded_files] as $file) {
try {
$new_filename = $this->lfm->upload($file);
} catch (\Exception $e) {
Log::error($e->getMessage(), [
'file' => $e->getFile(),
'line' => $e->getLine(),
'trace' => $e->getTraceAsString()
]);
array_push($error_bag, $e->getMessage());
}
}
if (is_array($uploaded_files)) {
$response = count($error_bag) > 0 ? $error_bag : parent::$success_response;
} else { // upload via ckeditor 'Upload' tab
if (is_null($new_filename)) {
$response = $error_bag[0];
} else {
$response = view(Lfm::PACKAGE_NAME . '::use')
->withFile($this->lfm->setName($new_filename)->url());
}
}
return $response;
}
}

View file

@ -0,0 +1,28 @@
<?php
namespace IqContent\LaravelFilemanager\Events;
class FileIsMoving
{
private $old_path;
private $new_path;
public function __construct($old_path, $new_path)
{
$this->old_path = $old_path;
$this->new_path = $new_path;
}
/**
* @return string
*/
public function oldPath()
{
return $this->old_path;
}
public function newPath()
{
return $this->new_path;
}
}

View file

@ -0,0 +1,28 @@
<?php
namespace IqContent\LaravelFilemanager\Events;
class FileWasMoving
{
private $old_path;
private $new_path;
public function __construct($old_path, $new_path)
{
$this->old_path = $old_path;
$this->new_path = $new_path;
}
/**
* @return string
*/
public function oldPath()
{
return $this->old_path;
}
public function newPath()
{
return $this->new_path;
}
}

View file

@ -0,0 +1,28 @@
<?php
namespace IqContent\LaravelFilemanager\Events;
class FolderIsMoving
{
private $old_path;
private $new_path;
public function __construct($old_path, $new_path)
{
$this->old_path = $old_path;
$this->new_path = $new_path;
}
/**
* @return string
*/
public function oldPath()
{
return $this->old_path;
}
public function newPath()
{
return $this->new_path;
}
}

View file

@ -0,0 +1,28 @@
<?php
namespace IqContent\LaravelFilemanager\Events;
class FolderIsRenaming
{
private $old_path;
private $new_path;
public function __construct($old_path, $new_path)
{
$this->old_path = $old_path;
$this->new_path = $new_path;
}
/**
* @return string
*/
public function oldPath()
{
return $this->old_path;
}
public function newPath()
{
return $this->new_path;
}
}

View file

@ -0,0 +1,28 @@
<?php
namespace IqContent\LaravelFilemanager\Events;
class FolderWasMoving
{
private $old_path;
private $new_path;
public function __construct($old_path, $new_path)
{
$this->old_path = $old_path;
$this->new_path = $new_path;
}
/**
* @return string
*/
public function oldPath()
{
return $this->old_path;
}
public function newPath()
{
return $this->new_path;
}
}

View file

@ -0,0 +1,28 @@
<?php
namespace IqContent\LaravelFilemanager\Events;
class FolderWasRenamed
{
private $old_path;
private $new_path;
public function __construct($old_path, $new_path)
{
$this->old_path = $old_path;
$this->new_path = $new_path;
}
/**
* @return string
*/
public function oldPath()
{
return $this->old_path;
}
public function newPath()
{
return $this->new_path;
}
}

View file

@ -0,0 +1,21 @@
<?php
namespace IqContent\LaravelFilemanager\Events;
class ImageIsCropping
{
private $path;
public function __construct($path)
{
$this->path = $path;
}
/**
* @return string
*/
public function path()
{
return $this->path;
}
}

View file

@ -0,0 +1,21 @@
<?php
namespace IqContent\LaravelFilemanager\Events;
class ImageIsDeleting
{
private $path;
public function __construct($path)
{
$this->path = $path;
}
/**
* @return string
*/
public function path()
{
return $this->path;
}
}

View file

@ -0,0 +1,28 @@
<?php
namespace IqContent\LaravelFilemanager\Events;
class ImageIsRenaming
{
private $old_path;
private $new_path;
public function __construct($old_path, $new_path)
{
$this->old_path = $old_path;
$this->new_path = $new_path;
}
/**
* @return string
*/
public function oldPath()
{
return $this->old_path;
}
public function newPath()
{
return $this->new_path;
}
}

View file

@ -0,0 +1,21 @@
<?php
namespace IqContent\LaravelFilemanager\Events;
class ImageIsResizing
{
private $path;
public function __construct($path)
{
$this->path = $path;
}
/**
* @return string
*/
public function path()
{
return $this->path;
}
}

View file

@ -0,0 +1,21 @@
<?php
namespace IqContent\LaravelFilemanager\Events;
class ImageIsUploading
{
private $path;
public function __construct($path)
{
$this->path = $path;
}
/**
* @return string
*/
public function path()
{
return $this->path;
}
}

View file

@ -0,0 +1,21 @@
<?php
namespace IqContent\LaravelFilemanager\Events;
class ImageWasCropped
{
private $path;
public function __construct($path)
{
$this->path = $path;
}
/**
* @return string
*/
public function path()
{
return $this->path;
}
}

View file

@ -0,0 +1,21 @@
<?php
namespace IqContent\LaravelFilemanager\Events;
class ImageWasDeleted
{
private $path;
public function __construct($path)
{
$this->path = $path;
}
/**
* @return string
*/
public function path()
{
return $this->path;
}
}

View file

@ -0,0 +1,28 @@
<?php
namespace IqContent\LaravelFilemanager\Events;
class ImageWasRenamed
{
private $old_path;
private $new_path;
public function __construct($old_path, $new_path)
{
$this->old_path = $old_path;
$this->new_path = $new_path;
}
/**
* @return string
*/
public function oldPath()
{
return $this->old_path;
}
public function newPath()
{
return $this->new_path;
}
}

View file

@ -0,0 +1,21 @@
<?php
namespace IqContent\LaravelFilemanager\Events;
class ImageWasResized
{
private $path;
public function __construct($path)
{
$this->path = $path;
}
/**
* @return string
*/
public function path()
{
return $this->path;
}
}

View file

@ -0,0 +1,21 @@
<?php
namespace IqContent\LaravelFilemanager\Events;
class ImageWasUploaded
{
private $path;
public function __construct($path)
{
$this->path = $path;
}
/**
* @return string
*/
public function path()
{
return $this->path;
}
}

View file

@ -0,0 +1,11 @@
<?php
namespace IqContent\LaravelFilemanager\Handlers;
class ConfigHandler
{
public function userField()
{
return auth()->id();
}
}

View file

@ -0,0 +1,11 @@
<?php
namespace App\Handlers;
class LfmConfigHandler extends \IqContent\LaravelFilemanager\Handlers\ConfigHandler
{
public function userField()
{
return parent::userField();
}
}

View file

@ -0,0 +1,62 @@
<?php
namespace IqContent\LaravelFilemanager;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\ServiceProvider;
/**
* Class LaravelFilemanagerServiceProvider.
*/
class LaravelFilemanagerServiceProvider extends ServiceProvider
{
/**
* Bootstrap the application services.
*
* @return void
*/
public function boot()
{
$this->loadTranslationsFrom(__DIR__.'/lang', 'laravel-filemanager');
$this->loadViewsFrom(__DIR__.'/views', 'laravel-filemanager');
$this->publishes([
__DIR__ . '/config/lfm.php' => base_path('config/lfm.php'),
], 'lfm_config');
$this->publishes([
__DIR__.'/../public' => public_path('vendor/laravel-filemanager'),
], 'lfm_public');
$this->publishes([
__DIR__.'/views' => base_path('resources/views/vendor/laravel-filemanager'),
], 'lfm_view');
$this->publishes([
__DIR__.'/Handlers/LfmConfigHandler.php' => base_path('app/Handlers/LfmConfigHandler.php'),
], 'lfm_handler');
$this->publishes([
__DIR__.'/../database/migrations' => base_path('database/migrations'),
], 'lfm_migrations');
if (config('lfm.use_package_routes')) {
Route::group(['prefix' => 'laravel-filemanager', 'middleware' => ['web', 'auth']], function () {
\IqContent\LaravelFilemanager\Lfm::routes();
});
}
}
/**
* Register the application services.
*
* @return void
*/
public function register()
{
$this->app->singleton('laravel-filemanager', function () {
return true;
});
}
}

View file

@ -0,0 +1,380 @@
<?php
namespace IqContent\LaravelFilemanager;
use Illuminate\Contracts\Config\Repository as Config;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use IqContent\LaravelFilemanager\Middlewares\CreateDefaultFolder;
use IqContent\LaravelFilemanager\Middlewares\MultiUser;
class Lfm
{
const PACKAGE_NAME = 'laravel-filemanager';
const DS = '/';
protected $config;
protected $request;
public function __construct(Config $config = null, Request $request = null)
{
$this->config = $config;
$this->request = $request;
}
public function getStorage($storage_path)
{
return new LfmStorageRepository($storage_path, $this);
}
public function input($key)
{
return $this->translateFromUtf8($this->request->input($key));
}
public function config($key)
{
return $this->config->get('lfm.' . $key);
}
/**
* Get only the file name.
*
* @param string $path Real path of a file.
* @return string
*/
public function getNameFromPath($path)
{
return pathinfo($path, PATHINFO_BASENAME);
}
public function getDirFromPath($path)
{
return pathinfo($path, PATHINFO_DIRNAME);
}
public function allowFolderType($type)
{
if ($type == 'user') {
return $this->allowMultiUser();
} else {
return $this->allowShareFolder();
}
}
public function getCategoryName()
{
$type = $this->currentLfmType();
return $this->config->get('lfm.folder_categories.' . $type . '.folder_name', 'files');
}
/**
* Get current lfm type.
*
* @return string
*/
public function currentLfmType()
{
$lfm_type = 'file';
$request_type = lcfirst(str_singular($this->input('type') ?: ''));
$available_types = array_keys($this->config->get('lfm.folder_categories') ?: []);
if (in_array($request_type, $available_types)) {
$lfm_type = $request_type;
}
return $lfm_type;
}
public function getDisplayMode()
{
$type_key = $this->currentLfmType();
$startup_view = $this->config->get('lfm.folder_categories.' . $type_key . '.startup_view');
$view_type = 'grid';
$target_display_type = $this->input('show_list') ?: $startup_view;
if (in_array($target_display_type, ['list', 'grid'])) {
$view_type = $target_display_type;
}
return $view_type;
}
public function getUserSlug()
{
$config = $this->config->get('lfm.user_folder_name');
if (is_callable($config)) {
return call_user_func($config);
}
if (class_exists($config)) {
return app()->make($config)->userField();
}
return empty(auth()->user()) ? '' : auth()->user()->$config;
}
public function getRootFolder($type = null)
{
if (is_null($type)) {
$type = 'share';
if ($this->allowFolderType('user')) {
$type = 'user';
}
}
if ($type === 'user') {
$folder = $this->getUserSlug();
} else {
$folder = $this->config->get('lfm.shared_folder_name');
}
// the slash is for url, dont replace it with directory seperator
return '/' . $folder;
}
public function getThumbFolderName()
{
return $this->config->get('lfm.thumb_folder_name');
}
public function getFileIcon($ext)
{
return $this->config->get("lfm.file_icon_array.{$ext}", 'fa-file');
}
public function getFileType($ext)
{
return $this->config->get("lfm.file_type_array.{$ext}", 'File');
}
public function availableMimeTypes()
{
return $this->config->get('lfm.folder_categories.' . $this->currentLfmType() . '.valid_mime');
}
public function maxUploadSize()
{
return $this->config->get('lfm.folder_categories.' . $this->currentLfmType() . '.max_size');
}
/**
* Check if users are allowed to use their private folders.
*
* @return bool
*/
public function allowMultiUser()
{
return $this->config->get('lfm.allow_multi_user') === true;
}
/**
* Check if users are allowed to use the shared folder.
* This can be disabled only when allowMultiUser() is true.
*
* @return bool
*/
public function allowShareFolder()
{
if (! $this->allowMultiUser()) {
return true;
}
return $this->config->get('lfm.allow_share_folder') === true;
}
/**
* Translate file name to make it compatible on Windows.
*
* @param string $input Any string.
* @return string
*/
public function translateFromUtf8($input)
{
if ($this->isRunningOnWindows()) {
$input = iconv('UTF-8', mb_detect_encoding($input), $input);
}
return $input;
}
/**
* Get directory seperator of current operating system.
*
* @return string
*/
public function ds()
{
$ds = Lfm::DS;
if ($this->isRunningOnWindows()) {
$ds = '\\';
}
return $ds;
}
/**
* Check current operating system is Windows or not.
*
* @return bool
*/
public function isRunningOnWindows()
{
return strtoupper(substr(PHP_OS, 0, 3)) === 'WIN';
}
/**
* Shorter function of getting localized error message..
*
* @param mixed $error_type Key of message in lang file.
* @param mixed $variables Variables the message needs.
* @return string
*/
public function error($error_type, $variables = [])
{
die(trans(self::PACKAGE_NAME . '::lfm.error-' . $error_type, $variables));
// throw new \Exception(trans(self::PACKAGE_NAME . '::lfm.error-' . $error_type, $variables));
}
public function getError($error_type, $variables = [])
{
return trans(self::PACKAGE_NAME . '::lfm.error-' . $error_type, $variables);
}
public function response($error_type, $variables = [])
{
return response()->json(['type' => $error_type, 'data'=>$variables], 200);
}
public function sanitize($string, $alphanumeric = true, $force_lowercase = false)
{
$strip = array("~", "`", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "_", "=", "+", "[", "{", "]",
"}", "\\", "|", ";", ":", "\"", "'", "&#8216;", "&#8217;", "&#8220;", "&#8221;", "&#8211;", "&#8212;",
"—", "–", ",", "<", ".", ">", "/", "?");
$clean = trim(str_replace($strip, "", strip_tags($string)));
$clean = preg_replace('/\s+/', "-", $clean); //leerzeichen
$clean = ($alphanumeric) ? preg_replace('/[^A-Za-z0-9\-\']/', "", $clean) : $clean ;
return ($force_lowercase) ?
(function_exists('mb_strtolower')) ?
mb_strtolower($clean, 'UTF-8') :
strtolower($clean) :
$clean;
}
/**
* Generates routes of this package.
*
* @return void
*/
public static function routes()
{
$middleware = [ CreateDefaultFolder::class, MultiUser::class ];
$as = 'iqcontent.lfm.';
$namespace = '\\IqContent\\LaravelFilemanager\\Controllers\\';
Route::group(compact('middleware', 'as', 'namespace'), function () {
// display main layout
Route::get('/', [
'uses' => 'LfmController@show',
'as' => 'show',
]);
// display integration error messages
Route::get('/errors', [
'uses' => 'LfmController@getErrors',
'as' => 'getErrors',
]);
// upload
Route::any('/upload', [
'uses' => 'UploadController@upload',
'as' => 'upload',
]);
// list images & files
Route::get('/jsonitems', [
'uses' => 'ItemsController@getItems',
'as' => 'getItems',
]);
Route::get('/move', [
'uses' => 'ItemsController@move',
'as' => 'move',
]);
Route::get('/domove', [
'uses' => 'ItemsController@domove',
'as' => 'domove'
]);
// folders
Route::get('/newfolder', [
'uses' => 'FolderController@getAddfolder',
'as' => 'getAddfolder',
]);
// list folders
Route::get('/folders', [
'uses' => 'FolderController@getFolders',
'as' => 'getFolders',
]);
// crop
Route::get('/crop', [
'uses' => 'CropController@getCrop',
'as' => 'getCrop',
]);
Route::get('/cropimage', [
'uses' => 'CropController@getCropimage',
'as' => 'getCropimage',
]);
Route::get('/cropnewimage', [
'uses' => 'CropController@getNewCropimage',
'as' => 'getCropimage',
]);
// rename
Route::get('/rename', [
'uses' => 'RenameController@getRename',
'as' => 'getRename',
]);
// scale/resize
Route::get('/resize', [
'uses' => 'ResizeController@getResize',
'as' => 'getResize',
]);
Route::get('/doresize', [
'uses' => 'ResizeController@performResize',
'as' => 'performResize',
]);
// download
Route::get('/download', [
'uses' => 'DownloadController@getDownload',
'as' => 'getDownload',
]);
// delete
Route::get('/delete', [
'uses' => 'DeleteController@getDelete',
'as' => 'getDelete',
]);
Route::get('/demo', 'DemoController@index');
});
}
}

View file

@ -0,0 +1,222 @@
<?php
namespace IqContent\LaravelFilemanager;
use Symfony\Component\HttpFoundation\File\UploadedFile;
class LfmItem
{
private $lfm;
private $helper;
private $columns = ['name', 'url', 'time', 'icon', 'is_file', 'is_image', 'thumb_url'];
public $attributes = [];
public function __construct(LfmPath $lfm, Lfm $helper)
{
$this->lfm = $lfm->thumb(false);
$this->helper = $helper;
}
public function __get($var_name)
{
if (!array_key_exists($var_name, $this->attributes)) {
$function_name = camel_case($var_name);
$this->attributes[$var_name] = $this->$function_name();
}
return $this->attributes[$var_name];
}
public function fill()
{
foreach ($this->columns as $column) {
$this->__get($column);
}
return $this;
}
public function name()
{
return $this->lfm->getName();
}
public function model()
{
return $this->lfm->getModel();
}
public function deleteModel()
{
return $this->lfm->deleteModel();
}
public function path($type = 'absolute')
{
return $this->lfm->path($type);
}
public function isDirectory()
{
return $this->lfm->isDirectory();
}
public function isFile()
{
return ! $this->isDirectory();
}
/**
* Check a file is image or not.
*
* @param mixed $file Real path of a file or instance of UploadedFile.
* @return bool
*/
public function isImage()
{
return starts_with($this->mimeType(), 'image');
}
/**
* Get mime type of a file.
*
* @param mixed $file Real path of a file or instance of UploadedFile.
* @return string
*/
// TODO: uploaded file
public function mimeType()
{
// if ($file instanceof UploadedFile) {
// return $file->getMimeType();
// }
return $this->lfm->mimeType();
}
public function extension()
{
return $this->lfm->extension();
}
public function url()
{
if ($this->isDirectory()) {
return $this->lfm->path('working_dir');
}
return $this->lfm->url();
}
public function size()
{
return $this->isFile() ? $this->humanFilesize($this->lfm->size()) : '';
}
public function time()
{
return $this->lfm->lastModified();
}
public function dimensions()
{
if ($this->isImage()) {
//return $this->lfm->thumb($this->hasThumb())->url(true);
}
return null;
}
public function thumbUrl()
{
//edit
if ($this->isDirectory()) {
return null;
}
if ($this->isImage()) {
return $this->lfm->thumb($this->hasThumb())->url(true);
}
return null;
}
public function icon()
{
//edit
if ($this->isDirectory()) {
return 'fa-folder';
}
if ($this->isImage()) {
return 'fa-image';
}
//edit
return $this->helper->getFileIcon($this->extension());
//return $this->extension();
}
public function type()
{
if ($this->isDirectory()) {
return trans(Lfm::PACKAGE_NAME . '::lfm.type-folder');
}
if ($this->isImage()) {
return $this->mimeType();
}
return $this->helper->getFileType($this->extension());
}
public function hasThumb()
{
if (!$this->isImage()) {
return false;
}
if (!$this->lfm->thumb()->exists()) {
return false;
}
return true;
}
public function shouldCreateThumb()
{
if (!$this->helper->config('should_create_thumbnails')) {
return false;
}
if (!$this->isImage()) {
return false;
}
if (in_array($this->mimeType(), ['image/gif', 'image/svg+xml'])) {
return false;
}
return true;
}
public function get()
{
return $this->lfm->get();
}
/**
* Make file size readable.
*
* @param int $bytes File size in bytes.
* @param int $decimals Decimals.
* @return string
*/
public function humanFilesize($bytes, $decimals = 2)
{
$size = ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
$factor = floor((strlen($bytes) - 1) / 3);
return sprintf("%.{$decimals}f %s", $bytes / pow(1024, $factor), @$size[$factor]);
}
}

View file

@ -0,0 +1,437 @@
<?php
namespace IqContent\LaravelFilemanager;
use Illuminate\Container\Container;
use Intervention\Image\Facades\Image;
use IqContent\LaravelFilemanager\Models\IQContentFile;
use IqContent\LaravelFilemanager\Models\IQContentFolder;
use phpDocumentor\Reflection\DocBlock\Tags\Return_;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use IqContent\LaravelFilemanager\Events\ImageIsUploading;
use IqContent\LaravelFilemanager\Events\ImageWasUploaded;
class LfmPath
{
private $working_dir;
private $item_name;
private $file_model;
private $folder_model;
private $is_thumb = false;
private $image_dimensions = "";
private $isDirectory = null;
private $helper;
public function __construct(Lfm $lfm = null)
{
$this->helper = $lfm;
}
public function __get($var_name)
{
if ($var_name == 'storage') {
return $this->helper->getStorage($this->path('url'));
}
}
public function __call($function_name, $arguments)
{
return $this->storage->$function_name(...$arguments);
}
public function dir($working_dir)
{
$this->working_dir = $working_dir;
return $this;
}
public function thumb($is_thumb = true)
{
$this->is_thumb = $is_thumb;
return $this;
}
public function setName($item_name)
{
$this->item_name = $item_name;
return $this;
}
public function setModel()
{
$working_dir = $this->path('url');
$parent_dir = substr($working_dir, 0, strrpos($working_dir, '/'));
$parent_folder = IQContentFolder::where('path', $parent_dir)->first();
if ($this->isDirectory()) {
if($this->folder_model == null){
$this->folder_model = new IQContentFolder();
}
}else{
if($this->file_model == null){
$this->file_model = new IQContentFile();
$this->file_model = IQContentFile::where('name', $this->item_name)->where('folder_id', $parent_folder->id)->first();
}
}
//
return $this;
}
public function renameModel($new_name)
{
if ($this->isDirectory()) {
if($this->folder_model != null){
$this->folder_model->name = $new_name;
$this->folder_mode->save();
}
}else{
if($this->file_model != null){
$this->file_model->name = $new_name;
$this->file_model->save();
}
}
return $this;
}
public function getName()
{
return $this->item_name;
}
public function getModel()
{
if ($this->isDirectory()) {
return $this->folder_model;
}else{
return $this->file_model;
}
}
public function path($type = 'storage')
{
if ($type == 'working_dir') {
// working directory: /{user_slug}
return $this->translateToLfmPath($this->normalizeWorkingDir());
} elseif ($type == 'url') {
// storage: files/{user_slug}
return $this->helper->getCategoryName() . $this->path('working_dir');
} elseif ($type == 'storage') {
// storage: files/{user_slug}
// storage on windows: files\{user_slug}
return $this->translateToOsPath($this->path('url'));
} elseif ($type == 'dirname') {
return $this->helper->getDirFromPath($this->path('storage'));
} else {
// absolute: /var/www/html/project/storage/app/files/{user_slug}
// absolute on windows: C:\project\storage\app\files\{user_slug}
return $this->storage->rootPath() . $this->path('storage');
}
}
public function translateToLfmPath($path)
{
return str_replace($this->helper->ds(), Lfm::DS, $path);
}
public function translateToOsPath($path)
{
return str_replace(Lfm::DS, $this->helper->ds(), $path);
}
public function url()
{
return $this->storage->url($this->path('url'));
}
public function folders()
{
$all_folders = array_map(function ($directory_path) {
return $this->pretty($directory_path);
}, $this->storage->directories());
$folders = array_filter($all_folders, function ($directory) {
return $directory->name !== $this->helper->getThumbFolderName();
});
return $this->sortByColumn($folders);
}
public function files()
{
$files = array_map(function ($file_path) {
return $this->pretty($file_path);
}, $this->storage->files());
return $this->sortByColumn($files);
}
public function pretty($item_path)
{
return Container::getInstance()->makeWith(LfmItem::class, [
'lfm' => (clone $this)
->setName($this->helper->getNameFromPath($item_path))
->setModel(),
'helper' => $this->helper,
]);
}
public function delete()
{
if ($this->isDirectory()) {
return $this->storage->deleteDirectory();
} else {
return $this->storage->delete();
}
}
public function deleteModel(){
if ($this->isDirectory()) {
if($this->folder_model){
$this->folder_model->delete();
return true;
}
}else{
if($this->file_model){
$this->file_model->delete();
return true;
}
}
return false;
}
/**
* Create folder if not exist.
*
* @param string $path Real path of a directory.
* @return bool
*/
public function createFolder()
{
if ($this->storage->exists($this)) {
return false;
}
$working_dir = $this->path('url');
$parent_dir = substr($working_dir, 0, strrpos($working_dir, '/'));
$parent_folder = IQContentFolder::where('path', $parent_dir)->first();
$this->storage->makeDirectory(0777, true, true);
IQContentFolder::create([
'folder_id' => $parent_folder->id,
'name' => $this->item_name,
'identifier' => $this->item_name,
'path' => $this->path('url'),
]);
}
public function isDirectory()
{
if($this->isDirectory !== null){
return $this->isDirectory;
}
$working_dir = $this->path('working_dir');
$parent_dir = substr($working_dir, 0, strrpos($working_dir, '/'));
$parent_directories = array_map(function ($directory_path) {
return app(static::class)->translateToLfmPath($directory_path);
}, app(static::class)->dir($parent_dir)->directories());
$this->isDirectory = in_array($this->path('url'), $parent_directories);
return $this->isDirectory;
}
/**
* Check a folder and its subfolders is empty or not.
*
* @param string $directory_path Real path of a directory.
* @return bool
*/
public function directoryIsEmpty()
{
return count($this->storage->allFiles()) == 0;
}
public function normalizeWorkingDir()
{
$path = $this->working_dir
?: $this->helper->input('working_dir')
?: $this->helper->getRootFolder();
if ($this->is_thumb) {
$path .= Lfm::DS . $this->helper->getThumbFolderName();
}
if ($this->getName()) {
$path .= Lfm::DS . $this->getName();
}
return $path;
}
/**
* Sort files and directories.
*
* @param mixed $arr_items Array of files or folders or both.
* @return array of object
*/
public function sortByColumn($arr_items)
{
$sort_by = $this->helper->input('sort_type');
if (in_array($sort_by, ['name', 'time'])) {
$key_to_sort = $sort_by;
} else {
$key_to_sort = 'name';
}
uasort($arr_items, function ($a, $b) use ($key_to_sort) {
return strcmp($a->{$key_to_sort}, $b->{$key_to_sort});
});
return $arr_items;
}
public function error($error_type, $variables = [])
{
return $this->helper->error($error_type, $variables);
}
// Upload section
public function upload($file)
{
$this->uploadValidator($file);
$new_file_name = $this->getNewName($file);
$new_file_path = $this->setName($new_file_name)->path('absolute');
$working_url = $this->path('url');
$working_dir = substr($working_url, 0, strrpos($working_url, '/'));
$working_folder = IQContentFolder::where('path', $working_dir)->first();
event(new ImageIsUploading($new_file_path));
try {
$new_file_name = $this->saveFile($file, $new_file_name);
} catch (\Exception $e) {
\Log::info($e);
return $this->error('invalid');
}
IQContentFile::create([
'folder_id' => $working_folder->id,
'name' => $new_file_name,
'identifier' => $new_file_name,
'ext' => $file->guessClientExtension(),
'mine' => $file->getMimeType(),
'size' => $file->getSize() / 1000,
'dimensions' => $this->image_dimensions,
'content' => $working_dir,
]);
// TODO should be "FileWasUploaded"
event(new ImageWasUploaded($new_file_path));
return $new_file_name;
}
private function uploadValidator($file)
{
if (empty($file)) {
return $this->error('file-empty');
} elseif (! $file instanceof UploadedFile) {
return $this->error('instance');
} elseif ($file->getError() == UPLOAD_ERR_INI_SIZE) {
return $this->error('file-size', ['max' => ini_get('upload_max_filesize')]);
} elseif ($file->getError() != UPLOAD_ERR_OK) {
throw new \Exception('File failed to upload. Error code: ' . $file->getError());
}
$new_file_name = $this->getNewName($file);
if ($this->setName($new_file_name)->exists() && !config('lfm.over_write_on_duplicate')) {
return $this->error('file-exist');
}
if (config('lfm.should_validate_mime', false)) {
$mimetype = $file->getMimeType();
if (false === in_array($mimetype, $this->helper->availableMimeTypes())) {
return $this->error('mime') . $mimetype;
}
}
if (config('lfm.should_validate_size', false)) {
// size to kb unit is needed
$file_size = $file->getSize() / 1000;
if ($file_size > $this->helper->maxUploadSize()) {
return $this->error('size') . $file_size;
}
}
return 'pass';
}
private function getNewName($file)
{
$new_file_name = $this->helper
->translateFromUtf8(trim(pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME)));
if (config('lfm.rename_file') === true) {
$new_file_name = uniqid();
} elseif (config('lfm.alphanumeric_filename') === true) {
$new_file_name = $this->helper->sanitize($new_file_name); //preg_replace('/[^A-Za-z0-9\-\']/', '_', $new_file_name);
}
$extension = $file->getClientOriginalExtension();
if ($extension) {
$new_file_name .= '.' . $extension;
}
return $new_file_name;
}
private function saveFile($file, $new_file_name)
{
$this->setName($new_file_name)->storage->save($file);
$this->makeThumbnail($new_file_name);
return $new_file_name;
}
public function makeThumbnail($file_name)
{
$original_image = $this->pretty($file_name);
if (!$original_image->shouldCreateThumb()) {
return;
}
// create folder for thumbnails
$this->setName(null)->thumb(true)->createFolder();
// generate cropped image content
$this->setName($file_name)->thumb(true);
$image = Image::make($original_image->get());
$this->image_dimensions = $image->width()."x".$image->height();
$image->fit(config('lfm.thumb_img_width', 200), config('lfm.thumb_img_height', 200));
$this->storage->put($image->stream()->detach());
}
}

Some files were not shown because too many files have changed in this diff Show more