Compare commits

..

56 Commits

Author SHA1 Message Date
Peter Papp
e5335c2472 Merge remote-tracking branch 'origin/exifBlacklist' into exifBlacklist
# Conflicts:
#	public/mix-manifest.json
#	resources/js/helpers.js
#	resources/js/i18n/lang/cn.json
#	resources/js/i18n/lang/en.json
#	resources/js/i18n/lang/sk.json
#	resources/js/store/modules/fileFunctions.js
#	resources/js/views/Admin/AppSettings/AppSettingsTabs/Others.vue
2020-10-24 10:40:59 +02:00
Peter Papp
a2ec066c6d added metadata to UpgradeAppController.php
updated readme with upgrading step to 1.7.11
Frontend build
2020-10-24 10:39:39 +02:00
Miloš Holba
15d4ffc349 Add to metadata Longitude and Latitude 2020-10-24 09:44:23 +02:00
Peter Papp
9af9e45b8a night mode for ImageMetaData.vue 2020-10-24 09:44:23 +02:00
Peter Papp
f30dd38b96 migration column in file_manager_files renamed from meta_data to metadata
context menu feractoring
Editor.php typo changes
FileInfoPanel.vue small changes when to show metadata info
Added new exif information and reordering existing in ImageMetaData.vue
Added new translation strings to i18n files
2020-10-24 09:44:23 +02:00
Miloš Holba
d5f3599b9e v0.1 Add enable empty input in uploadText method 2020-10-24 09:44:23 +02:00
Miloš Holba
de62ab535c v0.1 Fix exif data function just for jpeg and Fix config in index.blade for mimetype_blacklist 2020-10-24 09:44:18 +02:00
Miloš Holba
8b8562916b v0.1 exifBlacklist 2020-10-24 09:44:05 +02:00
Miloš Holba
0d7b920cbe v0.1 add check mimetype if mimetype of file is undefined 2020-10-24 09:44:04 +02:00
Miloš Holba
f3254a380f v0.1 exif Blacklist add frontend validation 2020-10-24 09:44:02 +02:00
Miloš Holba
0e12029efc v0.1 Blacklist and Exif data 2020-10-24 09:43:58 +02:00
Miloš Holba
fd6aa5c6b2 Lazy loading (#41)
* lazy-loading and solved bugs

* lazy-loading for pull request on master

* Update vuefilemanager.php

Co-authored-by: Peter Papp <peterpapp@makingcg.com>
2020-10-24 09:40:05 +02:00
Peter Papp
12d6b0bf57 scheduler changed from hourly to every minute 2020-10-13 17:09:05 +02:00
nourisk
961462fe54 Update package.json (#32)
The "vue" and "vue-template[..]" versions not being the same, this has the effect of breaking the "npm run prod" command with error "version missmatch".
For people who have already installed it, just change the version then "npm install" and that should solve the problem.
2020-10-07 08:19:39 +02:00
nourisk
f0e0103d81 Update router.js (#31)
Capital i missing
2020-10-06 17:03:26 +02:00
Miloš Holba
755523e07b Add to metadata Longitude and Latitude 2020-09-25 09:31:31 +02:00
Peter Papp
c4ace0697b version change to v1.7.10.3 2020-09-20 12:09:07 +02:00
Peter Papp
69489c4ee2 - fixed simultaneously action with show and download file on doubleclick
- Changed download name for Backblaze
- Price plan can be defined in 12 digit number
2020-09-20 12:08:29 +02:00
Peter Papp
e05d7468b5 night mode for ImageMetaData.vue 2020-09-20 11:23:50 +02:00
Peter Papp
1a6a91335e migration column in file_manager_files renamed from meta_data to metadata
context menu feractoring
Editor.php typo changes
FileInfoPanel.vue small changes when to show metadata info
Added new exif information and reordering existing in ImageMetaData.vue
Added new translation strings to i18n files
2020-09-20 11:17:34 +02:00
Miloš Holba
80de069c7b v0.1 Add enable empty input in uploadText method 2020-09-15 13:20:39 +02:00
Miloš Holba
2de8bbe548 v0.1 Fix exif data function just for jpeg and Fix config in index.blade for mimetype_blacklist 2020-09-15 10:33:39 +02:00
Peter Papp
7ba2a3bec1 fixed backblaze broken download link when filename contain space (Content Disposition header wasn't read properly with space) 2020-09-14 09:53:38 +02:00
Miloš Holba
89fa2fce23 v0.1 exifBlacklist 2020-09-12 11:15:32 +02:00
Miloš Holba
84b47416d6 v0.1 add check mimetype if mimetype of file is undefined 2020-09-06 20:29:16 +02:00
Miloš Holba
55c72cddba v0.1 exif Blacklist add frontend validation 2020-09-05 18:35:27 +02:00
Miloš Holba
6effd4a7d9 v0.1 Blacklist and Exif data 2020-09-05 14:35:51 +02:00
Peter Papp
95f6695d00 Fixed when default value incorrectly showing in switches in app settings 2020-09-01 07:41:05 +02:00
Peter Papp
fde7f0f73b Fixed when default value incorrectly showing in switches in app settings 2020-09-01 07:10:56 +02:00
Peter Papp
dd1bffec09 typo error 2020-08-31 08:38:40 +02:00
Peter Papp
83a951b3af cache bursting support in webpack.mix.js 2020-08-31 08:22:51 +02:00
Peter Papp
6762ed25dc frontend build 2020-08-30 15:33:52 +02:00
Peter Papp
6ba869234e FileFullPreview functionality in file grid view 2020-08-30 15:33:20 +02:00
Peter Papp
2c04376a61 v1.7.10 2020-08-30 11:18:28 +02:00
Peter Papp
f180f1fff8 expand tap area for FilePreviewActions.vue arrows 2020-08-30 11:15:51 +02:00
Peter Papp
ab65ca7a13 hide subscription, payment cards and invoices in profile menu when is 'Allow subscription payment' option disabled
frontend build
2020-08-30 11:05:08 +02:00
Peter Papp
8895b5062a small design improves 2020-08-30 09:01:00 +02:00
Peter Papp
2f4aafb1b3 frontend build 2020-08-28 16:17:53 +02:00
Peter Papp
be08c8487a Merge remote-tracking branch 'origin/master' into filepreview
# Conflicts:
#	.env.example
#	package-lock.json
#	package.json
#	public/js/main.js
#	public/mix-manifest.json
#	resources/js/App.vue
#	resources/js/helpers.js
#	resources/js/i18n/lang/en.json
#	resources/js/i18n/lang/sk.json
#	resources/js/store/modules/fileFunctions.js
#	resources/js/views/Shared/SharedPage.vue
2020-08-28 16:14:28 +02:00
Peter Papp
0d5df91d2d frontend build 2020-08-27 14:48:48 +02:00
Peter Papp
72e4067beb logo fix in shared page
Renamed option title in share popup
2020-08-27 14:46:07 +02:00
Peter Papp
ba4f888826 Updated README.md and dark mode logo in Dashboard.vue page 2020-08-27 08:25:08 +02:00
Miloš Holba
82b43eb996 FilePreview solved v0.3 issues 2020-08-26 22:37:33 +02:00
Peter Papp
777132ec40 dark mode expiration buttons fix 2020-08-26 18:30:10 +02:00
Miloš Holba
aac0aa755f fix arrow function when is just one file in MediaPreview 2020-08-22 17:55:03 +02:00
Miloš Holba
4be77c07ac remove addEventListener change the video sizing wrapper 2020-08-22 14:18:57 +02:00
Miloš Holba
9372906a3e open to click at audio for mobile 2020-08-22 12:19:23 +02:00
Miloš Holba
e12e521622 add audio mimetypes : mp4, flac 2020-08-21 17:32:36 +02:00
Miloš Holba
0082c3a6a8 change arrows position 2020-08-20 20:31:47 +02:00
Miloš Holba
f79973e922 Fix issues from FilePreview v0.2 2020-08-20 18:50:07 +02:00
Miloš Holba
59ca45e9b1 update filePreview ContextMenu 2020-08-18 19:22:54 +02:00
Miloš Holba
bb22ec1e88 key binds fix 2020-08-17 20:10:30 +02:00
Miloš Holba
ba315014fa split code to components, add arrow functions 2020-08-15 17:41:46 +02:00
Miloš Holba
8387f56048 Move item from gallery fix 2020-08-10 10:30:54 +02:00
Miloš Holba
68acf5f986 Add Gallery 2020-08-08 21:53:43 +02:00
Miloš Holba
785dade6b2 Add Gallery 2020-08-08 21:46:56 +02:00
122 changed files with 3938 additions and 3284 deletions

3
.gitignore vendored
View File

@@ -1,3 +1,4 @@
/app/Console/Commands/SetupDevelopmentEnvironment.php
/node_modules
/public/hot
/public/storage
@@ -11,8 +12,10 @@
.env.backup
.phpunit.result.cache
.phpstorm.meta.php
.vscode/
_ide_helper.php
Homestead.json
Homestead.yaml
npm-debug.log
yarn-error.log
prettier.json

122
README.md
View File

@@ -1,16 +1,6 @@
![logo](https://vuefilemanager.com/assets/images/vuefilemanager-horizontal-logo.svg)
# Private Cloud Storage Build on Laravel & Vue.js
## Supporting VueFileManager
Hi, we are trying make the best experience with VueFileManager. There is a lot things to do, and a lot of features we can make.
But, it can't be done without you, development is more and more complicated and we have to hire new colleagues to help with it. There is couple way you can support us, and then, we support you with all great new features which can be. Thanks you for participating on this awesome software!
- [Buy me a Coffe](https://www.buymeacoffee.com/pepe)
- [One-time donation via PayPal](https://www.paypal.me/peterpapp)
- [Become a backer or sponsor on Patreon](https://www.patreon.com/vuefilemanager)
- [Purchase Licence on CodeCanyon](https://codecanyon.net/item/vue-file-manager-with-laravel-backend/25815986)
## Contents
- [Installation](#installation)
@@ -18,14 +8,14 @@ But, it can't be done without you, development is more and more complicated and
- [Installation](#installation)
- [PHP Configuration](#php-configuration)
- [Chunk Upload](#chunk-upload)
- [Nginx Configuration](#nginx-configuration)
- [Apache Configuration](#apache-configuration)
- [Recover Failed Installation](#installation-failed)
- [Update Guide](#update-guide)
- [Instructions](#instructions)
- [Upgrade Guide](#upgrade-guide)
- [Common Instructions](#common-instructions)
- [Update from 1.7.10 to 1.7.11](#update-from-1710-to-1711)
- [Update from 1.7.8 to 1.7.9](#update-from-178-to-179)
- [Update from 1.7.x to 1.7.8](#update-from-17x-to-178)
- [Update from 1.6.x to 1.7](#update-from-16x-to-17)
- [Nginx Configuration](#nginx-configuration)
- [Apache Configuration](#apache-configuration)
- [Payments](#payments)
- [Get your active plans](#get-your-active-plans)
- [Manage Failed Payments](#manage-failed-payments)
@@ -71,7 +61,7 @@ But, it can't be done without you, development is more and more complicated and
## Installation
#### 1. Upload files on your server
Copy project files to web root folder of your domain. It's mostly located in `html`, `www` or `public_html` folder name.
Upload project files to web root folder of your domain. It's mostly located in `html`, `www` or `public_html` folder name.
#### 2. Configure your web root folder
Configure your web server's document root to point to the public directory of the files you previously uploaded. For example, if you've uploaded the files in `html` folder, your domain root directory should be changed to `html/project_files/public` folder or anything else where domain root is in project `/public` directory.
@@ -107,9 +97,10 @@ That was the hardest part of installation proces. Please follow instructions in
#### 7. Set up Cron
Add the following Cron entry to your server
Add the following Cron entry to your server. Just update your php path (if it's different) and project path:
```
* * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1
* * * * * /usr/local/bin/php /path-to-your-project/artisan schedule:run >> /dev/null 2>&1
```
## PHP Configuration
@@ -124,10 +115,47 @@ max_execution_time = 3600
```
## Chunk & Multipart Upload
VueFileManager in default supporting chunk upload. Default chunk upload size is `128MB`. If you wish change this default value, go to `/config/vuefilemanager.php` and change `chunk_size` attribute.
VueFileManager in default supporting chunk upload. Default chunk upload size is `128MB`. If you wish change this default value, go to your `.env` and change `CHUNK_SIZE` attribute.
When you use external storage, and upload large files, to prevent failing upload process make sure you have enough space in your application space and set higher `max_execution_time` in your php.ini to move your files to external storage.
## Upgrade Guide
### Common Instructions
`Don't forget create backup of your database before make any changes in your production application. If you serve your files in local storage driver pay attention and don't delete your /storage folder!`
These instructions is applicable for all updates. Please follow this step:
- Just rewrite all project files with new excluded `/.env` file and `/storage` folder. These items must be preserved!
### Update from 1.7.10 to 1.7.11
Before upload new files to your hosting, log in to VueFileManager as Admin. After uploading new files on your webhosting, visit this url `your-domain.com/service/upgrade-database` for upgrading your database.
### Update from 1.7.8 to 1.7.9
After rewrited old files with new files, log in as admin to the app and go to `your-domain.com/service/upgrade-database`. This will upgrade your database on the background.
Add the following Cron entry to your server. Just update your php path (if it's different) and project path:
```
* * * * * /usr/local/bin/php /path-to-your-project/artisan schedule:run >> /dev/null 2>&1
```
### Update from 1.7.x to 1.7.8
For those who have installed VueFileManager via git or any other repository synchronization tool, dont't forget after updated code run `composer update` command to update your vendors.
### Update from 1.6.x to 1.7
For those, who purchase extended licence, place these lines at the end of your `/.env` file:
```
CASHIER_LOGGER=stack
CASHIER_CURRENCY=
STRIPE_KEY=
STRIPE_SECRET=
STRIPE_WEBHOOK_SECRET=
CASHIER_PAYMENT_NOTIFICATION=App\Notifications\ConfirmPayment
```
Then go to https://your-domain.com/upgrade and follow the setup wizard instructions.
## Nginx Configuration
If you running VueFileManager undex Nginx, don't forget set this value in your `nginx.conf` file:
@@ -190,54 +218,6 @@ Make sure you have enabled mod_rewrite. There is an example config for running V
</VirtualHost>
```
## Installation Failed
What to do when installation fail and you can't continue, at first, try to fix issue why installation fail. Probably missing PHP extension or permissions wasn't set correctly.
At worst scenarios, to reset Setup Wizard, delete all tables in your previously created database, delete content of `/storage/framework/cache`. Then replace content in your `.env` file from `.env.example` file.
After these steps, installation will be reseted.
## Update Guide
### Instructions
`Don't forget create backup of your database and storage before make any changes in your production application.`
`If you serve your files in local storage driver pay attention and don't delete your /storage folder`
Follow this steps:
- Make a backup of the .env config file located on your server.
- Upload and replace all the files on your server with what's inside the app folder.
- Restore your `.env` config file on your server.
## Update from 1.7.8 to 1.7.9
After uploaded new files, log in as admin to the app and go to `your-domain.com/service/upgrade-database`. This will upgrade your database on the background.
After that, **update path to your project in command below** and add the following **Cron** entry to your server:
```
* * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1
```
That's all.
## Update from 1.7.x to 1.7.8
If you are upgrading app to 1.7.7 from 1.7.x, make sure you have copied new /vendor folder or if you are using terminal or git, run `composer update` command to update your vendors.
## Update from 1.6.x to 1.7
For those, who purchase extended licence, place these lines at the end of your `/.env` file:
```
CASHIER_LOGGER=stack
CASHIER_CURRENCY=
STRIPE_KEY=
STRIPE_SECRET=
STRIPE_WEBHOOK_SECRET=
CASHIER_PAYMENT_NOTIFICATION=App\Notifications\ConfirmPayment
```
Then go to https://your-domain.com/upgrade and follow the setup wizard instructions.
# Payments
VueFileManager is packed with **Stripe** payment options. To configure Stripe, you will be asked in Setup Wizard to set up. Or, if you skip this installation process, you will find stripe set up in you admin `Dashboard / Settings / Payments`.
@@ -377,6 +357,14 @@ The following support channels are available at your fingertips:
- [CodeCanyon support message](https://codecanyon.net/item/vue-file-manager-with-laravel-backend/25815986/support)
- [GitHub repository](https://vuefilemanager.com/github-access)
## Supporting VueFileManager
Hi, we are trying make the best experience with VueFileManager. There is a lot things to do, and a lot of features we can make.
But, it can't be done without you, development is more and more complicated and we have to hire new colleagues to help with it. There is couple way you can support us, and then, we support you with all great new features which can be. Thanks you for participating on this awesome software!
- [Buy me a Coffe](https://www.buymeacoffee.com/pepe)
- [One-time donation via PayPal](https://www.paypal.me/peterpapp)
## Security Vulnerabilities
If you discover a security vulnerability within this project, please send an e-mail to [peterpapp@makingcg.com](peterpapp@makingcg.com). All security vulnerabilities will be promptly addressed.

View File

@@ -3,9 +3,7 @@
namespace App\Console;
use App\Console\Commands\Deploy;
use App\Console\Commands\SetupDevEnvironment;
use App\Console\Commands\SetupProductionEnvironment;
use App\Console\Commands\UpgradeApp;
//use App\Console\Commands\SetupDevelopmentEnvironment;
use App\Share;
use Carbon\Carbon;
use Illuminate\Console\Scheduling\Schedule;
@@ -20,6 +18,7 @@ class Kernel extends ConsoleKernel
*/
protected $commands = [
Deploy::class,
//SetupDevelopmentEnvironment::class,
];
/**
@@ -32,7 +31,7 @@ class Kernel extends ConsoleKernel
{
$schedule->call(function () {
$this->delete_expired_shared_links();
})->hourly();
})->everyMinute();
}
/**

View File

@@ -67,6 +67,10 @@ class FileManagerFile extends Model
'file_url'
];
protected $casts = [
'metadata' => 'array',
];
/**
* Set routes with public access
*
@@ -148,7 +152,9 @@ class FileManagerFile extends Model
// Get file from external storage
if (is_storage_driver(['s3', 'spaces', 'wasabi', 'backblaze'])) {
$file_pretty_name = get_pretty_name($this->attributes['basename'], $this->attributes['name'], $this->attributes['mimetype']);
$file_pretty_name = is_storage_driver('backblaze')
? Str::snake(mb_strtolower($this->attributes['name']))
: get_pretty_name($this->attributes['basename'], $this->attributes['name'], $this->attributes['mimetype']);
$header = [
"ResponseAcceptRanges" => "bytes",

View File

@@ -101,7 +101,7 @@ class FileAccessController extends Controller
$shared = get_shared($token);
// Abort if shared is protected
if ((int) $shared->protected) {
if ((int)$shared->protected) {
abort(403, "Sorry, you don't have permission");
}
@@ -154,7 +154,7 @@ class FileAccessController extends Controller
$shared = get_shared($token);
// Abort if thumbnail is protected
if ((int) $shared->protected) {
if ((int)$shared->protected) {
abort(403, "Sorry, you don't have permission");
}

View File

@@ -171,5 +171,25 @@ class UpgradeAppController extends Controller
echo 'Operation failed.';
}
}
/*
* Upgrade expire_in in shares table
*
* @since v1.7.11
*/
if (! Schema::hasColumn('file_manager_files', 'metadata')) {
$command = Artisan::call('migrate', [
'--force' => true
]);
if ($command === 0) {
echo 'Operation was successful.';
}
if ($command === 1) {
echo 'Operation failed.';
}
}
}
}

View File

@@ -528,4 +528,11 @@ function get_pretty_name($basename, $name, $mimetype)
}
return $name . '.' . $mimetype;
}
}
function get_image_meta_data($file)
{
if(get_file_type_from_mimetype($file->getMimeType()) === 'jpeg') {
return exif_read_data($file);
}
}

View File

@@ -2,6 +2,7 @@
namespace App\Http\Requests\FileFunctions;
use App\Rules\MimetypeBlacklistValidation;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Auth;
@@ -26,7 +27,7 @@ class UploadRequest extends FormRequest
{
return [
'parent_id' => 'required|integer',
'file' => 'required|file',
'file' => ['required','file' , new MimetypeBlacklistValidation]
];
}
}

View File

@@ -257,6 +257,8 @@ class Editor
// If last then process file
if ($request->boolean('is_last')) {
$metadata = get_image_meta_data($file);
$disk_local = Storage::disk('local');
$unique_id = get_unique_id();
@@ -292,6 +294,7 @@ class Editor
'mimetype' => get_file_type_from_mimetype($file_mimetype),
'type' => get_file_type($file_mimetype),
'folder_id' => $request->parent_id,
'metadata' => $metadata,
'name' => $user_file_name,
'unique_id' => $unique_id,
'basename' => $disk_file_name,

View File

@@ -0,0 +1,42 @@
<?php
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
class MimetypeBlacklistValidation implements Rule
{
/**
* Create a new rule instance.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Determine if the validation rule passes.
*
* @param string $attribute
* @param mixed $value
* @return bool
*/
public function passes($attribute, $value)
{
$mimetype_blacklist = explode(',' ,get_setting('mimetypes_blacklist'));
$file_mimetype = explode('/' ,$value->getMimeType());
return !array_intersect($file_mimetype , $mimetype_blacklist);
}
/**
* Get the validation error message.
*
* @return string
*/
public function message()
{
abort (415,'Type of this mime type is not allowed.');
}
}

View File

@@ -2,8 +2,8 @@
return [
'version' => '1.7.9',
'version' => '1.7.11',
// Define size of chunk uploaded by MB. E.g. integer 128 means chunk size will be 128MB.
'chunk_size' => env('CHUNK_SIZE', '128'),
];
];

View File

@@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddExifDataToFileManagerFilesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('file_manager_files', function (Blueprint $table) {
$table->longText('metadata')->after('type')->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('file_manager_files', function (Blueprint $table) {
//
});
}
}

View File

@@ -24,7 +24,7 @@
"lodash": "^4.17.20",
"node-sass": "^4.14.1",
"vee-validate": "^3.3.9",
"vue": "^2.6.10",
"vue": "^2.6.11",
"vue-feather-icons": "^5.1.0",
"vue-i18n": "^8.21.0",
"vue-router": "^3.4.3",

View File

@@ -8,6 +8,7 @@
AddType video/ogg .ogv
AddType video/mp4 .mp4
AddType video/webm .webm
<ifModule mod_headers.c>
Header set Connection keep-alive
</ifModule>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
(window.webpackJsonp=window.webpackJsonp||[]).push([[42],{556:function(e,t,r){"use strict";r.r(t);var n=r(7);function o(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function i(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}var c={name:"SetupWizard",computed:function(e){for(var t=1;t<arguments.length;t++){var r=null!=arguments[t]?arguments[t]:{};t%2?o(Object(r),!0).forEach((function(t){i(e,t,r[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(r)):o(Object(r)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(r,t))}))}return e}({},Object(n.b)(["config"])),mounted:function(){"setup-done"!==this.config.installation&&"quiet-update"!==this.config.installation||this.$router.push({name:"SignIn"})}},u=r(0),a=Object(u.a)(c,(function(){var e=this.$createElement;return(this._self._c||e)("router-view")}),[],!1,null,null,null);t.default=a.exports}}]);
(window.webpackJsonp=window.webpackJsonp||[]).push([[42],{oBQg:function(e,t,r){"use strict";r.r(t);var n=r("L2JU");function o(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function i(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}var c={name:"SetupWizard",computed:function(e){for(var t=1;t<arguments.length;t++){var r=null!=arguments[t]?arguments[t]:{};t%2?o(Object(r),!0).forEach((function(t){i(e,t,r[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(r)):o(Object(r)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(r,t))}))}return e}({},Object(n.b)(["config"])),mounted:function(){"setup-done"!==this.config.installation&&"quiet-update"!==this.config.installation||this.$router.push({name:"SignIn"})}},u=r("KHd+"),a=Object(u.a)(c,(function(){var e=this.$createElement;return(this._self._c||e)("router-view")}),[],!1,null,null,null);t.default=a.exports}}]);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2
public/js/main.js vendored

File diff suppressed because one or more lines are too long

View File

@@ -5,7 +5,7 @@
*/
/*!
* vue-i18n v8.18.2
* vue-i18n v8.21.0
* (c) 2020 kazuya kawaguchi
* Released under the MIT License.
*/
@@ -17,7 +17,7 @@
*/
/**
* vee-validate v3.3.7
* vee-validate v3.3.9
* (c) 2020 Abdelrahman Awad
* @license MIT
*/

View File

@@ -1,66 +1,66 @@
{
"/chunks/files~chunks/shared-files~chunks/shared-page~chunks/trash.js": "/chunks/files~chunks/shared-files~chunks/shared-page~chunks/trash.js",
"/js/main.js": "/js/main.js",
"/css/app.css": "/css/app.css",
"/chunks/admin.js": "/chunks/admin.js",
"/chunks/admin-account.js": "/chunks/admin-account.js",
"/chunks/app-appearance.js": "/chunks/app-appearance.js",
"/chunks/app-billings.js": "/chunks/app-billings.js",
"/chunks/app-email.js": "/chunks/app-email.js",
"/chunks/app-index.js": "/chunks/app-index.js",
"/chunks/app-others.js": "/chunks/app-others.js",
"/chunks/app-payments.js": "/chunks/app-payments.js",
"/chunks/app-settings.js": "/chunks/app-settings.js",
"/chunks/app-setup.js": "/chunks/app-setup.js",
"/chunks/billings-detail.js": "/chunks/billings-detail.js",
"/chunks/contact-us.js": "/chunks/contact-us.js",
"/chunks/create-new-password.js": "/chunks/create-new-password.js",
"/chunks/dashboard.js": "/chunks/dashboard.js",
"/chunks/database.js": "/chunks/database.js",
"/chunks/dynamic-page.js": "/chunks/dynamic-page.js",
"/chunks/environment-setup.js": "/chunks/environment-setup.js",
"/chunks/files.js": "/chunks/files.js",
"/chunks/forgotten-password.js": "/chunks/forgotten-password.js",
"/chunks/installation-disclaimer.js": "/chunks/installation-disclaimer.js",
"/chunks/invoices.js": "/chunks/invoices.js",
"/chunks/landing-page.js": "/chunks/landing-page.js",
"/chunks/not-found-shared.js": "/chunks/not-found-shared.js",
"/chunks/page-edit.js": "/chunks/page-edit.js",
"/chunks/pages.js": "/chunks/pages.js",
"/chunks/plan.js": "/chunks/plan.js",
"/chunks/plan-create.js": "/chunks/plan-create.js",
"/chunks/plan-delete.js": "/chunks/plan-delete.js",
"/chunks/plan-settings.js": "/chunks/plan-settings.js",
"/chunks/plan-subscribers.js": "/chunks/plan-subscribers.js",
"/chunks/plans.js": "/chunks/plans.js",
"/chunks/profile.js": "/chunks/profile.js",
"/chunks/purchase-code.js": "/chunks/purchase-code.js",
"/chunks/settings.js": "/chunks/settings.js",
"/chunks/settings-create-payment-methods.js": "/chunks/settings-create-payment-methods.js",
"/chunks/settings-invoices.js": "/chunks/settings-invoices.js",
"/chunks/settings-password.js": "/chunks/settings-password.js",
"/chunks/settings-payment-methods.js": "/chunks/settings-payment-methods.js",
"/chunks/settings-storage.js": "/chunks/settings-storage.js",
"/chunks/settings-subscription.js": "/chunks/settings-subscription.js",
"/chunks/setup-wizard.js": "/chunks/setup-wizard.js",
"/chunks/shared-files.js": "/chunks/shared-files.js",
"/chunks/shared-page.js": "/chunks/shared-page.js",
"/chunks/sign-in.js": "/chunks/sign-in.js",
"/chunks/sign-up.js": "/chunks/sign-up.js",
"/chunks/stripe-credentials.js": "/chunks/stripe-credentials.js",
"/chunks/subscription-plans.js": "/chunks/subscription-plans.js",
"/chunks/subscription-service.js": "/chunks/subscription-service.js",
"/chunks/trash.js": "/chunks/trash.js",
"/chunks/upgrade.js": "/chunks/upgrade.js",
"/chunks/upgrade-billing.js": "/chunks/upgrade-billing.js",
"/chunks/upgrade-plan.js": "/chunks/upgrade-plan.js",
"/chunks/user.js": "/chunks/user.js",
"/chunks/user-create.js": "/chunks/user-create.js",
"/chunks/user-delete.js": "/chunks/user-delete.js",
"/chunks/user-detail.js": "/chunks/user-detail.js",
"/chunks/user-invoices.js": "/chunks/user-invoices.js",
"/chunks/user-password.js": "/chunks/user-password.js",
"/chunks/user-storage.js": "/chunks/user-storage.js",
"/chunks/user-subscription.js": "/chunks/user-subscription.js",
"/chunks/users.js": "/chunks/users.js"
"/chunks/files~chunks/shared-files~chunks/shared-page~chunks/trash.js": "/chunks/files~chunks/shared-files~chunks/shared-page~chunks/trash.js?id=b2c33d183697d06adb8d",
"/js/main.js": "/js/main.js?id=939ce82189c86f19db73",
"/css/app.css": "/css/app.css?id=dbf49843b327d3936c06",
"/chunks/admin.js": "/chunks/admin.js?id=bd7dce5ddfbb8ebeb7a8",
"/chunks/admin-account.js": "/chunks/admin-account.js?id=ed4d3b5d0e9da3b70862",
"/chunks/app-appearance.js": "/chunks/app-appearance.js?id=046065efc4a67ad8587f",
"/chunks/app-billings.js": "/chunks/app-billings.js?id=0d1e25be91f67ffab63f",
"/chunks/app-email.js": "/chunks/app-email.js?id=b7a3bca09764ae780d6c",
"/chunks/app-index.js": "/chunks/app-index.js?id=e1544ff1341ca6416bab",
"/chunks/app-others.js": "/chunks/app-others.js?id=aa5b12609a66adf1d918",
"/chunks/app-payments.js": "/chunks/app-payments.js?id=a1a84878ec6f645946f5",
"/chunks/app-settings.js": "/chunks/app-settings.js?id=424c67c7d7f6b9109a99",
"/chunks/app-setup.js": "/chunks/app-setup.js?id=3ac1935129bff281257d",
"/chunks/billings-detail.js": "/chunks/billings-detail.js?id=e6434e402adec1cf8ba1",
"/chunks/contact-us.js": "/chunks/contact-us.js?id=fa10e7582a311ab12c1f",
"/chunks/create-new-password.js": "/chunks/create-new-password.js?id=b33f6ac5e174f51a443b",
"/chunks/dashboard.js": "/chunks/dashboard.js?id=a61fe2622dca51ca1396",
"/chunks/database.js": "/chunks/database.js?id=ac688c888342c5cf4c53",
"/chunks/dynamic-page.js": "/chunks/dynamic-page.js?id=08a0c2de8f93e0222600",
"/chunks/environment-setup.js": "/chunks/environment-setup.js?id=fb825f5ace1deeac3509",
"/chunks/files.js": "/chunks/files.js?id=bc21124b8956b99ab01f",
"/chunks/forgotten-password.js": "/chunks/forgotten-password.js?id=9a8acda895c0c40ea30b",
"/chunks/installation-disclaimer.js": "/chunks/installation-disclaimer.js?id=59450c310fb5af95b9a1",
"/chunks/invoices.js": "/chunks/invoices.js?id=6dd9f68419f741465844",
"/chunks/landing-page.js": "/chunks/landing-page.js?id=4dbec1937d1f5b71d17d",
"/chunks/not-found-shared.js": "/chunks/not-found-shared.js?id=af1fa088daae3fe7a3a8",
"/chunks/page-edit.js": "/chunks/page-edit.js?id=30e6227554f2f26e0419",
"/chunks/pages.js": "/chunks/pages.js?id=8dc613c3a82637e99dc2",
"/chunks/plan.js": "/chunks/plan.js?id=6de54ce130dd2766f4c6",
"/chunks/plan-create.js": "/chunks/plan-create.js?id=efaeb3ebdf30ba62c613",
"/chunks/plan-delete.js": "/chunks/plan-delete.js?id=93b6dfdf61caa4335a7c",
"/chunks/plan-settings.js": "/chunks/plan-settings.js?id=d1457670c217e13e97f9",
"/chunks/plan-subscribers.js": "/chunks/plan-subscribers.js?id=fba30dc476ddcd0c4b22",
"/chunks/plans.js": "/chunks/plans.js?id=f03834c110c677b6db0d",
"/chunks/profile.js": "/chunks/profile.js?id=23e34e6d7d73abcf317d",
"/chunks/purchase-code.js": "/chunks/purchase-code.js?id=f74384a387ce82e7751f",
"/chunks/settings.js": "/chunks/settings.js?id=a3b1c88c6b6af9b6308c",
"/chunks/settings-create-payment-methods.js": "/chunks/settings-create-payment-methods.js?id=543b594932039ef2b061",
"/chunks/settings-invoices.js": "/chunks/settings-invoices.js?id=b26452dbf79dcc6e2787",
"/chunks/settings-password.js": "/chunks/settings-password.js?id=919f57ae52204ea5ee54",
"/chunks/settings-payment-methods.js": "/chunks/settings-payment-methods.js?id=4cb65919f0627c8f9db1",
"/chunks/settings-storage.js": "/chunks/settings-storage.js?id=1a45b3cee17b17a7598d",
"/chunks/settings-subscription.js": "/chunks/settings-subscription.js?id=34e34dcb29cb84ab53bf",
"/chunks/setup-wizard.js": "/chunks/setup-wizard.js?id=47090233afc7b0cdf855",
"/chunks/shared-files.js": "/chunks/shared-files.js?id=bb6943a620f520324003",
"/chunks/shared-page.js": "/chunks/shared-page.js?id=2414f43c0129d239b907",
"/chunks/sign-in.js": "/chunks/sign-in.js?id=a0e2b7e58484e778848b",
"/chunks/sign-up.js": "/chunks/sign-up.js?id=774e43bc25ae4e3893a2",
"/chunks/stripe-credentials.js": "/chunks/stripe-credentials.js?id=e3639ebc187c3e568a20",
"/chunks/subscription-plans.js": "/chunks/subscription-plans.js?id=24f8053a0d89684699ad",
"/chunks/subscription-service.js": "/chunks/subscription-service.js?id=e0c770e988a664a18f76",
"/chunks/trash.js": "/chunks/trash.js?id=1ee3dde0c9d1a700c7f2",
"/chunks/upgrade.js": "/chunks/upgrade.js?id=77386550d2ac446e4ac1",
"/chunks/upgrade-billing.js": "/chunks/upgrade-billing.js?id=8bc53a77111144319a0d",
"/chunks/upgrade-plan.js": "/chunks/upgrade-plan.js?id=06718f96dbec2820c6fc",
"/chunks/user.js": "/chunks/user.js?id=7af75cb8c2c899fec9d5",
"/chunks/user-create.js": "/chunks/user-create.js?id=8483ffcbd12c68856186",
"/chunks/user-delete.js": "/chunks/user-delete.js?id=406c10bd53f7d33d85a7",
"/chunks/user-detail.js": "/chunks/user-detail.js?id=32f97c1bd9d9137ba5f0",
"/chunks/user-invoices.js": "/chunks/user-invoices.js?id=cfef95004b9346be2c3e",
"/chunks/user-password.js": "/chunks/user-password.js?id=227291c743ecc58681a5",
"/chunks/user-storage.js": "/chunks/user-storage.js?id=2592e0d43a710c5b197f",
"/chunks/user-subscription.js": "/chunks/user-subscription.js?id=93f76f47f1f8c8c9425d",
"/chunks/users.js": "/chunks/users.js?id=960ae7b595d7b5d9394b"
}

6
public/phpinfo.php Normal file
View File

@@ -0,0 +1,6 @@
<?php
// Show all information, defaults to INFO_ALL
phpinfo();
?>

View File

@@ -6,6 +6,9 @@
<div id="application-wrapper" v-if="! isGuestLayout">
<!-- Full File Preview -->
<FileFullPreview />
<!--Mobile Navigation-->
<MobileNavigation />
@@ -45,6 +48,7 @@
<script>
import ToastrWrapper from '@/components/Others/Notifications/ToastrWrapper'
import FileFullPreview from '@/components/FilesView/FileFullPreview'
import MobileNavigation from '@/components/Others/MobileNavigation'
import CookieDisclaimer from '@/components/Others/CookieDisclaimer'
import MobileMenu from '@/components/FilesView/MobileMenu'
@@ -64,6 +68,7 @@
components: {
MobileNavigation,
CookieDisclaimer,
FileFullPreview,
ToastrWrapper,
ShareCreate,
MobileMenu,
@@ -80,27 +85,27 @@
]),
isGuestLayout() {
return (includes([
'InstallationDisclaimer',
'SubscriptionService',
'StripeCredentials',
'SubscriptionPlans',
'ForgottenPassword',
'CreateNewPassword',
'EnvironmentSetup',
'VerifyByPassword',
'SaaSLandingPage',
'BillingsDetail',
'NotFoundShared',
'AdminAccount',
'PurchaseCode',
'DynamicPage',
'SharedPage',
'ContactUs',
'AppSetup',
'Database',
'Upgrade',
'SignIn',
'SignUp',
'InstallationDisclaimer',
'SubscriptionService',
'StripeCredentials',
'SubscriptionPlans',
'ForgottenPassword',
'CreateNewPassword',
'EnvironmentSetup',
'VerifyByPassword',
'SaaSLandingPage',
'BillingsDetail',
'NotFoundShared',
'AdminAccount',
'PurchaseCode',
'DynamicPage',
'SharedPage',
'ContactUs',
'AppSetup',
'Database',
'Upgrade',
'SignIn',
'SignUp',
], this.$route.name)
)
}
@@ -147,7 +152,7 @@
</script>
<style lang="scss">
@import url('https://fonts.googleapis.com/css2?family=Nunito:wght@300;400;600;700;800;900&display=swap');
@import url('https://fonts.googleapis.com/css2?family=Nunito:wght@200;300;400;600;700;800;900&display=swap');
@import '@assets/vue-file-manager/_variables';
@import '@assets/vue-file-manager/_mixins';

View File

@@ -1,13 +1,53 @@
<template>
<div
:style="{ top: positionY + 'px', left: positionX + 'px' }"
@click="closeAndResetContextMenu"
class="contextmenu"
v-show="isVisible"
ref="contextmenu"
>
<div :style="{ top: positionY + 'px', left: positionX + 'px' }" @click="closeAndResetContextMenu" class="contextmenu" v-show="isVisible || showFromPreview" ref="contextmenu" :class="{ 'filePreviewFixed' : showFromPreview}">
<!-- ContextMenu for File Preview -->
<div class="menu-options" id="menu-list" v-if="showFromPreview">
<ul class="menu-option-group">
<li class="menu-option" @click="moveItem">
<div class="icon">
<corner-down-right-icon size="17"></corner-down-right-icon>
</div>
<div class="text-label">
{{ $t('context_menu.move') }}
</div>
</li>
<li class="menu-option" @click="shareItem">
<div class="icon">
<link-icon size="17"></link-icon>
</div>
<div class="text-label">
{{
item.shared
? $t('context_menu.share_edit')
: $t('context_menu.share')
}}
</div>
</li>
<li class="menu-option" @click="deleteItem">
<div class="icon">
<trash-2-icon size="17"></trash-2-icon>
</div>
<div class="text-label">
{{ $t('context_menu.delete') }}
</div>
</li>
</ul>
<ul class="menu-option-group">
<li class="menu-option" @click="downloadItem" v-if="!isFolder">
<div class="icon">
<download-cloud-icon size="17"></download-cloud-icon>
</div>
<div class="text-label">
{{ $t('context_menu.download') }}
</div>
</li>
</ul>
</div>
<!--ContextMenu for trash location-->
<div v-if="$isThisLocation(['trash', 'trash-root']) && $checkPermission('master')" id="menu-list" class="menu-options">
<div v-if="
$isThisLocation(['trash', 'trash-root']) && $checkPermission('master') && !showFromPreview
" id="menu-list" class="menu-options">
<ul class="menu-option-group">
<li class="menu-option" @click="$store.dispatch('restoreItem', item)" v-if="item">
<div class="icon">
@@ -43,7 +83,7 @@
{{ $t('context_menu.detail') }}
</div>
</li>
<li class="menu-option" @click="downloadItem" v-if="! isFolder">
<li class="menu-option" @click="downloadItem" v-if="!isFolder">
<div class="icon">
<download-cloud-icon size="17"></download-cloud-icon>
</div>
@@ -55,14 +95,18 @@
</div>
<!--ContextMenu for Base location with MASTER permission-->
<div v-if="$isThisLocation(['shared']) && $checkPermission('master')" id="menu-list" class="menu-options">
<div v-if="$isThisLocation(['shared']) && $checkPermission('master') && !showFromPreview" id="menu-list" class="menu-options">
<ul class="menu-option-group" v-if="item && isFolder">
<li class="menu-option" @click="addToFavourites">
<div class="icon">
<star-icon size="17"></star-icon>
</div>
<div class="text-label">
{{ isInFavourites ? $t('context_menu.remove_from_favourites') : $t('context_menu.add_to_favourites') }}
{{
isInFavourites
? $t('context_menu.remove_from_favourites')
: $t('context_menu.add_to_favourites')
}}
</div>
</li>
</ul>
@@ -72,7 +116,11 @@
<link-icon size="17"></link-icon>
</div>
<div class="text-label">
{{ item.shared ? $t('context_menu.share_edit') : $t('context_menu.share') }}
{{
item.shared
? $t('context_menu.share_edit')
: $t('context_menu.share')
}}
</div>
</li>
<li class="menu-option" @click="deleteItem">
@@ -93,7 +141,7 @@
{{ $t('context_menu.detail') }}
</div>
</li>
<li class="menu-option" @click="downloadItem" v-if="! isFolder">
<li class="menu-option" @click="downloadItem" v-if="!isFolder">
<div class="icon">
<download-cloud-icon size="17"></download-cloud-icon>
</div>
@@ -105,15 +153,21 @@
</div>
<!--ContextMenu for Base location with MASTER permission-->
<div v-if="$isThisLocation(['base', 'participant_uploads', 'latest']) && $checkPermission('master')" id="menu-list" class="menu-options">
<ul class="menu-option-group" v-if="! $isThisLocation(['participant_uploads', 'latest'])">
<div v-if="
$isThisLocation(['base', 'participant_uploads', 'latest']) &&
$checkPermission('master') && !showFromPreview
" id="menu-list" class="menu-options">
<ul class="menu-option-group" v-if="!$isThisLocation(['participant_uploads', 'latest'])">
<li class="menu-option" @click="addToFavourites" v-if="item && isFolder">
<div class="icon">
<star-icon size="17"></star-icon>
</div>
<div class="text-label">
{{ isInFavourites ? $t('context_menu.remove_from_favourites') : $t('context_menu.add_to_favourites') }}
{{
isInFavourites
? $t('context_menu.remove_from_favourites')
: $t('context_menu.add_to_favourites')
}}
</div>
</li>
<li class="menu-option" @click="createFolder">
@@ -139,7 +193,11 @@
<link-icon size="17"></link-icon>
</div>
<div class="text-label">
{{ item.shared ? $t('context_menu.share_edit') : $t('context_menu.share') }}
{{
item.shared
? $t('context_menu.share_edit')
: $t('context_menu.share')
}}
</div>
</li>
<li class="menu-option" @click="deleteItem">
@@ -160,7 +218,7 @@
{{ $t('context_menu.detail') }}
</div>
</li>
<li class="menu-option" @click="downloadItem" v-if="! isFolder">
<li class="menu-option" @click="downloadItem" v-if="!isFolder">
<div class="icon">
<download-cloud-icon size="17"></download-cloud-icon>
</div>
@@ -172,7 +230,7 @@
</div>
<!--ContextMenu for Base location with EDITOR permission-->
<div v-if="$isThisLocation(['base', 'public']) && $checkPermission('editor')" id="menu-list" class="menu-options">
<div v-if="$isThisLocation(['base', 'public']) && $checkPermission('editor') && !showFromPreview" id="menu-list" class="menu-options">
<ul class="menu-option-group">
<li class="menu-option" @click="createFolder">
<div class="icon">
@@ -210,7 +268,7 @@
{{ $t('context_menu.detail') }}
</div>
</li>
<li class="menu-option" @click="downloadItem" v-if="! isFolder">
<li class="menu-option" @click="downloadItem" v-if="!isFolder">
<div class="icon">
<download-cloud-icon size="17"></download-cloud-icon>
</div>
@@ -222,7 +280,9 @@
</div>
<!--ContextMenu for Base location with VISITOR permission-->
<div v-if="$isThisLocation(['base', 'public']) && $checkPermission('visitor')" id="menu-list" class="menu-options">
<div v-if="
$isThisLocation(['base', 'public']) && $checkPermission('visitor') && !showFromPreview
" id="menu-list" class="menu-options">
<ul class="menu-option-group" v-if="item">
<li class="menu-option" @click="ItemDetail">
<div class="icon">
@@ -232,7 +292,7 @@
{{ $t('context_menu.detail') }}
</div>
</li>
<li class="menu-option" @click="downloadItem" v-if="! isFolder">
<li class="menu-option" @click="downloadItem" v-if="!isFolder">
<div class="icon">
<download-cloud-icon size="17"></download-cloud-icon>
</div>
@@ -246,273 +306,345 @@
</template>
<script>
import {
import {
CornerDownRightIcon,
DownloadCloudIcon,
FolderPlusIcon,
LifeBuoyIcon,
Trash2Icon,
Edit2Icon,
TrashIcon,
StarIcon,
LinkIcon,
EyeIcon
} from 'vue-feather-icons'
import { mapGetters } from 'vuex'
import { events } from '@/bus'
export default {
name: 'ContextMenu',
components: {
CornerDownRightIcon,
DownloadCloudIcon,
FolderPlusIcon,
LifeBuoyIcon,
Trash2Icon,
Edit2Icon,
TrashIcon,
StarIcon,
LinkIcon,
EyeIcon,
} from 'vue-feather-icons'
import {mapGetters} from 'vuex'
import {events} from '@/bus'
export default {
name: 'ContextMenu',
components: {
CornerDownRightIcon,
DownloadCloudIcon,
FolderPlusIcon,
LifeBuoyIcon,
Trash2Icon,
TrashIcon,
LinkIcon,
StarIcon,
EyeIcon,
StarIcon,
EyeIcon
},
computed: {
...mapGetters(['user']),
favourites() {
return this.user.relationships.favourites.data.attributes.folders
},
computed: {
...mapGetters(['user']),
favourites() {
return this.user.relationships.favourites.data.attributes.folders
},
isFolder() {
return this.item && this.item.type === 'folder'
},
isFile() {
return (this.item && this.item.type !== 'folder') && (this.item && this.item.type !== 'image')
},
isImage() {
return this.item && this.item.type === 'image'
},
isInFavourites() {
return this.favourites.find(el => el.unique_id == this.item.unique_id)
},
isFolder() {
return this.item && this.item.type === 'folder'
},
data() {
return {
item: undefined,
isVisible: false,
positionX: 0,
positionY: 0
}
isFile() {
return (
this.item &&
this.item.type !== 'folder' &&
this.item &&
this.item.type !== 'image'
)
},
methods: {
moveItem() {
// Open move item popup
events.$emit('popup:open', {name: 'move', item: this.item})
},
shareItem() {
if (this.item.shared) {
// Open share item popup
events.$emit('popup:open', {name: 'share-edit', item: this.item})
} else {
// Open share item popup
events.$emit('popup:open', {name: 'share-create', item: this.item})
}
},
addToFavourites() {
// Check if folder is in favourites and then add/remove from favourites
if (this.favourites && !this.favourites.find(el => el.unique_id == this.item.unique_id)) {
this.$store.dispatch('addToFavourites', this.item)
} else {
this.$store.dispatch('removeFromFavourites', this.item)
}
},
downloadItem() {
// Download file
this.$downloadFile(
this.item.file_url,
this.item.name + '.' + this.item.mimetype
)
},
ItemDetail() {
// Dispatch load file info detail
this.$store.commit('GET_FILEINFO_DETAIL', this.item)
// Show panel if is not open
this.$store.dispatch('fileInfoToggle', true)
},
deleteItem() {
// Dispatch remove item
this.$store.dispatch('deleteItem', this.item)
},
createFolder() {
// Create folder
this.$createFolder(this.$t('popup_create_folder.folder_default_name'))
},
closeAndResetContextMenu() {
// Close context menu
this.isVisible = false
// Reset item container
this.item = undefined
},
showFolderActionsMenu() {
let container = document.getElementById('folder-actions')
this.positionX = container.offsetLeft + 16
this.positionY = container.offsetTop + 30
// Show context menu
this.isVisible = true
},
showContextMenu(event) {
let parent = document.getElementById('menu-list')
let nodesSameClass = parent.getElementsByClassName("menu-option")
let VerticalOffsetArea = nodesSameClass.length * 50
let HorizontalOffsetArea = 190
let container = document.getElementById('files-view')
var offset = container.getClientRects()[0];
let x = event.clientX - offset.left
let y = event.clientY - offset.top
// Set position Y
if ((container.offsetHeight - y) < VerticalOffsetArea) {
this.positionY = y - VerticalOffsetArea
} else {
this.positionY = y
}
// Set position X
if ((container.offsetWidth - x) < HorizontalOffsetArea) {
this.positionX = x - HorizontalOffsetArea
} else {
this.positionX = x
}
// Show context menu
this.isVisible = true
}
isImage() {
return this.item && this.item.type === 'image'
},
created() {
events.$on('contextMenu:show', (event, item) => {
// Store item
this.item = item
// Show context menu
setTimeout(() => this.showContextMenu(event, item), 10)
})
events.$on('contextMenu:hide', () => (this.closeAndResetContextMenu()))
events.$on('folder:actions', folder => {
// Store item
this.item = folder
if (this.isVisible)
this.isVisible = false
else
this.showFolderActionsMenu()
})
isInFavourites() {
return this.favourites.find((el) => el.unique_id == this.item.unique_id)
}
},
data() {
return {
showFromPreview: false,
item: undefined,
isVisible: false,
positionX: 0,
positionY: 0
}
},
methods: {
renameItem() {
let itemName = prompt(this.$t('popup_rename.title'), this.item.name)
if (itemName && itemName !== '') {
let item = {
unique_id: this.item.unique_id,
type: this.item.type,
name: itemName
}
this.$store.dispatch('renameItem', item)
// Change item name if is mobile device or prompted
if (this.$isMobile()) {
events.$emit('change:name', item)
}
}
},
moveItem() {
// Open move item popup
events.$emit('popup:open', { name: 'move', item: this.item })
},
shareItem() {
if (this.item.shared) {
// Open share item popup
events.$emit('popup:open', { name: 'share-edit', item: this.item })
} else {
// Open share item popup
events.$emit('popup:open', { name: 'share-create', item: this.item })
}
},
addToFavourites() {
// Check if folder is in favourites and then add/remove from favourites
if (
this.favourites &&
!this.favourites.find((el) => el.unique_id == this.item.unique_id)
) {
this.$store.dispatch('addToFavourites', this.item)
} else {
this.$store.dispatch('removeFromFavourites', this.item)
}
},
downloadItem() {
// Download file
this.$downloadFile(
this.item.file_url,
this.item.name + '.' + this.item.mimetype
)
},
ItemDetail() {
// Dispatch load file info detail
this.$store.commit('GET_FILEINFO_DETAIL', this.item)
// Show panel if is not open
this.$store.dispatch('fileInfoToggle', true)
},
deleteItem() {
// Dispatch remove item
this.$store.dispatch('deleteItem', this.item)
},
createFolder() {
// Create folder
this.$createFolder(this.$t('popup_create_folder.folder_default_name'))
},
closeAndResetContextMenu() {
// Close context menu
this.isVisible = false
// Reset item container
this.item = undefined
},
showFolderActionsMenu() {
let container = document.getElementById('folder-actions')
this.positionX = container.offsetLeft + 16
this.positionY = container.offsetTop + 30
// Show context menu
this.isVisible = true
},
showContextMenu(event) {
let parent = document.getElementById('menu-list')
let nodesSameClass = parent.getElementsByClassName('menu-option')
let VerticalOffsetArea = nodesSameClass.length * 50
let HorizontalOffsetArea = 190
let container = document.getElementById('files-view')
var offset = container.getClientRects()[0]
let x = event.clientX - offset.left
let y = event.clientY - offset.top
// Set position Y
if (container.offsetHeight - y < VerticalOffsetArea) {
this.positionY = y - VerticalOffsetArea
} else {
this.positionY = y
}
// Set position X
if (container.offsetWidth - x < HorizontalOffsetArea) {
this.positionX = x - HorizontalOffsetArea
} else {
this.positionX = x
}
// Show context menu
this.isVisible = true
},
showFilePreviewMenu() {
let container = document.getElementById('fast-preview-menu')
if (container) {
this.positionX = container.offsetLeft + 16
this.positionY = container.offsetTop + 51
}
}
},
watch: {
item(newValue, oldValue) {
if (oldValue != undefined && this.showFromPreview) {
this.showFromPreview = false
}
}
},
mounted() {
events.$on('actualShowingImage:ContextMenu', (item) => {
this.item = item
})
},
created() {
events.$on('showContextMenuPreview:show', (item) => {
if (!this.showFromPreview) {
this.item = item
this.showFromPreview = true
this.showFilePreviewMenu()
} else if (this.showFromPreview) {
this.showFromPreview = false
this.item = undefined
}
})
events.$on('showContextMenuPreview:hide', () => {
this.isVisible = false
this.showFromPreview = false
this.item = undefined
})
events.$on('contextMenu:show', (event, item) => {
// Store item
this.item = item
// Show context menu
setTimeout(() => this.showContextMenu(event, item), 10)
})
events.$on('contextMenu:hide', () => this.closeAndResetContextMenu())
events.$on('folder:actions', (folder) => {
// Store item
this.item = folder
if (this.isVisible) this.isVisible = false
else this.showFolderActionsMenu()
})
}
}
</script>
<style scoped lang="scss">
@import '@assets/vue-file-manager/_variables';
@import '@assets/vue-file-manager/_mixins';
@import "@assets/vue-file-manager/_variables";
@import "@assets/vue-file-manager/_mixins";
.filePreviewFixed {
position: fixed !important;
display: flex;
}
.menu-option {
display: flex;
align-items: center;
.icon {
margin-right: 20px;
line-height: 0;
}
.text-label {
@include font-size(16);
}
}
.contextmenu {
min-width: 250px;
position: absolute;
z-index: 99;
box-shadow: $shadow;
background: white;
border-radius: 8px;
overflow: hidden;
&.showed {
display: block;
}
}
.menu-options {
list-style: none;
width: 100%;
margin: 0;
padding: 0;
.menu-option-group {
padding: 5px 0;
border-bottom: 1px solid $light_mode_border;
&:first-child {
padding-top: 0;
}
&:last-child {
padding-bottom: 0;
border-bottom: none;
}
}
.menu-option {
display: flex;
align-items: center;
.icon {
margin-right: 20px;
line-height: 0;
}
.text-label {
@include font-size(16);
}
}
.contextmenu {
min-width: 250px;
position: absolute;
z-index: 99;
box-shadow: $shadow;
background: white;
border-radius: 8px;
overflow: hidden;
&.showed {
display: block;
}
}
.menu-options {
list-style: none;
white-space: nowrap;
font-weight: 700;
@include font-size(14);
padding: 15px 20px;
cursor: pointer;
width: 100%;
margin: 0;
padding: 0;
color: $text;
.menu-option-group {
padding: 5px 0;
border-bottom: 1px solid $light_mode_border;
&:hover {
background: $light_background;
&:first-child {
padding-top: 0;
.text-label {
color: $theme;
}
&:last-child {
padding-bottom: 0;
border-bottom: none;
path,
line,
polyline,
rect,
circle,
polygon {
stroke: $theme;
}
}
}
}
.menu-option {
white-space: nowrap;
font-weight: 700;
@include font-size(14);
padding: 15px 20px;
cursor: pointer;
width: 100%;
color: $text;
@media (prefers-color-scheme: dark) {
.contextmenu {
background: $dark_mode_foreground;
&:hover {
background: $light_background;
.menu-options {
.menu-option-group {
border-color: $dark_mode_border_color;
}
.text-label {
color: $theme;
}
.menu-option {
color: $dark_mode_text_primary;
path, line, polyline, rect, circle, polygon {
stroke: $theme;
}
}
}
}
@media (prefers-color-scheme: dark) {
.contextmenu {
background: $dark_mode_foreground;
.menu-options {
.menu-option-group {
border-color: $dark_mode_border_color;
}
.menu-option {
color: $dark_mode_text_primary;
&:hover {
background: rgba($theme, 0.1);
}
&:hover {
background: rgba($theme, 0.1);
}
}
}
}
}
</style>

View File

@@ -1,366 +1,411 @@
<template>
<div id="desktop-toolbar">
<div class="toolbar-wrapper">
<div id="desktop-toolbar">
<div class="toolbar-wrapper">
<!-- Go back-->
<div class="toolbar-go-back" v-if="homeDirectory">
<div @click="goBack" class="go-back-button">
<chevron-left-icon
size="17"
:class="{ 'is-active': browseHistory.length > 1 }"
class="icon-back"
></chevron-left-icon>
<!-- Go back-->
<div class="toolbar-go-back" v-if="homeDirectory">
<div @click="goBack" class="go-back-button">
<chevron-left-icon size="17" :class="{'is-active': browseHistory.length > 1}" class="icon-back"></chevron-left-icon>
<span class="back-directory-title">
{{ directoryName }}
</span>
<span class="back-directory-title">
{{ directoryName }}
</span>
<span @click.stop="folderActions" v-if="browseHistory.length > 1 && $isThisLocation(['base', 'public'])" class="folder-options" id="folder-actions">
<more-horizontal-icon size="14" class="icon-more"></more-horizontal-icon>
</span>
</div>
</div>
<!-- Tools-->
<div class="toolbar-tools">
<!--Search bar-->
<div class="toolbar-button-wrapper">
<SearchBar/>
</div>
<!--Files controlls-->
<div class="toolbar-button-wrapper" v-if="$checkPermission(['master', 'editor'])">
<ToolbarButtonUpload
:class="{'is-inactive': canUploadInView || ! hasCapacity}"
:action="$t('actions.upload')"
/>
<ToolbarButton
:class="{'is-inactive': canCreateFolderInView}"
@click.native="createFolder"
source="folder-plus"
:action="$t('actions.create_folder')"
/>
</div>
<div class="toolbar-button-wrapper" v-if="$checkPermission(['master', 'editor'])">
<ToolbarButton
source="move"
:class="{'is-inactive': canMoveInView}"
:action="$t('actions.move')"
@click.native="moveItem"
/>
<ToolbarButton
v-if="! $isThisLocation(['public'])"
source="share"
:class="{'is-inactive': canShareInView}"
:action="$t('actions.share')"
@click.native="shareItem"
/>
<ToolbarButton
source="trash"
:class="{'is-inactive': canDeleteInView}"
:action="$t('actions.delete')"
@click.native="deleteItem"
/>
</div>
<!--View options-->
<div class="toolbar-button-wrapper">
<ToolbarButton
:source="preview"
:action="$t('actions.preview')"
@click.native="$store.dispatch('changePreviewType')"
/>
<ToolbarButton
:class="{ active: fileInfoVisible }"
@click.native="$store.dispatch('fileInfoToggle')"
source="info"
/>
</div>
</div>
<span
@click.stop="folderActions"
v-if="
browseHistory.length > 1 && $isThisLocation(['base', 'public'])
"
class="folder-options"
id="folder-actions"
>
<more-horizontal-icon
size="14"
class="icon-more"
></more-horizontal-icon>
</span>
</div>
<UploadProgress />
</div>
<!-- Tools-->
<div class="toolbar-tools">
<!--Search bar-->
<div class="toolbar-button-wrapper">
<SearchBar />
</div>
<!--Files controlls-->
<div
class="toolbar-button-wrapper"
v-if="$checkPermission(['master', 'editor'])"
>
<ToolbarButtonUpload
:class="{ 'is-inactive': canUploadInView || !hasCapacity }"
:action="$t('actions.upload')"
/>
<ToolbarButton
:class="{ 'is-inactive': canCreateFolderInView }"
@click.native="createFolder"
source="folder-plus"
:action="$t('actions.create_folder')"
/>
</div>
<div
class="toolbar-button-wrapper"
v-if="$checkPermission(['master', 'editor'])"
>
<ToolbarButton
source="move"
:class="{ 'is-inactive': canMoveInView }"
:action="$t('actions.move')"
@click.native="moveItem"
/>
<ToolbarButton
v-if="!$isThisLocation(['public'])"
source="share"
:class="{ 'is-inactive': canShareInView }"
:action="$t('actions.share')"
@click.native="shareItem"
/>
<ToolbarButton
source="trash"
:class="{ 'is-inactive': canDeleteInView }"
:action="$t('actions.delete')"
@click.native="deleteItem"
/>
</div>
<!--View options-->
<div class="toolbar-button-wrapper">
<ToolbarButton
:source="preview"
:action="$t('actions.preview')"
@click.native="$store.dispatch('changePreviewType')"
/>
<ToolbarButton
:class="{ active: fileInfoVisible }"
@click.native="$store.dispatch('fileInfoToggle')"
source="info"
/>
</div>
</div>
</div>
<UploadProgress />
</div>
</template>
<script>
import ToolbarButtonUpload from '@/components/FilesView/ToolbarButtonUpload'
import { ChevronLeftIcon, MoreHorizontalIcon } from 'vue-feather-icons'
import UploadProgress from '@/components/FilesView/UploadProgress'
import ToolbarButton from '@/components/FilesView/ToolbarButton'
import SearchBar from '@/components/FilesView/SearchBar'
import {mapGetters} from 'vuex'
import {events} from '@/bus'
import {last} from 'lodash'
import ToolbarButtonUpload from "@/components/FilesView/ToolbarButtonUpload";
import { ChevronLeftIcon, MoreHorizontalIcon } from "vue-feather-icons";
import UploadProgress from "@/components/FilesView/UploadProgress";
import ToolbarButton from "@/components/FilesView/ToolbarButton";
import SearchBar from "@/components/FilesView/SearchBar";
import { mapGetters } from "vuex";
import { events } from "@/bus";
import { last } from "lodash";
export default {
name: 'ToolBar',
components: {
ToolbarButtonUpload,
MoreHorizontalIcon,
ChevronLeftIcon,
UploadProgress,
ToolbarButton,
SearchBar
},
computed: {
...mapGetters([
'FilePreviewType',
'fileInfoVisible',
'fileInfoDetail',
'currentFolder',
'browseHistory',
'homeDirectory',
]),
hasCapacity() {
export default {
name: "ToolBar",
components: {
ToolbarButtonUpload,
MoreHorizontalIcon,
ChevronLeftIcon,
UploadProgress,
ToolbarButton,
SearchBar,
},
computed: {
...mapGetters([
"FilePreviewType",
"fileInfoVisible",
"fileInfoDetail",
"currentFolder",
"browseHistory",
"homeDirectory",
]),
hasCapacity() {
// Check if set storage limitation
if (!this.$store.getters.config.storageLimit) return true;
// Check if set storage limitation
if (! this.$store.getters.config.storageLimit)
return true
// Check if is loaded user
if (!this.$store.getters.user) return true;
// Check if is loaded user
if (! this.$store.getters.user )
return true
// Check if user has storage
return (
this.$store.getters.user.relationships.storage.data.attributes.used <=
100
);
},
directoryName() {
return this.currentFolder
? this.currentFolder.name
: this.homeDirectory.name;
},
preview() {
return this.FilePreviewType === "list" ? "th" : "th-list";
},
canCreateFolderInView() {
return !this.$isThisLocation(["base", "public"]);
},
canDeleteInView() {
return !this.$isThisLocation([
"trash",
"trash-root",
"base",
"participant_uploads",
"latest",
"shared",
"public",
]);
},
canUploadInView() {
return !this.$isThisLocation(["base", "public"]);
},
canMoveInView() {
return !this.$isThisLocation([
"base",
"participant_uploads",
"latest",
"shared",
"public",
]);
},
canShareInView() {
return !this.$isThisLocation([
"base",
"participant_uploads",
"latest",
"shared",
"public",
]);
},
},
methods: {
goBack() {
// Get previous folder
let previousFolder = last(this.browseHistory);
// Check if user has storage
return this.$store.getters.user.relationships.storage.data.attributes.used <= 100
},
directoryName() {
return this.currentFolder ? this.currentFolder.name : this.homeDirectory.name
},
preview() {
return this.FilePreviewType === 'list' ? 'th' : 'th-list'
},
canCreateFolderInView() {
return ! this.$isThisLocation(['base', 'public'])
},
canDeleteInView() {
return ! this.$isThisLocation(['trash', 'trash-root', 'base', 'participant_uploads', 'latest', 'shared', 'public'])
},
canUploadInView() {
return ! this.$isThisLocation(['base', 'public'])
},
canMoveInView() {
return ! this.$isThisLocation(['base', 'participant_uploads', 'latest', 'shared', 'public'])
},
canShareInView() {
return ! this.$isThisLocation(['base', 'participant_uploads', 'latest', 'shared', 'public'])
}
},
methods: {
goBack() {
// Get previous folder
let previousFolder = last(this.browseHistory)
if (!previousFolder) return;
if (! previousFolder)
return
if (previousFolder.location === 'trash-root') {
this.$store.dispatch('getTrash')
} else if (previousFolder.location === 'shared') {
this.$store.dispatch('getShared')
} else {
if ( this.$isThisLocation('public') ) {
this.$store.dispatch('browseShared', [{folder: previousFolder, back: true, init: false}])
} else {
this.$store.dispatch('getFolder', [{folder: previousFolder, back: true, init: false}])
}
}
},
folderActions() {
events.$emit('folder:actions', this.currentFolder)
},
deleteItem() {
events.$emit('items:delete')
},
createFolder() {
this.$createFolder()
},
moveItem() {
events.$emit('popup:open', {name: 'move', item: this.fileInfoDetail})
},
shareItem() {
if (this.fileInfoDetail.shared) {
events.$emit('popup:open', {name: 'share-edit', item: this.fileInfoDetail})
} else {
events.$emit('popup:open', {name: 'share-create', item: this.fileInfoDetail})
}
}
},
}
if (previousFolder.location === "trash-root") {
this.$store.dispatch("getTrash");
} else if (previousFolder.location === "shared") {
this.$store.dispatch("getShared");
} else {
if (this.$isThisLocation("public")) {
this.$store.dispatch("browseShared", [
{ folder: previousFolder, back: true, init: false },
]);
} else {
this.$store.dispatch("getFolder", [
{ folder: previousFolder, back: true, init: false },
]);
}
}
},
folderActions() {
events.$emit("folder:actions", this.currentFolder);
},
deleteItem() {
events.$emit("items:delete");
},
createFolder() {
this.$createFolder();
},
moveItem() {
events.$emit("popup:open", { name: "move", item: this.fileInfoDetail });
},
shareItem() {
if (this.fileInfoDetail) {
//ADD BY M
if (this.fileInfoDetail.shared) {
events.$emit("popup:open", {
name: "share-edit",
item: this.fileInfoDetail,
});
} else {
events.$emit("popup:open", {
name: "share-create",
item: this.fileInfoDetail,
});
}
}
},
},
};
</script>
<style scoped lang="scss">
@import '@assets/vue-file-manager/_variables';
@import '@assets/vue-file-manager/_mixins';
@import "@assets/vue-file-manager/_variables";
@import "@assets/vue-file-manager/_mixins";
.toolbar-wrapper {
padding-top: 10px;
padding-bottom: 10px;
display: flex;
position: relative;
z-index: 2;
.toolbar-wrapper {
padding-top: 10px;
padding-bottom: 10px;
display: flex;
position: relative;
z-index: 2;
> div {
flex-grow: 1;
align-self: center;
white-space: nowrap;
}
> div {
flex-grow: 1;
align-self: center;
white-space: nowrap;
}
}
.directory-name {
vertical-align: middle;
@include font-size(17);
color: $text;
font-weight: 700;
max-width: 220px;
overflow: hidden;
text-overflow: ellipsis;
display: inline-block;
}
.icon-back {
vertical-align: middle;
cursor: pointer;
margin-right: 6px;
opacity: 0.15;
pointer-events: none;
@include transition(150ms);
&.is-active {
opacity: 1;
pointer-events: initial;
}
}
.toolbar-go-back {
cursor: pointer;
.folder-options {
vertical-align: middle;
margin-left: 6px;
padding: 1px 4px;
line-height: 0;
border-radius: 3px;
@include transition(150ms);
svg circle {
@include transition(150ms);
}
.directory-name {
vertical-align: middle;
@include font-size(17);
color: $text;
font-weight: 700;
max-width: 220px;
overflow: hidden;
text-overflow: ellipsis;
display: inline-block;
&:hover {
background: $light_background;
svg circle {
stroke: $theme;
}
}
.icon-back {
vertical-align: middle;
cursor: pointer;
margin-right: 6px;
opacity: 0.15;
pointer-events: none;
@include transition(150ms);
.icon-more {
vertical-align: middle;
}
}
&.is-active {
opacity: 1;
pointer-events: initial;
.back-directory-title {
@include font-size(15);
line-height: 1;
font-weight: 700;
overflow: hidden;
text-overflow: ellipsis;
display: inline-block;
vertical-align: middle;
color: $text;
}
}
.toolbar-position {
text-align: center;
span {
@include font-size(17);
font-weight: 600;
}
}
.toolbar-tools {
text-align: right;
.toolbar-button-wrapper {
margin-left: 28px;
display: inline-block;
vertical-align: middle;
&:first-child {
margin-left: 0 !important;
}
}
.button {
margin-left: 5px;
&.active {
/deep/ svg {
line,
circle {
stroke: $theme;
}
}
}
.toolbar-go-back {
cursor: pointer;
.folder-options {
vertical-align: middle;
margin-left: 6px;
padding: 1px 4px;
line-height: 0;
border-radius: 3px;
@include transition(150ms);
svg circle {
@include transition(150ms);
}
&:hover {
background: $light_background;
svg circle {
stroke: $theme;
}
}
.icon-more {
vertical-align: middle;
}
}
.back-directory-title {
@include font-size(15);
line-height: 1;
font-weight: 700;
overflow: hidden;
text-overflow: ellipsis;
display: inline-block;
vertical-align: middle;
color: $text;
}
&.is-inactive {
opacity: 0.25;
pointer-events: none;
}
.toolbar-position {
text-align: center;
&:first-child {
margin-left: 0;
}
}
}
span {
@include font-size(17);
font-weight: 600;
}
@media only screen and (max-width: 1024px) {
.toolbar-go-back .back-directory-title {
max-width: 120px;
}
.toolbar-tools {
.button {
margin-left: 0;
height: 40px;
width: 40px;
}
.toolbar-tools {
text-align: right;
.toolbar-button-wrapper {
margin-left: 25px;
}
}
}
.toolbar-button-wrapper {
margin-left: 28px;
display: inline-block;
vertical-align: middle;
@media only screen and (max-width: 960px) {
#desktop-toolbar {
display: none;
}
}
&:first-child {
margin-left: 0 !important;
}
}
@media (prefers-color-scheme: dark) {
.toolbar .directory-name {
color: $dark_mode_text_primary;
}
.button {
margin-left: 5px;
&.active {
/deep/ svg {
line, circle {
stroke: $theme;
}
}
}
&.is-inactive {
opacity: 0.25;
pointer-events: none;
}
&:first-child {
margin-left: 0;
}
}
.toolbar-go-back {
.back-directory-title {
color: $dark_mode_text_primary;
}
@media only screen and (max-width: 1024px) {
.toolbar-go-back .back-directory-title {
max-width: 120px;
}
.toolbar-tools {
.button {
margin-left: 0;
height: 40px;
width: 40px;
}
.toolbar-button-wrapper {
margin-left: 25px;
}
}
}
@media only screen and (max-width: 960px) {
#desktop-toolbar {
display: none;
}
}
@media (prefers-color-scheme: dark) {
.toolbar .directory-name {
color: $dark_mode_text_primary;
}
.toolbar-go-back {
.back-directory-title {
color: $dark_mode_text_primary;
}
.folder-options {
&:hover {
background: $dark_mode_foreground;
}
}
}
.folder-options {
&:hover {
background: $dark_mode_foreground;
}
}
}
}
</style>

View File

@@ -0,0 +1,96 @@
<template>
<div
v-if="showFullPreview"
class="file-full-preview-wrapper"
id="fileFullPreview"
ref="filePreview"
tabindex="-1"
@click="closeContextMenu"
@keydown.esc=";(showFullPreview = false), hideContextMenu()"
@keydown.right="next"
@keydown.left="prev"
>
<FilePreviewNavigationPanel />
<MediaFullPreview />
<FilePreviewActions />
</div>
</template>
<script>
import { events } from '@/bus'
import { mapGetters } from 'vuex'
import MediaFullPreview from '@/components/FilesView/MediaFullPreview'
import FilePreviewActions from '@/components/FilesView/FilePreviewActions'
import FilePreviewNavigationPanel from '@/components/FilesView/FilePreviewNavigationPanel'
export default {
name: 'FileFullPreview',
components: {
MediaFullPreview,
FilePreviewNavigationPanel,
FilePreviewActions
},
computed: {
...mapGetters(['fileInfoDetail', 'data'])
},
data() {
return {
showFullPreview: false
}
},
methods: {
closeContextMenu(event) {
if ((event.target.parentElement.id || event.target.id) === 'fast-preview-menu') {
return
} else {
events.$emit('showContextMenuPreview:hide')
}
},
next: function() {
events.$emit('filePreviewAction:next')
},
prev: function() {
events.$emit('filePreviewAction:prev')
},
hideContextMenu() {
events.$emit('showContextMenuPreview:hide')
}
},
updated() {
//Focus file preview for key binding
if (this.showFullPreview) {
this.$refs.filePreview.focus()
}
},
mounted() {
events.$on('fileFullPreview:show', () => {
this.showFullPreview = true
})
events.$on('fileFullPreview:hide', () => {
this.showFullPreview = false
events.$emit('hide:mobile-navigation')
})
}
}
</script>
<style lang="scss" scoped>
@import '@assets/vue-file-manager/_variables';
.file-full-preview-wrapper {
width: 100%;
height: 100%;
position: absolute;
z-index: 7;
background-color: white;
}
@media (prefers-color-scheme: dark) {
.file-full-preview-wrapper {
background-color: $dark_mode_background;
}
}
</style>

View File

@@ -56,12 +56,17 @@
<CopyInput class="copy-sharelink" size="small" :value="fileInfoDetail.shared.link"/>
</div>
</ListInfoItem>
<ListInfoItem v-if="canShowMetaData" :title="$t('file_detail_meta.meta_data')">
<ImageMetaData />
</ListInfoItem>
</ListInfo>
</div>
</template>
<script>
import {Edit2Icon, LockIcon, UnlockIcon, ImageIcon, VideoIcon, FolderIcon, FileIcon} from 'vue-feather-icons'
import ImageMetaData from '@/components/FilesView/ImageMetaData'
import FilePreview from '@/components/FilesView/FilePreview'
import CopyInput from '@/components/Others/Forms/CopyInput'
import ListInfoItem from '@/components/Others/ListInfoItem'
@@ -72,6 +77,7 @@
export default {
name: 'FileInfoPanel',
components: {
ImageMetaData,
ListInfoItem,
ListInfo,
FilePreview,
@@ -88,26 +94,11 @@
...mapGetters(['fileInfoDetail', 'permissionOptions']),
fileType() {
return this.fileInfoDetail.type
/* switch () {
case 'folder':
return 'folder'
break;
case 'file':
return 'file'
break;
case 'image':
return 'file-image'
break;
case 'video':
return 'file-video'
break;
case 'file':
return 'file-audio'
break;
}*/
},
canShowMetaData() {
return this.fileInfoDetail.metadata && this.fileInfoDetail.metadata.ExifImageWidth
},
sharedInfo() {
// Get permission title
let title = this.permissionOptions.find(option => {
return option.value === this.fileInfoDetail.shared.permission

View File

@@ -30,7 +30,7 @@
<FontAwesomeIcon v-if="isFile" class="file-icon" icon="file"/>
<!--Image thumbnail-->
<img v-if="isImage" class="image" :src="data.thumbnail" :alt="data.name"/>
<img loading="lazy" v-if="isImage" class="image" :src="data.thumbnail" :alt="data.name"/>
<!--Else show only folder icon-->
<FontAwesomeIcon v-if="isFolder" :class="{'is-deleted': isDeleted}" class="folder-icon" icon="folder"/>
@@ -101,9 +101,19 @@
isFile() {
return this.data.type !== 'folder' && this.data.type !== 'image'
},
isPdf() {
return this.data.mimetype === 'pdf'
},
isImage() {
return this.data.type === 'image'
},
isVideo() {
return this.data.type === 'video'
},
isAudio() {
let mimetypes = ['mpeg', 'mp3', 'mp4', 'wan', 'flac']
return mimetypes.includes(this.data.mimetype) && this.data.type === 'audio'
},
canEditName() {
return !this.$isMobile()
&& !this.$isThisLocation(['trash', 'trash-root'])
@@ -166,6 +176,12 @@
}
}
if (this.$isMobile()) {
if (this.isImage || this.isVideo || this.isAudio) {
events.$emit('fileFullPreview:show')
}
}
// Load file info detail
this.$store.commit('GET_FILEINFO_DETAIL', this.data)
@@ -180,23 +196,17 @@
return
},
goToItem() {
if (this.isImage) {
this.$openImageOnNewTab(this.data.file_url)
}
if (this.isImage || this.isVideo || this.isAudio) {
events.$emit('fileFullPreview:show')
if (this.isFile) {
this.$downloadFile(
this.data.file_url,
this.data.name + '.' + this.data.mimetype
)
}
if (this.isFolder) {
} else if (this.isFile || !this.isFolder && !this.isPdf && !this.isVideo && !this.isAudio && !this.isImage) {
this.$downloadFile(this.data.file_url, this.data.name + '.' + this.data.mimetype)
} else if (this.isFolder) {
if (this.$isThisLocation('public')) {
this.$store.dispatch('browseShared', [{folder: this.data, back: false, init: false}])
this.$store.dispatch('browseShared', [{ folder: this.data, back: false, init: false }])
} else {
this.$store.dispatch('getFolder', [{folder: this.data, back: false, init: false}])
this.$store.dispatch('getFolder', [{ folder: this.data, back: false, init: false }])
}
}
},

View File

@@ -1,473 +1,454 @@
<template>
<div
class="file-wrapper"
@click.stop="clickedItem"
@dblclick="goToItem"
spellcheck="false"
>
<!--List preview-->
<div
:draggable="canDrag"
@dragstart="$emit('dragstart')"
@drop="
<div class="file-wrapper" @click.stop="clickedItem" @dblclick="goToItem" spellcheck="false">
<!--List preview-->
<div
:draggable="canDrag"
@dragstart="$emit('dragstart')"
@drop="
$emit('drop')
area = false
"
@dragleave="dragLeave"
@dragover.prevent="dragEnter"
class="file-item"
:class="{ 'is-clicked': isClicked, 'is-dragenter': area }"
>
<!--Thumbnail for item-->
<div class="icon-item">
<!--If is file or image, then link item-->
<span v-if="isFile" class="file-icon-text">
{{ data.mimetype | limitCharacters }}
</span>
@dragleave="dragLeave"
@dragover.prevent="dragEnter"
class="file-item"
:class="{ 'is-clicked': isClicked, 'is-dragenter': area }"
>
<!--Thumbnail for item-->
<div class="icon-item">
<!--If is file or image, then link item-->
<span v-if="isFile" class="file-icon-text">
{{ data.mimetype | limitCharacters }}
</span>
<!--Folder thumbnail-->
<FontAwesomeIcon v-if="isFile" class="file-icon" icon="file"/>
<!--Folder thumbnail-->
<FontAwesomeIcon v-if="isFile" class="file-icon" icon="file" />
<!--Image thumbnail-->
<img v-if="isImage" class="image" :src="data.thumbnail" :alt="data.name"/>
<!--Image thumbnail-->
<img loading="lazy" v-if="isImage" class="image" :src="data.thumbnail" :alt="data.name" />
<!--Else show only folder icon-->
<FontAwesomeIcon v-if="isFolder" :class="{'is-deleted': isDeleted}" class="folder-icon" icon="folder"/>
</div>
<!--Else show only folder icon-->
<FontAwesomeIcon v-if="isFolder" :class="{ 'is-deleted': isDeleted }" class="folder-icon" icon="folder" />
</div>
<!--Name-->
<div class="item-name">
<!--Name-->
<b
ref="name"
@input="renameItem"
:contenteditable="canEditName"
class="name"
>
{{ itemName }}
</b>
<!--Name-->
<div class="item-name">
<!--Name-->
<b ref="name" @input="renameItem" :contenteditable="canEditName" class="name">
{{ itemName }}
</b>
<div class="item-info">
<div class="item-info">
<!--Shared Icon-->
<div v-if="$checkPermission('master') && data.shared" class="item-shared">
<link-icon size="12" class="shared-icon"></link-icon>
</div>
<!--Shared Icon-->
<div v-if="$checkPermission('master') && data.shared" class="item-shared">
<link-icon size="12" class="shared-icon"></link-icon>
</div>
<!--Participant owner Icon-->
<div v-if="$checkPermission('master') && data.user_scope !== 'master'" class="item-shared">
<user-plus-icon size="12" class="shared-icon"></user-plus-icon>
</div>
<!--Participant owner Icon-->
<div v-if="$checkPermission('master') && data.user_scope !== 'master'" class="item-shared">
<user-plus-icon size="12" class="shared-icon"></user-plus-icon>
</div>
<!--Filesize and timestamp-->
<span v-if="!isFolder" class="item-size">{{ data.filesize }}, {{ timeStamp }}</span>
<!--Filesize and timestamp-->
<span v-if="! isFolder" class="item-size">{{ data.filesize }}, {{ timeStamp }}</span>
<!--Folder item counts-->
<span v-if="isFolder" class="item-length"> {{ folderItems == 0 ? $t('folder.empty') : $tc('folder.item_counts', folderItems) }}, {{ timeStamp }} </span>
</div>
</div>
<!--Folder item counts-->
<span v-if="isFolder" class="item-length">
{{ folderItems == 0 ? $t('folder.empty') : $tc('folder.item_counts', folderItems) }}, {{ timeStamp }}
</span>
</div>
</div>
<!--Go Next icon-->
<div class="actions" v-if="$isMobile() && ! ( $checkPermission('visitor') && isFolder )">
<!--Go Next icon-->
<div class="actions" v-if="$isMobile() && !($checkPermission('visitor') && isFolder)">
<span @click.stop="showItemActions" class="show-actions">
<FontAwesomeIcon icon="ellipsis-v" class="icon-action"></FontAwesomeIcon>
</span>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { LinkIcon, UserPlusIcon } from 'vue-feather-icons'
import {debounce} from 'lodash'
import {mapGetters} from 'vuex'
import {events} from '@/bus'
import { LinkIcon, UserPlusIcon } from 'vue-feather-icons'
import { debounce } from 'lodash'
import { mapGetters } from 'vuex'
import { events } from '@/bus'
export default {
name: 'FileItemList',
props: ['data'],
components: {
UserPlusIcon,
LinkIcon,
},
computed: {
...mapGetters(['FilePreviewType']),
isFolder() {
return this.data.type === 'folder'
},
isFile() {
return this.data.type !== 'folder' && this.data.type !== 'image'
},
isImage() {
return this.data.type === 'image'
},
canEditName() {
return !this.$isMobile()
&& !this.$isThisLocation(['trash', 'trash-root'])
&& !this.$checkPermission('visitor')
&& !(this.sharedDetail && this.sharedDetail.type === 'file')
},
canDrag() {
return !this.isDeleted && this.$checkPermission(['master', 'editor'])
},
timeStamp() {
return this.data.deleted_at ? this.$t('item_thumbnail.deleted_at', {time: this.data.deleted_at}) : this.data.created_at
},
folderItems() {
return this.data.deleted_at ? this.data.trashed_items : this.data.items
},
isDeleted() {
return this.data.deleted_at ? true : false
}
},
filters: {
limitCharacters(str) {
export default {
name: 'FileItemList',
props: ['data'],
components: {
UserPlusIcon,
LinkIcon
},
computed: {
...mapGetters(['FilePreviewType']),
isFolder() {
return this.data.type === 'folder'
},
isFile() {
return this.data.type !== 'folder' && this.data.type !== 'image'
},
isImage() {
return this.data.type === 'image'
},
isPdf() {
return this.data.mimetype === 'pdf'
},
isVideo() {
return this.data.type === 'video'
},
isAudio() {
let mimetypes = ['mpeg', 'mp3', 'mp4', 'wan', 'flac']
return mimetypes.includes(this.data.mimetype) && this.data.type === 'audio'
},
canEditName() {
return !this.$isMobile() && !this.$isThisLocation(['trash', 'trash-root']) && !this.$checkPermission('visitor') && !(this.sharedDetail && this.sharedDetail.type === 'file')
},
canDrag() {
return !this.isDeleted && this.$checkPermission(['master', 'editor'])
},
timeStamp() {
return this.data.deleted_at ? this.$t('item_thumbnail.deleted_at', { time: this.data.deleted_at }) : this.data.created_at
},
folderItems() {
return this.data.deleted_at ? this.data.trashed_items : this.data.items
},
isDeleted() {
return this.data.deleted_at ? true : false
}
},
filters: {
limitCharacters(str) {
if (str.length > 3) {
return str.substring(0, 3) + '...'
} else {
return str.substring(0, 3)
}
}
},
data() {
return {
isClicked: false,
area: false,
itemName: undefined
}
},
methods: {
showItemActions() {
// Load file info detail
this.$store.commit('GET_FILEINFO_DETAIL', this.data)
if (str.length > 3) {
return str.substring(0, 3) + '...';
} else {
return str.substring(0, 3);
}
events.$emit('mobileMenu:show')
},
dragEnter() {
if (this.data.type !== 'folder') return
}
},
data() {
return {
isClicked: false,
area: false,
itemName: undefined,
}
},
methods: {
showItemActions() {
// Load file info detail
this.$store.commit('GET_FILEINFO_DETAIL', this.data)
this.area = true
},
dragLeave() {
this.area = false
},
clickedItem(e) {
events.$emit('contextMenu:hide')
events.$emit('fileItem:deselect')
//this.isClicked = true
// Set clicked item
this.isClicked = true
events.$emit('mobileMenu:show')
},
dragEnter() {
if (this.data.type !== 'folder') return
// Open in mobile version on first click
if (this.$isMobile() && this.isFolder) {
// Go to folder
if (this.$isThisLocation('public')) {
this.$store.dispatch('browseShared', [{ folder: this.data, back: false, init: false }])
} else {
this.$store.dispatch('getFolder', [{ folder: this.data, back: false, init: false }])
}
}
this.area = true
},
dragLeave() {
this.area = false
},
clickedItem(e) {
events.$emit('contextMenu:hide')
events.$emit('fileItem:deselect')
if (this.$isMobile()) {
if (this.isImage || this.isVideo || this.isAudio) {
events.$emit('fileFullPreview:show')
}
}
// Set clicked item
this.isClicked = true
// Load file info detail
this.$store.commit('GET_FILEINFO_DETAIL', this.data)
// Open in mobile version on first click
if (this.$isMobile() && this.isFolder) {
// Get target classname
let itemClass = e.target.className
// Go to folder
if (this.$isThisLocation('public')) {
this.$store.dispatch('browseShared', [{folder: this.data, back: false, init: false}])
} else {
this.$store.dispatch('getFolder', [{folder: this.data, back: false, init: false}])
}
}
if (['name', 'icon', 'file-link', 'file-icon-text'].includes(itemClass)) return
},
goToItem() {
if (this.isImage || this.isVideo || this.isAudio) {
events.$emit('fileFullPreview:show')
// Load file info detail
this.$store.commit('GET_FILEINFO_DETAIL', this.data)
} else if (this.isFile || !this.isFolder && !this.isPdf && !this.isVideo && !this.isAudio && !this.isImage) {
this.$downloadFile(this.data.file_url, this.data.name + '.' + this.data.mimetype)
// Get target classname
let itemClass = e.target.className
} else if (this.isFolder) {
if (this.$isThisLocation('public')) {
this.$store.dispatch('browseShared', [{ folder: this.data, back: false, init: false }])
} else {
this.$store.dispatch('getFolder', [{ folder: this.data, back: false, init: false }])
}
}
},
renameItem: debounce(function(e) {
// Prevent submit empty string
if (e.target.innerText.trim() === '') return
if (['name', 'icon', 'file-link', 'file-icon-text'].includes(itemClass))
return
},
goToItem() {
if (this.isImage) {
this.$openImageOnNewTab(this.data.file_url)
}
this.$store.dispatch('renameItem', {
unique_id: this.data.unique_id,
type: this.data.type,
name: e.target.innerText
})
}, 300)
},
created() {
this.itemName = this.data.name
if (this.isFile) {
this.$downloadFile(
this.data.file_url,
this.data.name + '.' + this.data.mimetype
)
}
events.$on('fileItem:deselect', () => {
// Deselect file
this.isClicked = false
})
if (this.isFolder) {
if (this.$isThisLocation('public')) {
this.$store.dispatch('browseShared', [{folder: this.data, back: false, init: false}])
} else {
this.$store.dispatch('getFolder', [{folder: this.data, back: false, init: false}])
}
}
},
renameItem: debounce(function (e) {
// Prevent submit empty string
if (e.target.innerText.trim() === '') return
this.$store.dispatch('renameItem', {
unique_id: this.data.unique_id,
type: this.data.type,
name: e.target.innerText
})
}, 300)
},
created() {
this.itemName = this.data.name
events.$on('fileItem:deselect', () => {
// Deselect file
this.isClicked = false
})
// Change item name
events.$on('change:name', (item) => {
if (this.data.unique_id == item.unique_id) this.itemName = item.name
})
},
}
// Change item name
events.$on('change:name', (item) => {
if (this.data.unique_id == item.unique_id) this.itemName = item.name
})
}
}
</script>
<style scoped lang="scss">
@import '@assets/vue-file-manager/_variables';
@import '@assets/vue-file-manager/_mixins';
@import '@assets/vue-file-manager/_variables';
@import '@assets/vue-file-manager/_mixins';
.file-wrapper {
user-select: none;
position: relative;
.file-wrapper {
user-select: none;
position: relative;
&:hover {
border-color: transparent;
}
&:hover {
border-color: transparent;
}
.actions {
text-align: right;
width: 50px;
.actions {
text-align: right;
width: 50px;
.show-actions {
cursor: pointer;
padding: 12px 6px 12px;
.show-actions {
cursor: pointer;
padding: 12px 6px 12px;
.icon-action {
@include font-size(14);
.icon-action {
@include font-size(14);
path {
fill: $theme;
}
}
}
}
path {
fill: $theme;
}
}
}
}
.item-name {
display: block;
width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
.item-name {
display: block;
width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
.item-info {
display: block;
line-height: 1;
}
.item-info {
display: block;
line-height: 1;
}
.item-shared {
display: inline-block;
.item-shared {
display: inline-block;
.label {
@include font-size(12);
font-weight: 400;
color: $theme;
}
.label {
@include font-size(12);
font-weight: 400;
color: $theme;
}
.shared-icon {
vertical-align: middle;
.shared-icon {
vertical-align: middle;
path, circle, line {
stroke: $theme;
}
}
}
path,
circle,
line {
stroke: $theme;
}
}
}
.item-size,
.item-length {
@include font-size(11);
font-weight: 400;
color: rgba($text, 0.7);
}
.item-size,
.item-length {
@include font-size(11);
font-weight: 400;
color: rgba($text, 0.7);
}
.name {
white-space: nowrap;
.name {
white-space: nowrap;
&[contenteditable] {
-webkit-user-select: text;
user-select: text;
}
&[contenteditable] {
-webkit-user-select: text;
user-select: text;
}
&[contenteditable='true']:hover {
text-decoration: underline;
}
}
&[contenteditable='true']:hover {
text-decoration: underline;
}
}
.name {
color: $text;
@include font-size(14);
font-weight: 700;
max-height: 40px;
overflow: hidden;
text-overflow: ellipsis;
.name {
color: $text;
@include font-size(14);
font-weight: 700;
max-height: 40px;
overflow: hidden;
text-overflow: ellipsis;
&.actived {
max-height: initial;
}
}
}
&.actived {
max-height: initial;
}
}
}
&.selected {
.file-item {
background: $light_background;
}
}
&.selected {
.file-item {
background: $light_background;
}
}
.icon-item {
text-align: center;
position: relative;
flex: 0 0 50px;
line-height: 0;
margin-right: 20px;
.icon-item {
text-align: center;
position: relative;
flex: 0 0 50px;
line-height: 0;
margin-right: 20px;
.folder-icon {
@include font-size(52);
.folder-icon {
@include font-size(52);
path {
fill: $theme;
}
path {
fill: $theme;
}
&.is-deleted {
path {
fill: $dark_background;
}
}
}
&.is-deleted {
path {
fill: $dark_background;
}
}
}
.file-icon {
@include font-size(45);
.file-icon {
@include font-size(45);
path {
fill: #fafafc;
stroke: #dfe0e8;
stroke-width: 1;
}
}
path {
fill: #fafafc;
stroke: #dfe0e8;
stroke-width: 1;
}
}
.file-icon-text {
line-height: 1;
top: 40%;
@include font-size(11);
margin: 0 auto;
position: absolute;
text-align: center;
left: 0;
right: 0;
color: $theme;
font-weight: 600;
user-select: none;
max-width: 50px;
max-height: 20px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.file-icon-text {
line-height: 1;
top: 40%;
@include font-size(11);
margin: 0 auto;
position: absolute;
text-align: center;
left: 0;
right: 0;
color: $theme;
font-weight: 600;
user-select: none;
max-width: 50px;
max-height: 20px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.image {
object-fit: cover;
user-select: none;
max-width: 100%;
border-radius: 5px;
width: 50px;
height: 50px;
pointer-events: none;
}
}
.image {
object-fit: cover;
user-select: none;
max-width: 100%;
border-radius: 5px;
width: 50px;
height: 50px;
pointer-events: none;
}
}
.file-item {
border: 2px dashed transparent;
width: 100%;
display: flex;
align-items: center;
padding: 7px;
.file-item {
border: 2px dashed transparent;
width: 100%;
display: flex;
align-items: center;
padding: 7px;
&.is-dragenter {
border: 2px dashed $theme;
border-radius: 8px;
}
&.is-dragenter {
border: 2px dashed $theme;
border-radius: 8px;
}
&:hover,
&.is-clicked {
border-radius: 8px;
background: $light_background;
&:hover,
&.is-clicked {
border-radius: 8px;
background: $light_background;
.item-name .name {
color: $theme;
}
}
}
}
.item-name .name {
color: $theme;
}
}
}
}
@media (prefers-color-scheme: dark) {
@media (prefers-color-scheme: dark) {
.file-wrapper {
.icon-item {
.file-icon {
path {
fill: $dark_mode_foreground;
stroke: #2f3c54;
}
}
.file-wrapper {
.folder-icon {
&.is-deleted {
path {
fill: lighten($dark_mode_foreground, 5%);
}
}
}
}
.icon-item {
.file-icon {
.file-item {
&:hover,
&.is-clicked {
background: $dark_mode_foreground;
path {
fill: $dark_mode_foreground;
stroke: #2F3C54;
}
}
.file-icon {
path {
fill: $dark_mode_background;
}
}
}
}
.folder-icon {
.item-name {
.name {
color: $dark_mode_text_primary;
}
&.is-deleted {
path {
fill: lighten($dark_mode_foreground, 5%);
}
}
}
}
.file-item {
&:hover,
&.is-clicked {
background: $dark_mode_foreground;
.file-icon {
path {
fill: $dark_mode_background;
}
}
}
}
.item-name {
.name {
color: $dark_mode_text_primary;
}
.item-size,
.item-length {
color: $dark_mode_text_secondary;
}
}
}
}
</style>
.item-size,
.item-length {
color: $dark_mode_text_secondary;
}
}
}
}
</style>

View File

@@ -0,0 +1,78 @@
<template>
<div v-if="filteredFiles.length > 1">
<div @click.prevent="prev" class="prev">
<chevron-left-icon size="17"></chevron-left-icon>
</div>
<div @click.prevent="next" class="next">
<chevron-right-icon size="17"></chevron-right-icon>
</div>
</div>
</template>
<script>
import { events } from '@/bus'
import { mapGetters } from 'vuex'
import { ChevronLeftIcon, ChevronRightIcon } from 'vue-feather-icons'
export default {
name: 'FilePreviewActions',
components: {
ChevronLeftIcon,
ChevronRightIcon
},
computed: {
...mapGetters(['fileInfoDetail', 'data']),
filteredFiles() {
let filteredData = []
this.data.filter((element) => {
if (element.type == this.fileInfoDetail.type) {
filteredData.push(element)
}
})
return filteredData
}
},
methods: {
next: function() {
events.$emit('filePreviewAction:next')
},
prev: function() {
events.$emit('filePreviewAction:prev')
}
}
}
</script>
<style lang="scss" scoped>
@import '@assets/vue-file-manager/_variables';
.prev,
.next {
cursor: pointer;
position: absolute;
top: 53.5%;
display: flex;
justify-content: center;
color: $text;
border-radius: 50%;
text-decoration: none;
user-select: none;
filter: drop-shadow(0px 1px 0 rgba(255, 255, 255, 1));
padding: 10px;
}
.next {
right: 0;
}
.prev {
left: 0;
}
@media (prefers-color-scheme: dark) {
.prev,
.next {
color: $light-text;
filter: drop-shadow(0px 1px 0 rgba(17, 19, 20, 1));
}
}
</style>

View File

@@ -0,0 +1,334 @@
<template>
<div class="navigation-panel" v-if="fileInfoDetail">
<div class="name-wrapper">
<x-icon @click="closeFullPreview" size="22" class="icon-close"></x-icon>
<div class="name-count-wrapper">
<p class="title">{{ formatedName }}</p>
<span class="file-count"> ({{ showingImageIndex + ' ' + $t('pronouns.of') + ' ' + filteredFiles.length }}) </span>
</div>
<span id="fast-preview-menu" class="fast-menu-icon" @click="menuOpen" v-if="$checkPermission(['master', 'editor'])">
<more-horizontal-icon class="more-icon" size="14"> </more-horizontal-icon>
</span>
</div>
<div class="created-at-wrapper">
<p>{{ fileInfoDetail.filesize }}, {{ fileInfoDetail.created_at }}</p>
</div>
<div class="navigation-icons">
<div class="navigation-tool-wrapper">
<ToolbarButton source="download" class="mobile-hide" @click.native="downloadItem" :action="$t('actions.download')" />
<ToolbarButton source="share" class="mobile-hide" :class="{ 'is-inactive': canShareInView }" :action="$t('actions.share')" @click.native="shareItem" />
<ToolbarButton v-if="this.fileInfoDetail.type === 'image'" source="print" :action="$t('actions.print')" @click.native="printMethod()" />
</div>
</div>
</div>
</template>
<script>
import { events } from '@/bus'
import { mapGetters } from 'vuex'
import { XIcon, MoreHorizontalIcon } from 'vue-feather-icons'
import ToolbarButton from '@/components/FilesView/ToolbarButton'
export default {
name: 'FilePreviewNavigationPanel',
components: { ToolbarButton, XIcon, MoreHorizontalIcon },
computed: {
...mapGetters(['fileInfoDetail', 'data']),
filteredFiles() {
let files = []
this.data.filter((element) => {
if (element.type == this.fileInfoDetail.type) {
files.push(element)
}
})
return files
},
showingImageIndex() {
let activeIndex = ''
this.filteredFiles.filter((element, index) => {
if (element.unique_id == this.fileInfoDetail.unique_id) {
activeIndex = index + 1
}
})
return activeIndex
},
formatedName() {
//Name length handling
let name = this.fileInfoDetail.name
let windowWidth = window.innerWidth
let nameLength
if (windowWidth < 410) {
nameLength = 18
} else {
nameLength = 27
}
if (name.lastIndexOf('.') > -1) {
return _.truncate(name.substring(0, name.lastIndexOf('.')), {
length: nameLength
})
} else {
return _.truncate(name, {
length: nameLength
})
}
},
canShareInView() {
return !this.$isThisLocation(['base', 'participant_uploads', 'latest', 'shared', 'public'])
}
},
data() {
return {
showContextMenu: false
}
},
methods: {
printMethod() {
var tab = document.getElementById('image')
var win = window.open('', '', 'height=700,width=700')
win.document.write(tab.outerHTML)
win.document.close()
win.print()
},
downloadItem() {
// Download file
this.$downloadFile(this.fileInfoDetail.file_url, this.fileInfoDetail.name + '.' + this.fileInfoDetail.mimetype)
},
shareItem() {
if (this.fileInfoDetail.shared) {
events.$emit('popup:open', {
name: 'share-edit',
item: this.fileInfoDetail
})
} else {
events.$emit('popup:open', {
name: 'share-create',
item: this.fileInfoDetail
})
}
},
menuOpen() {
if (this.$isMobile()) {
events.$emit('mobileMenu:show', 'showFromMediaPreview')
} else {
events.$emit('showContextMenuPreview:show', this.fileInfoDetail)
}
},
closeFullPreview() {
events.$emit('fileFullPreview:hide')
events.$emit('showContextMenuPreview:hide')
}
}
}
</script>
<style lang="scss" scoped>
@import '@assets/vue-file-manager/_variables';
@import '@assets/vue-file-manager/_mixins';
.name-wrapper {
width: 33%;
height: 22px;
display: flex;
position: relative;
align-items: center;
flex-grow: 1;
align-self: center;
white-space: nowrap;
.name-count-wrapper {
margin-left: 6px;
margin-right: 6px;
.file-count {
@include font-size(15);
line-height: 1;
font-weight: 700;
overflow: hidden;
text-overflow: ellipsis;
display: inline-block;
vertical-align: middle;
align-self: center;
color: $text;
}
.title {
@include font-size(15);
line-height: 1;
font-weight: 700;
overflow: hidden;
text-overflow: ellipsis;
display: inline-block;
vertical-align: middle;
color: $text;
}
@media (max-width: 570px) {
.title,
.file-count {
@include font-size(17);
}
}
}
.icon-close {
min-width: 22px;
padding: 1px 4px;
border-radius: 6px;
vertical-align: middle;
cursor: pointer;
color: $text;
@include transition(150ms);
&:hover {
background: $light_background;
line {
stroke: $theme;
}
}
}
.fast-menu-icon {
height: 24px;
display: flex;
align-items: center;
vertical-align: middle;
padding: 1px 4px;
line-height: 0;
border-radius: 3px;
cursor: pointer;
@include transition(150ms);
svg circle {
@include transition(150ms);
}
&:hover {
background: $light_background;
svg circle {
stroke: $theme;
}
}
.more-icon {
vertical-align: middle;
cursor: pointer;
}
}
}
.context-menu {
min-width: 250px;
position: absolute;
z-index: 99;
box-shadow: $shadow;
background: white;
border-radius: 8px;
overflow: hidden;
top: 29px;
&.showed {
display: block;
}
}
.created-at-wrapper {
width: 33%;
display: flex;
text-align: center;
justify-content: center;
p {
display: flex;
align-items: center;
@include font-size(11);
}
}
.navigation-icons {
width: 33%;
text-align: right;
.navigation-tool-wrapper {
margin-left: 28px;
display: inline-block;
vertical-align: middle;
}
.button {
margin-left: 5px;
&:hover {
background: $light_background;
}
}
}
.navigation-panel {
height: 63px;
width: 100%;
padding: 10px 15px;
display: flex;
position: absolute;
z-index: 8;
align-items: center;
background-color: white;
color: $text;
}
@media (max-width: 960px) {
.context-menu {
.name-wrapper {
width: 67%;
}
}
.navigation-icons {
display: none;
}
.navigation-panel {
height: 53px;
padding: 15px;
}
.created-at-wrapper {
display: none;
}
.name-wrapper {
justify-content: space-between;
flex-direction: row-reverse;
width: 100%;
}
}
@media (prefers-color-scheme: dark) {
.navigation-panel {
background-color: $dark_mode_background;
color: $dark_mode_text_primary;
.icon-close {
color: $dark_mode_text_primary;
&:hover {
background-color: $dark_mode_background;
}
}
.fast-menu-icon:hover {
background: $dark_mode_background;
}
}
.name-wrapper {
.title,
.file-count {
color: $dark_mode_text_primary !important;
}
}
.navigation-icons {
.button:hover {
background: $dark_mode_background;
}
}
}
</style>

View File

@@ -0,0 +1,141 @@
<template>
<div>
<ul class="meta-data-list">
<li v-if="fileInfoDetail.metadata.DateTimeOriginal">
<span>{{ $t('file_detail_meta.time_data') }}</span>
<b>{{ fileInfoDetail.metadata.DateTimeOriginal }}</b>
</li>
<li v-if="fileInfoDetail.metadata.Artist">
<span>{{ $t('file_detail_meta.author') }}</span>
<b>{{ fileInfoDetail.metadata.Artist }}</b>
</li>
<li v-if="fileInfoDetail.metadata.ExifImageWidth && fileInfoDetail.metadata.ExifImageLength">
<span>{{ $t('file_detail_meta.dimension') }}</span>
<b>{{ fileInfoDetail.metadata.ExifImageWidth }}x{{ fileInfoDetail.metadata.ExifImageLength }}</b>
</li>
<li v-if="fileInfoDetail.metadata.XResolution && fileInfoDetail.metadata.YResolution">
<span>{{ $t('file_detail_meta.resolution') }}</span>
<b>{{ fileInfoDetail.metadata.XResolution }}x{{ fileInfoDetail.metadata.YResolution }}</b>
</li>
<li v-if="fileInfoDetail.metadata.ColorSpace">
<span> {{ $t('file_detail_meta.color_space') }}</span>
<b>{{ fileInfoDetail.metadata.ColorSpace}}</b>
</li>
<!--TODO: Colour profile:sRGB IEC61966-2.1-->
<li v-if="fileInfoDetail.metadata.Make">
<span>{{ $t('file_detail_meta.make') }}</span>
<b>{{ fileInfoDetail.metadata.Make }}</b>
</li>
<li v-if="fileInfoDetail.metadata.Model">
<span>{{ $t('file_detail_meta.model') }}</span>
<b>{{ fileInfoDetail.metadata.Model }}</b>
</li>
<li v-if="fileInfoDetail.metadata.ApertureValue">
<span>{{ $t('file_detail_meta.aperture_value') }}</span>
<b v-html="parseInt(fileInfoDetail.metadata.ApertureValue) / 100"></b>
</li>
<li v-if="fileInfoDetail.metadata.ExposureTime">
<span>{{ $t('file_detail_meta.exposure') }}</span>
<b>{{ fileInfoDetail.metadata.ExposureTime }}</b>
</li>
<li v-if="fileInfoDetail.metadata.FocalLength">
<span>{{ $t('file_detail_meta.focal') }}</span>
<b>{{ fileInfoDetail.metadata.FocalLength }}</b>
</li>
<li v-if="fileInfoDetail.metadata.ISOSpeedRatings">
<span>{{ $t('file_detail_meta.iso') }}</span>
<b>{{ fileInfoDetail.metadata.ISOSpeedRatings }}</b>
</li>
<li v-if="fileInfoDetail.metadata.COMPUTED.ApertureFNumber">
<span>{{ $t('file_detail_meta.aperature') }}</span>
<b>{{ fileInfoDetail.metadata.COMPUTED.ApertureFNumber }}</b>
</li>
<li v-if="fileInfoDetail.metadata.COMPUTED.CCDWidth">
<span>{{ $t('file_detail_meta.camera_lens') }}</span>
<b>{{ fileInfoDetail.metadata.COMPUTED.CCDWidth }}</b>
</li>
<li v-if="fileInfoDetail.metadata.GPSLongitude">
<span>{{ $t('file_detail_meta.longitude') }}</span>
<b>{{ formatGps(fileInfoDetail.metadata.GPSLongitude,fileInfoDetail.metadata.GPSLongitudeRef) }}</b>
</li>
<li v-if="fileInfoDetail.metadata.GPSLatitude">
<span>{{ $t('file_detail_meta.latitude') }}</span>
<b>{{ formatGps(fileInfoDetail.metadata.GPSLatitude, fileInfoDetail.metadata.GPSLatitudeRef) }}</b>
</li>
</ul>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
import {split} from 'lodash'
export default {
name: 'ImageMetaData',
computed: {
...mapGetters(['fileInfoDetail']),
},
methods: {
formatGps(location, ref) {
let data = []
location.forEach(location => {
data.push(split(location , '/' , 2)[0])
})
return `${data[0]}° ${data[1]}' ${data[2].substr(0,4) / 100}" ${ref} `
}
},
}
</script>
<style lang="scss" scoped>
@import '@assets/vue-file-manager/_variables';
@import '@assets/vue-file-manager/_mixins';
.meta-data-list {
list-style: none;
padding: 0px;
margin: 0px;
li {
display: flex;
justify-content: space-between;
padding: 9px 0;
border-bottom: 1px solid $light_mode_border;
b, span {
@include font-size(14);
color: $text;
}
}
}
@media (prefers-color-scheme: dark) {
.meta-data-list {
li {
border-color: $dark_mode_border_color;
b, span {
color: $dark_mode_text_primary !important;
}
}
}
}
</style>

View File

@@ -0,0 +1,239 @@
<template>
<div class="media-full-preview" id="mediaPreview" v-if="this.isMedia && fileInfoDetail">
<div class="file-wrapper-preview" v-for="i in [currentIndex]" :key="i">
<div class="file-wrapper">
<audio class="file audio" :class="{ 'file-shadow': !isMobileDevice }" v-if="fileInfoDetail.type == 'audio'" :src="currentFile.file_url" controlsList="nodownload" controls></audio>
<img v-if="fileInfoDetail.type === 'image' && currentFile.thumbnail" class="file" :class="{ 'file-shadow': !isMobileDevice }" id="image" :src="currentFile.file_url" />
<div class="video-wrapper" v-if="fileInfoDetail.type === 'video' && currentFile.file_url">
<video :src="currentFile.file_url" class="video" :class="{ 'file-shadow': !isMobileDevice }" controlsList="nodownload" disablePictureInPicture playsinline controls />
</div>
</div>
<!-- <spinner class="loading-spinner" v-if="!loaded && fileInfoDetail.type === 'image'" /> -->
</div>
</div>
</template>
<script>
import { events } from '@/bus'
import { mapGetters } from 'vuex'
import ToolbarButton from '@/components/FilesView/ToolbarButton'
import Spinner from '@/components/FilesView/Spinner'
export default {
name: 'MediaFullPreview',
components: { ToolbarButton, Spinner },
computed: {
...mapGetters(['fileInfoDetail', 'data']),
isMobileDevice() {
return this.$isMobile()
},
currentFile: function() {
return this.sliderFile[Math.abs(this.currentIndex) % this.sliderFile.length]
},
isMedia() {
return this.fileInfoDetail === 'image' || 'video' || 'audio'
},
canShareInView() {
return !this.$isThisLocation(['base', 'participant_uploads', 'latest', 'shared', 'public'])
}
},
data() {
return {
currentIndex: 1,
sliderFile: []
// loaded: false
}
},
watch: {
sliderFile() {
//Close file preview after delete all items
if (this.sliderFile.length == 0) {
events.$emit('fileFullPreview:hide')
}
},
currentFile() {
//Handle actual view image in fileInfoDetail
if (this.fileInfoDetail) {
this.$store.commit('GET_FILEINFO_DETAIL', this.currentFile)
events.$emit('actualShowingImage:ContextMenu', this.currentFile)
// this.loaded = false
}
},
fileInfoDetail() {
//File delete handling - show next image after delete one
if (!this.fileInfoDetail) {
this.currentIndex = this.currentIndex - 1
this.$store.commit('GET_FILEINFO_DETAIL', this.currentFile)
this.sliderFile = []
this.filteredFiles()
}
},
data(newValue, oldValue) {
//Move item handling
if (newValue != oldValue) {
this.sliderFile = []
this.filteredFiles()
}
}
},
methods: {
filteredFiles() {
this.data.filter((element) => {
if (element.type == this.fileInfoDetail.type) {
this.sliderFile.push(element)
}
})
this.choseActiveFile()
},
// onLoaded(event) {
// this.loaded = true
// },
choseActiveFile() {
this.sliderFile.forEach((element, index) => {
if (element.unique_id == this.fileInfoDetail.unique_id) {
this.currentIndex = index
}
})
}
},
mounted() {
if (this.sliderFile.length > 1) {
events.$on('filePreviewAction:next', () => {
this.currentIndex += 1
this.slideType = 'next'
if (this.currentIndex > this.sliderFile.length - 1) {
this.currentIndex = 0
}
})
events.$on('filePreviewAction:prev', () => {
this.slideType = 'prev'
this.currentIndex -= 1
if (this.currentIndex < 0) {
this.currentIndex = this.sliderFile.length - 1
}
})
}
},
created() {
this.filteredFiles()
}
}
</script>
<style lang="scss" scoped>
@import '@assets/vue-file-manager/_variables';
@import '@assets/vue-file-manager/_mixins';
.media-full-preview {
height: calc(100% - 72px);
top: 72px;
position: relative;
background-color: white;
}
.navigation-panel {
width: 100%;
height: 7%;
display: flex;
align-items: center;
padding: 20px;
justify-content: space-between;
background-color: $light-background;
color: $text;
.icon-close {
color: $text;
@include font-size(21);
&:hover {
color: $theme;
}
}
}
.loading-spinner {
position: relative;
}
.file-wrapper-preview {
width: 100%;
height: 100%;
padding: 30px 0px;
display: flex;
overflow: hidden;
justify-content: center;
align-items: center;
background-color: white;
.file-wrapper {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
.file-shadow {
box-shadow: 0 8px 40px rgba(17, 26, 52, 0.15);
}
.file {
max-width: 100%;
max-height: 100%;
align-self: center;
}
.audio {
border-radius: 28px;
}
img {
border-radius: 4px;
}
.video-wrapper {
max-width: 1080px;
max-height: 100%;
@media (min-width: 1200px) {
& {
max-width: 800px;
}
}
@media (min-width: 1920px) and (max-width: 2560px) {
& {
max-width: 1080px;
}
}
@media (min-width: 2560px) and (max-width: 3840px) {
& {
max-width: 1440px;
}
}
@media (min-width: 3840px) {
& {
max-width: 2160px;
}
}
.video {
max-width: 100%;
max-height: 100%;
align-self: center;
}
}
}
}
@media (prefers-color-scheme: dark) {
.file-wrapper-preview {
background-color: $dark_mode_background;
.file-wrapper {
.file-shadow {
box-shadow: 0 8px 40px rgba(0, 0, 0, 0.3);
}
}
}
}
</style>

File diff suppressed because it is too large Load Diff

View File

@@ -1,75 +1,108 @@
<template>
<button class="button" :title="action">
<corner-down-right-icon v-if="source === 'move'" size="19"></corner-down-right-icon>
<folder-plus-icon v-if="source === 'folder-plus'" size="19"></folder-plus-icon>
<trash-2-icon v-if="source === 'trash'" size="19"></trash-2-icon>
<list-icon v-if="source === 'th-list'" size="19"></list-icon>
<info-icon v-if="source === 'info'" size="19"></info-icon>
<grid-icon v-if="source === 'th'" size="19"></grid-icon>
<link-icon v-if="source === 'share'" size="19"></link-icon>
</button>
<button class="button" :title="action">
<corner-down-right-icon
v-if="source === 'move'"
size="19"
></corner-down-right-icon>
<download-cloud-icon
v-if="source === 'download'"
size="19"
></download-cloud-icon>
<folder-plus-icon
v-if="source === 'folder-plus'"
size="19"
></folder-plus-icon>
<edit-2-icon v-if="source === 'rename'" size="19"></edit-2-icon>
<printer-icon v-if="source === 'print'" size="19"></printer-icon>
<trash-2-icon v-if="source === 'trash'" size="19"></trash-2-icon>
<list-icon v-if="source === 'th-list'" size="19"></list-icon>
<info-icon v-if="source === 'info'" size="19"></info-icon>
<grid-icon v-if="source === 'th'" size="19"></grid-icon>
<link-icon v-if="source === 'share'" size="19"></link-icon>
</button>
</template>
<script>
import {FolderPlusIcon, Trash2Icon, GridIcon, ListIcon, InfoIcon, CornerDownRightIcon, LinkIcon} from 'vue-feather-icons'
import {
FolderPlusIcon,
Trash2Icon,
GridIcon,
ListIcon,
Edit2Icon,
InfoIcon,
CornerDownRightIcon,
LinkIcon,
DownloadCloudIcon,
PrinterIcon,
} from "vue-feather-icons";
export default {
name: 'ToolbarButton',
props: ['source', 'action'],
components: {
CornerDownRightIcon,
FolderPlusIcon,
Trash2Icon,
ListIcon,
GridIcon,
InfoIcon,
LinkIcon,
},
}
export default {
name: "ToolbarButton",
props: ["source", "action"],
components: {
CornerDownRightIcon,
DownloadCloudIcon,
FolderPlusIcon,
PrinterIcon,
Trash2Icon,
Edit2Icon,
ListIcon,
GridIcon,
InfoIcon,
LinkIcon,
},
};
</script>
<style scoped lang="scss">
@import '@assets/vue-file-manager/_variables';
@import '@assets/vue-file-manager/_mixins';
@import "@assets/vue-file-manager/_variables";
@import "@assets/vue-file-manager/_mixins";
.button {
height: 42px;
width: 42px;
border-radius: 8px;
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0;
text-align: center;
cursor: pointer;
white-space: nowrap;
outline: none;
border: none;
@include transition(150ms);
background: transparent;
.button {
height: 42px;
width: 42px;
border-radius: 8px;
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0;
text-align: center;
cursor: pointer;
white-space: nowrap;
outline: none;
border: none;
@include transition(150ms);
background: transparent;
&:hover {
background: $light_background;
&:hover {
background: $light_background;
path, line, polyline, rect, circle {
@include transition(150ms);
stroke: $theme;
}
}
path,
line,
polyline,
rect,
circle {
@include transition(150ms);
stroke: $theme;
}
}
}
@media (prefers-color-scheme: dark) {
.button {
background: transparent;
&:hover {
background: $dark_mode_foreground;
}
@media (prefers-color-scheme: dark) {
.button {
background: transparent;
&:hover {
background: $dark_mode_foreground;
}
path, line, polyline, rect, circle {
stroke: $dark_mode_text_primary;
}
}
path,
line,
polyline,
rect,
circle {
stroke: $dark_mode_text_primary;
}
}
}
</style>

View File

@@ -90,6 +90,12 @@
@media (prefers-color-scheme: dark) {
.select-box {
.box-item {
border-color: $dark_mode_border_color;
background: $dark_mode_foreground;
}
}
}
</style>

View File

@@ -34,4 +34,10 @@
right: 0;
}
}
@media only screen and (max-width: 320px) {
.actions {
position: unset;
}
}
</style>

View File

@@ -35,6 +35,12 @@
}
}
@media only screen and (max-width: 320px) {
.popup-content {
position: unset;
}
}
@keyframes popup-in {
0% {
opacity: 0;

View File

@@ -134,6 +134,12 @@
}
}
@media only screen and (max-width: 320px){
.popup-wrapper {
overflow-y: auto;
}
}
@media (prefers-color-scheme: dark) {
.popup-wrapper {
background: $dark_mode_background;

View File

@@ -1,3 +1,4 @@
import i18n from '@/i18n/index'
import store from './store/index'
import {debounce, includes} from "lodash";
import {events} from './bus'
@@ -8,8 +9,10 @@ const Helpers = {
install(Vue) {
Vue.prototype.$updateText = debounce(function (route, name, value) {
let enableEmptyInput = ['mimetypes_blacklist' , 'google_analytics']
if (value === '') return
if (value === '' && !enableEmptyInput.includes(name)) return
axios.post(this.$store.getters.api + route, {name, value, _method: 'patch'})
.catch(error => {
@@ -145,6 +148,10 @@ const Helpers = {
if (error.response.status === 500)
isNotGeneralError = false
//Break if mimetype of file is in blacklist
if(error.response.status === 415)
isNotGeneralError = false
// Show Error
if (attempts === 3)
this.$isSomethingWrong()
@@ -170,6 +177,8 @@ const Helpers = {
if (files.length == 0) return
if (!this.$checkFileMimetype(files)) return
this.$handleUploading(files, undefined)
}
@@ -273,6 +282,31 @@ const Helpers = {
message: this.$t('popup_error.message'),
})
}
Vue.prototype.$checkFileMimetype = function(files) {
let validated = true
let mimetypesBlacklist = store.getters.config.mimetypesBlacklist
for (let i = 0 ; i<files.length; i++ ) {
let fileType = files[i].type.split('/')
if(!fileType[0]) {
fileType[1] = _.last(files[i].name.split('.'))
}
if(mimetypesBlacklist.includes(fileType[1])) {
validated = false
events.$emit('alert:open', {
emoji: '😬😬😬',
title: i18n.t('popup_mimetypes_blacklist.title'),
message: i18n.t('popup_mimetypes_blacklist.message', {mimetype: fileType[1]}),
})
}else {
validated = true
}
}
return validated
}
}
}

View File

@@ -208,6 +208,9 @@
"username_plac": "输入您的邮件用户名"
},
"others": {
"mimetypes_blacklist": "Mimetypes Blacklist",
"mimetypes_blacklist_plac":"Add mimetypes to Blacklist" ,
"mimetypes_blacklist_help" :"If you want to prevent upload some type of files, just add them to blacklist like this: x-php,mp3,jpeg <br/> Use a comma between each mimetype. Don't use a dot before mimetypes." ,
"section_cache": "Application Cache",
"cache_disclaimer": "Did you change anything in your .env file or change your stripe credentials? Then clear your cache.",
"cache_clear": "Clear Cache",
@@ -295,6 +298,24 @@
"size": "大小",
"where": "地址"
},
"file_detail_meta": {
"dimension": "Dimensions",
"resolution": "Resolution",
"color_space": "Color Space",
"aperture_value": "Aperture Value",
"meta_data": "Metadata",
"author": "Author",
"time_data": "Content Created",
"make": "Camera",
"model": "Model",
"camera_lens": "Camera Lens",
"aperature": "F Number",
"iso": "ISO",
"focal": "Focal Length",
"exposure": "Exposure Time",
"longitude": "Longitude",
"latitude": "Latitude"
},
"folder": {
"empty": "空的",
"item_counts": "{count} 个文件 | {count} 个文件"
@@ -473,6 +494,10 @@
},
"title": "选择付款方式"
},
"popup_mimetypes_blacklist": {
"title": "Oh no",
"message": "File of this type ({mimetype}) is not allowed to upload."
},
"popup_create_folder": {
"folder_default_name": "新文件夹",
"title": "请填入新文件夹名称"

View File

@@ -5,7 +5,9 @@
"move": "Move item",
"preview": "Change preview",
"share": "Share item",
"upload": "Upload file"
"upload": "Upload file",
"download": "Download item",
"print": "Print item"
},
"activation": {
"stripe": {
@@ -208,6 +210,9 @@
"username_plac": "Type your mail username"
},
"others": {
"mimetypes_blacklist": "Mimetypes Blacklist",
"mimetypes_blacklist_plac":"Add mimetypes to Blacklist" ,
"mimetypes_blacklist_help" :"If you want to prevent upload some type of files, just add them to blacklist like this: x-php,mp3,jpeg <br/> Use a comma between each mimetype. Don't use a dot before mimetypes." ,
"section_cache": "Application Cache",
"cache_disclaimer": "Did you change anything in your .env file or change your Stripe credentials? Then clear your cache.",
"cache_clear": "Clear Cache",
@@ -295,6 +300,24 @@
"size": "Size",
"where": "Where"
},
"file_detail_meta": {
"dimension": "Dimensions",
"resolution": "Resolution",
"color_space": "Color Space",
"aperture_value": "Aperture Value",
"meta_data": "Metadata",
"author": "Author",
"time_data": "Content Created",
"make": "Camera",
"model": "Model",
"camera_lens": "Camera Lens",
"aperature": "F Number",
"iso": "ISO",
"focal": "Focal Length",
"exposure": "Exposure Time",
"longitude": "Longitude",
"latitude": "Latitude"
},
"folder": {
"empty": "Empty",
"item_counts": "{count} Item | {count} Items"
@@ -473,6 +496,10 @@
},
"title": "Choose Payment Method"
},
"popup_mimetypes_blacklist": {
"title": "Oh No",
"message": "File of this type ({mimetype}) is not allowed to upload."
},
"popup_create_folder": {
"folder_default_name": "New Folder",
"title": "Please enter your new folder name"
@@ -626,7 +653,7 @@
"visitor": "Can only view and download"
},
"shared_form": {
"button_more_options": "More Options",
"button_more_options": "Set Expiration",
"button_close_options": "Close Options",
"button_done": "Awesome, Im done!",
"button_generate": "Generate Link",

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