mirror of
https://github.com/VueFileManager/vuefilemanager.git
synced 2026-04-06 18:53:48 +00:00
Compare commits
34 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bc7950b245 | ||
|
|
321bac6c9f | ||
|
|
6392ce1727 | ||
|
|
181f090901 | ||
|
|
252b6fd0bf | ||
|
|
a76d1dec3b | ||
|
|
143aca64dc | ||
|
|
3dc3f37cf6 | ||
|
|
deff8d8741 | ||
|
|
beae4277ca | ||
|
|
be7d1bdc73 | ||
|
|
2eaf399441 | ||
|
|
65f902fbcf | ||
|
|
5df0fa93b3 | ||
|
|
67b9416f64 | ||
|
|
8255597fd5 | ||
|
|
633bef7660 | ||
|
|
dfe4991177 | ||
|
|
8daa05f710 | ||
|
|
62434bcedb | ||
|
|
6272f62e85 | ||
|
|
4c8028696f | ||
|
|
355f6a96ff | ||
|
|
bdcfc26af7 | ||
|
|
d2c4f2aa23 | ||
|
|
41656235fc | ||
|
|
26e79e7baa | ||
|
|
cfecf542ca | ||
|
|
edd0b5195d | ||
|
|
232d560cc4 | ||
|
|
b8b56584bd | ||
|
|
ce2daaf6c4 | ||
|
|
78d9e0bd2a | ||
|
|
55695ba06c |
12
.env.example
12
.env.example
@@ -6,6 +6,7 @@ APP_URL=http://localhost
|
||||
|
||||
LOG_CHANNEL=stack
|
||||
SCOUT_DRIVER=tntsearch
|
||||
FILESYSTEM_DRIVER=local
|
||||
|
||||
DB_CONNECTION=mysql
|
||||
DB_HOST=127.0.0.1
|
||||
@@ -38,13 +39,16 @@ AWS_SECRET_ACCESS_KEY=
|
||||
AWS_DEFAULT_REGION=us-east-1
|
||||
AWS_BUCKET=
|
||||
|
||||
PUSHER_APP_ID=
|
||||
PUSHER_APP_KEY=
|
||||
PUSHER_APP_SECRET=
|
||||
PUSHER_APP_CLUSTER=mt1
|
||||
DO_SPACES_KEY=
|
||||
DO_SPACES_SECRET=
|
||||
DO_SPACES_ENDPOINT=
|
||||
DO_SPACES_REGION=
|
||||
DO_SPACES_BUCKET=
|
||||
|
||||
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
|
||||
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
|
||||
|
||||
PASSPORT_CLIENT_ID=
|
||||
PASSPORT_CLIENT_SECRET=
|
||||
|
||||
APP_DEPLOY_SECRET=
|
||||
@@ -1,3 +1,10 @@
|
||||
### Documentation
|
||||
[Read online documentation](https://vuefilemanager.com/docs/)
|
||||
|
||||
### Demo & dev preview links
|
||||
* For visit demo version click here [demo.vuefilemanager.com](https://demo.vuefilemanager.com/)
|
||||
* For visit dev version click here [dev.vuefilemanager.com](https://dev.vuefilemanager.com/) (It's auto deployed dev branch. Can be unstable and not ready for production)
|
||||
|
||||
### Installation setup
|
||||
|
||||
Run these commands to install vendors:
|
||||
@@ -42,4 +49,4 @@ To compiles for production, run this command
|
||||
```
|
||||
npm run prod
|
||||
```
|
||||
That's all, happy coding! :tada: :tada: :tada:
|
||||
That's all, happy coding! :tada: :tada: :tada:
|
||||
|
||||
66
app/Console/Commands/Deploy.php
Normal file
66
app/Console/Commands/Deploy.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class Deploy extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'deploy:production';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Automatic deployment for production';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
// Start deployment
|
||||
$this->info('Running auto deployment');
|
||||
$this->call('down');
|
||||
|
||||
// Exec commands
|
||||
exec('git pull origin ' . config('app.deploy_branch'));
|
||||
//exec('composer update --no-interaction --prefer-dist');
|
||||
$this->migrateDatabase();
|
||||
|
||||
// Stop deployment
|
||||
$this->call('up');
|
||||
$this->info('Everything is done, congratulations! 🥳🥳🥳');
|
||||
|
||||
Log::info('Application was updated!');
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate database
|
||||
*/
|
||||
public function migrateDatabase()
|
||||
{
|
||||
$this->call('migrate', [
|
||||
'--force' => true,
|
||||
]);
|
||||
}
|
||||
}
|
||||
124
app/Console/Commands/SetupDevEnvironment.php
Normal file
124
app/Console/Commands/SetupDevEnvironment.php
Normal file
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\User;
|
||||
use App\UserSettings;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class SetupDevEnvironment extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'setup:dev';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Setting production environment';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->info('Setting up production environment');
|
||||
|
||||
$this->migrateDatabase();
|
||||
$this->generateKey();
|
||||
$this->createPassportKeys();
|
||||
$this->createPassportClientPassword();
|
||||
$this->createPassportClientPersonal();
|
||||
$this->createDefaultUser();
|
||||
|
||||
$this->info('Everything is done, congratulations! 🥳🥳🥳');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate database
|
||||
*/
|
||||
public function generateKey()
|
||||
{
|
||||
$this->call('key:generate');
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate database
|
||||
*/
|
||||
public function migrateDatabase()
|
||||
{
|
||||
$this->call('migrate:fresh');
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Passport Encryption keys
|
||||
*/
|
||||
public function createPassportKeys()
|
||||
{
|
||||
$this->call('passport:keys', [
|
||||
'--force' => true
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Password grant client
|
||||
*/
|
||||
public function createPassportClientPassword()
|
||||
{
|
||||
$this->call('passport:client', [
|
||||
'--password' => true,
|
||||
'--name' => 'vuefilemanager',
|
||||
]);
|
||||
|
||||
$this->alert('Please copy these first password grant Client ID & Client secret above to your /.env file.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Personal access client
|
||||
*/
|
||||
public function createPassportClientPersonal()
|
||||
{
|
||||
$this->call('passport:client', [
|
||||
'--personal' => true,
|
||||
'--name' => 'shared',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Default User
|
||||
*/
|
||||
public function createDefaultUser()
|
||||
{
|
||||
$user = User::create([
|
||||
'name' => 'Jane Doe',
|
||||
'email' => 'howdy@hi5ve.digital',
|
||||
'role' => 'admin',
|
||||
'password' => \Hash::make('vuefilemanager'),
|
||||
]);
|
||||
|
||||
// Create settings
|
||||
$settings = UserSettings::create([
|
||||
'user_id' => $user->id
|
||||
]);
|
||||
|
||||
$this->info('Test user created. Email: ' . $user->email . ' Password: vuefilemanager');
|
||||
}
|
||||
}
|
||||
@@ -84,6 +84,8 @@ class SetupProductionEnvironment extends Command
|
||||
'--password' => true,
|
||||
'--name' => 'vuefilemanager',
|
||||
]);
|
||||
|
||||
$this->alert('Please copy these first password grant Client ID & Client secret above to your /.env file.');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
88
app/Console/Commands/UpgradeApp.php
Normal file
88
app/Console/Commands/UpgradeApp.php
Normal file
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\User;
|
||||
use App\UserSettings;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class UpgradeApp extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'upgrade:app {version}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Upgrade application to new version';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->info('Upgrading your application to version ' . $this->argument('version'));
|
||||
$this->call('down');
|
||||
|
||||
// Version 1.6
|
||||
if ($this->argument('version') === 'v1.6') {
|
||||
$this->version_1_6();
|
||||
}
|
||||
|
||||
$this->call('up');
|
||||
$this->info('Your application was upgraded! 🥳🥳🥳');
|
||||
}
|
||||
|
||||
/**
|
||||
* Upgrade script to version 1.6
|
||||
*/
|
||||
public function version_1_6() {
|
||||
|
||||
// Migrate new tables and changes
|
||||
$this->call('migrate');
|
||||
|
||||
// Create user settings records
|
||||
$this->info('Updating users options...');
|
||||
|
||||
User::all()->each(function ($user) {
|
||||
$this->info('Update user with id: ' . $user->id);
|
||||
UserSettings::create(['user_id' => $user->id]);
|
||||
});
|
||||
|
||||
$this->info('Updating user options is done!');
|
||||
|
||||
// Set up admin
|
||||
$email = $this->ask('Which user would you like set up as admin? Please type user email');
|
||||
|
||||
$admin = User::where('email', $email)->first();
|
||||
|
||||
if (! $admin) {
|
||||
$email = $this->ask('We can\'t find user with this email, please try it again');
|
||||
$admin = User::where('email', $email)->first();
|
||||
}
|
||||
|
||||
// Save new role for selected user
|
||||
$admin->role = 'admin';
|
||||
$admin->save();
|
||||
|
||||
$this->info('Admin was set up successfully');
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,10 @@
|
||||
|
||||
namespace App\Console;
|
||||
|
||||
use App\Console\Commands\Deploy;
|
||||
use App\Console\Commands\SetupDevEnvironment;
|
||||
use App\Console\Commands\SetupProductionEnvironment;
|
||||
use App\Console\Commands\UpgradeApp;
|
||||
use Illuminate\Console\Scheduling\Schedule;
|
||||
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
||||
|
||||
@@ -15,6 +18,9 @@ class Kernel extends ConsoleKernel
|
||||
*/
|
||||
protected $commands = [
|
||||
SetupProductionEnvironment::class,
|
||||
SetupDevEnvironment::class,
|
||||
UpgradeApp::class,
|
||||
Deploy::class,
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App;
|
||||
|
||||
use ByteUnits\Metric;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
use Laravel\Scout\Searchable;
|
||||
use TeamTNT\TNTSearch\Indexer\TNTIndexer;
|
||||
@@ -52,7 +53,6 @@ use \Illuminate\Database\Eloquent\SoftDeletes;
|
||||
* @method static \Illuminate\Database\Query\Builder|\App\FileManagerFile withoutTrashed()
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
|
||||
class FileManagerFile extends Model
|
||||
{
|
||||
use Searchable, SoftDeletes;
|
||||
@@ -72,7 +72,8 @@ class FileManagerFile extends Model
|
||||
*
|
||||
* @param $token
|
||||
*/
|
||||
public function setPublicUrl($token) {
|
||||
public function setPublicUrl($token)
|
||||
{
|
||||
$this->public_access = $token;
|
||||
}
|
||||
|
||||
@@ -93,7 +94,7 @@ class FileManagerFile extends Model
|
||||
*/
|
||||
public function getDeletedAtAttribute()
|
||||
{
|
||||
if (! $this->attributes['deleted_at']) return null;
|
||||
if (!$this->attributes['deleted_at']) return null;
|
||||
|
||||
return format_date($this->attributes['deleted_at'], __('vuefilemanager.time'));
|
||||
}
|
||||
@@ -115,7 +116,14 @@ class FileManagerFile extends Model
|
||||
*/
|
||||
public function getThumbnailAttribute()
|
||||
{
|
||||
if ($this->attributes['thumbnail']) {
|
||||
// Get thumbnail from s3
|
||||
if ($this->attributes['thumbnail'] && is_storage_driver(['s3', 'spaces'])) {
|
||||
|
||||
return Storage::temporaryUrl('file-manager/' . $this->attributes['thumbnail'], now()->addDay());
|
||||
}
|
||||
|
||||
// Get thumbnail from local storage
|
||||
if ($this->attributes['thumbnail'] && is_storage_driver('local')) {
|
||||
|
||||
// Thumbnail route
|
||||
$route = route('thumbnail', ['name' => $this->attributes['thumbnail']]);
|
||||
@@ -137,13 +145,31 @@ class FileManagerFile extends Model
|
||||
*/
|
||||
public function getFileUrlAttribute()
|
||||
{
|
||||
$route = route('file', ['name' => $this->attributes['basename']]);
|
||||
// Get file from s3
|
||||
if (is_storage_driver(['s3', 'spaces'])) {
|
||||
|
||||
if ($this->public_access) {
|
||||
return $route . '/public/' . $this->public_access;
|
||||
$header = [
|
||||
"ResponseAcceptRanges" => "bytes",
|
||||
"ResponseContentType" => $this->attributes['mimetype'],
|
||||
"ResponseContentLength" => $this->attributes['filesize'],
|
||||
"ResponseContentRange" => "bytes 0-600/" . $this->attributes['filesize'],
|
||||
'ResponseContentDisposition' => 'attachment; filename=' . $this->attributes['name'] . '.' . $this->attributes['mimetype'],
|
||||
];
|
||||
|
||||
return Storage::temporaryUrl('file-manager/' . $this->attributes['basename'], now()->addDay(), $header);
|
||||
}
|
||||
|
||||
return $route;
|
||||
// Get thumbnail from local storage
|
||||
if (is_storage_driver('local')) {
|
||||
|
||||
$route = route('file', ['name' => $this->attributes['basename']]);
|
||||
|
||||
if ($this->public_access) {
|
||||
return $route . '/public/' . $this->public_access;
|
||||
}
|
||||
|
||||
return $route;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
222
app/Http/Controllers/Admin/UserController.php
Normal file
222
app/Http/Controllers/Admin/UserController.php
Normal file
@@ -0,0 +1,222 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\FileManagerFile;
|
||||
use App\FileManagerFolder;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Admin\ChangeRoleRequest;
|
||||
use App\Http\Requests\Admin\ChangeStorageCapacityRequest;
|
||||
use App\Http\Requests\Admin\CreateUserByAdmin;
|
||||
use App\Http\Requests\Admin\DeleteUserRequest;
|
||||
use App\Http\Resources\UsersCollection;
|
||||
use App\Http\Resources\UserResource;
|
||||
use App\Http\Resources\UserStorageResource;
|
||||
use App\Http\Tools\Demo;
|
||||
use App\Share;
|
||||
use App\User;
|
||||
use App\UserSettings;
|
||||
use Illuminate\Contracts\Routing\ResponseFactory;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Password;
|
||||
use Illuminate\Support\Str;
|
||||
use Storage;
|
||||
|
||||
class UserController extends Controller
|
||||
{
|
||||
/**
|
||||
* Get user details
|
||||
*
|
||||
* @param $id
|
||||
* @return UserResource
|
||||
*/
|
||||
public function details($id)
|
||||
{
|
||||
return new UserResource(User::findOrFail($id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user storage details
|
||||
*
|
||||
* @param $id
|
||||
* @return UserStorageResource
|
||||
*/
|
||||
public function storage($id)
|
||||
{
|
||||
return new UserStorageResource(User::findOrFail($id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all users
|
||||
*
|
||||
* @return UsersCollection
|
||||
*/
|
||||
public function users()
|
||||
{
|
||||
return new UsersCollection(User::all());
|
||||
}
|
||||
|
||||
/**
|
||||
* Change user role
|
||||
*
|
||||
* @param ChangeRoleRequest $request
|
||||
* @param $id
|
||||
* @return UserResource
|
||||
*/
|
||||
public function change_role(ChangeRoleRequest $request, $id)
|
||||
{
|
||||
$user = User::findOrFail($id);
|
||||
|
||||
// Demo preview
|
||||
if (env('APP_DEMO') && $id == 1) {
|
||||
return new UserResource($user);
|
||||
}
|
||||
|
||||
$user->update($request->input('attributes'));
|
||||
|
||||
return new UserResource($user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change user storage capacity
|
||||
*
|
||||
* @param ChangeStorageCapacityRequest $request
|
||||
* @param $id
|
||||
* @return UserStorageResource
|
||||
*/
|
||||
public function change_storage_capacity(ChangeStorageCapacityRequest $request, $id)
|
||||
{
|
||||
$user = User::findOrFail($id);
|
||||
|
||||
$user->settings()->update($request->input('attributes'));
|
||||
|
||||
return new UserStorageResource($user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send user password reset link
|
||||
*
|
||||
* @param $id
|
||||
* @return ResponseFactory|\Illuminate\Http\Response
|
||||
*/
|
||||
public function send_password_reset_email($id)
|
||||
{
|
||||
$user = User::findOrFail($id);
|
||||
|
||||
// Demo preview
|
||||
if (env('APP_DEMO')) {
|
||||
return response('Done!', 204);
|
||||
}
|
||||
|
||||
// Get password token
|
||||
$token = Password::getRepository()->create($user);
|
||||
|
||||
// Send user email
|
||||
$user->sendPasswordResetNotification($token);
|
||||
|
||||
return response('Done!', 204);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new user by admin
|
||||
*
|
||||
* @param CreateUserByAdmin $request
|
||||
* @return UserResource
|
||||
*/
|
||||
public function create_user(CreateUserByAdmin $request)
|
||||
{
|
||||
// Store avatar
|
||||
if ($request->hasFile('avatar')) {
|
||||
|
||||
// Update avatar
|
||||
$avatar = store_avatar($request->file('avatar'), 'avatars');
|
||||
}
|
||||
|
||||
// Create user
|
||||
$user = User::create([
|
||||
'avatar' => $request->hasFile('avatar') ? $avatar : null,
|
||||
'name' => $request->name,
|
||||
'role' => $request->role,
|
||||
'email' => $request->email,
|
||||
'password' => Hash::make($request->password),
|
||||
]);
|
||||
|
||||
// Create settings
|
||||
$settings = UserSettings::create([
|
||||
'user_id' => $user->id,
|
||||
'storage_capacity' => $request->storage_capacity,
|
||||
]);
|
||||
|
||||
return new UserResource($user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete user with all user data
|
||||
*
|
||||
* @param DeleteUserRequest $request
|
||||
* @param $id
|
||||
* @return ResponseFactory|\Illuminate\Http\Response
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function delete_user(DeleteUserRequest $request, $id)
|
||||
{
|
||||
$user = User::findOrFail($id);
|
||||
|
||||
// Demo preview
|
||||
if (env('APP_DEMO')) {
|
||||
return response('Done!', 204);
|
||||
}
|
||||
|
||||
// Check for self deleted account
|
||||
if ($user->id === Auth::id()) {
|
||||
abort(406, 'You can\'t delete your account');
|
||||
}
|
||||
|
||||
// Validate user name
|
||||
if ($user->name !== $request->name) abort(403);
|
||||
|
||||
$shares = Share::where('user_id', $user->id)->get();
|
||||
$files = FileManagerFile::withTrashed()
|
||||
->where('user_id', $user->id)
|
||||
->get();
|
||||
$folders = FileManagerFolder::withTrashed()
|
||||
->where('user_id', $user->id)
|
||||
->get();
|
||||
|
||||
// Remove all files and thumbnails
|
||||
$files->each(function ($file) {
|
||||
|
||||
// Delete file
|
||||
Storage::delete('/file-manager/' . $file->basename);
|
||||
|
||||
// Delete thumbnail if exist
|
||||
if (!is_null($file->thumbnail)) {
|
||||
Storage::delete('/file-manager/' . $file->getOriginal('thumbnail'));
|
||||
}
|
||||
|
||||
// Delete file permanently
|
||||
$file->forceDelete();
|
||||
});
|
||||
|
||||
// Remove avatar
|
||||
if ($user->avatar) {
|
||||
Storage::delete('/avatars/' . $user->avatar);
|
||||
}
|
||||
|
||||
// Remove folders & shares
|
||||
$folders->each->forceDelete();
|
||||
$shares->each->forceDelete();
|
||||
|
||||
// Remove favourites
|
||||
$user->settings->delete();
|
||||
$user->favourites()->sync([]);
|
||||
|
||||
// Delete user
|
||||
$user->delete();
|
||||
|
||||
return response('Done!', 204);
|
||||
}
|
||||
}
|
||||
@@ -2,20 +2,14 @@
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\ClientProfile;
|
||||
use App\Models\User\UserAttribute;
|
||||
use App\Models\User\UserNotificationSetting;
|
||||
use App\ProviderProfile;
|
||||
use App\Http\Requests\Auth\CheckAccountRequest;
|
||||
use App\User;
|
||||
use GuzzleHttp\Client;
|
||||
use App\UserSettings;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Cookie;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
|
||||
class AuthController extends Controller
|
||||
{
|
||||
@@ -26,12 +20,7 @@ class AuthController extends Controller
|
||||
* @param Request $request
|
||||
* @return mixed
|
||||
*/
|
||||
public function check_account(Request $request) {
|
||||
|
||||
// Validate request
|
||||
$request->validate([
|
||||
'email' => ['required', 'string', 'email'],
|
||||
]);
|
||||
public function check_account(CheckAccountRequest $request) {
|
||||
|
||||
// Get User
|
||||
$user = User::where('email', $request->input('email'))->select(['name', 'avatar'])->first();
|
||||
@@ -85,12 +74,17 @@ class AuthController extends Controller
|
||||
]);
|
||||
|
||||
// Create user
|
||||
User::create([
|
||||
$user = User::create([
|
||||
'name' => $request->name,
|
||||
'email' => $request->email,
|
||||
'password' => Hash::make($request->password),
|
||||
]);
|
||||
|
||||
// Create settings
|
||||
$settings = UserSettings::create([
|
||||
'user_id' => $user->id
|
||||
]);
|
||||
|
||||
$response = Route::dispatch(self::make_request($request));
|
||||
|
||||
if ($response->isSuccessful()) {
|
||||
@@ -111,6 +105,12 @@ class AuthController extends Controller
|
||||
*/
|
||||
public function logout()
|
||||
{
|
||||
// Demo preview
|
||||
if (is_demo( Auth::id())) {
|
||||
return response('Logout successfull', 204)
|
||||
->cookie('access_token', '', -1);
|
||||
}
|
||||
|
||||
// Get user tokens and remove it
|
||||
auth()->user()->tokens()->each(function ($token) {
|
||||
|
||||
@@ -118,7 +118,8 @@ class AuthController extends Controller
|
||||
$token->delete();
|
||||
});
|
||||
|
||||
return response('Logout successfull', 200)->cookie('access_token', '', -1);
|
||||
return response('Logout successfull', 204)
|
||||
->cookie('access_token', '', -1);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -128,7 +129,7 @@ class AuthController extends Controller
|
||||
* @param string $provider
|
||||
* @return Request
|
||||
*/
|
||||
private static function make_request(Request $request)
|
||||
private static function make_request($request)
|
||||
{
|
||||
$request->request->add([
|
||||
'grant_type' => 'password',
|
||||
|
||||
44
app/Http/Controllers/DeployController.php
Normal file
44
app/Http/Controllers/DeployController.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Artisan;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Validation\UnauthorizedException;
|
||||
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||
|
||||
class DeployController extends Controller
|
||||
{
|
||||
/**
|
||||
* Get web hook payload and verify request
|
||||
*
|
||||
* @param Request $request
|
||||
* @return \Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response
|
||||
*/
|
||||
public function github(Request $request) {
|
||||
|
||||
if (($signature = $request->headers->get('X-Hub-Signature')) == null) {
|
||||
throw new BadRequestHttpException('Header not set');
|
||||
}
|
||||
|
||||
$signature_parts = explode('=', $signature);
|
||||
|
||||
if (count($signature_parts) != 2) {
|
||||
throw new BadRequestHttpException('signature has invalid format');
|
||||
}
|
||||
|
||||
$known_signature = hash_hmac('sha1', $request->getContent(), config('app.deploy_secret'));
|
||||
|
||||
if (! hash_equals($known_signature, $signature_parts[1])) {
|
||||
throw new UnauthorizedException('Could not verify request signature ' . $signature_parts[1]);
|
||||
}
|
||||
|
||||
// Run deploying
|
||||
Artisan::call('deploy:production');
|
||||
|
||||
Log::info('The GitHub webhook was accepted');
|
||||
|
||||
return response('The GitHub webhook was accepted', 202);
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Http\Request;
|
||||
use App\FileManagerFile;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
use Response;
|
||||
|
||||
@@ -26,19 +27,13 @@ class FileAccessController extends Controller
|
||||
public function get_avatar($basename)
|
||||
{
|
||||
// Get file path
|
||||
$path = storage_path() . '/app/avatars/' . $basename;
|
||||
$path = '/avatars/' . $basename;
|
||||
|
||||
// Check if file exist
|
||||
if (!File::exists($path)) abort(404);
|
||||
if (!Storage::exists($path)) abort(404);
|
||||
|
||||
$file = File::get($path);
|
||||
$type = File::mimeType($path);
|
||||
|
||||
// Create response
|
||||
$response = Response::make($file, 200);
|
||||
$response->header("Content-Type", $type);
|
||||
|
||||
return $response;
|
||||
// Return avatar
|
||||
return Storage::download($path, $basename);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -61,7 +56,7 @@ class FileAccessController extends Controller
|
||||
->firstOrFail();
|
||||
|
||||
// Check user permission
|
||||
if ( ! $request->user()->tokenCan('master') ) {
|
||||
if (!$request->user()->tokenCan('master')) {
|
||||
|
||||
// Get shared token
|
||||
$shared = get_shared($request->cookie('shared_token'));
|
||||
@@ -119,7 +114,7 @@ class FileAccessController extends Controller
|
||||
->firstOrFail();
|
||||
|
||||
// Check user permission
|
||||
if ( ! $request->user()->tokenCan('master') ) {
|
||||
if (!$request->user()->tokenCan('master')) {
|
||||
$this->check_file_access($request, $file);
|
||||
}
|
||||
|
||||
@@ -187,24 +182,20 @@ class FileAccessController extends Controller
|
||||
$file_pretty_name = $file->name . '.' . $file->mimetype;
|
||||
|
||||
// Get file path
|
||||
$path = storage_path() . '/app/file-manager/' . $file->basename;
|
||||
$path = '/file-manager/' . $file->basename;
|
||||
|
||||
// Check if file exist
|
||||
if (!File::exists($path)) abort(404);
|
||||
if (!Storage::exists($path)) abort(404);
|
||||
|
||||
$file = File::get($path);
|
||||
$type = File::mimeType($path);
|
||||
$size = File::size($path);
|
||||
$header = [
|
||||
"Content-Type" => Storage::mimeType($path),
|
||||
"Content-Length" => Storage::size($path),
|
||||
"Accept-Ranges" => "bytes",
|
||||
"Content-Range" => "bytes 0-600/" . Storage::size($path),
|
||||
];
|
||||
|
||||
// Create response
|
||||
$response = Response::make($file, 200);
|
||||
$response->header("Content-Type", $type);
|
||||
$response->header("Content-Disposition", 'attachment; filename=' . $file_pretty_name);
|
||||
$response->header("Content-Length", $size);
|
||||
$response->header("Accept-Ranges", "bytes");
|
||||
$response->header("Content-Range", "bytes 0-" . $size . "/" . $size);
|
||||
|
||||
return $response;
|
||||
// Get file
|
||||
return Storage::download($path, $file_pretty_name, $header);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -215,18 +206,12 @@ class FileAccessController extends Controller
|
||||
private function thumbnail_file($file)
|
||||
{
|
||||
// Get file path
|
||||
$path = storage_path() . '/app/file-manager/' . $file->getOriginal('thumbnail');
|
||||
$path = '/file-manager/' . $file->getOriginal('thumbnail');
|
||||
|
||||
// Check if file exist
|
||||
if (!File::exists($path)) abort(404);
|
||||
if (!Storage::exists($path)) abort(404);
|
||||
|
||||
$file = File::get($path);
|
||||
$type = File::mimeType($path);
|
||||
|
||||
// Create response
|
||||
$response = Response::make($file, 200);
|
||||
$response->header("Content-Type", $type);
|
||||
|
||||
return $response;
|
||||
// Return image thumbnail
|
||||
return Storage::download($path, $file->getOriginal('thumbnail'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace App\Http\Controllers\FileBrowser;
|
||||
|
||||
use App\Http\Requests\FileBrowser\SearchRequest;
|
||||
use App\User;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use App\Http\Controllers\Controller;
|
||||
@@ -26,17 +28,19 @@ class BrowseController extends Controller
|
||||
|
||||
// Get folders and files
|
||||
$folders_trashed = FileManagerFolder::onlyTrashed()
|
||||
->with(['trashed_folders'])
|
||||
->with(['trashed_folders', 'parent'])
|
||||
->where('user_id', $user_id)
|
||||
->get(['parent_id', 'unique_id', 'name']);
|
||||
|
||||
$folders = FileManagerFolder::onlyTrashed()
|
||||
->with(['parent'])
|
||||
->where('user_id', $user_id)
|
||||
->whereIn('unique_id', filter_folders_ids($folders_trashed))
|
||||
->get();
|
||||
|
||||
// Get files trashed
|
||||
$files_trashed = FileManagerFile::onlyTrashed()
|
||||
->with(['parent'])
|
||||
->where('user_id', $user_id)
|
||||
->whereNotIn('folder_id', array_values(array_unique(recursiveFind($folders_trashed->toArray(), 'unique_id'))))
|
||||
->get();
|
||||
@@ -79,6 +83,35 @@ class BrowseController extends Controller
|
||||
return collect([$folders, $files])->collapse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get latest user uploads
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function latest() {
|
||||
|
||||
// Get User
|
||||
$user = User::with(['latest_uploads'])
|
||||
->where('id', Auth::id())
|
||||
->first();
|
||||
|
||||
return $user->latest_uploads->makeHidden(['user_id', 'basename']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get participant uploads
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function participant_uploads() {
|
||||
|
||||
// Get User
|
||||
$uploads = FileManagerFile::with(['parent'])->where('user_id', Auth::id())
|
||||
->whereUserScope('editor')->orderBy('created_at', 'DESC')->get();
|
||||
|
||||
return $uploads;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get directory with files
|
||||
*
|
||||
@@ -96,14 +129,14 @@ class BrowseController extends Controller
|
||||
|
||||
// Get folders and files
|
||||
$folders = FileManagerFolder::onlyTrashed()
|
||||
->where('user_id', $user_id)
|
||||
->with('parent')
|
||||
->where('user_id', $user_id)
|
||||
->where('parent_id', $unique_id)
|
||||
->get();
|
||||
|
||||
$files = FileManagerFile::onlyTrashed()
|
||||
->where('user_id', $user_id)
|
||||
->with('parent')
|
||||
->where('user_id', $user_id)
|
||||
->where('folder_id', $unique_id)
|
||||
->get();
|
||||
|
||||
@@ -120,6 +153,7 @@ class BrowseController extends Controller
|
||||
$files = FileManagerFile::with(['parent', 'shared:token,id,item_id,permission,protected'])
|
||||
->where('user_id', $user_id)
|
||||
->where('folder_id', $unique_id)
|
||||
->orderBy('created_at', 'DESC')
|
||||
->get();
|
||||
|
||||
// Collect folders and files to single array
|
||||
@@ -154,16 +188,8 @@ class BrowseController extends Controller
|
||||
* @param Request $request
|
||||
* @return \Illuminate\Database\Eloquent\Collection
|
||||
*/
|
||||
public function search(Request $request)
|
||||
public function search(SearchRequest $request)
|
||||
{
|
||||
// Validate request
|
||||
$validator = Validator::make($request->all(), [
|
||||
'query' => 'required|string',
|
||||
]);
|
||||
|
||||
// Return error
|
||||
if ($validator->fails()) abort(400, 'Bad input');
|
||||
|
||||
// Get user
|
||||
$user_id = Auth::id();
|
||||
|
||||
|
||||
@@ -39,10 +39,10 @@ class TrashController extends Controller
|
||||
foreach ($files as $file) {
|
||||
|
||||
// Delete file
|
||||
Storage::disk('local')->delete('/file-manager/' . $file->basename);
|
||||
Storage::delete('/file-manager/' . $file->basename);
|
||||
|
||||
// Delete thumbnail if exist
|
||||
if ($file->thumbnail) Storage::disk('local')->delete('/file-manager/' . $file->getOriginal('thumbnail'));
|
||||
if ($file->thumbnail) Storage::delete('/file-manager/' . $file->getOriginal('thumbnail'));
|
||||
|
||||
// Delete file permanently
|
||||
$file->forceDelete();
|
||||
|
||||
@@ -6,6 +6,7 @@ use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Share\AuthenticateShareRequest;
|
||||
use App\Http\Resources\ShareResource;
|
||||
use App\Http\Tools\Guardian;
|
||||
use http\Env\Response;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use Illuminate\Support\Facades\Cookie;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
@@ -17,6 +18,7 @@ use App\FileManagerFolder;
|
||||
use App\FileManagerFile;
|
||||
use App\User;
|
||||
use App\Share;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class FileSharingController extends Controller
|
||||
{
|
||||
@@ -32,7 +34,7 @@ class FileSharingController extends Controller
|
||||
$shared = Share::where(\DB::raw('BINARY `token`'), $token)
|
||||
->first();
|
||||
|
||||
if (!$shared) {
|
||||
if (! $shared) {
|
||||
return view("index");
|
||||
}
|
||||
|
||||
@@ -46,10 +48,51 @@ class FileSharingController extends Controller
|
||||
Cookie::queue('shared_token', $token, 43200);
|
||||
}
|
||||
|
||||
// Check if shared is image file and then show it
|
||||
if ($shared->type === 'file' && ! $shared->protected) {
|
||||
|
||||
$image = FileManagerFile::where('user_id', $shared->user_id)
|
||||
->where('type', 'image')
|
||||
->where('unique_id', $shared->item_id)
|
||||
->first();
|
||||
|
||||
if ($image) {
|
||||
return $this->show_image($image);
|
||||
}
|
||||
}
|
||||
|
||||
// Return page index
|
||||
return view("index");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get image from storage and show it
|
||||
*
|
||||
* @param $file
|
||||
* @return \Symfony\Component\HttpFoundation\StreamedResponse
|
||||
*/
|
||||
private function show_image($file)
|
||||
{
|
||||
// Format pretty filename
|
||||
$file_pretty_name = $file->name . '.' . $file->mimetype;
|
||||
|
||||
// Get file path
|
||||
$path = '/file-manager/' . $file->basename;
|
||||
|
||||
// Check if file exist
|
||||
if (!Storage::exists($path)) abort(404);
|
||||
|
||||
$header = [
|
||||
"Content-Type" => Storage::mimeType($path),
|
||||
"Content-Length" => Storage::size($path),
|
||||
"Accept-Ranges" => "bytes",
|
||||
"Content-Range" => "bytes 0-600/" . Storage::size($path),
|
||||
];
|
||||
|
||||
// Get file
|
||||
return Storage::response($path, $file_pretty_name, $header);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check Password for protected item
|
||||
*
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
namespace App\Http\Controllers\User;
|
||||
|
||||
use App\FileManagerFile;
|
||||
use App\FileManagerFolder;
|
||||
use App\Http\Resources\StorageDetailResource;
|
||||
use App\Http\Resources\UserStorageResource;
|
||||
use App\Http\Tools\Demo;
|
||||
use Illuminate\Contracts\Routing\ResponseFactory;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
@@ -26,18 +30,34 @@ class AccountController extends Controller
|
||||
->where('id', Auth::id())
|
||||
->first();
|
||||
|
||||
// Get folder tree
|
||||
$tree = FileManagerFolder::with(['folders.shared', 'shared:token,id,item_id,permission,protected'])
|
||||
->where('parent_id', 0)
|
||||
->where('user_id', $user->id)
|
||||
->get();
|
||||
|
||||
return [
|
||||
'user' => $user->only(['name', 'email', 'avatar']),
|
||||
'favourites' => $user->favourites->makeHidden(['pivot']),
|
||||
'latest_uploads' => $user->latest_uploads->makeHidden(['user_id', 'basename']),
|
||||
'storage' => [
|
||||
'user' => $user->only(['name', 'email', 'avatar', 'role']),
|
||||
'favourites' => $user->favourites->makeHidden(['pivot']),
|
||||
'tree' => $tree,
|
||||
'storage' => [
|
||||
'used' => Metric::bytes($user->used_capacity)->format(),
|
||||
'capacity' => format_gigabytes(config('vuefilemanager.user_storage_capacity')),
|
||||
'percentage' => get_storage_fill_percentage($user->used_capacity, config('vuefilemanager.user_storage_capacity')),
|
||||
'capacity' => format_gigabytes($user->settings->storage_capacity),
|
||||
'percentage' => get_storage_fill_percentage($user->used_capacity, $user->settings->storage_capacity),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get storage details
|
||||
*
|
||||
* @return UserStorageResource
|
||||
*/
|
||||
public function storage()
|
||||
{
|
||||
return new UserStorageResource(Auth::user());
|
||||
}
|
||||
|
||||
/**
|
||||
* Update user profile
|
||||
*
|
||||
@@ -48,9 +68,9 @@ class AccountController extends Controller
|
||||
{
|
||||
// Validate request
|
||||
$validator = Validator::make($request->all(), [
|
||||
'avatar' => 'file',
|
||||
'name' => 'string',
|
||||
'value' => 'string',
|
||||
'avatar' => 'file',
|
||||
'name' => 'string',
|
||||
'value' => 'string',
|
||||
]);
|
||||
|
||||
// Return error
|
||||
@@ -59,10 +79,15 @@ class AccountController extends Controller
|
||||
// Get user
|
||||
$user = Auth::user();
|
||||
|
||||
// Check if is demo
|
||||
if (is_demo($user->id)) {
|
||||
return Demo::response_204();
|
||||
}
|
||||
|
||||
// Check role
|
||||
if ($request->has('role')) abort(403);
|
||||
|
||||
// Update data
|
||||
if ($request->hasFile('avatar')) {
|
||||
|
||||
// Update avatar
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Http;
|
||||
|
||||
use App\Http\Middleware\AdminCheck;
|
||||
use App\Http\Middleware\CookieAuth;
|
||||
use App\Http\Middleware\LastCheck;
|
||||
use App\Http\Middleware\SharedAuth;
|
||||
@@ -58,6 +59,7 @@ class Kernel extends HttpKernel
|
||||
protected $routeMiddleware = [
|
||||
'auth.master' => CookieAuth::class,
|
||||
'auth.shared' => SharedAuth::class,
|
||||
'auth.admin' => AdminCheck::class,
|
||||
'auth' => \App\Http\Middleware\Authenticate::class,
|
||||
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
|
||||
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||
|
||||
26
app/Http/Middleware/AdminCheck.php
Normal file
26
app/Http/Middleware/AdminCheck.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
|
||||
class AdminCheck
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle($request, Closure $next)
|
||||
{
|
||||
// Check if user have access to administration settings
|
||||
if ( ! Gate::allows('admin-settings')) {
|
||||
abort(403, 'You don\'t have access for this operation!');
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,6 @@ class VerifyCsrfToken extends Middleware
|
||||
* @var array
|
||||
*/
|
||||
protected $except = [
|
||||
//
|
||||
'/deploy',
|
||||
];
|
||||
}
|
||||
|
||||
31
app/Http/Requests/Admin/ChangeRoleRequest.php
Normal file
31
app/Http/Requests/Admin/ChangeRoleRequest.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Admin;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class ChangeRoleRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'attributes' => 'required|array',
|
||||
'attributes.role' => 'required|string'
|
||||
];
|
||||
}
|
||||
}
|
||||
31
app/Http/Requests/Admin/ChangeStorageCapacityRequest.php
Normal file
31
app/Http/Requests/Admin/ChangeStorageCapacityRequest.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Admin;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class ChangeStorageCapacityRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'attributes' => 'required|array',
|
||||
'attributes.storage_capacity' => 'required|digits_between:1,9'
|
||||
];
|
||||
}
|
||||
}
|
||||
35
app/Http/Requests/Admin/CreateUserByAdmin.php
Normal file
35
app/Http/Requests/Admin/CreateUserByAdmin.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Admin;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class CreateUserByAdmin extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'email' => 'required|string|email|max:255|unique:users',
|
||||
'password' => 'required|string|min:6|confirmed',
|
||||
'name' => 'required|string|max:255',
|
||||
'storage_capacity' => 'required|digits_between:1,9',
|
||||
'role' => 'required|string',
|
||||
'avatar' => 'sometimes|file',
|
||||
];
|
||||
}
|
||||
}
|
||||
30
app/Http/Requests/Admin/DeleteUserRequest.php
Normal file
30
app/Http/Requests/Admin/DeleteUserRequest.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Admin;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class DeleteUserRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'name' => 'required|string|max:255',
|
||||
];
|
||||
}
|
||||
}
|
||||
30
app/Http/Requests/Auth/CheckAccountRequest.php
Normal file
30
app/Http/Requests/Auth/CheckAccountRequest.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Auth;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class CheckAccountRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'email' => 'required|string|email',
|
||||
];
|
||||
}
|
||||
}
|
||||
30
app/Http/Requests/FileBrowser/SearchRequest.php
Normal file
30
app/Http/Requests/FileBrowser/SearchRequest.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\FileBrowser;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class SearchRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'query' => 'required|string',
|
||||
];
|
||||
}
|
||||
}
|
||||
38
app/Http/Resources/UserResource.php
Normal file
38
app/Http/Resources/UserResource.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use Faker\Factory;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class UserResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function toArray($request)
|
||||
{
|
||||
// Faker only for demo purpose
|
||||
$faker = Factory::create();
|
||||
|
||||
return [
|
||||
'data' => [
|
||||
'id' => (string)$this->id,
|
||||
'type' => 'user',
|
||||
'attributes' => [
|
||||
'name' => env('APP_DEMO') ? $faker->name : $this->name,
|
||||
'email' => env('APP_DEMO') ? $faker->email : $this->email,
|
||||
'avatar' => $this->avatar,
|
||||
'role' => $this->role,
|
||||
'storage' => $this->storage,
|
||||
'created_at_formatted' => format_date($this->created_at, '%d. %B. %Y'),
|
||||
'created_at' => $this->created_at,
|
||||
'updated_at' => $this->updated_at,
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
89
app/Http/Resources/UserStorageResource.php
Normal file
89
app/Http/Resources/UserStorageResource.php
Normal file
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use App\FileManagerFile;
|
||||
use ByteUnits\Metric;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class UserStorageResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function toArray($request)
|
||||
{
|
||||
$document_mimetypes = [
|
||||
'pdf', 'numbers', 'xlsx', 'xls', 'txt', 'md', 'rtf', 'pptx', 'ppt', 'odt', 'ods', 'odp', 'epub', 'docx', 'doc', 'csv', 'pages'
|
||||
];
|
||||
|
||||
// Get all images
|
||||
$images = FileManagerFile::where('user_id', $this->id)
|
||||
->where('type', 'image')->get()->map(function ($item) {
|
||||
return (int)$item->getOriginal('filesize');
|
||||
})->sum();
|
||||
|
||||
// Get all audios
|
||||
$audios = FileManagerFile::where('user_id', $this->id)
|
||||
->where('type', 'audio')->get()->map(function ($item) {
|
||||
return (int)$item->getOriginal('filesize');
|
||||
})->sum();
|
||||
|
||||
// Get all videos
|
||||
$videos = FileManagerFile::where('user_id', $this->id)
|
||||
->where('type', 'video')->get()->map(function ($item) {
|
||||
return (int)$item->getOriginal('filesize');
|
||||
})->sum();
|
||||
|
||||
// Get all documents
|
||||
$documents = FileManagerFile::where('user_id', $this->id)
|
||||
->whereIn('mimetype', $document_mimetypes)->get()->map(function ($item) {
|
||||
return (int)$item->getOriginal('filesize');
|
||||
})->sum();
|
||||
|
||||
// Get all other files
|
||||
$others = FileManagerFile::where('user_id', $this->id)
|
||||
->whereNotIn('mimetype', $document_mimetypes)
|
||||
->whereNotIn('type', ['audio', 'video', 'image'])
|
||||
->get()->map(function ($item) {
|
||||
return (int)$item->getOriginal('filesize');
|
||||
})->sum();
|
||||
|
||||
return [
|
||||
'data' => [
|
||||
'id' => (string)$this->id,
|
||||
'type' => 'user-storage',
|
||||
'attributes' => [
|
||||
'used' => Metric::bytes($this->used_capacity)->format(),
|
||||
'capacity' => format_gigabytes($this->settings->storage_capacity),
|
||||
'percentage' => (float)get_storage_fill_percentage($this->used_capacity, $this->settings->storage_capacity),
|
||||
],
|
||||
'meta' => [
|
||||
'images' => [
|
||||
'used' => Metric::bytes($images)->format(),
|
||||
'percentage' => (float)get_storage_fill_percentage($images, $this->settings->storage_capacity),
|
||||
],
|
||||
'audios' => [
|
||||
'used' => Metric::bytes($audios)->format(),
|
||||
'percentage' => (float)get_storage_fill_percentage($audios, $this->settings->storage_capacity),
|
||||
],
|
||||
'videos' => [
|
||||
'used' => Metric::bytes($videos)->format(),
|
||||
'percentage' => (float)get_storage_fill_percentage($videos, $this->settings->storage_capacity),
|
||||
],
|
||||
'documents' => [
|
||||
'used' => Metric::bytes($documents)->format(),
|
||||
'percentage' => (float)get_storage_fill_percentage($documents, $this->settings->storage_capacity),
|
||||
],
|
||||
'others' => [
|
||||
'used' => Metric::bytes($others)->format(),
|
||||
'percentage' => (float)get_storage_fill_percentage($others, $this->settings->storage_capacity),
|
||||
],
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
23
app/Http/Resources/UsersCollection.php
Normal file
23
app/Http/Resources/UsersCollection.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use Illuminate\Http\Resources\Json\ResourceCollection;
|
||||
|
||||
class UsersCollection extends ResourceCollection
|
||||
{
|
||||
public $collects = UserResource::class;
|
||||
|
||||
/**
|
||||
* Transform the resource collection into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function toArray($request)
|
||||
{
|
||||
return [
|
||||
'data' => $this->collection,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -121,10 +121,10 @@ class Editor
|
||||
foreach ($files as $file) {
|
||||
|
||||
// Delete file
|
||||
Storage::disk('local')->delete('/file-manager/' . $file->basename);
|
||||
Storage::delete('/file-manager/' . $file->basename);
|
||||
|
||||
// Delete thumbnail if exist
|
||||
if (!is_null($file->thumbnail)) Storage::disk('local')->delete('/file-manager/' . $file->getOriginal('thumbnail'));
|
||||
if (!is_null($file->thumbnail)) Storage::delete('/file-manager/' . $file->getOriginal('thumbnail'));
|
||||
|
||||
// Delete file permanently
|
||||
$file->forceDelete();
|
||||
@@ -169,10 +169,10 @@ class Editor
|
||||
if ($request->force_delete) {
|
||||
|
||||
// Delete file
|
||||
Storage::disk('local')->delete('/file-manager/' . $file->basename);
|
||||
Storage::delete('/file-manager/' . $file->basename);
|
||||
|
||||
// Delete thumbnail if exist
|
||||
if ($file->thumbnail) Storage::disk('local')->delete('/file-manager/' . $file->getOriginal('thumbnail'));
|
||||
if ($file->thumbnail) Storage::delete('/file-manager/' . $file->getOriginal('thumbnail'));
|
||||
|
||||
// Delete file permanently
|
||||
$file->forceDelete();
|
||||
@@ -213,15 +213,15 @@ class Editor
|
||||
$thumbnail = null;
|
||||
|
||||
// create directory if not exist
|
||||
if (!Storage::disk('local')->exists($directory)) {
|
||||
Storage::disk('local')->makeDirectory($directory);
|
||||
if (!Storage::exists($directory)) {
|
||||
Storage::makeDirectory($directory);
|
||||
}
|
||||
|
||||
// Store to disk
|
||||
Storage::disk('local')->putFileAs($directory, $file, $filename, 'public');
|
||||
Storage::putFileAs($directory, $file, $filename, 'private');
|
||||
|
||||
// Create image thumbnail
|
||||
if ($filetype == 'image') {
|
||||
if (in_array($file->getMimeType(), ['image/gif', 'image/jpeg', 'image/jpg', 'image/png', 'image/webp'])) {
|
||||
|
||||
// Get thumbnail name
|
||||
$thumbnail = 'thumbnail-' . $filename;
|
||||
@@ -230,12 +230,16 @@ class Editor
|
||||
$image = Image::make($file->getRealPath())->orientate();
|
||||
|
||||
// Resize image
|
||||
$image->resize(256, null, function ($constraint) {
|
||||
$image->resize(564, null, function ($constraint) {
|
||||
$constraint->aspectRatio();
|
||||
})->stream();
|
||||
|
||||
// Store thumbnail to disk
|
||||
Storage::disk('local')->put($directory . '/' . $thumbnail, $image);
|
||||
Storage::put($directory . '/' . $thumbnail, $image);
|
||||
|
||||
} elseif ($file->getMimeType() == 'image/svg+xml') {
|
||||
|
||||
$thumbnail = $filename;
|
||||
}
|
||||
|
||||
// Store file
|
||||
|
||||
@@ -11,6 +11,38 @@ use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
use Intervention\Image\ImageManagerStatic as Image;
|
||||
|
||||
/**
|
||||
* Get app version from config
|
||||
*
|
||||
* @return \Illuminate\Config\Repository|mixed
|
||||
*/
|
||||
function get_storage() {
|
||||
return env('FILESYSTEM_DRIVER');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if is running AWS s3 as storage
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
function is_storage_driver($driver) {
|
||||
|
||||
if (is_array($driver)) {
|
||||
return in_array(env('FILESYSTEM_DRIVER'), $driver);
|
||||
}
|
||||
|
||||
return env('FILESYSTEM_DRIVER') === $driver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get app version from config
|
||||
*
|
||||
* @return \Illuminate\Config\Repository|mixed
|
||||
*/
|
||||
function get_version() {
|
||||
return config('vuefilemanager.version');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if is demo
|
||||
*
|
||||
@@ -102,16 +134,19 @@ function store_avatar($image, $path)
|
||||
$path = check_directory($path);
|
||||
|
||||
// Store avatar
|
||||
$image_path = $path . '/' . Str::random(8) . '-' . $image->getClientOriginalName();
|
||||
$image_path = Str::random(8) . '-' . $image->getClientOriginalName();
|
||||
|
||||
// Create intervention image
|
||||
$img = Image::make($image->getRealPath());
|
||||
|
||||
// Generate thumbnail
|
||||
$img->fit('150', '150')->save(storage_path() . "/app/" . $image_path, 90);
|
||||
$img->fit('150', '150')->stream();
|
||||
|
||||
// Store thumbnail to disk
|
||||
Storage::put($path . '/' . $image_path, $img);
|
||||
|
||||
// Return path to image
|
||||
return $image_path;
|
||||
return $path . '/' . $image_path;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -153,9 +188,9 @@ function make_single_input($request)
|
||||
* @param $gigabytes
|
||||
* @return string
|
||||
*/
|
||||
function format_gigabytes($megabytes)
|
||||
function format_gigabytes($gigabytes)
|
||||
{
|
||||
return Metric::megabytes($megabytes)->format();
|
||||
return Metric::gigabytes($gigabytes)->format();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -168,7 +203,7 @@ function format_gigabytes($megabytes)
|
||||
function get_storage_fill_percentage($used, $capacity)
|
||||
{
|
||||
// Format gigabytes to bytes
|
||||
$total = intval(Metric::megabytes($capacity)->numberOfBytes());
|
||||
$total = intval(Metric::gigabytes($capacity)->numberOfBytes());
|
||||
|
||||
// Count progress
|
||||
$progress = ($used * 100) / $total;
|
||||
@@ -186,7 +221,7 @@ function user_storage_percentage()
|
||||
{
|
||||
$user = Auth::user();
|
||||
|
||||
return get_storage_fill_percentage($user->used_capacity, config('vuefilemanager.user_storage_capacity'));
|
||||
return get_storage_fill_percentage($user->used_capacity, $user->settings->storage_capacity);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -43,10 +43,12 @@ class ResetPassword extends Notification
|
||||
$reset_url = url('/create-new-password?token=' . $this->token);
|
||||
|
||||
return (new MailMessage)
|
||||
->subject('Reset password for your account on ' . config('vuefilemanager.app_name'))
|
||||
->line('You are receiving this email because we received a password reset request for your account.')
|
||||
->action('Reset Password', $reset_url)
|
||||
->line('If you did not request a password reset, no further action is required.');
|
||||
->subject(__('vuefilemanager.reset_password_subject') . config('vuefilemanager.app_name'))
|
||||
->greeting(__('vuefilemanager.reset_password_greeting'))
|
||||
->line(__('vuefilemanager.reset_password_line_1'))
|
||||
->action(__('vuefilemanager.reset_password_action'), $reset_url)
|
||||
->line(__('vuefilemanager.reset_password_line_2'))
|
||||
->salutation(__('vuefilemanager.salutation') . ', ' . config('vuefilemanager.app_name'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -26,6 +26,11 @@ class AuthServiceProvider extends ServiceProvider
|
||||
{
|
||||
$this->registerPolicies();
|
||||
|
||||
// Define admin settings gate
|
||||
Gate::define('admin-settings', function ($user) {
|
||||
return $user->role === 'admin';
|
||||
});
|
||||
|
||||
Passport::routes();
|
||||
|
||||
Passport::tokensCan([
|
||||
|
||||
32
app/User.php
32
app/User.php
@@ -63,7 +63,7 @@ class User extends Authenticatable
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name', 'email', 'password', 'avatar',
|
||||
'name', 'email', 'password', 'avatar', 'role',
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -86,9 +86,23 @@ class User extends Authenticatable
|
||||
];
|
||||
|
||||
protected $appends = [
|
||||
'used_capacity'
|
||||
'used_capacity', 'storage'
|
||||
];
|
||||
|
||||
/**
|
||||
* Get user used storage details
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getStorageAttribute() {
|
||||
|
||||
return [
|
||||
'used' => (float) get_storage_fill_percentage($this->used_capacity, $this->settings->storage_capacity),
|
||||
'capacity' => $this->settings->storage_capacity,
|
||||
'capacity_formatted' => Metric::gigabytes($this->settings->storage_capacity)->format(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user used storage capacity in bytes
|
||||
*
|
||||
@@ -135,7 +149,7 @@ class User extends Authenticatable
|
||||
*/
|
||||
public function favourites()
|
||||
{
|
||||
return $this->belongsToMany(FileManagerFolder::class, 'favourite_folder', 'user_id', 'folder_unique_id', 'id', 'unique_id')->select(['unique_id', 'name', 'type']);
|
||||
return $this->belongsToMany(FileManagerFolder::class, 'favourite_folder', 'user_id', 'folder_unique_id', 'id', 'unique_id')->with('shared:token,id,item_id,permission,protected');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -145,7 +159,7 @@ class User extends Authenticatable
|
||||
*/
|
||||
public function latest_uploads() {
|
||||
|
||||
return $this->hasMany(FileManagerFile::class)->orderBy('created_at', 'DESC')->take(7);
|
||||
return $this->hasMany(FileManagerFile::class)->with(['parent'])->orderBy('created_at', 'DESC')->take(40);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -167,4 +181,14 @@ class User extends Authenticatable
|
||||
|
||||
return $this->hasMany(FileManagerFile::class)->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user attributes
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasOne
|
||||
*/
|
||||
public function settings() {
|
||||
|
||||
return $this->hasOne(UserSettings::class);
|
||||
}
|
||||
}
|
||||
|
||||
12
app/UserSettings.php
Normal file
12
app/UserSettings.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class UserSettings extends Model
|
||||
{
|
||||
public $timestamps = false;
|
||||
|
||||
protected $guarded = ['id'];
|
||||
}
|
||||
@@ -18,6 +18,8 @@
|
||||
"laravel/passport": "^8.4",
|
||||
"laravel/scout": "^7.2",
|
||||
"laravel/tinker": "^2.0",
|
||||
"league/flysystem-aws-s3-v3": "^1.0",
|
||||
"league/flysystem-cached-adapter": "^1.0",
|
||||
"teamtnt/laravel-scout-tntsearch-driver": "^7.2"
|
||||
},
|
||||
"require-dev": {
|
||||
|
||||
283
composer.lock
generated
283
composer.lock
generated
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "3e3a0dfbec5dd01d1b88b57db1c18bbf",
|
||||
"content-hash": "dd291c7d30131e81dbca3d2127e2fc0d",
|
||||
"packages": [
|
||||
{
|
||||
"name": "asm89/stack-cors",
|
||||
@@ -58,6 +58,90 @@
|
||||
],
|
||||
"time": "2019-12-24T22:41:47+00:00"
|
||||
},
|
||||
{
|
||||
"name": "aws/aws-sdk-php",
|
||||
"version": "3.137.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/aws/aws-sdk-php.git",
|
||||
"reference": "af91c2fc467a326e5bcb5665e4e2ad3d84d28be2"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/af91c2fc467a326e5bcb5665e4e2ad3d84d28be2",
|
||||
"reference": "af91c2fc467a326e5bcb5665e4e2ad3d84d28be2",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"ext-pcre": "*",
|
||||
"ext-simplexml": "*",
|
||||
"guzzlehttp/guzzle": "^5.3.3|^6.2.1|^7.0",
|
||||
"guzzlehttp/promises": "^1.0",
|
||||
"guzzlehttp/psr7": "^1.4.1",
|
||||
"mtdowling/jmespath.php": "^2.5",
|
||||
"php": ">=5.5"
|
||||
},
|
||||
"require-dev": {
|
||||
"andrewsville/php-token-reflection": "^1.4",
|
||||
"aws/aws-php-sns-message-validator": "~1.0",
|
||||
"behat/behat": "~3.0",
|
||||
"doctrine/cache": "~1.4",
|
||||
"ext-dom": "*",
|
||||
"ext-openssl": "*",
|
||||
"ext-pcntl": "*",
|
||||
"ext-sockets": "*",
|
||||
"nette/neon": "^2.3",
|
||||
"phpunit/phpunit": "^4.8.35|^5.4.3",
|
||||
"psr/cache": "^1.0",
|
||||
"psr/simple-cache": "^1.0",
|
||||
"sebastian/comparator": "^1.2.3"
|
||||
},
|
||||
"suggest": {
|
||||
"aws/aws-php-sns-message-validator": "To validate incoming SNS notifications",
|
||||
"doctrine/cache": "To use the DoctrineCacheAdapter",
|
||||
"ext-curl": "To send requests using cURL",
|
||||
"ext-openssl": "Allows working with CloudFront private distributions and verifying received SNS messages",
|
||||
"ext-sockets": "To use client-side monitoring"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.0-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Aws\\": "src/"
|
||||
},
|
||||
"files": [
|
||||
"src/functions.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"Apache-2.0"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Amazon Web Services",
|
||||
"homepage": "http://aws.amazon.com"
|
||||
}
|
||||
],
|
||||
"description": "AWS SDK for PHP - Use Amazon Web Services in your PHP project",
|
||||
"homepage": "http://aws.amazon.com/sdkforphp",
|
||||
"keywords": [
|
||||
"amazon",
|
||||
"aws",
|
||||
"cloud",
|
||||
"dynamodb",
|
||||
"ec2",
|
||||
"glacier",
|
||||
"s3",
|
||||
"sdk"
|
||||
],
|
||||
"time": "2020-05-04T18:13:52+00:00"
|
||||
},
|
||||
{
|
||||
"name": "defuse/php-encryption",
|
||||
"version": "v2.2.1",
|
||||
@@ -1881,6 +1965,100 @@
|
||||
],
|
||||
"time": "2020-04-16T13:21:26+00:00"
|
||||
},
|
||||
{
|
||||
"name": "league/flysystem-aws-s3-v3",
|
||||
"version": "1.0.24",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/thephpleague/flysystem-aws-s3-v3.git",
|
||||
"reference": "4382036bde5dc926f9b8b337e5bdb15e5ec7b570"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/thephpleague/flysystem-aws-s3-v3/zipball/4382036bde5dc926f9b8b337e5bdb15e5ec7b570",
|
||||
"reference": "4382036bde5dc926f9b8b337e5bdb15e5ec7b570",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"aws/aws-sdk-php": "^3.0.0",
|
||||
"league/flysystem": "^1.0.40",
|
||||
"php": ">=5.5.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"henrikbjorn/phpspec-code-coverage": "~1.0.1",
|
||||
"phpspec/phpspec": "^2.0.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"League\\Flysystem\\AwsS3v3\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Frank de Jonge",
|
||||
"email": "info@frenky.net"
|
||||
}
|
||||
],
|
||||
"description": "Flysystem adapter for the AWS S3 SDK v3.x",
|
||||
"time": "2020-02-23T13:31:58+00:00"
|
||||
},
|
||||
{
|
||||
"name": "league/flysystem-cached-adapter",
|
||||
"version": "1.0.9",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/thephpleague/flysystem-cached-adapter.git",
|
||||
"reference": "08ef74e9be88100807a3b92cc9048a312bf01d6f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/thephpleague/flysystem-cached-adapter/zipball/08ef74e9be88100807a3b92cc9048a312bf01d6f",
|
||||
"reference": "08ef74e9be88100807a3b92cc9048a312bf01d6f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"league/flysystem": "~1.0",
|
||||
"psr/cache": "^1.0.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"mockery/mockery": "~0.9",
|
||||
"phpspec/phpspec": "^3.4",
|
||||
"phpunit/phpunit": "^5.7",
|
||||
"predis/predis": "~1.0",
|
||||
"tedivm/stash": "~0.12"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-phpredis": "Pure C implemented extension for PHP"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"League\\Flysystem\\Cached\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "frankdejonge",
|
||||
"email": "info@frenky.net"
|
||||
}
|
||||
],
|
||||
"description": "An adapter decorator to enable meta-data caching.",
|
||||
"time": "2018-07-09T20:51:04+00:00"
|
||||
},
|
||||
{
|
||||
"name": "league/oauth2-server",
|
||||
"version": "8.1.0",
|
||||
@@ -2039,6 +2217,63 @@
|
||||
],
|
||||
"time": "2019-12-20T14:22:59+00:00"
|
||||
},
|
||||
{
|
||||
"name": "mtdowling/jmespath.php",
|
||||
"version": "2.5.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/jmespath/jmespath.php.git",
|
||||
"reference": "52168cb9472de06979613d365c7f1ab8798be895"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/52168cb9472de06979613d365c7f1ab8798be895",
|
||||
"reference": "52168cb9472de06979613d365c7f1ab8798be895",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.4.0",
|
||||
"symfony/polyfill-mbstring": "^1.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"composer/xdebug-handler": "^1.2",
|
||||
"phpunit/phpunit": "^4.8.36|^7.5.15"
|
||||
},
|
||||
"bin": [
|
||||
"bin/jp.php"
|
||||
],
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.5-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"JmesPath\\": "src/"
|
||||
},
|
||||
"files": [
|
||||
"src/JmesPath.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Michael Dowling",
|
||||
"email": "mtdowling@gmail.com",
|
||||
"homepage": "https://github.com/mtdowling"
|
||||
}
|
||||
],
|
||||
"description": "Declaratively specify how to extract elements from a JSON document",
|
||||
"keywords": [
|
||||
"json",
|
||||
"jsonpath"
|
||||
],
|
||||
"time": "2019-12-30T18:03:34+00:00"
|
||||
},
|
||||
{
|
||||
"name": "nesbot/carbon",
|
||||
"version": "2.33.0",
|
||||
@@ -2528,6 +2763,52 @@
|
||||
],
|
||||
"time": "2020-04-04T23:17:33+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/cache",
|
||||
"version": "1.0.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/cache.git",
|
||||
"reference": "d11b50ad223250cf17b86e38383413f5a6764bf8"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/cache/zipball/d11b50ad223250cf17b86e38383413f5a6764bf8",
|
||||
"reference": "d11b50ad223250cf17b86e38383413f5a6764bf8",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Psr\\Cache\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "http://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"description": "Common interface for caching libraries",
|
||||
"keywords": [
|
||||
"cache",
|
||||
"psr",
|
||||
"psr-6"
|
||||
],
|
||||
"time": "2016-08-06T20:24:11+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/container",
|
||||
"version": "1.0.0",
|
||||
|
||||
@@ -233,6 +233,9 @@ return [
|
||||
|
||||
],
|
||||
|
||||
'deploy_secret' => env('APP_DEPLOY_SECRET'),
|
||||
'deploy_branch' => env('APP_DEPLOY_BRANCH'),
|
||||
|
||||
'debug_blacklist' => [
|
||||
'_ENV' => [
|
||||
'APP_KEY',
|
||||
|
||||
@@ -64,6 +64,15 @@ return [
|
||||
'url' => env('AWS_URL'),
|
||||
],
|
||||
|
||||
'spaces' => [
|
||||
'driver' => 's3',
|
||||
'key' => env('DO_SPACES_KEY'),
|
||||
'secret' => env('DO_SPACES_SECRET'),
|
||||
'endpoint' => env('DO_SPACES_ENDPOINT'),
|
||||
'region' => env('DO_SPACES_REGION'),
|
||||
'bucket' => env('DO_SPACES_BUCKET'),
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
];
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
return [
|
||||
|
||||
'version' => '1.6.3',
|
||||
|
||||
// Your app name
|
||||
'app_name' => 'VueFileManager',
|
||||
|
||||
@@ -13,7 +15,4 @@ return [
|
||||
|
||||
// Limit your storage size for every user if this option is enabled
|
||||
'limit_storage_by_capacity' => true,
|
||||
|
||||
// Define user storage capacity in MB. E.g. value 2000 is 2.00GB
|
||||
'user_storage_capacity' => 300,
|
||||
];
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddRoleToUsersTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->enum('role', ['admin', 'user'])->default('user')->after('id');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
//
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateUserSettingsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('user_settings', function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->integer('user_id');
|
||||
$table->integer('storage_capacity')->default(5);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('user_attributes');
|
||||
}
|
||||
}
|
||||
@@ -1,273 +0,0 @@
|
||||
# ************************************************************
|
||||
# Sequel Pro SQL dump
|
||||
# Version 4541
|
||||
#
|
||||
# http://www.sequelpro.com/
|
||||
# https://github.com/sequelpro/sequelpro
|
||||
#
|
||||
# Host: 127.0.0.1 (MySQL 5.7.25)
|
||||
# Database: file-manager
|
||||
# Generation Time: 2020-04-03 07:57:39 +0000
|
||||
# ************************************************************
|
||||
|
||||
|
||||
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
|
||||
/*!40101 SET NAMES utf8 */;
|
||||
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
|
||||
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
|
||||
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
|
||||
|
||||
|
||||
# Dump of table failed_jobs
|
||||
# ------------------------------------------------------------
|
||||
|
||||
DROP TABLE IF EXISTS `failed_jobs`;
|
||||
|
||||
CREATE TABLE `failed_jobs` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`connection` text COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`queue` text COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`payload` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`exception` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`failed_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
|
||||
|
||||
# Dump of table favourite_folder
|
||||
# ------------------------------------------------------------
|
||||
|
||||
DROP TABLE IF EXISTS `favourite_folder`;
|
||||
|
||||
CREATE TABLE `favourite_folder` (
|
||||
`user_id` bigint(20) NOT NULL,
|
||||
`folder_unique_id` bigint(20) NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
|
||||
|
||||
# Dump of table file_manager_files
|
||||
# ------------------------------------------------------------
|
||||
|
||||
DROP TABLE IF EXISTS `file_manager_files`;
|
||||
|
||||
CREATE TABLE `file_manager_files` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`user_id` bigint(20) DEFAULT NULL,
|
||||
`unique_id` int(11) NOT NULL,
|
||||
`folder_id` int(11) NOT NULL DEFAULT '0',
|
||||
`thumbnail` text COLLATE utf8mb4_unicode_ci,
|
||||
`name` text COLLATE utf8mb4_unicode_ci,
|
||||
`basename` text COLLATE utf8mb4_unicode_ci,
|
||||
`mimetype` text COLLATE utf8mb4_unicode_ci,
|
||||
`filesize` text COLLATE utf8mb4_unicode_ci,
|
||||
`type` text COLLATE utf8mb4_unicode_ci,
|
||||
`deleted_at` timestamp NULL DEFAULT NULL,
|
||||
`created_at` timestamp NULL DEFAULT NULL,
|
||||
`updated_at` timestamp NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
|
||||
|
||||
# Dump of table file_manager_folders
|
||||
# ------------------------------------------------------------
|
||||
|
||||
DROP TABLE IF EXISTS `file_manager_folders`;
|
||||
|
||||
CREATE TABLE `file_manager_folders` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`user_id` bigint(20) DEFAULT NULL,
|
||||
`unique_id` int(11) NOT NULL,
|
||||
`parent_id` int(11) NOT NULL DEFAULT '0',
|
||||
`name` text COLLATE utf8mb4_unicode_ci,
|
||||
`type` text COLLATE utf8mb4_unicode_ci,
|
||||
`deleted_at` timestamp NULL DEFAULT NULL,
|
||||
`created_at` timestamp NULL DEFAULT NULL,
|
||||
`updated_at` timestamp NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
|
||||
|
||||
# Dump of table migrations
|
||||
# ------------------------------------------------------------
|
||||
|
||||
DROP TABLE IF EXISTS `migrations`;
|
||||
|
||||
CREATE TABLE `migrations` (
|
||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`migration` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`batch` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
LOCK TABLES `migrations` WRITE;
|
||||
/*!40000 ALTER TABLE `migrations` DISABLE KEYS */;
|
||||
|
||||
INSERT INTO `migrations` (`id`, `migration`, `batch`)
|
||||
VALUES
|
||||
(1,'2014_10_12_000000_create_users_table',1),
|
||||
(2,'2014_10_12_100000_create_password_resets_table',1),
|
||||
(3,'2016_06_01_000001_create_oauth_auth_codes_table',1),
|
||||
(4,'2016_06_01_000002_create_oauth_access_tokens_table',1),
|
||||
(5,'2016_06_01_000003_create_oauth_refresh_tokens_table',1),
|
||||
(6,'2016_06_01_000004_create_oauth_clients_table',1),
|
||||
(7,'2016_06_01_000005_create_oauth_personal_access_clients_table',1),
|
||||
(8,'2019_08_15_171328_create_file_manager_folders',1),
|
||||
(9,'2019_08_15_171345_create_file_manager_files',1),
|
||||
(10,'2019_08_19_000000_create_failed_jobs_table',1),
|
||||
(11,'2020_03_03_065147_add_user_id_to_file_manager_files_table',2),
|
||||
(12,'2020_03_03_065155_add_user_id_to_file_manager_folders_table',2),
|
||||
(13,'2020_03_03_070319_create_favourites_folders_table',3),
|
||||
(14,'2020_04_02_055021_change_type_attribute_in_file_manager_files_table',4);
|
||||
|
||||
/*!40000 ALTER TABLE `migrations` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
|
||||
# Dump of table oauth_access_tokens
|
||||
# ------------------------------------------------------------
|
||||
|
||||
DROP TABLE IF EXISTS `oauth_access_tokens`;
|
||||
|
||||
CREATE TABLE `oauth_access_tokens` (
|
||||
`id` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`user_id` bigint(20) unsigned DEFAULT NULL,
|
||||
`client_id` bigint(20) unsigned NOT NULL,
|
||||
`name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`scopes` text COLLATE utf8mb4_unicode_ci,
|
||||
`revoked` tinyint(1) NOT NULL,
|
||||
`created_at` timestamp NULL DEFAULT NULL,
|
||||
`updated_at` timestamp NULL DEFAULT NULL,
|
||||
`expires_at` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `oauth_access_tokens_user_id_index` (`user_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
|
||||
|
||||
# Dump of table oauth_auth_codes
|
||||
# ------------------------------------------------------------
|
||||
|
||||
DROP TABLE IF EXISTS `oauth_auth_codes`;
|
||||
|
||||
CREATE TABLE `oauth_auth_codes` (
|
||||
`id` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`user_id` bigint(20) unsigned NOT NULL,
|
||||
`client_id` bigint(20) unsigned NOT NULL,
|
||||
`scopes` text COLLATE utf8mb4_unicode_ci,
|
||||
`revoked` tinyint(1) NOT NULL,
|
||||
`expires_at` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `oauth_auth_codes_user_id_index` (`user_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
|
||||
|
||||
# Dump of table oauth_clients
|
||||
# ------------------------------------------------------------
|
||||
|
||||
DROP TABLE IF EXISTS `oauth_clients`;
|
||||
|
||||
CREATE TABLE `oauth_clients` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`user_id` bigint(20) unsigned DEFAULT NULL,
|
||||
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`secret` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`redirect` text COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`personal_access_client` tinyint(1) NOT NULL,
|
||||
`password_client` tinyint(1) NOT NULL,
|
||||
`revoked` tinyint(1) NOT NULL,
|
||||
`created_at` timestamp NULL DEFAULT NULL,
|
||||
`updated_at` timestamp NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `oauth_clients_user_id_index` (`user_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
LOCK TABLES `oauth_clients` WRITE;
|
||||
/*!40000 ALTER TABLE `oauth_clients` DISABLE KEYS */;
|
||||
|
||||
INSERT INTO `oauth_clients` (`id`, `user_id`, `name`, `secret`, `redirect`, `personal_access_client`, `password_client`, `revoked`, `created_at`, `updated_at`)
|
||||
VALUES
|
||||
(1,1,'vue-filemanager-auth','oULKgESrN8egvBWW0DGNW3aE8yaHWISUODq3ZDRn','/',0,1,0,'2020-03-01 07:49:48','2020-03-01 07:49:48');
|
||||
|
||||
/*!40000 ALTER TABLE `oauth_clients` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
|
||||
# Dump of table oauth_personal_access_clients
|
||||
# ------------------------------------------------------------
|
||||
|
||||
DROP TABLE IF EXISTS `oauth_personal_access_clients`;
|
||||
|
||||
CREATE TABLE `oauth_personal_access_clients` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`client_id` bigint(20) unsigned NOT NULL,
|
||||
`created_at` timestamp NULL DEFAULT NULL,
|
||||
`updated_at` timestamp NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
|
||||
|
||||
# Dump of table oauth_refresh_tokens
|
||||
# ------------------------------------------------------------
|
||||
|
||||
DROP TABLE IF EXISTS `oauth_refresh_tokens`;
|
||||
|
||||
CREATE TABLE `oauth_refresh_tokens` (
|
||||
`id` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`access_token_id` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`revoked` tinyint(1) NOT NULL,
|
||||
`expires_at` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
|
||||
|
||||
# Dump of table password_resets
|
||||
# ------------------------------------------------------------
|
||||
|
||||
DROP TABLE IF EXISTS `password_resets`;
|
||||
|
||||
CREATE TABLE `password_resets` (
|
||||
`email` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`token` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`created_at` timestamp NULL DEFAULT NULL,
|
||||
KEY `password_resets_email_index` (`email`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
|
||||
|
||||
# Dump of table users
|
||||
# ------------------------------------------------------------
|
||||
|
||||
DROP TABLE IF EXISTS `users`;
|
||||
|
||||
CREATE TABLE `users` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`email` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`email_verified_at` timestamp NULL DEFAULT NULL,
|
||||
`password` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`avatar` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`remember_token` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`created_at` timestamp NULL DEFAULT NULL,
|
||||
`updated_at` timestamp NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `users_email_unique` (`email`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
|
||||
|
||||
|
||||
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
|
||||
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
|
||||
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
|
||||
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
|
||||
18
package-lock.json
generated
18
package-lock.json
generated
@@ -1671,6 +1671,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"babel-helper-vue-jsx-merge-props": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-2.0.3.tgz",
|
||||
"integrity": "sha512-gsLiKK7Qrb7zYJNgiXKpXblxbV5ffSwR0f5whkPAaBAR4fhi6bwRZxX9wBlIc5M/v8CCkXUbXZL4N/nSE97cqg=="
|
||||
},
|
||||
"babel-loader": {
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.1.0.tgz",
|
||||
@@ -2755,11 +2760,6 @@
|
||||
"timsort": "^0.3.0"
|
||||
}
|
||||
},
|
||||
"css-element-queries": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/css-element-queries/-/css-element-queries-1.2.3.tgz",
|
||||
"integrity": "sha512-QK9uovYmKTsV2GXWQiMOByVNrLn2qz6m3P7vWpOR4IdD6I3iXoDw5qtgJEN3Xq7gIbdHVKvzHjdAtcl+4Arc4Q=="
|
||||
},
|
||||
"css-loader": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/css-loader/-/css-loader-1.0.1.tgz",
|
||||
@@ -10359,6 +10359,14 @@
|
||||
"resolved": "https://registry.npmjs.org/vue/-/vue-2.6.11.tgz",
|
||||
"integrity": "sha512-VfPwgcGABbGAue9+sfrD4PuwFar7gPb1yl1UK1MwXoQPAw0BKSqWfoYCT/ThFrdEVWoI51dBuyCoiNU9bZDZxQ=="
|
||||
},
|
||||
"vue-feather-icons": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/vue-feather-icons/-/vue-feather-icons-5.0.0.tgz",
|
||||
"integrity": "sha512-uvi2l3i0aeRRzG3vX24/AH+8BkcXkSJboAv8XkUZ6aPkEC9n6LXDcKp5/ho+3moVQ9wlga3N6BjL89pmkHBzPw==",
|
||||
"requires": {
|
||||
"babel-helper-vue-jsx-merge-props": "^2.0.2"
|
||||
}
|
||||
},
|
||||
"vue-hot-reload-api": {
|
||||
"version": "2.3.4",
|
||||
"resolved": "https://registry.npmjs.org/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz",
|
||||
|
||||
@@ -21,11 +21,11 @@
|
||||
"@fortawesome/fontawesome-svg-core": "^1.2.28",
|
||||
"@fortawesome/free-solid-svg-icons": "^5.13.0",
|
||||
"@fortawesome/vue-fontawesome": "^0.1.9",
|
||||
"css-element-queries": "^1.2.3",
|
||||
"lodash": "^4.17.15",
|
||||
"node-sass": "^4.14.0",
|
||||
"vee-validate": "^3.3.0",
|
||||
"vue": "^2.6.10",
|
||||
"vue-feather-icons": "^5.0.0",
|
||||
"vue-i18n": "^8.17.4",
|
||||
"vue-router": "^3.1.6",
|
||||
"vuex": "^3.3.0"
|
||||
|
||||
4
public/css/app.css
vendored
4
public/css/app.css
vendored
File diff suppressed because one or more lines are too long
BIN
public/favicon.png
Normal file
BIN
public/favicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.4 KiB |
2
public/js/main.js
vendored
2
public/js/main.js
vendored
File diff suppressed because one or more lines are too long
@@ -1,62 +1,72 @@
|
||||
<template>
|
||||
<div id="vue-file-manager" :class="appSize">
|
||||
<div id="vue-file-manager" v-cloak>
|
||||
|
||||
<!--System alerts-->
|
||||
<Alert/>
|
||||
|
||||
<div id="application-wrapper" v-if="layout === 'authorized'">
|
||||
|
||||
<MobileNavigation />
|
||||
|
||||
<!--Share Item setup-->
|
||||
<ShareCreate />
|
||||
<ShareEdit />
|
||||
<ShareCreate/>
|
||||
<ShareEdit/>
|
||||
|
||||
<!--Move item setup-->
|
||||
<MoveItem />
|
||||
|
||||
<!--System alerts-->
|
||||
<Alert />
|
||||
<MoveItem/>
|
||||
|
||||
<!--Mobile Menu-->
|
||||
<MobileMenu />
|
||||
|
||||
<!--Background vignette-->
|
||||
<Vignette />
|
||||
<MobileMenu/>
|
||||
|
||||
<!--Navigation Sidebar-->
|
||||
<Sidebar/>
|
||||
<MenuBar/>
|
||||
|
||||
<!--Toastr-->
|
||||
<ToastrWrapper/>
|
||||
|
||||
<!--File page-->
|
||||
<router-view/>
|
||||
<keep-alive :include="['Admin', 'Users']">
|
||||
<router-view :class="{'is-scaled-down': isScaledDown}"/>
|
||||
</keep-alive>
|
||||
</div>
|
||||
|
||||
<router-view v-if="layout === 'unauthorized'"/>
|
||||
|
||||
<!--Background vignette-->
|
||||
<Vignette/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ToastrWrapper from '@/components/Others/Notifications/ToastrWrapper'
|
||||
import MobileNavigation from '@/components/Others/MobileNavigation'
|
||||
import MobileMenu from '@/components/FilesView/MobileMenu'
|
||||
import ShareCreate from '@/components/Others/ShareCreate'
|
||||
import ShareEdit from '@/components/Others/ShareEdit'
|
||||
import MoveItem from '@/components/Others/MoveItem'
|
||||
import Vignette from '@/components/Others/Vignette'
|
||||
import Sidebar from '@/components/Sidebar/Sidebar'
|
||||
import MenuBar from '@/components/Sidebar/MenuBar'
|
||||
import Alert from '@/components/FilesView/Alert'
|
||||
import {ResizeSensor} from 'css-element-queries'
|
||||
import { includes } from 'lodash'
|
||||
import {includes} from 'lodash'
|
||||
import {mapGetters} from 'vuex'
|
||||
import {events} from "./bus"
|
||||
|
||||
export default {
|
||||
name: 'app',
|
||||
components: {
|
||||
MobileNavigation,
|
||||
ToastrWrapper,
|
||||
ShareCreate,
|
||||
MobileMenu,
|
||||
ShareEdit,
|
||||
MoveItem,
|
||||
Vignette,
|
||||
Sidebar,
|
||||
MenuBar,
|
||||
Alert,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'appSize', 'isLogged', 'isGuest'
|
||||
'isLogged', 'isGuest'
|
||||
]),
|
||||
layout() {
|
||||
if (includes(['VerifyByPassword', 'SharedPage', 'NotFoundShared', 'SignIn', 'SignUp', 'ForgottenPassword', 'CreateNewPassword'], this.$route.name)) {
|
||||
@@ -66,34 +76,43 @@
|
||||
return 'authorized'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleAppResize() {
|
||||
let appView = document.getElementById('vue-file-manager')
|
||||
.offsetWidth
|
||||
|
||||
if (appView <= 690)
|
||||
this.$store.commit('SET_APP_WIDTH', 'small')
|
||||
if (appView > 690 && appView < 960)
|
||||
this.$store.commit('SET_APP_WIDTH', 'medium')
|
||||
if (appView > 960)
|
||||
this.$store.commit('SET_APP_WIDTH', 'large')
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isScaledDown: false,
|
||||
}
|
||||
},
|
||||
beforeMount() {
|
||||
|
||||
// Store config to vuex
|
||||
this.$store.commit('SET_AUTHORIZED', this.$root.$data.config.hasAuthCookie)
|
||||
this.$store.commit('SET_CONFIG', this.$root.$data.config)
|
||||
this.$store.commit('INIT', {
|
||||
authCookie: this.$root.$data.config.hasAuthCookie,
|
||||
config: this.$root.$data.config,
|
||||
rootDirectory: {
|
||||
name: this.$t('locations.home'),
|
||||
location: 'base',
|
||||
unique_id: 0,
|
||||
}
|
||||
})
|
||||
},
|
||||
mounted() {
|
||||
// Handle VueFileManager width
|
||||
var VueFileManager = document.getElementById('vue-file-manager');
|
||||
new ResizeSensor(VueFileManager, this.handleAppResize);
|
||||
// Handle mobile navigation scale animation
|
||||
events.$on('show:mobile-navigation', () => this.isScaledDown = true)
|
||||
events.$on('hide:mobile-navigation', () => this.isScaledDown = false)
|
||||
events.$on('mobileMenu:show', () => this.isScaledDown = true)
|
||||
events.$on('fileItem:deselect', () => this.isScaledDown = false)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import "@assets/app.scss";
|
||||
@import url('https://fonts.googleapis.com/css2?family=Nunito:wght@200;300;400;600;700;900&display=swap');
|
||||
@import '@assets/vue-file-manager/_variables';
|
||||
@import '@assets/vue-file-manager/_mixins';
|
||||
|
||||
[v-cloak],
|
||||
[v-cloak] > * {
|
||||
display: none
|
||||
}
|
||||
|
||||
* {
|
||||
outline: 0;
|
||||
@@ -107,19 +126,6 @@
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
// Dark mode support
|
||||
@media (prefers-color-scheme: dark) {
|
||||
|
||||
body, html {
|
||||
background: $dark_mode_background;
|
||||
color: $dark_mode_text_primary;
|
||||
|
||||
img {
|
||||
opacity: .8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#auth {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
@@ -131,4 +137,24 @@
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 690px) {
|
||||
|
||||
.is-scaled-down {
|
||||
@include transform(scale(0.95));
|
||||
}
|
||||
}
|
||||
|
||||
// Dark mode support
|
||||
@media (prefers-color-scheme: dark) {
|
||||
|
||||
body, html {
|
||||
background: $dark_mode_background;
|
||||
color: $dark_mode_text_primary;
|
||||
|
||||
img {
|
||||
opacity: .95;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -28,7 +28,8 @@
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "@assets/app.scss";
|
||||
@import '@assets/vue-file-manager/_variables';
|
||||
@import '@assets/vue-file-manager/_mixins';
|
||||
|
||||
.button {
|
||||
cursor: pointer;
|
||||
|
||||
@@ -19,8 +19,3 @@
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
|
||||
</style>
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "@assets/app.scss";
|
||||
|
||||
#auth {
|
||||
height: 100%;
|
||||
|
||||
@@ -88,7 +88,8 @@
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "@assets/app.scss";
|
||||
@import '@assets/vue-file-manager/_variables';
|
||||
@import '@assets/vue-file-manager/_mixins';
|
||||
|
||||
.popup {
|
||||
position: absolute;
|
||||
@@ -136,7 +137,7 @@
|
||||
|
||||
.message {
|
||||
@include font-size(16);
|
||||
color: #8b8f9a;
|
||||
color: #333;
|
||||
margin-top: 5px;
|
||||
}
|
||||
}
|
||||
@@ -149,8 +150,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Small screen size
|
||||
.small {
|
||||
@media only screen and (max-width: 690px) {
|
||||
.popup-wrapper {
|
||||
padding: 40px 20px 20px;
|
||||
left: 15px;
|
||||
|
||||
@@ -15,10 +15,11 @@
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "@assets/app.scss";
|
||||
@import '@assets/vue-file-manager/_variables';
|
||||
@import '@assets/vue-file-manager/_mixins';
|
||||
|
||||
.button-base {
|
||||
@include font-size(16);
|
||||
@include font-size(15);
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
transition: 0.15s all ease;
|
||||
@@ -26,6 +27,7 @@
|
||||
border: 0;
|
||||
padding: 10px 28px;
|
||||
display: inline-block;
|
||||
white-space: nowrap;
|
||||
|
||||
&:active {
|
||||
transform: scale(0.95);
|
||||
|
||||
@@ -31,10 +31,11 @@
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "@assets/app.scss";
|
||||
@import '@assets/vue-file-manager/_variables';
|
||||
@import '@assets/vue-file-manager/_mixins';
|
||||
|
||||
.button-base {
|
||||
@include font-size(16);
|
||||
@include font-size(15);
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
transition: 0.15s all ease;
|
||||
|
||||
@@ -7,105 +7,272 @@
|
||||
ref="contextmenu"
|
||||
>
|
||||
<!--ContextMenu for trash location-->
|
||||
<ul v-if="$isThisLocation(['trash', 'trash-root']) && $checkPermission('master')" class="menu-options" ref="list">
|
||||
<li class="menu-option" @click="deleteItem" v-if="item">
|
||||
{{ $t('context_menu.delete') }}
|
||||
</li>
|
||||
<li class="menu-option" @click="$store.dispatch('restoreItem', item)" v-if="item">
|
||||
{{ $t('context_menu.restore') }}
|
||||
</li>
|
||||
<li class="menu-option" @click="$store.dispatch('emptyTrash')">
|
||||
{{ $t('context_menu.empty_trash') }}
|
||||
</li>
|
||||
<li class="menu-option" @click="ItemDetail" v-if="item">
|
||||
{{ $t('context_menu.detail') }}
|
||||
</li>
|
||||
<li class="menu-option" @click="downloadItem" v-if="! isFolder && item">
|
||||
{{ $t('context_menu.download') }}
|
||||
</li>
|
||||
</ul>
|
||||
<div v-if="$isThisLocation(['trash', 'trash-root']) && $checkPermission('master')" 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">
|
||||
<life-buoy-icon size="17"></life-buoy-icon>
|
||||
</div>
|
||||
<div class="text-label">
|
||||
{{ $t('context_menu.restore') }}
|
||||
</div>
|
||||
</li>
|
||||
<li class="menu-option" @click="deleteItem" v-if="item">
|
||||
<div class="icon">
|
||||
<trash-2-icon size="17"></trash-2-icon>
|
||||
</div>
|
||||
<div class="text-label">
|
||||
{{ $t('context_menu.delete') }}
|
||||
</div>
|
||||
</li>
|
||||
<li class="menu-option" @click="$store.dispatch('emptyTrash')">
|
||||
<div class="icon">
|
||||
<trash-icon size="17"></trash-icon>
|
||||
</div>
|
||||
<div class="text-label">
|
||||
{{ $t('context_menu.empty_trash') }}
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="menu-option-group" v-if="item">
|
||||
<li class="menu-option" @click="ItemDetail">
|
||||
<div class="icon">
|
||||
<eye-icon size="17"></eye-icon>
|
||||
</div>
|
||||
<div class="text-label">
|
||||
{{ $t('context_menu.detail') }}
|
||||
</div>
|
||||
</li>
|
||||
<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 Base location with MASTER permission-->
|
||||
<ul v-if="$isThisLocation(['shared']) && $checkPermission('master')" class="menu-options" ref="list">
|
||||
<li class="menu-option" @click="addToFavourites" v-if="item && isFolder">
|
||||
{{ isInFavourites ? $t('context_menu.remove_from_favourites') : $t('context_menu.add_to_favourites') }}
|
||||
</li>
|
||||
<li class="menu-option" @click="deleteItem" v-if="item">
|
||||
{{ $t('context_menu.delete') }}
|
||||
</li>
|
||||
<li class="menu-option" @click="shareItem" v-if="item">
|
||||
{{ item.shared ? $t('context_menu.share_edit') : $t('context_menu.share') }}
|
||||
</li>
|
||||
<li class="menu-option" @click="ItemDetail" v-if="item">
|
||||
{{ $t('context_menu.detail') }}
|
||||
</li>
|
||||
<li class="menu-option" @click="downloadItem" v-if="! isFolder && item">
|
||||
{{ $t('context_menu.download') }}
|
||||
</li>
|
||||
</ul>
|
||||
<div v-if="$isThisLocation(['shared']) && $checkPermission('master')" 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') }}
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="menu-option-group" v-if="item">
|
||||
<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" v-if="item">
|
||||
<li class="menu-option" @click="ItemDetail" v-if="item">
|
||||
<div class="icon">
|
||||
<eye-icon size="17"></eye-icon>
|
||||
</div>
|
||||
<div class="text-label">
|
||||
{{ $t('context_menu.detail') }}
|
||||
</div>
|
||||
</li>
|
||||
<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 Base location with MASTER permission-->
|
||||
<ul v-if="$isThisLocation(['base']) && $checkPermission('master')" class="menu-options" ref="list">
|
||||
<li class="menu-option" @click="addToFavourites" v-if="item && isFolder">
|
||||
{{ isInFavourites ? $t('context_menu.remove_from_favourites') : $t('context_menu.add_to_favourites') }}
|
||||
</li>
|
||||
<li class="menu-option" @click="createFolder">
|
||||
{{ $t('context_menu.create_folder') }}
|
||||
</li>
|
||||
<li class="menu-option" @click="deleteItem" v-if="item">
|
||||
{{ $t('context_menu.delete') }}
|
||||
</li>
|
||||
<li class="menu-option" @click="moveItem" v-if="item">
|
||||
{{ $t('context_menu.move') }}
|
||||
</li>
|
||||
<li class="menu-option" @click="shareItem" v-if="item">
|
||||
{{ item.shared ? $t('context_menu.share_edit') : $t('context_menu.share') }}
|
||||
</li>
|
||||
<li class="menu-option" @click="ItemDetail" v-if="item">
|
||||
{{ $t('context_menu.detail') }}
|
||||
</li>
|
||||
<li class="menu-option" @click="downloadItem" v-if="! isFolder && item">
|
||||
{{ $t('context_menu.download') }}
|
||||
</li>
|
||||
</ul>
|
||||
<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'])">
|
||||
<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') }}
|
||||
</div>
|
||||
</li>
|
||||
<li class="menu-option" @click="createFolder">
|
||||
<div class="icon">
|
||||
<folder-plus-icon size="17"></folder-plus-icon>
|
||||
</div>
|
||||
<div class="text-label">
|
||||
{{ $t('context_menu.create_folder') }}
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="menu-option-group" v-if="item">
|
||||
<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" v-if="item">
|
||||
<li class="menu-option" @click="ItemDetail">
|
||||
<div class="icon">
|
||||
<eye-icon size="17"></eye-icon>
|
||||
</div>
|
||||
<div class="text-label">
|
||||
{{ $t('context_menu.detail') }}
|
||||
</div>
|
||||
</li>
|
||||
<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 Base location with EDITOR permission-->
|
||||
<ul v-if="$isThisLocation(['base', 'public']) && $checkPermission('editor')" class="menu-options" ref="list">
|
||||
<li class="menu-option" @click="createFolder">
|
||||
{{ $t('context_menu.create_folder') }}
|
||||
</li>
|
||||
<li class="menu-option" @click="deleteItem" v-if="item">
|
||||
{{ $t('context_menu.delete') }}
|
||||
</li>
|
||||
<li class="menu-option" @click="moveItem" v-if="item">
|
||||
{{ $t('context_menu.move') }}
|
||||
</li>
|
||||
<li class="menu-option" @click="ItemDetail" v-if="item">
|
||||
{{ $t('context_menu.detail') }}
|
||||
</li>
|
||||
<li class="menu-option" @click="downloadItem" v-if="! isFolder && item">
|
||||
{{ $t('context_menu.download') }}
|
||||
</li>
|
||||
</ul>
|
||||
<div v-if="$isThisLocation(['base', 'public']) && $checkPermission('editor')" id="menu-list" class="menu-options">
|
||||
<ul class="menu-option-group">
|
||||
<li class="menu-option" @click="createFolder">
|
||||
<div class="icon">
|
||||
<folder-plus-icon size="17"></folder-plus-icon>
|
||||
</div>
|
||||
<div class="text-label">
|
||||
{{ $t('context_menu.create_folder') }}
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="menu-option-group" v-if="item">
|
||||
<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="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" v-if="item">
|
||||
<li class="menu-option" @click="ItemDetail">
|
||||
<div class="icon">
|
||||
<eye-icon size="17"></eye-icon>
|
||||
</div>
|
||||
<div class="text-label">
|
||||
{{ $t('context_menu.detail') }}
|
||||
</div>
|
||||
</li>
|
||||
<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 Base location with VISITOR permission-->
|
||||
<ul v-if="$isThisLocation(['base', 'public']) && $checkPermission('visitor')" class="menu-options" ref="list">
|
||||
<li class="menu-option" @click="ItemDetail" v-if="item">
|
||||
{{ $t('context_menu.detail') }}
|
||||
</li>
|
||||
<li class="menu-option" @click="downloadItem" v-if="! isFolder && item">
|
||||
{{ $t('context_menu.download') }}
|
||||
</li>
|
||||
</ul>
|
||||
<div v-if="$isThisLocation(['base', 'public']) && $checkPermission('visitor')" id="menu-list" class="menu-options">
|
||||
<ul class="menu-option-group" v-if="item">
|
||||
<li class="menu-option" @click="ItemDetail">
|
||||
<div class="icon">
|
||||
<eye-icon size="17"></eye-icon>
|
||||
</div>
|
||||
<div class="text-label">
|
||||
{{ $t('context_menu.detail') }}
|
||||
</div>
|
||||
</li>
|
||||
<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>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
CornerDownRightIcon,
|
||||
DownloadCloudIcon,
|
||||
FolderPlusIcon,
|
||||
LifeBuoyIcon,
|
||||
Trash2Icon,
|
||||
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,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['app']),
|
||||
isFolder() {
|
||||
@@ -145,7 +312,7 @@
|
||||
},
|
||||
addToFavourites() {
|
||||
// Check if folder is in favourites and then add/remove from favourites
|
||||
if (this.app.favourites && ! this.app.favourites.find(el => el.unique_id == this.item.unique_id)) {
|
||||
if (this.app.favourites && !this.app.favourites.find(el => el.unique_id == this.item.unique_id)) {
|
||||
this.$store.dispatch('addToFavourites', this.item)
|
||||
} else {
|
||||
this.$store.dispatch('removeFromFavourites', this.item)
|
||||
@@ -159,6 +326,7 @@
|
||||
)
|
||||
},
|
||||
ItemDetail() {
|
||||
|
||||
// Dispatch load file info detail
|
||||
this.$store.commit('GET_FILEINFO_DETAIL', this.item)
|
||||
|
||||
@@ -180,8 +348,20 @@
|
||||
// Reset item container
|
||||
this.item = undefined
|
||||
},
|
||||
showContextMenu(event, item) {
|
||||
let VerticalOffsetArea = item ? this.$refs.list.children.length * 50 : 50
|
||||
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')
|
||||
@@ -220,15 +400,42 @@
|
||||
})
|
||||
|
||||
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/app.scss";
|
||||
@import '@assets/vue-file-manager/_variables';
|
||||
@import '@assets/vue-file-manager/_mixins';
|
||||
|
||||
.menu-option {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.icon {
|
||||
margin-right: 20px;
|
||||
line-height: 0;
|
||||
}
|
||||
|
||||
.text-label {
|
||||
@include font-size(16);
|
||||
}
|
||||
}
|
||||
|
||||
.contextmenu {
|
||||
min-width: 190px;
|
||||
min-width: 250px;
|
||||
position: absolute;
|
||||
z-index: 99;
|
||||
box-shadow: $shadow;
|
||||
@@ -239,26 +446,47 @@
|
||||
&.showed {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.menu-options {
|
||||
list-style: none;
|
||||
.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 {
|
||||
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 {
|
||||
white-space: nowrap;
|
||||
font-weight: 700;
|
||||
@include font-size(15);
|
||||
padding: 15px 30px;
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
color: $text;
|
||||
&:hover {
|
||||
background: $light_background;
|
||||
|
||||
&:hover {
|
||||
background: $light_background;
|
||||
.text-label {
|
||||
color: $theme;
|
||||
}
|
||||
|
||||
path, line, polyline, rect, circle, polygon {
|
||||
stroke: $theme;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -268,11 +496,18 @@
|
||||
.contextmenu {
|
||||
background: $dark_mode_foreground;
|
||||
|
||||
.menu-options .menu-option {
|
||||
color: $dark_mode_text_primary;
|
||||
.menu-options {
|
||||
|
||||
&:hover {
|
||||
background: $dark_mode_background;
|
||||
.menu-option-group {
|
||||
border-color: $dark_mode_border_color;
|
||||
}
|
||||
|
||||
.menu-option {
|
||||
color: $dark_mode_text_primary;
|
||||
|
||||
&:hover {
|
||||
background: rgba($theme, 0.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
<template>
|
||||
<div id="desktop-toolbar" v-if="! $isMinimalScale()">
|
||||
<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">
|
||||
<FontAwesomeIcon
|
||||
v-if="browseHistory.length > 0"
|
||||
class="icon-back"
|
||||
icon="chevron-left"
|
||||
></FontAwesomeIcon>
|
||||
<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 @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/>
|
||||
@@ -27,21 +27,40 @@
|
||||
<!--Files controlls-->
|
||||
<div class="toolbar-button-wrapper" v-if="$checkPermission(['master', 'editor'])">
|
||||
<ToolbarButtonUpload
|
||||
source="upload"
|
||||
:class="{'is-inactive': canUploadInView}"
|
||||
:action="$t('actions.upload')"
|
||||
/>
|
||||
<ToolbarButton
|
||||
source="trash-alt"
|
||||
:action="$t('actions.delete')"
|
||||
@click.native="deleteItems"
|
||||
/>
|
||||
<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
|
||||
@@ -63,89 +82,108 @@
|
||||
|
||||
<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'
|
||||
|
||||
export default {
|
||||
name: 'ToolBar',
|
||||
components: {
|
||||
ToolbarButtonUpload,
|
||||
MoreHorizontalIcon,
|
||||
ChevronLeftIcon,
|
||||
UploadProgress,
|
||||
ToolbarButton,
|
||||
SearchBar
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'FilePreviewType',
|
||||
'fileInfoVisible',
|
||||
'fileInfoDetail',
|
||||
'currentFolder',
|
||||
'browseHistory',
|
||||
'homeDirectory',
|
||||
'FilePreviewType',
|
||||
]),
|
||||
directoryName() {
|
||||
return this.currentFolder ? this.currentFolder.name : this.homeDirectory.name
|
||||
},
|
||||
previousFolder() {
|
||||
const length = this.browseHistory.length - 2
|
||||
|
||||
return this.browseHistory[length] ? this.browseHistory[length] : this.homeDirectory
|
||||
},
|
||||
preview() {
|
||||
return this.FilePreviewType === 'list' ? 'th' : 'th-list'
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isSidebarMenu: false,
|
||||
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: {
|
||||
showSidebarMenu() {
|
||||
this.isSidebarMenu = ! this.isSidebarMenu
|
||||
events.$emit('show:sidebar')
|
||||
},
|
||||
goBack() {
|
||||
// Get previous folder
|
||||
let previousFolder = last(this.browseHistory)
|
||||
|
||||
if (this.previousFolder.location === 'trash-root') {
|
||||
if (! previousFolder)
|
||||
return
|
||||
|
||||
if (previousFolder.location === 'trash-root') {
|
||||
this.$store.dispatch('getTrash')
|
||||
this.$store.commit('FLUSH_BROWSER_HISTORY')
|
||||
|
||||
} else if (previousFolder.location === 'shared') {
|
||||
this.$store.dispatch('getShared')
|
||||
|
||||
} else {
|
||||
|
||||
if ( this.$isThisLocation('public') ) {
|
||||
this.$store.dispatch('browseShared', [this.previousFolder, true])
|
||||
this.$store.dispatch('browseShared', [{folder: previousFolder, back: true, init: false}])
|
||||
} else {
|
||||
this.$store.dispatch('getFolder', [this.previousFolder, true])
|
||||
this.$store.dispatch('getFolder', [{folder: previousFolder, back: true, init: false}])
|
||||
}
|
||||
}
|
||||
},
|
||||
deleteItems() {
|
||||
folderActions() {
|
||||
events.$emit('folder:actions', this.currentFolder)
|
||||
},
|
||||
deleteItem() {
|
||||
events.$emit('items:delete')
|
||||
},
|
||||
createFolder() {
|
||||
if (! this.$isThisLocation(['trash', 'trash-root']))
|
||||
this.$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})
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
// Listen for hide sidebar
|
||||
events.$on('show:content', () => {
|
||||
if (this.isSidebarMenu) this.isSidebarMenu = false
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "@assets/app.scss";
|
||||
|
||||
@import '@assets/vue-file-manager/_variables';
|
||||
@import '@assets/vue-file-manager/_mixins';
|
||||
|
||||
.toolbar-wrapper {
|
||||
padding-top: 15px;
|
||||
padding-bottom: 15px;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
display: flex;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
@@ -171,13 +209,48 @@
|
||||
.icon-back {
|
||||
vertical-align: middle;
|
||||
cursor: pointer;
|
||||
margin-right: 12px;
|
||||
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);
|
||||
}
|
||||
|
||||
&: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;
|
||||
@@ -201,7 +274,7 @@
|
||||
text-align: right;
|
||||
|
||||
.toolbar-button-wrapper {
|
||||
margin-left: 75px;
|
||||
margin-left: 28px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
|
||||
@@ -210,8 +283,21 @@
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
margin-left: 20px;
|
||||
.button {
|
||||
margin-left: 5px;
|
||||
|
||||
&.active {
|
||||
/deep/ svg {
|
||||
line, circle {
|
||||
stroke: $theme;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.is-inactive {
|
||||
opacity: 0.25;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
margin-left: 0;
|
||||
@@ -219,6 +305,33 @@
|
||||
}
|
||||
}
|
||||
|
||||
@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;
|
||||
@@ -229,6 +342,13 @@
|
||||
.back-directory-title {
|
||||
color: $dark_mode_text_primary;
|
||||
}
|
||||
|
||||
.folder-options {
|
||||
|
||||
&:hover {
|
||||
background: $dark_mode_foreground;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,21 +1,27 @@
|
||||
<template>
|
||||
<div class="empty-message">
|
||||
<div class="message">
|
||||
<FontAwesomeIcon class="icon" :icon="icon"/>
|
||||
<eye-off-icon v-if="icon === 'eye-off'" size="36" class="icon"></eye-off-icon>
|
||||
<p>{{ message }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { EyeOffIcon } from 'vue-feather-icons'
|
||||
|
||||
export default {
|
||||
name: 'EmptyMessage',
|
||||
props: ['icon', 'message']
|
||||
props: ['icon', 'message'],
|
||||
components: {
|
||||
EyeOffIcon
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "@assets/app.scss";
|
||||
@import '@assets/vue-file-manager/_variables';
|
||||
@import '@assets/vue-file-manager/_mixins';
|
||||
|
||||
.empty-message {
|
||||
text-align: center;
|
||||
@@ -29,26 +35,32 @@
|
||||
p {
|
||||
margin-top: 10px;
|
||||
max-width: 130px;
|
||||
@include font-size(14);
|
||||
@include font-size(13);
|
||||
font-weight: 500;
|
||||
color: $text-muted;
|
||||
}
|
||||
|
||||
.icon {
|
||||
@include font-size(36);
|
||||
color: $text;
|
||||
|
||||
path {
|
||||
fill: $text;
|
||||
path, line, polyline, rect, circle {
|
||||
stroke: $text;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.empty-message .message .icon {
|
||||
path {
|
||||
fill: $dark_mode_text_secondary;
|
||||
.empty-message {
|
||||
|
||||
.message {
|
||||
.icon {
|
||||
path, line, polyline, rect, circle {
|
||||
stroke: $dark_mode_text_secondary;
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
color: $dark_mode_text_secondary;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,14 +12,17 @@
|
||||
<h1 class="title">{{ $t('empty_page.title') }}</h1>
|
||||
</div>
|
||||
|
||||
<!--Trash empty message-->
|
||||
<div class="text-content" v-if="$isThisLocation(['participant_uploads']) && ! isLoading">
|
||||
<h1 class="title">{{ $t('messages.nothing_from_participants') }}</h1>
|
||||
</div>
|
||||
|
||||
<!--Base file browser empty message-->
|
||||
<div class="text-content" v-if="$isThisLocation(['base', 'public']) && !isLoading">
|
||||
<div class="text-content" v-if="$isThisLocation(['base', 'public', 'latest']) && !isLoading">
|
||||
<h1 class="title">{{ $t('empty_page.title') }}</h1>
|
||||
<p v-if="$checkPermission(['master', 'editor'])" class="description">{{ $t('empty_page.description') }}</p>
|
||||
<ButtonUpload
|
||||
v-if="$checkPermission(['master', 'editor'])"
|
||||
@input.native="$uploadFiles(files)"
|
||||
v-model="files"
|
||||
button-style="theme"
|
||||
>
|
||||
{{ $t('empty_page.call_to_action') }}
|
||||
@@ -49,19 +52,15 @@
|
||||
computed: {
|
||||
...mapGetters(['data', 'isLoading', 'currentFolder']),
|
||||
isEmpty() {
|
||||
return this.data.length == 0
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
files: undefined
|
||||
return this.data && this.data.length == 0
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "@assets/app.scss";
|
||||
@import '@assets/vue-file-manager/_variables';
|
||||
@import '@assets/vue-file-manager/_mixins';
|
||||
|
||||
.empty-page {
|
||||
position: absolute;
|
||||
@@ -85,14 +84,14 @@
|
||||
margin: 30px 0;
|
||||
|
||||
.title {
|
||||
@include font-size(24);
|
||||
@include font-size(20);
|
||||
color: $text;
|
||||
font-weight: 700;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.description {
|
||||
@include font-size(15);
|
||||
@include font-size(13);
|
||||
color: $text-muted;
|
||||
margin-bottom: 20px;
|
||||
display: block;
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<MobileToolbar />
|
||||
|
||||
<!--Searchbar-->
|
||||
<SearchBar v-if="$isMinimalScale()" class="mobile-search"/>
|
||||
<SearchBar class="mobile-search" />
|
||||
|
||||
<!--Mobile Actions-->
|
||||
<MobileActions />
|
||||
@@ -77,7 +77,7 @@
|
||||
<FileInfoPanel v-if="fileInfoDetail"/>
|
||||
|
||||
<!--If file info panel empty show message-->
|
||||
<EmptyMessage v-if="!fileInfoDetail" :message="$t('messages.nothing_to_preview')" icon="eye-slash"/>
|
||||
<EmptyMessage v-if="!fileInfoDetail" :message="$t('messages.nothing_to_preview')" icon="eye-off"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -209,7 +209,8 @@
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "@assets/app.scss";
|
||||
@import '@assets/vue-file-manager/_variables';
|
||||
@import '@assets/vue-file-manager/_mixins';
|
||||
|
||||
.button-upload {
|
||||
display: block;
|
||||
@@ -218,13 +219,13 @@
|
||||
}
|
||||
|
||||
.mobile-search {
|
||||
display: none;
|
||||
margin-bottom: 10px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.file-content {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
|
||||
&.is-dragging {
|
||||
@include transform(scale(0.99));
|
||||
@@ -232,6 +233,7 @@
|
||||
}
|
||||
|
||||
.files-container {
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
flex: 0 0 100%;
|
||||
@include transition(150ms);
|
||||
@@ -276,11 +278,71 @@
|
||||
transform: translateX(-20px);
|
||||
}
|
||||
|
||||
.file-leave-active {
|
||||
position: absolute;
|
||||
@media only screen and (min-width: 960px) {
|
||||
|
||||
.file-content {
|
||||
position: absolute;
|
||||
top: 72px;
|
||||
left: 15px;
|
||||
right: 15px;
|
||||
bottom: 0;
|
||||
@include transition;
|
||||
overflow-y: auto;
|
||||
|
||||
&.is-offset {
|
||||
margin-top: 50px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 660px) {
|
||||
@media only screen and (max-width: 960px) {
|
||||
|
||||
.file-info-container {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.mobile-search {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 690px) {
|
||||
|
||||
.files-container {
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
position: fixed;
|
||||
overflow-y: auto;
|
||||
|
||||
.file-list {
|
||||
|
||||
&.grid {
|
||||
grid-template-columns: repeat(auto-fill, 120px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.file-content {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
bottom: 0;
|
||||
@include transition;
|
||||
|
||||
&.is-offset {
|
||||
margin-top: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
.mobile-search {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.file-info-container {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -7,12 +7,15 @@
|
||||
<div class="flex">
|
||||
<div class="icon">
|
||||
<div class="icon-preview">
|
||||
<FontAwesomeIcon :icon="filePreviewIcon"></FontAwesomeIcon>
|
||||
<image-icon v-if="fileType === 'image'" size="21"></image-icon>
|
||||
<video-icon v-if="fileType === 'video'" size="21"></video-icon>
|
||||
<folder-icon v-if="fileType === 'folder'" size="21"></folder-icon>
|
||||
<file-icon v-if="fileType === 'file'" size="21"></file-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="file-info">
|
||||
<span ref="name" class="name">{{ fileInfoDetail.name }}</span>
|
||||
<span class="mimetype" v-if="fileInfoDetail.mimetype">{{ fileInfoDetail.mimetype }}</span>
|
||||
<span class="mimetype" v-if="fileInfoDetail.mimetype">.{{ fileInfoDetail.mimetype }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -42,8 +45,8 @@
|
||||
<li v-if="$checkPermission(['master'])" class="list-info-item">
|
||||
<b>{{ $t('file_detail.where') }}</b>
|
||||
<div class="action-button" @click="moveItem">
|
||||
<FontAwesomeIcon class="icon" icon="pencil-alt" />
|
||||
<span>{{ fileInfoDetail.parent ? fileInfoDetail.parent.name : $t('locations.home') }}</span>
|
||||
<edit-2-icon size="10" class="edit-icon"></edit-2-icon>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
@@ -51,11 +54,13 @@
|
||||
<li v-if="$checkPermission('master') && fileInfoDetail.shared" class="list-info-item">
|
||||
<b>{{ $t('file_detail.shared') }}</b>
|
||||
<div class="action-button" @click="shareItemOptions">
|
||||
<FontAwesomeIcon class="icon" :icon="sharedIcon" />
|
||||
<span>{{ sharedInfo }}</span>
|
||||
<edit-2-icon size="10" class="edit-icon"></edit-2-icon>
|
||||
</div>
|
||||
<div class="sharelink">
|
||||
<FontAwesomeIcon class="lock-icon" :icon="lockIcon" @click="shareItemOptions" />
|
||||
<lock-icon v-if="isLocked" @click="shareItemOptions" class="lock-icon" size="17"></lock-icon>
|
||||
<unlock-icon v-if="! isLocked" @click="shareItemOptions" class="lock-icon" size="17"></unlock-icon>
|
||||
|
||||
<CopyInput class="copy-sharelink" size="small" :value="fileInfoDetail.shared.link" />
|
||||
</div>
|
||||
</li>
|
||||
@@ -64,6 +69,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Edit2Icon, LockIcon, UnlockIcon, ImageIcon, VideoIcon, FolderIcon, FileIcon } from 'vue-feather-icons'
|
||||
import FilePreview from '@/components/FilesView/FilePreview'
|
||||
import CopyInput from '@/components/Others/Forms/CopyInput'
|
||||
import {mapGetters} from 'vuex'
|
||||
@@ -73,12 +79,20 @@
|
||||
name: 'FileInfoPanel',
|
||||
components: {
|
||||
FilePreview,
|
||||
FolderIcon,
|
||||
UnlockIcon,
|
||||
VideoIcon,
|
||||
CopyInput,
|
||||
ImageIcon,
|
||||
FileIcon,
|
||||
Edit2Icon,
|
||||
LockIcon,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['fileInfoDetail', 'permissionOptions']),
|
||||
filePreviewIcon() {
|
||||
switch (this.fileInfoDetail.type) {
|
||||
fileType() {
|
||||
return this.fileInfoDetail.type
|
||||
/* switch () {
|
||||
case 'folder':
|
||||
return 'folder'
|
||||
break;
|
||||
@@ -94,7 +108,7 @@
|
||||
case 'file':
|
||||
return 'file-audio'
|
||||
break;
|
||||
}
|
||||
}*/
|
||||
},
|
||||
sharedInfo() {
|
||||
|
||||
@@ -117,8 +131,8 @@
|
||||
return 'download'
|
||||
}
|
||||
},
|
||||
lockIcon() {
|
||||
return this.fileInfoDetail.shared.protected ? 'lock' : 'lock-open'
|
||||
isLocked() {
|
||||
return this.fileInfoDetail.shared.protected
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -135,15 +149,14 @@
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "@assets/app.scss";
|
||||
@import '@assets/vue-file-manager/_variables';
|
||||
@import '@assets/vue-file-manager/_mixins';
|
||||
|
||||
.file-info-content {
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
.file-headline {
|
||||
background: $light_background;
|
||||
padding: 12px;
|
||||
margin-bottom: 20px;
|
||||
border-radius: 8px;
|
||||
|
||||
@@ -153,37 +166,19 @@
|
||||
}
|
||||
|
||||
.icon-preview {
|
||||
height: 42px;
|
||||
width: 42px;
|
||||
border-radius: 8px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0;
|
||||
background: white;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
outline: none;
|
||||
border: none;
|
||||
|
||||
/deep/ svg {
|
||||
@include font-size(22);
|
||||
|
||||
path {
|
||||
fill: $theme;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.icon path {
|
||||
fill: $theme;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.file-info {
|
||||
padding-left: 12px;
|
||||
padding-left: 10px;
|
||||
width: 100%;
|
||||
word-break: break-all;
|
||||
|
||||
@@ -196,7 +191,7 @@
|
||||
}
|
||||
|
||||
.mimetype {
|
||||
@include font-size(14);
|
||||
@include font-size(12);
|
||||
font-weight: 600;
|
||||
color: $theme;
|
||||
display: block;
|
||||
@@ -205,11 +200,10 @@
|
||||
}
|
||||
|
||||
.list-info {
|
||||
padding-left: 12px;
|
||||
|
||||
.list-info-item {
|
||||
display: block;
|
||||
padding-top: 15px;
|
||||
padding-top: 20px;
|
||||
|
||||
&:first-child {
|
||||
padding-top: 0;
|
||||
@@ -218,14 +212,9 @@
|
||||
.action-button {
|
||||
cursor: pointer;
|
||||
|
||||
.icon {
|
||||
@include font-size(10);
|
||||
.edit-icon {
|
||||
display: inline-block;
|
||||
margin-right: 2px;
|
||||
|
||||
path {
|
||||
fill: $theme;
|
||||
}
|
||||
margin-left: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,22 +241,10 @@
|
||||
margin-top: 10px;
|
||||
|
||||
.lock-icon {
|
||||
@include font-size(10);
|
||||
display: inline-block;
|
||||
width: 10px;
|
||||
width: 15px;
|
||||
margin-right: 9px;
|
||||
cursor: pointer;
|
||||
|
||||
path {
|
||||
fill: $text;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
|
||||
path {
|
||||
fill: $theme;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.copy-sharelink {
|
||||
@@ -278,11 +255,6 @@
|
||||
@media (prefers-color-scheme: dark) {
|
||||
|
||||
.file-headline {
|
||||
background: $dark_mode_foreground;
|
||||
|
||||
.icon-preview {
|
||||
background: $dark_mode_background;
|
||||
}
|
||||
|
||||
.file-info {
|
||||
|
||||
@@ -313,14 +285,10 @@
|
||||
|
||||
.lock-icon {
|
||||
|
||||
path {
|
||||
fill: $dark_mode_text_primary;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
|
||||
path {
|
||||
fill: $theme;
|
||||
path, rect {
|
||||
stroke: $theme;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,12 +52,12 @@
|
||||
|
||||
<!--Shared Icon-->
|
||||
<div v-if="$checkPermission('master') && data.shared" class="item-shared">
|
||||
<FontAwesomeIcon class="shared-icon" icon="share"/>
|
||||
<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">
|
||||
<FontAwesomeIcon class="shared-icon" icon="user-edit"/>
|
||||
<user-plus-icon size="12" class="shared-icon"></user-plus-icon>
|
||||
</div>
|
||||
|
||||
<!--Filesize-->
|
||||
@@ -79,6 +79,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { LinkIcon, UserPlusIcon } from 'vue-feather-icons'
|
||||
import {debounce} from 'lodash'
|
||||
import {mapGetters} from 'vuex'
|
||||
import {events} from '@/bus'
|
||||
@@ -86,6 +87,10 @@
|
||||
export default {
|
||||
name: 'FileItemGrid',
|
||||
props: ['data'],
|
||||
components: {
|
||||
UserPlusIcon,
|
||||
LinkIcon,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'FilePreviewType', 'sharedDetail'
|
||||
@@ -155,9 +160,9 @@
|
||||
|
||||
// Go to folder
|
||||
if (this.$isThisLocation('public')) {
|
||||
this.$store.dispatch('browseShared', [this.data, false])
|
||||
this.$store.dispatch('browseShared', [{folder: this.data, back: false, init: false}])
|
||||
} else {
|
||||
this.$store.dispatch('getFolder', [this.data, false])
|
||||
this.$store.dispatch('getFolder', [{folder: this.data, back: false, init: false}])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,11 +192,11 @@
|
||||
}
|
||||
|
||||
if (this.isFolder) {
|
||||
// Go to folder
|
||||
|
||||
if (this.$isThisLocation('public')) {
|
||||
this.$store.dispatch('browseShared', [this.data, false])
|
||||
this.$store.dispatch('browseShared', [{folder: this.data, back: false, init: false}])
|
||||
} else {
|
||||
this.$store.dispatch('getFolder', [this.data, false])
|
||||
this.$store.dispatch('getFolder', [{folder: this.data, back: false, init: false}])
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -224,7 +229,8 @@
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "@assets/app.scss";
|
||||
@import '@assets/vue-file-manager/_variables';
|
||||
@import '@assets/vue-file-manager/_mixins';
|
||||
|
||||
.show-actions {
|
||||
cursor: pointer;
|
||||
@@ -240,6 +246,7 @@
|
||||
}
|
||||
|
||||
.file-wrapper {
|
||||
user-select: none;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
@@ -254,9 +261,9 @@
|
||||
|
||||
.item-size,
|
||||
.item-length {
|
||||
@include font-size(12);
|
||||
@include font-size(11);
|
||||
font-weight: 400;
|
||||
color: $text-muted;
|
||||
color: rgba($text, 0.7);
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
@@ -275,10 +282,10 @@
|
||||
}
|
||||
|
||||
.shared-icon {
|
||||
@include font-size(9);
|
||||
vertical-align: middle;
|
||||
|
||||
path {
|
||||
fill: $theme;
|
||||
path, circle, line {
|
||||
stroke: $theme;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -367,6 +374,7 @@
|
||||
left: 0;
|
||||
right: 0;
|
||||
color: $theme;
|
||||
@include font-size(12);
|
||||
font-weight: 600;
|
||||
user-select: none;
|
||||
max-width: 65px;
|
||||
@@ -404,14 +412,75 @@
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 960px) {
|
||||
|
||||
.file-wrapper {
|
||||
|
||||
.icon-item {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 690px) {
|
||||
.file-wrapper {
|
||||
|
||||
.file-item {
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
.icon-item {
|
||||
margin-bottom: 10px;
|
||||
height: 90px;
|
||||
|
||||
.file-icon {
|
||||
@include font-size(75);
|
||||
}
|
||||
|
||||
.file-icon-text {
|
||||
@include font-size(12);
|
||||
}
|
||||
|
||||
.folder-icon {
|
||||
@include font-size(75);
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.image {
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
}
|
||||
|
||||
.item-name .name {
|
||||
@include font-size(13);
|
||||
line-height: .9;
|
||||
max-height: 30px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.file-wrapper {
|
||||
|
||||
.icon-item .file-icon {
|
||||
.icon-item {
|
||||
|
||||
path {
|
||||
fill: $dark_mode_foreground;
|
||||
stroke: #2F3C54;
|
||||
.file-icon {
|
||||
|
||||
path {
|
||||
fill: $dark_mode_foreground;
|
||||
stroke: #2F3C54;
|
||||
}
|
||||
}
|
||||
|
||||
.folder-icon {
|
||||
|
||||
&.is-deleted {
|
||||
path {
|
||||
fill: lighten($dark_mode_foreground, 5%);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -430,9 +499,18 @@
|
||||
}
|
||||
}
|
||||
|
||||
.item-name .name {
|
||||
color: $dark_mode_text_primary;
|
||||
.item-name {
|
||||
|
||||
.name {
|
||||
color: $dark_mode_text_primary;
|
||||
}
|
||||
|
||||
.item-size,
|
||||
.item-length {
|
||||
color: $dark_mode_text_secondary;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -51,12 +51,12 @@
|
||||
|
||||
<!--Shared Icon-->
|
||||
<div v-if="$checkPermission('master') && data.shared" class="item-shared">
|
||||
<FontAwesomeIcon class="shared-icon" icon="share"/>
|
||||
<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">
|
||||
<FontAwesomeIcon class="shared-icon" icon="user-edit"/>
|
||||
<user-plus-icon size="12" class="shared-icon"></user-plus-icon>
|
||||
</div>
|
||||
|
||||
<!--Filesize and timestamp-->
|
||||
@@ -80,6 +80,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { LinkIcon, UserPlusIcon } from 'vue-feather-icons'
|
||||
import {debounce} from 'lodash'
|
||||
import {mapGetters} from 'vuex'
|
||||
import {events} from '@/bus'
|
||||
@@ -87,6 +88,10 @@
|
||||
export default {
|
||||
name: 'FileItemList',
|
||||
props: ['data'],
|
||||
components: {
|
||||
UserPlusIcon,
|
||||
LinkIcon,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['FilePreviewType']),
|
||||
isFolder() {
|
||||
@@ -164,9 +169,9 @@
|
||||
|
||||
// Go to folder
|
||||
if (this.$isThisLocation('public')) {
|
||||
this.$store.dispatch('browseShared', [this.data, false])
|
||||
this.$store.dispatch('browseShared', [{folder: this.data, back: false, init: false}])
|
||||
} else {
|
||||
this.$store.dispatch('getFolder', [this.data, false])
|
||||
this.$store.dispatch('getFolder', [{folder: this.data, back: false, init: false}])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,9 +199,9 @@
|
||||
if (this.isFolder) {
|
||||
|
||||
if (this.$isThisLocation('public')) {
|
||||
this.$store.dispatch('browseShared', [this.data, false])
|
||||
this.$store.dispatch('browseShared', [{folder: this.data, back: false, init: false}])
|
||||
} else {
|
||||
this.$store.dispatch('getFolder', [this.data, false])
|
||||
this.$store.dispatch('getFolder', [{folder: this.data, back: false, init: false}])
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -229,9 +234,11 @@
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "@assets/app.scss";
|
||||
@import '@assets/vue-file-manager/_variables';
|
||||
@import '@assets/vue-file-manager/_mixins';
|
||||
|
||||
.file-wrapper {
|
||||
user-select: none;
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
@@ -278,19 +285,19 @@
|
||||
}
|
||||
|
||||
.shared-icon {
|
||||
@include font-size(9);
|
||||
vertical-align: middle;
|
||||
|
||||
path {
|
||||
fill: $theme;
|
||||
path, circle, line {
|
||||
stroke: $theme;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.item-size,
|
||||
.item-length {
|
||||
@include font-size(12);
|
||||
@include font-size(11);
|
||||
font-weight: 400;
|
||||
color: $text-muted;
|
||||
color: rgba($text, 0.7);
|
||||
}
|
||||
|
||||
.name {
|
||||
@@ -412,16 +419,29 @@
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
|
||||
.file-wrapper {
|
||||
|
||||
.icon-item .file-icon {
|
||||
.icon-item {
|
||||
.file-icon {
|
||||
|
||||
path {
|
||||
fill: $dark_mode_foreground;
|
||||
stroke: #2F3C54;
|
||||
path {
|
||||
fill: $dark_mode_foreground;
|
||||
stroke: #2F3C54;
|
||||
}
|
||||
}
|
||||
|
||||
.folder-icon {
|
||||
|
||||
&.is-deleted {
|
||||
path {
|
||||
fill: lighten($dark_mode_foreground, 5%);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.file-item {
|
||||
|
||||
&:hover,
|
||||
@@ -437,8 +457,16 @@
|
||||
}
|
||||
}
|
||||
|
||||
.item-name .name {
|
||||
color: $dark_mode_text_primary;
|
||||
.item-name {
|
||||
|
||||
.name {
|
||||
color: $dark_mode_text_primary;
|
||||
}
|
||||
|
||||
.item-size,
|
||||
.item-length {
|
||||
color: $dark_mode_text_secondary;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,8 @@
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "@assets/app.scss";
|
||||
@import '@assets/vue-file-manager/_variables';
|
||||
@import '@assets/vue-file-manager/_mixins';
|
||||
|
||||
.preview {
|
||||
width: 100%;
|
||||
|
||||
@@ -1,24 +1,39 @@
|
||||
<template>
|
||||
<button class="mobile-action-button">
|
||||
<FontAwesomeIcon class="icon" :icon="icon"></FontAwesomeIcon>
|
||||
<span class="label">
|
||||
<slot></slot>
|
||||
</span>
|
||||
<div class="flex">
|
||||
<folder-plus-icon v-if="icon === 'folder-plus'" size="15" class="icon"></folder-plus-icon>
|
||||
<list-icon v-if="icon === 'th-list'" size="15" class="icon"></list-icon>
|
||||
<trash-icon v-if="icon === 'trash'" size="15" class="icon"></trash-icon>
|
||||
<grid-icon v-if="icon === 'th'" size="15" class="icon"></grid-icon>
|
||||
<user-plus-icon v-if="icon === 'user-plus'" size="15" class="icon"></user-plus-icon>
|
||||
<span class="label">
|
||||
<slot></slot>
|
||||
</span>
|
||||
</div>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { FolderPlusIcon, ListIcon, GridIcon, TrashIcon, UserPlusIcon } from 'vue-feather-icons'
|
||||
|
||||
export default {
|
||||
name: 'MobileActionButton',
|
||||
props: [
|
||||
'icon'
|
||||
],
|
||||
components: {
|
||||
FolderPlusIcon,
|
||||
UserPlusIcon,
|
||||
TrashIcon,
|
||||
ListIcon,
|
||||
GridIcon,
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "@assets/app.scss";
|
||||
|
||||
@import '@assets/vue-file-manager/_variables';
|
||||
@import '@assets/vue-file-manager/_mixins';
|
||||
|
||||
.mobile-action-button {
|
||||
background: $light_background;
|
||||
@@ -27,25 +42,54 @@
|
||||
padding: 7px 10px;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
@include transition(150ms);
|
||||
|
||||
.flex {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.icon {
|
||||
margin-right: 10px;
|
||||
@include font-size(14);
|
||||
|
||||
path, line, polyline, rect, circle {
|
||||
@include transition(150ms);
|
||||
}
|
||||
}
|
||||
|
||||
.label {
|
||||
@include transition(150ms);
|
||||
@include font-size(14);
|
||||
font-weight: 700;
|
||||
color: $text;
|
||||
}
|
||||
|
||||
&:active {
|
||||
@include transform(scale(0.95));
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: rgba($theme, 0.1);
|
||||
|
||||
.icon {
|
||||
path, line, polyline, rect, circle {
|
||||
stroke: $theme;
|
||||
}
|
||||
}
|
||||
|
||||
.label {
|
||||
color: $theme;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.mobile-action-button {
|
||||
background: $dark_mode_foreground;
|
||||
|
||||
.icon path {
|
||||
fill: $theme;
|
||||
path, line, polyline, rect, circle {
|
||||
stroke: $theme;
|
||||
}
|
||||
|
||||
.label {
|
||||
|
||||
@@ -1,31 +1,29 @@
|
||||
<template>
|
||||
<button class="mobile-action-button">
|
||||
<FontAwesomeIcon class="icon" :icon="icon"></FontAwesomeIcon>
|
||||
<label label="file" class="label button file-input button-base">
|
||||
<slot></slot>
|
||||
<input
|
||||
accept="*"
|
||||
v-show="false"
|
||||
@change="emmitFiles"
|
||||
id="file"
|
||||
type="file"
|
||||
name="files[]"
|
||||
multiple
|
||||
/>
|
||||
</label>
|
||||
<div class="flex">
|
||||
<upload-cloud-icon class="icon" size="15"></upload-cloud-icon>
|
||||
<label label="file" class="label button file-input button-base">
|
||||
<slot></slot>
|
||||
<input
|
||||
@change="emmitFiles"
|
||||
v-show="false"
|
||||
id="file"
|
||||
type="file"
|
||||
name="files[]"
|
||||
multiple
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { UploadCloudIcon } from 'vue-feather-icons'
|
||||
|
||||
export default {
|
||||
name: 'MobileActionButtonUpload',
|
||||
props: [
|
||||
'icon'
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
files: undefined
|
||||
}
|
||||
components: {
|
||||
UploadCloudIcon,
|
||||
},
|
||||
methods: {
|
||||
emmitFiles(e) {
|
||||
@@ -36,8 +34,8 @@
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "@assets/app.scss";
|
||||
|
||||
@import '@assets/vue-file-manager/_variables';
|
||||
@import '@assets/vue-file-manager/_mixins';
|
||||
|
||||
.mobile-action-button {
|
||||
background: $light_background;
|
||||
@@ -47,12 +45,19 @@
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
|
||||
.flex {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.icon {
|
||||
margin-right: 8px;
|
||||
vertical-align: middle;
|
||||
margin-right: 10px;
|
||||
@include font-size(14);
|
||||
}
|
||||
|
||||
.label {
|
||||
vertical-align: middle;
|
||||
@include font-size(14);
|
||||
font-weight: 700;
|
||||
color: $text;
|
||||
@@ -63,8 +68,8 @@
|
||||
.mobile-action-button {
|
||||
background: $dark_mode_foreground;
|
||||
|
||||
.icon path {
|
||||
fill: $theme;
|
||||
path, line, polyline, rect, circle {
|
||||
stroke: $theme;
|
||||
}
|
||||
|
||||
.label {
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
<template>
|
||||
<div id="mobile-actions-wrapper" v-if="$isMinimalScale()">
|
||||
<div id="mobile-actions-wrapper">
|
||||
|
||||
<!--Actions for trash location with MASTER permission--->
|
||||
<div v-if="$isThisLocation(['trash', 'trash-root']) && $checkPermission('master')" class="mobile-actions">
|
||||
<MobileActionButton @click.native="switchPreview" :icon="previewIcon">
|
||||
{{ previewText }}
|
||||
</MobileActionButton>
|
||||
<MobileActionButton @click.native="$store.dispatch('emptyTrash')" icon="trash-alt">
|
||||
<MobileActionButton @click.native="$store.dispatch('emptyTrash')" icon="trash">
|
||||
{{ $t('context_menu.empty_trash') }}
|
||||
</MobileActionButton>
|
||||
</div>
|
||||
|
||||
<!--ContextMenu for Base location with MASTER permission-->
|
||||
<div v-if="$isThisLocation(['base', 'shared', 'public']) && $checkPermission(['master', 'editor'])" class="mobile-actions">
|
||||
<div v-if="$isThisLocation(['base', 'public']) && $checkPermission(['master', 'editor'])" class="mobile-actions">
|
||||
<MobileActionButton @click.native="createFolder" icon="folder-plus">
|
||||
{{ $t('context_menu.add_folder') }}
|
||||
</MobileActionButton>
|
||||
<MobileActionButtonUpload @input.native="$uploadFiles" icon="upload">
|
||||
<MobileActionButtonUpload>
|
||||
{{ $t('context_menu.upload') }}
|
||||
</MobileActionButtonUpload>
|
||||
<MobileActionButton @click.native="switchPreview" :icon="previewIcon">
|
||||
@@ -25,7 +25,7 @@
|
||||
</div>
|
||||
|
||||
<!--ContextMenu for Base location with VISITOR permission-->
|
||||
<div v-if="$isThisLocation(['base', 'shared', 'public']) && $checkPermission('visitor')" class="mobile-actions">
|
||||
<div v-if="($isThisLocation(['base', 'shared', 'public']) && $checkPermission('visitor')) || ($isThisLocation(['latest', 'shared']) && $checkPermission('master'))" class="mobile-actions">
|
||||
<MobileActionButton @click.native="switchPreview" :icon="previewIcon">
|
||||
{{ previewText }}
|
||||
</MobileActionButton>
|
||||
@@ -81,10 +81,11 @@
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "@assets/app.scss";
|
||||
|
||||
@import '@assets/vue-file-manager/_variables';
|
||||
@import '@assets/vue-file-manager/_mixins';
|
||||
|
||||
#mobile-actions-wrapper {
|
||||
display: none;
|
||||
background: white;
|
||||
position: sticky;
|
||||
top: 35px;
|
||||
@@ -98,6 +99,13 @@
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 960px) {
|
||||
|
||||
#mobile-actions-wrapper {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
#mobile-actions-wrapper {
|
||||
background: $dark_mode_background;
|
||||
|
||||
@@ -9,79 +9,213 @@
|
||||
>
|
||||
<div class="menu-wrapper">
|
||||
|
||||
<!--Item Thumbnail-->
|
||||
<ThumbnailItem class="item-thumbnail" :item="fileInfoDetail" info="metadata"/>
|
||||
|
||||
<!--Mobile for trash location-->
|
||||
<ul v-if="$isThisLocation(['trash', 'trash-root']) && $checkPermission('master')" class="menu-options">
|
||||
<li class="menu-option" @click="$store.dispatch('restoreItem', fileInfoDetail)" v-if="fileInfoDetail">
|
||||
{{ $t('context_menu.restore') }}
|
||||
</li>
|
||||
<li class="menu-option" @click="downloadItem" v-if="! isFolder">
|
||||
{{ $t('context_menu.download') }}
|
||||
</li>
|
||||
<li class="menu-option delete" @click="deleteItem" v-if="fileInfoDetail">
|
||||
{{ $t('context_menu.delete') }}
|
||||
</li>
|
||||
</ul>
|
||||
<div v-if="$isThisLocation(['trash', 'trash-root']) && $checkPermission('master')" class="menu-options">
|
||||
|
||||
<ul class="menu-option-group">
|
||||
<li class="menu-option" @click="$store.dispatch('restoreItem', fileInfoDetail)" v-if="fileInfoDetail">
|
||||
<div class="icon">
|
||||
<life-buoy-icon size="17"></life-buoy-icon>
|
||||
</div>
|
||||
<div class="text-label">
|
||||
{{ $t('context_menu.restore') }}
|
||||
</div>
|
||||
</li>
|
||||
<li class="menu-option delete" @click="deleteItem" v-if="fileInfoDetail">
|
||||
<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" v-if="! isFolder">
|
||||
<li class="menu-option" @click="downloadItem">
|
||||
<div class="icon">
|
||||
<download-cloud-icon size="17"></download-cloud-icon>
|
||||
</div>
|
||||
<div class="text-label">
|
||||
{{ $t('context_menu.download') }}
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!--Mobile for Base location-->
|
||||
<ul v-if="$isThisLocation(['shared']) && $checkPermission('master')" class="menu-options">
|
||||
<li class="menu-option" @click="addToFavourites" v-if="fileInfoDetail && isFolder">
|
||||
{{ isInFavourites ? $t('context_menu.remove_from_favourites') : $t('context_menu.add_to_favourites') }}
|
||||
</li>
|
||||
<li class="menu-option" @click="renameItem" v-if="fileInfoDetail">
|
||||
{{ $t('context_menu.rename') }}
|
||||
</li>
|
||||
<li class="menu-option" @click="shareItem" v-if="fileInfoDetail">
|
||||
{{ fileInfoDetail.shared ? $t('context_menu.share_edit') : $t('context_menu.share') }}
|
||||
</li>
|
||||
<li class="menu-option" @click="downloadItem" v-if="! isFolder">
|
||||
{{ $t('context_menu.download') }}
|
||||
</li>
|
||||
<li class="menu-option delete" @click="deleteItem" v-if="fileInfoDetail">
|
||||
{{ $t('context_menu.delete') }}
|
||||
</li>
|
||||
</ul>
|
||||
<div v-if="$isThisLocation(['shared']) && $checkPermission('master')" class="menu-options">
|
||||
|
||||
<ul class="menu-option-group">
|
||||
<li class="menu-option" @click="addToFavourites" v-if="fileInfoDetail && 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') }}
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul class="menu-option-group">
|
||||
<li class="menu-option" @click="renameItem" v-if="fileInfoDetail">
|
||||
<div class="icon">
|
||||
<edit-2-icon size="17"></edit-2-icon>
|
||||
</div>
|
||||
<div class="text-label">
|
||||
{{ $t('context_menu.rename') }}
|
||||
</div>
|
||||
</li>
|
||||
<li class="menu-option" @click="shareItem" v-if="fileInfoDetail">
|
||||
<div class="icon">
|
||||
<link-icon size="17"></link-icon>
|
||||
</div>
|
||||
<div class="text-label">
|
||||
{{ fileInfoDetail.shared ? $t('context_menu.share_edit') : $t('context_menu.share') }}
|
||||
</div>
|
||||
</li>
|
||||
<li class="menu-option delete" @click="deleteItem" v-if="fileInfoDetail">
|
||||
<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>
|
||||
|
||||
<!--Mobile for Base location-->
|
||||
<ul v-if="$isThisLocation(['base']) && $checkPermission('master')" class="menu-options">
|
||||
<li class="menu-option" @click="addToFavourites" v-if="fileInfoDetail && isFolder">
|
||||
{{ isInFavourites ? $t('context_menu.remove_from_favourites') : $t('context_menu.add_to_favourites') }}
|
||||
</li>
|
||||
<li class="menu-option" @click="renameItem" v-if="fileInfoDetail">
|
||||
{{ $t('context_menu.rename') }}
|
||||
</li>
|
||||
<li class="menu-option" @click="moveItem" v-if="fileInfoDetail">
|
||||
{{ $t('context_menu.move') }}
|
||||
</li>
|
||||
<li class="menu-option" @click="shareItem" v-if="fileInfoDetail">
|
||||
{{ fileInfoDetail.shared ? $t('context_menu.share_edit') : $t('context_menu.share') }}
|
||||
</li>
|
||||
<li class="menu-option" @click="downloadItem" v-if="! isFolder">
|
||||
{{ $t('context_menu.download') }}
|
||||
</li>
|
||||
<li class="menu-option delete" @click="deleteItem" v-if="fileInfoDetail">
|
||||
{{ $t('context_menu.delete') }}
|
||||
</li>
|
||||
</ul>
|
||||
<div v-if="$isThisLocation(['base', 'participant_uploads', 'latest']) && $checkPermission('master')" class="menu-options">
|
||||
<ul class="menu-option-group" v-if="fileInfoDetail && 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') }}
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul class="menu-option-group">
|
||||
<li class="menu-option" @click="renameItem" v-if="fileInfoDetail">
|
||||
<div class="icon">
|
||||
<edit-2-icon size="17"></edit-2-icon>
|
||||
</div>
|
||||
<div class="text-label">
|
||||
{{ $t('context_menu.rename') }}
|
||||
</div>
|
||||
</li>
|
||||
<li class="menu-option" @click="moveItem" v-if="fileInfoDetail">
|
||||
<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" v-if="fileInfoDetail">
|
||||
<div class="icon">
|
||||
<link-icon size="17"></link-icon>
|
||||
</div>
|
||||
<div class="text-label">
|
||||
{{ fileInfoDetail.shared ? $t('context_menu.share_edit') : $t('context_menu.share') }}
|
||||
</div>
|
||||
</li>
|
||||
<li class="menu-option delete" @click="deleteItem" v-if="fileInfoDetail">
|
||||
<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>
|
||||
|
||||
<!--Mobile for Base location with EDITOR permission-->
|
||||
<ul v-if="$isThisLocation(['base', 'public']) && $checkPermission('editor')" class="menu-options">
|
||||
<li class="menu-option" @click="renameItem" v-if="fileInfoDetail">
|
||||
{{ $t('context_menu.rename') }}
|
||||
</li>
|
||||
<li class="menu-option" @click="moveItem" v-if="fileInfoDetail">
|
||||
{{ $t('context_menu.move') }}
|
||||
</li>
|
||||
<li class="menu-option" @click="downloadItem" v-if="! isFolder">
|
||||
{{ $t('context_menu.download') }}
|
||||
</li>
|
||||
</ul>
|
||||
<div v-if="$isThisLocation(['base', 'public']) && $checkPermission('editor')" class="menu-options">
|
||||
|
||||
<ul class="menu-option-group">
|
||||
<li class="menu-option" @click="renameItem" v-if="fileInfoDetail">
|
||||
<div class="icon">
|
||||
<edit-2-icon size="17"></edit-2-icon>
|
||||
</div>
|
||||
<div class="text-label">
|
||||
{{ $t('context_menu.rename') }}
|
||||
</div>
|
||||
</li>
|
||||
<li class="menu-option" @click="moveItem" v-if="fileInfoDetail">
|
||||
<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="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>
|
||||
|
||||
<!--Mobile for Base location with VISITOR permission-->
|
||||
<ul v-if="$isThisLocation(['base', 'public']) && $checkPermission('visitor')" class="menu-options">
|
||||
<li class="menu-option" @click="downloadItem" v-if="! isFolder">
|
||||
{{ $t('context_menu.download') }}
|
||||
</li>
|
||||
</ul>
|
||||
<div v-if="$isThisLocation(['base', 'public']) && $checkPermission('visitor')" class="menu-options">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
@@ -92,11 +226,37 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ThumbnailItem from '@/components/Others/ThumbnailItem'
|
||||
import {
|
||||
CornerDownRightIcon,
|
||||
DownloadCloudIcon,
|
||||
FolderPlusIcon,
|
||||
LifeBuoyIcon,
|
||||
Trash2Icon,
|
||||
Edit2Icon,
|
||||
TrashIcon,
|
||||
StarIcon,
|
||||
LinkIcon,
|
||||
EyeIcon,
|
||||
} from 'vue-feather-icons'
|
||||
import {events} from '@/bus'
|
||||
import {mapGetters} from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'MobileMenu',
|
||||
components: {
|
||||
CornerDownRightIcon,
|
||||
DownloadCloudIcon,
|
||||
FolderPlusIcon,
|
||||
ThumbnailItem,
|
||||
LifeBuoyIcon,
|
||||
Trash2Icon,
|
||||
Edit2Icon,
|
||||
TrashIcon,
|
||||
LinkIcon,
|
||||
StarIcon,
|
||||
EyeIcon,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['fileInfoDetail', 'app']),
|
||||
isInFavourites() {
|
||||
@@ -139,14 +299,12 @@
|
||||
}
|
||||
},
|
||||
downloadItem() {
|
||||
// Download file
|
||||
this.$downloadFile(
|
||||
this.fileInfoDetail.file_url,
|
||||
this.fileInfoDetail.name + '.' + this.fileInfoDetail.mimetype
|
||||
)
|
||||
},
|
||||
deleteItem() {
|
||||
// Dispatch remove item
|
||||
this.$store.dispatch('deleteItem', this.fileInfoDetail)
|
||||
},
|
||||
renameItem() {
|
||||
@@ -194,10 +352,25 @@
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "@assets/app.scss";
|
||||
@import '@assets/vue-file-manager/_variables';
|
||||
@import '@assets/vue-file-manager/_mixins';
|
||||
|
||||
.menu-option {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.icon {
|
||||
margin-right: 20px;
|
||||
line-height: 0;
|
||||
}
|
||||
|
||||
.text-label {
|
||||
@include font-size(16);
|
||||
}
|
||||
}
|
||||
|
||||
.vignette {
|
||||
background: rgba(0, 0, 0, 0.15);
|
||||
background: rgba(0, 0, 0, 0.35);
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
@@ -215,29 +388,46 @@
|
||||
right: 0;
|
||||
z-index: 99;
|
||||
overflow: hidden;
|
||||
background: white;
|
||||
border-top-left-radius: 12px;
|
||||
border-top-right-radius: 12px;
|
||||
|
||||
&.showed {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.item-thumbnail {
|
||||
padding: 20px 20px 10px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
.menu-options {
|
||||
margin-top: 10px;
|
||||
box-shadow: $shadow;
|
||||
background: white;
|
||||
border-top-left-radius: 8px;
|
||||
border-top-right-radius: 8px;
|
||||
list-style: none;
|
||||
width: 100%;
|
||||
|
||||
.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 {
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.15px;
|
||||
@include font-size(15);
|
||||
@include font-size(14);
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
padding: 20px 10px;
|
||||
padding: 17px 20px;
|
||||
text-align: center;
|
||||
border-bottom: 1px solid $light_mode_border;
|
||||
|
||||
&:last-child {
|
||||
border: none;
|
||||
@@ -253,12 +443,16 @@
|
||||
}
|
||||
|
||||
.options {
|
||||
background: $dark_mode_background;
|
||||
|
||||
.menu-options {
|
||||
background: $dark_mode_background;
|
||||
|
||||
.menu-option {
|
||||
.menu-option-group {
|
||||
border-color: $dark_mode_border_color;
|
||||
}
|
||||
|
||||
.menu-option {
|
||||
color: $dark_mode_text_primary;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
<template>
|
||||
<div class="mobile-toolbar" v-if="$isMinimalScale()">
|
||||
<div class="mobile-toolbar">
|
||||
|
||||
<!-- Go back-->
|
||||
<div @click="goBack" class="go-back-button">
|
||||
<FontAwesomeIcon
|
||||
:class="{'is-visible': browseHistory.length > 0}"
|
||||
class="icon-back"
|
||||
icon="chevron-left"
|
||||
></FontAwesomeIcon>
|
||||
<chevron-left-icon size="17" :class="{'is-visible': browseHistory.length > 1}" class="icon-back"></chevron-left-icon>
|
||||
</div>
|
||||
|
||||
<!--Folder Title-->
|
||||
@@ -15,8 +11,8 @@
|
||||
|
||||
<!--More Actions-->
|
||||
<div class="more-actions-button">
|
||||
<div class="tap-area" @click="showSidebarMenu" v-if="$checkPermission('master')">
|
||||
<FontAwesomeIcon icon="bars" v-if="isSmallAppSize"></FontAwesomeIcon>
|
||||
<div class="tap-area" @click="showMobileNavigation" v-if="$checkPermission('master')">
|
||||
<menu-icon size="17"></menu-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -26,15 +22,19 @@
|
||||
import ToolbarButtonUpload from '@/components/FilesView/ToolbarButtonUpload'
|
||||
import ToolbarButton from '@/components/FilesView/ToolbarButton'
|
||||
import SearchBar from '@/components/FilesView/SearchBar'
|
||||
import { MenuIcon, ChevronLeftIcon } from 'vue-feather-icons'
|
||||
import {mapGetters} from 'vuex'
|
||||
import {events} from '@/bus'
|
||||
import {last} from 'lodash'
|
||||
|
||||
export default {
|
||||
name: 'MobileToolBar',
|
||||
components: {
|
||||
ToolbarButtonUpload,
|
||||
ChevronLeftIcon,
|
||||
ToolbarButton,
|
||||
SearchBar
|
||||
SearchBar,
|
||||
MenuIcon,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
@@ -44,41 +44,31 @@
|
||||
'currentFolder',
|
||||
'browseHistory',
|
||||
'homeDirectory',
|
||||
'appSize',
|
||||
]),
|
||||
directoryName() {
|
||||
return this.currentFolder ? this.currentFolder.name : this.homeDirectory.name
|
||||
},
|
||||
previousFolder() {
|
||||
const length = this.browseHistory.length - 2
|
||||
|
||||
return this.browseHistory[length] ? this.browseHistory[length] : this.homeDirectory
|
||||
},
|
||||
isSmallAppSize() {
|
||||
return this.appSize === 'small'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isSidebarMenu: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
showSidebarMenu() {
|
||||
this.isSidebarMenu = ! this.isSidebarMenu
|
||||
events.$emit('show:sidebar')
|
||||
showMobileNavigation() {
|
||||
events.$emit('show:mobile-navigation')
|
||||
},
|
||||
goBack() {
|
||||
|
||||
if (this.previousFolder.location === 'trash-root') {
|
||||
let previousFolder = last(this.browseHistory)
|
||||
|
||||
if (previousFolder.location === 'trash-root') {
|
||||
this.$store.dispatch('getTrash')
|
||||
this.$store.commit('FLUSH_BROWSER_HISTORY')
|
||||
|
||||
} else if (previousFolder.location === 'shared') {
|
||||
this.$store.dispatch('getShared')
|
||||
|
||||
} else {
|
||||
|
||||
if ( this.$isThisLocation('public') ) {
|
||||
this.$store.dispatch('browseShared', [this.previousFolder, true])
|
||||
this.$store.dispatch('browseShared', [{folder: previousFolder, back: true, init: false}])
|
||||
} else {
|
||||
this.$store.dispatch('getFolder', [this.previousFolder, true])
|
||||
this.$store.dispatch('getFolder', [{folder: previousFolder, back: true, init: false}])
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -93,13 +83,13 @@
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "@assets/app.scss";
|
||||
|
||||
@import '@assets/vue-file-manager/_variables';
|
||||
@import '@assets/vue-file-manager/_mixins';
|
||||
|
||||
.mobile-toolbar {
|
||||
background: white;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
display: none;
|
||||
padding: 10px 0;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
@@ -121,6 +111,7 @@
|
||||
cursor: pointer;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
margin-top: -2px;
|
||||
|
||||
&.is-visible {
|
||||
opacity: 1;
|
||||
@@ -154,10 +145,21 @@
|
||||
position: absolute;
|
||||
right: -10px;
|
||||
top: -20px;
|
||||
|
||||
path, line, polyline, rect, circle {
|
||||
stroke: $text;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 960px) {
|
||||
|
||||
.mobile-toolbar {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
|
||||
.mobile-toolbar {
|
||||
@@ -167,8 +169,11 @@
|
||||
color: $dark_mode_text_primary;
|
||||
}
|
||||
|
||||
.more-actions-button svg path {
|
||||
fill: $dark_mode_text_primary;
|
||||
.more-actions-button .tap-area {
|
||||
|
||||
path, line, polyline, rect, circle {
|
||||
stroke: $dark_mode_text_primary;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,8 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "@assets/app.scss";
|
||||
@import '@assets/vue-file-manager/_variables';
|
||||
@import '@assets/vue-file-manager/_mixins';
|
||||
|
||||
.progress-bar {
|
||||
width: 100%;
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
<template>
|
||||
<div class="search-bar">
|
||||
<div class="icon" v-if="!isQuery">
|
||||
<search-icon size="19"></search-icon>
|
||||
</div>
|
||||
<div class="icon" v-if="isQuery" @click="resetQuery">
|
||||
<x-icon class="pointer" size="19"></x-icon>
|
||||
</div>
|
||||
<input
|
||||
v-model="query"
|
||||
class="query"
|
||||
@@ -7,22 +13,21 @@
|
||||
name="query"
|
||||
:placeholder="$t('inputs.placeholder_search_files')"
|
||||
/>
|
||||
<div class="icon" v-if="!isQuery">
|
||||
<FontAwesomeIcon icon="search"></FontAwesomeIcon>
|
||||
</div>
|
||||
<div class="icon" v-if="isQuery" @click="resetQuery">
|
||||
<FontAwesomeIcon icon="times" class="pointer"></FontAwesomeIcon>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { SearchIcon, XIcon } from 'vue-feather-icons'
|
||||
import {mapGetters} from 'vuex'
|
||||
import {debounce} from 'lodash'
|
||||
import {events} from '@/bus'
|
||||
|
||||
export default {
|
||||
name: 'SearchBar',
|
||||
components: {
|
||||
SearchIcon,
|
||||
XIcon,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['currentFolder']),
|
||||
isQuery() {
|
||||
@@ -52,9 +57,9 @@
|
||||
|
||||
// Get back after delete query to previosly folder
|
||||
if ( this.$isThisLocation('public') ) {
|
||||
this.$store.dispatch('browseShared', [this.currentFolder, true])
|
||||
this.$store.dispatch('browseShared', [{folder: this.currentFolder, back: true, init: false}])
|
||||
} else {
|
||||
this.$store.dispatch('getFolder', [this.currentFolder, true])
|
||||
this.$store.dispatch('getFolder', [{folder: this.currentFolder, back: true, init: false}])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,20 +74,20 @@
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "@assets/app.scss";
|
||||
@import '@assets/vue-file-manager/_variables';
|
||||
@import '@assets/vue-file-manager/_mixins';
|
||||
|
||||
.search-bar {
|
||||
position: relative;
|
||||
|
||||
input {
|
||||
//width: 100%;
|
||||
background: $light_background;
|
||||
background: transparent;
|
||||
border-radius: 8px;
|
||||
outline: 0;
|
||||
padding: 9px 20px;
|
||||
font-weight: 100;
|
||||
padding: 9px 20px 9px 43px;
|
||||
font-weight: 400;
|
||||
@include font-size(16);
|
||||
min-width: 380px;
|
||||
min-width: 175px;
|
||||
transition: 0.15s all ease;
|
||||
border: 1px solid white;
|
||||
-webkit-appearance: none;
|
||||
@@ -90,7 +95,7 @@
|
||||
&::placeholder {
|
||||
color: $text;
|
||||
@include font-size(14);
|
||||
font-weight: 400;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
@@ -108,8 +113,8 @@
|
||||
.icon {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
padding: 10px 15px;
|
||||
left: 0;
|
||||
padding: 11px 15px;
|
||||
|
||||
.pointer {
|
||||
cursor: pointer;
|
||||
@@ -117,11 +122,42 @@
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1024px) {
|
||||
|
||||
.search-bar input {
|
||||
max-width: 190px;
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 690px) {
|
||||
|
||||
.search-bar {
|
||||
|
||||
input {
|
||||
min-width: initial;
|
||||
width: 100%;
|
||||
max-width: initial;
|
||||
padding: 9px 20px 9px 30px;
|
||||
|
||||
&:focus {
|
||||
border: 1px solid transparent;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
padding: 11px 15px 11px 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.search-bar {
|
||||
input {
|
||||
background: $dark_mode_foreground;
|
||||
border-color: $dark_mode_foreground;
|
||||
border-color: transparent;
|
||||
color: $dark_mode_text_primary;
|
||||
|
||||
&::placeholder {
|
||||
color: $dark_mode_text_secondary;
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "@assets/app.scss";
|
||||
@import '@assets/vue-file-manager/_variables';
|
||||
@import '@assets/vue-file-manager/_mixins';
|
||||
|
||||
#loading-bar-spinner.spinner {
|
||||
left: 50%;
|
||||
|
||||
@@ -1,18 +1,36 @@
|
||||
<template>
|
||||
<button class="button" :title="action">
|
||||
<FontAwesomeIcon class="icon" :icon="source"></FontAwesomeIcon>
|
||||
<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>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {FolderPlusIcon, Trash2Icon, GridIcon, ListIcon, InfoIcon, CornerDownRightIcon, LinkIcon} from 'vue-feather-icons'
|
||||
|
||||
export default {
|
||||
name: 'ToolbarButton',
|
||||
props: ['source', 'action']
|
||||
props: ['source', 'action'],
|
||||
components: {
|
||||
CornerDownRightIcon,
|
||||
FolderPlusIcon,
|
||||
Trash2Icon,
|
||||
ListIcon,
|
||||
GridIcon,
|
||||
InfoIcon,
|
||||
LinkIcon,
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "@assets/app.scss";
|
||||
@import '@assets/vue-file-manager/_variables';
|
||||
@import '@assets/vue-file-manager/_mixins';
|
||||
|
||||
.button {
|
||||
height: 42px;
|
||||
@@ -22,42 +40,36 @@
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0;
|
||||
background: $light_background;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
outline: none;
|
||||
border: none;
|
||||
|
||||
.icon {
|
||||
@include font-size(16);
|
||||
}
|
||||
@include transition(150ms);
|
||||
background: transparent;
|
||||
|
||||
&:hover {
|
||||
background: rgba($theme, .1);
|
||||
background: $light_background;
|
||||
|
||||
/deep/ svg path {
|
||||
@include transition;
|
||||
fill: $theme;
|
||||
}
|
||||
}
|
||||
|
||||
&.active {
|
||||
background: rgba($theme, .1);
|
||||
|
||||
/deep/ svg path {
|
||||
fill: $theme;
|
||||
path, line, polyline, rect, circle {
|
||||
@include transition(150ms);
|
||||
stroke: $theme;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.button {
|
||||
background: $dark_mode_foreground;
|
||||
}
|
||||
|
||||
.icon path {
|
||||
fill: $dark_mode_text_primary;
|
||||
.button {
|
||||
background: transparent;
|
||||
|
||||
&:hover {
|
||||
background: $dark_mode_foreground;
|
||||
}
|
||||
|
||||
path, line, polyline, rect, circle {
|
||||
stroke: $dark_mode_text_primary;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<label label="file" class="button file-input">
|
||||
<FontAwesomeIcon class="icon" :icon="source"></FontAwesomeIcon>
|
||||
<upload-cloud-icon size="17"></upload-cloud-icon>
|
||||
<input
|
||||
@change="emmitFiles"
|
||||
v-show="false"
|
||||
@@ -8,15 +8,19 @@
|
||||
type="file"
|
||||
name="files[]"
|
||||
multiple
|
||||
:disabled="$isThisLocation(['trash', 'trash-root'])"
|
||||
/>
|
||||
</label>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { UploadCloudIcon } from 'vue-feather-icons'
|
||||
|
||||
export default {
|
||||
name: 'ToolbarButtonUpload',
|
||||
props: ['source', 'action'],
|
||||
props: ['action'],
|
||||
components: {
|
||||
UploadCloudIcon,
|
||||
},
|
||||
methods: {
|
||||
emmitFiles(e) {
|
||||
this.$uploadFiles(e.target.files)
|
||||
@@ -26,7 +30,8 @@
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "@assets/app.scss";
|
||||
@import '@assets/vue-file-manager/_variables';
|
||||
@import '@assets/vue-file-manager/_mixins';
|
||||
|
||||
.button {
|
||||
height: 42px;
|
||||
@@ -36,7 +41,6 @@
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0;
|
||||
background: $light_background;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
@@ -44,26 +48,27 @@
|
||||
border: none;
|
||||
|
||||
&:hover {
|
||||
background: rgba($theme, .1);
|
||||
background: $light_background;
|
||||
|
||||
/deep/ svg path {
|
||||
@include transition;
|
||||
fill: $theme;
|
||||
path, line, polyline, rect, circle {
|
||||
@include transition(150ms);
|
||||
stroke: $theme;
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
@include font-size(16);
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.button {
|
||||
background: $dark_mode_foreground;
|
||||
}
|
||||
|
||||
.icon path {
|
||||
fill: $dark_mode_text_primary;
|
||||
.button {
|
||||
background: transparent;
|
||||
|
||||
&:hover {
|
||||
background: $dark_mode_foreground;
|
||||
}
|
||||
|
||||
path, line, polyline, rect, circle {
|
||||
stroke: $dark_mode_text_primary;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -25,7 +25,8 @@
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "@assets/app.scss";
|
||||
@import '@assets/vue-file-manager/_variables';
|
||||
@import '@assets/vue-file-manager/_mixins';
|
||||
|
||||
.info-panel-enter-active,
|
||||
.info-panel-leave-active {
|
||||
|
||||
124
resources/js/components/Mobile/MenuItemList.vue
Normal file
124
resources/js/components/Mobile/MenuItemList.vue
Normal file
@@ -0,0 +1,124 @@
|
||||
<template>
|
||||
<ul class="link-group">
|
||||
<router-link :to="{name: link.routeName}" v-for="(link, i) in navigation" :key="i" v-if="link.isVisible" :class="link.icon" class="link-item" @click.native="$emit('menu', link.icon)">
|
||||
<div class="menu-icon">
|
||||
<hard-drive-icon v-if="link.icon === 'hard-drive'" size="17"></hard-drive-icon>
|
||||
<share-icon v-if="link.icon === 'share'" size="17"></share-icon>
|
||||
<trash2-icon v-if="link.icon === 'trash'" size="17"></trash2-icon>
|
||||
<power-icon v-if="link.icon === 'power'" size="17"></power-icon>
|
||||
<settings-icon v-if="link.icon === 'settings'" size="17"></settings-icon>
|
||||
<upload-cloud-icon v-if="link.icon === 'latest'" size="17"></upload-cloud-icon>
|
||||
<user-icon v-if="link.icon === 'user'" size="17"></user-icon>
|
||||
<users-icon v-if="link.icon === 'users'" size="17"></users-icon>
|
||||
<lock-icon v-if="link.icon === 'lock'" size="17"></lock-icon>
|
||||
</div>
|
||||
<b class="menu-link">
|
||||
<span>{{ link.title }}</span>
|
||||
<chevron-right-icon size="15" class="arrow-right"></chevron-right-icon>
|
||||
</b>
|
||||
</router-link>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
ChevronRightIcon,
|
||||
UploadCloudIcon,
|
||||
HardDriveIcon,
|
||||
SettingsIcon,
|
||||
Trash2Icon,
|
||||
PowerIcon,
|
||||
ShareIcon,
|
||||
UsersIcon,
|
||||
UserIcon,
|
||||
LockIcon,
|
||||
} from 'vue-feather-icons'
|
||||
|
||||
export default {
|
||||
name: 'MenuBar',
|
||||
components: {
|
||||
ChevronRightIcon,
|
||||
UploadCloudIcon,
|
||||
HardDriveIcon,
|
||||
SettingsIcon,
|
||||
Trash2Icon,
|
||||
PowerIcon,
|
||||
UsersIcon,
|
||||
ShareIcon,
|
||||
LockIcon,
|
||||
UserIcon,
|
||||
},
|
||||
props: [
|
||||
'navigation'
|
||||
],
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '@assets/vue-file-manager/_variables';
|
||||
@import '@assets/vue-file-manager/_mixins';
|
||||
|
||||
.link-item {
|
||||
display: flex;
|
||||
text-decoration: none;
|
||||
padding: 17px 0;
|
||||
width: 100%;
|
||||
|
||||
&.power {
|
||||
|
||||
.menu-icon {
|
||||
|
||||
path, line, polyline, rect, circle {
|
||||
stroke: $red;
|
||||
}
|
||||
}
|
||||
|
||||
.menu-link {
|
||||
color: $red;
|
||||
}
|
||||
}
|
||||
|
||||
.menu-icon {
|
||||
display: block;
|
||||
margin-right: 20px;
|
||||
|
||||
svg {
|
||||
margin-top: -1px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
path, line, polyline, rect, circle {
|
||||
stroke: $text;
|
||||
}
|
||||
}
|
||||
|
||||
.menu-link {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
color: $text;
|
||||
|
||||
span {
|
||||
@include font-size(14);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.link-item {
|
||||
|
||||
.menu-icon {
|
||||
|
||||
path, line, polyline, rect, circle {
|
||||
stroke: $dark_mode_text_primary;
|
||||
}
|
||||
}
|
||||
|
||||
.menu-link {
|
||||
color: $dark_mode_text_primary;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
111
resources/js/components/Mobile/MobileHeader.vue
Normal file
111
resources/js/components/Mobile/MobileHeader.vue
Normal file
@@ -0,0 +1,111 @@
|
||||
<template>
|
||||
<header class="mobile-header">
|
||||
|
||||
<!-- Go back-->
|
||||
<div @click="goBack" class="go-back">
|
||||
<chevron-left-icon size="17" class="icon"></chevron-left-icon>
|
||||
</div>
|
||||
|
||||
<!--Folder Title-->
|
||||
<div class="location-name">{{ title }}</div>
|
||||
|
||||
<!--More Actions-->
|
||||
<div @click="showMobileNavigation" class="mobile-menu">
|
||||
<menu-icon size="17" class="icon"></menu-icon>
|
||||
</div>
|
||||
</header>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {events} from '@/bus'
|
||||
|
||||
import {
|
||||
ChevronLeftIcon,
|
||||
MenuIcon,
|
||||
} from 'vue-feather-icons'
|
||||
|
||||
export default {
|
||||
name: 'MenuBar',
|
||||
props: [
|
||||
'title'
|
||||
],
|
||||
components: {
|
||||
ChevronLeftIcon,
|
||||
MenuIcon,
|
||||
},
|
||||
methods: {
|
||||
showMobileNavigation() {
|
||||
events.$emit('show:mobile-navigation')
|
||||
},
|
||||
goBack() {
|
||||
this.$router.back();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '@assets/vue-file-manager/_variables';
|
||||
@import '@assets/vue-file-manager/_mixins';
|
||||
|
||||
.mobile-header {
|
||||
padding: 10px 0;
|
||||
text-align: center;
|
||||
background: white;
|
||||
position: sticky;
|
||||
display: none;
|
||||
z-index: 6;
|
||||
top: 0;
|
||||
|
||||
> div {
|
||||
flex-grow: 1;
|
||||
align-self: center;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.go-back {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.location-name {
|
||||
line-height: 1;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
vertical-align: middle;
|
||||
@include font-size(15);
|
||||
color: $text;
|
||||
font-weight: 700;
|
||||
max-width: 220px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.mobile-menu {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.icon {
|
||||
vertical-align: middle;
|
||||
margin-top: -4px;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 690px) {
|
||||
.mobile-header {
|
||||
display: flex;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.mobile-header {
|
||||
background: $dark_mode_background;
|
||||
|
||||
.location-name {
|
||||
color: $dark_mode_text_primary;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -15,7 +15,8 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "@assets/app.scss";
|
||||
@import '@assets/vue-file-manager/_variables';
|
||||
@import '@assets/vue-file-manager/_mixins';
|
||||
|
||||
.action-button {
|
||||
cursor: pointer;
|
||||
|
||||
44
resources/js/components/Others/ColorLabel.vue
Normal file
44
resources/js/components/Others/ColorLabel.vue
Normal file
@@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<b class="color-label" :class="color">
|
||||
<slot></slot>
|
||||
</b>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ColorLabel',
|
||||
props: ['color'],
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '@assets/vue-file-manager/_variables';
|
||||
@import '@assets/vue-file-manager/_mixins';
|
||||
|
||||
.color-label {
|
||||
text-transform: capitalize;
|
||||
@include font-size(12);
|
||||
display: inline-block;
|
||||
border-radius: 6px;
|
||||
font-weight: 700;
|
||||
padding: 4px 6px;
|
||||
|
||||
&.purple {
|
||||
color: $purple;
|
||||
background: rgba($purple, 0.1);
|
||||
}
|
||||
|
||||
&.yellow {
|
||||
color: $yellow;
|
||||
background: rgba($yellow, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1024px) {
|
||||
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
|
||||
}
|
||||
</style>
|
||||
63
resources/js/components/Others/ContentFileView.vue
Normal file
63
resources/js/components/Others/ContentFileView.vue
Normal file
@@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<div @click="fileViewClick"
|
||||
@contextmenu.prevent.capture="contextMenu($event, undefined)"
|
||||
id="files-view">
|
||||
<ContextMenu/>
|
||||
<DesktopToolbar/>
|
||||
<FileBrowser/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import DesktopToolbar from '@/components/FilesView/DesktopToolbar'
|
||||
import FileBrowser from '@/components/FilesView/FileBrowser'
|
||||
import ContextMenu from '@/components/FilesView/ContextMenu'
|
||||
import {mapGetters} from 'vuex'
|
||||
import {events} from '@/bus'
|
||||
|
||||
export default {
|
||||
name: 'FilesView',
|
||||
components: {
|
||||
DesktopToolbar,
|
||||
FileBrowser,
|
||||
ContextMenu,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['config']),
|
||||
},
|
||||
methods: {
|
||||
fileViewClick() {
|
||||
events.$emit('contextMenu:hide')
|
||||
},
|
||||
contextMenu(event, item) {
|
||||
events.$emit('contextMenu:show', event, item)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import '@assets/vue-file-manager/_variables';
|
||||
@import '@assets/vue-file-manager/_mixins';
|
||||
|
||||
#files-view {
|
||||
font-family: 'Nunito', sans-serif;
|
||||
font-size: 16px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
min-width: 320px;
|
||||
overflow-x: hidden;
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 690px) {
|
||||
#files-view {
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -2,15 +2,22 @@
|
||||
<div class="inline-wrapper icon-append copy-input" :class="size" @click="copyUrl">
|
||||
<input ref="sel" :value="value" id="link-input" type="text" class="input-text" readonly>
|
||||
<div class="icon">
|
||||
<FontAwesomeIcon :icon="isCopiedLink ? 'check' : 'link'"/>
|
||||
<link-icon v-if="! isCopiedLink" size="14"></link-icon>
|
||||
<check-icon v-if="isCopiedLink" size="14"></check-icon>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { LinkIcon, CheckIcon } from 'vue-feather-icons'
|
||||
|
||||
export default {
|
||||
name: 'CopyInput',
|
||||
props: ['size', 'value'],
|
||||
components: {
|
||||
CheckIcon,
|
||||
LinkIcon,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isCopiedLink: false,
|
||||
@@ -40,8 +47,10 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "@assets/app.scss";
|
||||
@import '@assets/vue-file-manager/_variables';
|
||||
@import '@assets/vue-file-manager/_mixins';
|
||||
@import "@assets/vue-file-manager/_inapp-forms.scss";
|
||||
@import "@assets/vue-file-manager/_forms.scss";
|
||||
|
||||
// Single page
|
||||
.copy-input {
|
||||
@@ -51,8 +60,7 @@
|
||||
&.icon-append {
|
||||
|
||||
.icon {
|
||||
padding: 8px 10px;
|
||||
@include font-size(11);
|
||||
padding: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,9 +89,6 @@
|
||||
.copy-input {
|
||||
input {
|
||||
color: $dark_mode_text_primary;
|
||||
|
||||
&:disabled {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
166
resources/js/components/Others/Forms/ImageInput.vue
Normal file
166
resources/js/components/Others/Forms/ImageInput.vue
Normal file
@@ -0,0 +1,166 @@
|
||||
<template>
|
||||
<div class="dropzone" :class="{ 'is-error': error }">
|
||||
<input
|
||||
ref="file"
|
||||
type="file"
|
||||
@change="showImagePreview($event)"
|
||||
class="dummy"
|
||||
/>
|
||||
<img
|
||||
ref="image"
|
||||
:src="imagePreview"
|
||||
class="image-preview"
|
||||
v-if="imagePreview"
|
||||
/>
|
||||
|
||||
<div class="dropzone-message" v-show="! isData">
|
||||
<upload-icon size="19" class="icon-upload"></upload-icon>
|
||||
<span class="dropzone-title">
|
||||
{{ $t('input_image.title') }}
|
||||
</span>
|
||||
<span class="dropzone-description">
|
||||
{{ $t('input_image.supported') }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { UploadIcon } from 'vue-feather-icons'
|
||||
|
||||
export default {
|
||||
name: 'ImageInput',
|
||||
props: [
|
||||
'image', 'error'
|
||||
],
|
||||
components: {
|
||||
UploadIcon
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
imagePreview: undefined
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isData() {
|
||||
return typeof this.imagePreview === 'undefined' || this.imagePreview === '' ? false : true
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
showImagePreview(event) {
|
||||
const imgPath = event.target.files[0].name,
|
||||
extn = imgPath
|
||||
.substring(imgPath.lastIndexOf('.') + 1)
|
||||
.toLowerCase()
|
||||
|
||||
if (['png', 'jpg', 'jpeg'].includes(extn)) {
|
||||
const file = event.target.files[0],
|
||||
reader = new FileReader()
|
||||
|
||||
reader.onload = () => (this.imagePreview = reader.result)
|
||||
|
||||
reader.readAsDataURL(file)
|
||||
|
||||
// Update user avatar
|
||||
this.$emit('input', event.target.files[0])
|
||||
} else {
|
||||
alert( this.$t('validation_errors.wrong_image') )
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
// If has default image then load
|
||||
if (this.image) this.imagePreview = this.image
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '@assets/vue-file-manager/_variables';
|
||||
@import '@assets/vue-file-manager/_mixins';
|
||||
|
||||
.dropzone {
|
||||
border: 1px dashed #a1abc2;
|
||||
border-radius: 8px;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-height: 210px;
|
||||
|
||||
&.is-error {
|
||||
border: 2px dashed rgba(253, 57, 122, 0.3);
|
||||
|
||||
.dropzone-title {
|
||||
color: $danger;
|
||||
}
|
||||
|
||||
.icon-upload path {
|
||||
fill: $danger
|
||||
}
|
||||
}
|
||||
|
||||
input[type='file'] {
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.image-preview {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
left: 0;
|
||||
padding: 7px;
|
||||
display: block;
|
||||
|
||||
&.fit-image {
|
||||
object-fit: cover;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.dropzone-message {
|
||||
padding: 50px 0;
|
||||
width: 100%;
|
||||
|
||||
.dropzone-title {
|
||||
@include font-size(16);
|
||||
font-weight: 700;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.dropzone-description {
|
||||
color: $text_muted;
|
||||
@include font-size(12);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.dropzone {
|
||||
|
||||
.dropzone-message {
|
||||
|
||||
.icon-upload {
|
||||
path, polyline, line {
|
||||
stroke: $theme;
|
||||
}
|
||||
}
|
||||
|
||||
.dropzone-description {
|
||||
color: $dark_mode_text_secondary;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -7,7 +7,8 @@
|
||||
<!--If is selected-->
|
||||
<div class="selected" v-if="selected">
|
||||
<div class="option-icon" v-if="selected.icon">
|
||||
<FontAwesomeIcon :icon="selected.icon" />
|
||||
<user-icon v-if="selected.icon === 'user'" size="14"></user-icon>
|
||||
<edit2-icon v-if="selected.icon === 'user-edit'" size="14"></edit2-icon>
|
||||
</div>
|
||||
<span class="option-value">{{ selected.label }}</span>
|
||||
</div>
|
||||
@@ -17,7 +18,7 @@
|
||||
<span class="option-value placehoder">{{ placeholder }}</span>
|
||||
</div>
|
||||
|
||||
<FontAwesomeIcon icon="chevron-down" class="chevron"/>
|
||||
<chevron-down-icon size="19" class="chevron"></chevron-down-icon>
|
||||
</div>
|
||||
|
||||
<!--Options-->
|
||||
@@ -25,7 +26,8 @@
|
||||
<ul class="input-options" v-if="isOpen">
|
||||
<li class="option-item" @click="selectOption(option)" v-for="(option, i) in options" :key="i">
|
||||
<div class="option-icon" v-if="option.icon">
|
||||
<FontAwesomeIcon :icon="option.icon" />
|
||||
<user-icon v-if="option.icon === 'user'" size="14"></user-icon>
|
||||
<edit2-icon v-if="option.icon === 'user-edit'" size="14"></edit2-icon>
|
||||
</div>
|
||||
<span class="option-value">{{ option.label }}</span>
|
||||
</li>
|
||||
@@ -35,9 +37,16 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ChevronDownIcon, Edit2Icon, UserIcon } from 'vue-feather-icons'
|
||||
|
||||
export default {
|
||||
name:'SelectInput',
|
||||
props: ['options', 'isError', 'default', 'placeholder'],
|
||||
components: {
|
||||
Edit2Icon,
|
||||
UserIcon,
|
||||
ChevronDownIcon
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selected: undefined,
|
||||
@@ -69,15 +78,17 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "@assets/app.scss";
|
||||
@import '@assets/vue-file-manager/_variables';
|
||||
@import '@assets/vue-file-manager/_mixins';
|
||||
|
||||
.select {
|
||||
position: relative;
|
||||
user-select: none;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.input-options {
|
||||
background: $light_background;
|
||||
background: $light_mode_input_background;
|
||||
border-radius: 8px;
|
||||
position: absolute;
|
||||
overflow: hidden;
|
||||
@@ -104,9 +115,9 @@
|
||||
}
|
||||
|
||||
.input-area {
|
||||
border: 1px solid #ebebeb;
|
||||
justify-content: space-between;
|
||||
background: $light_background;
|
||||
border: 1px solid transparent;
|
||||
background: $light_mode_input_background;
|
||||
@include transition(150ms);
|
||||
align-items: center;
|
||||
border-radius: 8px;
|
||||
@@ -138,16 +149,22 @@
|
||||
.option-icon {
|
||||
width: 20px;
|
||||
display: inline-block;
|
||||
@include font-size(12);
|
||||
@include font-size(10);
|
||||
|
||||
svg {
|
||||
margin-top: -4px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
.option-value {
|
||||
@include font-size(15);
|
||||
@include font-size(14);
|
||||
font-weight: 700;
|
||||
width: 100%;
|
||||
vertical-align: middle;
|
||||
|
||||
&.placehoder {
|
||||
color: $light_text;
|
||||
color: rgba($text, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,6 +182,7 @@
|
||||
|
||||
.input-area {
|
||||
background: $dark_mode_foreground;
|
||||
border-color: $dark_mode_foreground;
|
||||
|
||||
.option-icon {
|
||||
path {
|
||||
@@ -189,6 +207,13 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.option-value {
|
||||
|
||||
&.placehoder {
|
||||
color: $dark_mode_text_secondary;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
151
resources/js/components/Others/Forms/SetupBox.vue
Normal file
151
resources/js/components/Others/Forms/SetupBox.vue
Normal file
@@ -0,0 +1,151 @@
|
||||
<template>
|
||||
<div class="setup-box" :class="theme">
|
||||
<b class="title">{{ title }}</b>
|
||||
<p class="description">{{ description }}</p>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'SetupBox',
|
||||
props: ['title', 'description', 'theme'],
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '@assets/vue-file-manager/_variables';
|
||||
@import '@assets/vue-file-manager/_mixins';
|
||||
|
||||
.setup-box {
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
|
||||
.title {
|
||||
@include font-size(19);
|
||||
margin-bottom: 15px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.description {
|
||||
@include font-size(15);
|
||||
line-height: 1.5;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
&.base {
|
||||
background: $light_background;
|
||||
}
|
||||
|
||||
&.danger {
|
||||
background: $light_background;
|
||||
|
||||
.title {
|
||||
color: $danger;
|
||||
}
|
||||
}
|
||||
|
||||
/deep/ input {
|
||||
|
||||
&[type='text'],
|
||||
&[type='number'],
|
||||
.input-area {
|
||||
background: white;
|
||||
}
|
||||
}
|
||||
|
||||
/deep/ .input-area {
|
||||
background: white;
|
||||
}
|
||||
|
||||
/deep/ .form {
|
||||
margin-top: 20px;
|
||||
|
||||
&.block-form {
|
||||
max-width: 450px;
|
||||
|
||||
.single-line-form {
|
||||
display: flex;
|
||||
|
||||
.submit-button {
|
||||
margin-left: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 960px) {
|
||||
|
||||
.setup-box {
|
||||
|
||||
/deep/ .form {
|
||||
|
||||
&.block-form {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
input {
|
||||
min-width: initial;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 690px) {
|
||||
|
||||
.setup-box {
|
||||
padding: 15px;
|
||||
|
||||
.title {
|
||||
@include font-size(17);
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.description {
|
||||
@include font-size(14);
|
||||
}
|
||||
|
||||
/deep/ .form.block-form {
|
||||
|
||||
.single-line-form {
|
||||
display: block;
|
||||
|
||||
.submit-button {
|
||||
margin-left: 0;
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
|
||||
.setup-box {
|
||||
|
||||
&.base {
|
||||
background: $dark_mode_foreground;
|
||||
}
|
||||
|
||||
&.danger {
|
||||
background: $dark_mode_foreground;
|
||||
}
|
||||
|
||||
/deep/ input {
|
||||
|
||||
&[type='text'],
|
||||
&[type='number'],
|
||||
.input-area {
|
||||
background: $dark_mode_background;
|
||||
}
|
||||
}
|
||||
|
||||
/deep/ .input-area {
|
||||
background: $dark_mode_background;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -39,7 +39,8 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "@assets/app.scss";
|
||||
@import '@assets/vue-file-manager/_variables';
|
||||
@import '@assets/vue-file-manager/_mixins';
|
||||
|
||||
.input-wrapper {
|
||||
display: flex;
|
||||
|
||||
195
resources/js/components/Others/MobileNavigation.vue
Normal file
195
resources/js/components/Others/MobileNavigation.vue
Normal file
@@ -0,0 +1,195 @@
|
||||
<template>
|
||||
<div class="mobile-main-navigation" v-if="app">
|
||||
<transition name="context-menu">
|
||||
<nav v-if="isVisible" class="mobile-navigation">
|
||||
|
||||
<!--User Info-->
|
||||
<div class="user-info">
|
||||
<UserAvatar size="large"/>
|
||||
<UserHeadline/>
|
||||
</div>
|
||||
|
||||
<!--Navigation-->
|
||||
<MenuItemList :navigation="navigation" @menu="action"/>
|
||||
</nav>
|
||||
</transition>
|
||||
<transition name="fade">
|
||||
<div v-show="isVisible" class="vignette" @click="closeAndResetContextMenu"></div>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import UserHeadline from '@/components/Sidebar/UserHeadline'
|
||||
import MenuItemList from '@/components/Mobile/MenuItemList'
|
||||
import UserAvatar from '@/components/Others/UserAvatar'
|
||||
import {mapGetters} from 'vuex'
|
||||
import {events} from '@/bus'
|
||||
|
||||
export default {
|
||||
name: 'MenuBar',
|
||||
components: {
|
||||
MenuItemList,
|
||||
UserHeadline,
|
||||
UserAvatar,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['app', 'homeDirectory']),
|
||||
navigation() {
|
||||
return [
|
||||
{
|
||||
icon: 'hard-drive',
|
||||
title: this.$t('menu.files'),
|
||||
routeName: 'Files',
|
||||
isVisible: true,
|
||||
},
|
||||
{
|
||||
icon: 'latest',
|
||||
title: this.$t('menu.latest'),
|
||||
routeName: 'Files',
|
||||
isVisible: true,
|
||||
},
|
||||
{
|
||||
icon: 'share',
|
||||
title: this.$t('menu.shared'),
|
||||
routeName: 'SharedFiles',
|
||||
isVisible: true,
|
||||
},
|
||||
{
|
||||
icon: 'trash',
|
||||
title: this.$t('menu.trash'),
|
||||
routeName: 'Trash',
|
||||
isVisible: true,
|
||||
},
|
||||
{
|
||||
icon: 'settings',
|
||||
title: this.$t('menu.settings'),
|
||||
routeName: 'Profile',
|
||||
isVisible: true,
|
||||
},
|
||||
{
|
||||
icon: 'users',
|
||||
title: this.$t('menu.admin'),
|
||||
routeName: 'Users',
|
||||
isVisible: this.app.user.role === 'admin',
|
||||
},
|
||||
{
|
||||
icon: 'power',
|
||||
title: this.$t('menu.logout'),
|
||||
routeName: 'LogOut',
|
||||
isVisible: true,
|
||||
},
|
||||
]
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isVisible: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
action(name) {
|
||||
|
||||
if (name === 'latest') {
|
||||
this.$store.dispatch('getLatest')
|
||||
}
|
||||
|
||||
if (name === 'hard-drive') {
|
||||
this.$store.dispatch('getFolder', [{folder: this.homeDirectory, back: false, init: true}])
|
||||
}
|
||||
|
||||
if (name === 'power') {
|
||||
this.$store.dispatch('logOut')
|
||||
}
|
||||
|
||||
this.closeAndResetContextMenu()
|
||||
},
|
||||
closeAndResetContextMenu() {
|
||||
this.isVisible = false
|
||||
|
||||
events.$emit('hide:mobile-navigation')
|
||||
}
|
||||
},
|
||||
created() {
|
||||
events.$on('show:mobile-navigation', () => {
|
||||
this.isVisible = true
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '@assets/vue-file-manager/_variables';
|
||||
@import '@assets/vue-file-manager/_mixins';
|
||||
|
||||
.mobile-navigation {
|
||||
padding: 20px;
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 99;
|
||||
background: white;
|
||||
border-top-left-radius: 12px;
|
||||
border-top-right-radius: 12px;
|
||||
min-height: 440px;
|
||||
max-height: 80%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.vignette {
|
||||
background: rgba(0, 0, 0, 0.35);
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 9;
|
||||
cursor: pointer;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.user-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 690px) {
|
||||
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.mobile-navigation {
|
||||
background: $dark_mode_background;
|
||||
}
|
||||
}
|
||||
|
||||
// Transition
|
||||
.context-menu-enter-active,
|
||||
.fade-enter-active {
|
||||
transition: all 200ms;
|
||||
}
|
||||
|
||||
.context-menu-leave-active,
|
||||
.fade-leave-active {
|
||||
transition: all 200ms;
|
||||
}
|
||||
|
||||
.fade-enter,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.context-menu-enter,
|
||||
.context-menu-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateY(100%);
|
||||
}
|
||||
|
||||
.context-menu-leave-active {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<PopupWrapper name="move">
|
||||
<!--Title-->
|
||||
<PopupHeader :title="$t('popup_move_item.title')" />
|
||||
<PopupHeader :title="$t('popup_move_item.title')" icon="move" />
|
||||
|
||||
<!--Content-->
|
||||
<PopupContent type="height-limited" v-if="pickedItem">
|
||||
@@ -123,7 +123,6 @@
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "@assets/app.scss";
|
||||
|
||||
.item-thumbnail {
|
||||
margin-bottom: 20px;
|
||||
|
||||
161
resources/js/components/Others/Notifications/ToastrItem.vue
Normal file
161
resources/js/components/Others/Notifications/ToastrItem.vue
Normal file
@@ -0,0 +1,161 @@
|
||||
<template>
|
||||
<transition appear name="fade">
|
||||
<li class="toastr-item" :class="item.type" v-show="isActive">
|
||||
<div class="toastr-content-wrapper">
|
||||
<span class="toastr-icon">
|
||||
<check-icon v-if="item.type == 'success'" size="22"></check-icon>
|
||||
<x-icon v-if="item.type == 'danger'" size="22"></x-icon>
|
||||
</span>
|
||||
<div class="toastr-content">
|
||||
<p class="toastr-description">{{ item.message }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="progressbar">
|
||||
<span></span>
|
||||
</div>
|
||||
</li>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {XIcon, CheckIcon} from 'vue-feather-icons'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
XIcon,
|
||||
CheckIcon
|
||||
},
|
||||
props: ['item'],
|
||||
data() {
|
||||
return {
|
||||
isActive: 0
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.isActive = 1
|
||||
|
||||
setTimeout(() => (this.isActive = 0), 5000)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '@assets/vue-file-manager/_variables';
|
||||
@import '@assets/vue-file-manager/_mixins';
|
||||
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: 0.3s ease;
|
||||
}
|
||||
|
||||
.fade-enter,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
@include transform(translateX(100%));
|
||||
}
|
||||
|
||||
.toastr-content-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 15px;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.7);
|
||||
}
|
||||
|
||||
.progressbar {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
opacity: 0.35;
|
||||
|
||||
span {
|
||||
width: 0;
|
||||
height: 3px;
|
||||
display: block;
|
||||
background: $theme;
|
||||
animation: progressbar 5s linear;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes progressbar {
|
||||
0% {
|
||||
width: 0;
|
||||
}
|
||||
100% {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.toastr-item {
|
||||
max-width: 320px;
|
||||
margin-bottom: 20px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
display: block;
|
||||
border-radius: 8px;
|
||||
|
||||
.toastr-description {
|
||||
@include font-size(15);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.toastr-icon {
|
||||
height: 42px;
|
||||
width: 42px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0;
|
||||
font-size: 20px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
&.success {
|
||||
background: $theme_light;
|
||||
|
||||
polyline {
|
||||
stroke: $theme;
|
||||
}
|
||||
|
||||
.toastr-description {
|
||||
color: $theme;
|
||||
}
|
||||
}
|
||||
|
||||
&.danger {
|
||||
background: rgba($danger, 0.1);
|
||||
|
||||
polyline {
|
||||
stroke: $danger;
|
||||
}
|
||||
|
||||
.toastr-description {
|
||||
color: $danger;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 690px) {
|
||||
|
||||
.toastr-item {
|
||||
margin-bottom: 0;
|
||||
margin-top: 20px;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.fade-enter,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
@include transform(translateY(100%));
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.toastr-item {
|
||||
|
||||
&.success, &.danger {
|
||||
background: $dark_mode_foreground;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<div id="toastr-wrapper">
|
||||
<ToastrItem :item="item" v-for="(item, i) in notifications" :key="i"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ToastrItem from '@/components/Others/Notifications/ToastrItem'
|
||||
import {events} from "@/bus";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ToastrItem,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
notifications: []
|
||||
}
|
||||
},
|
||||
created() {
|
||||
events.$on('toaster', notification => this.notifications.push(notification))
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '@assets/vue-file-manager/_variables';
|
||||
@import '@assets/vue-file-manager/_mixins';
|
||||
|
||||
.toastr-list {
|
||||
transition: all 5s ease;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.toastr-list-enter,
|
||||
.toastr-list-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateY(-100%);
|
||||
}
|
||||
|
||||
.toastr-list-leave-active {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
#toastr-wrapper {
|
||||
position: absolute;
|
||||
right: 30px;
|
||||
top: 30px;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 690px) {
|
||||
|
||||
#toastr-wrapper {
|
||||
top: initial;
|
||||
right: 15px;
|
||||
left: 15px;
|
||||
bottom: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="page-header" @click="goHome">
|
||||
<div class="icon" v-if="isSmallAppSize">
|
||||
<FontAwesomeIcon icon="chevron-left" />
|
||||
<div class="page-header">
|
||||
<div class="go-back" v-if="canBack" @click="$router.back()">
|
||||
<chevron-left-icon size="17"></chevron-left-icon>
|
||||
</div>
|
||||
<div class="content">
|
||||
<h1 class="title">{{ title }}</h1>
|
||||
@@ -10,60 +10,53 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {mapGetters} from 'vuex'
|
||||
import {events} from '@/bus'
|
||||
import { ChevronLeftIcon } from 'vue-feather-icons'
|
||||
|
||||
export default {
|
||||
name: 'PageHeader',
|
||||
props: [
|
||||
'title', 'description'
|
||||
'title', 'canBack'
|
||||
],
|
||||
computed: {
|
||||
...mapGetters(['appSize']),
|
||||
isSmallAppSize() {
|
||||
return this.appSize === 'small'
|
||||
}
|
||||
components: {
|
||||
ChevronLeftIcon
|
||||
},
|
||||
methods: {
|
||||
goHome() {
|
||||
if (this.isSmallAppSize) {
|
||||
events.$emit('show:sidebar')
|
||||
this.$router.push({name: 'Files'})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "@assets/app.scss";
|
||||
@import '@assets/vue-file-manager/_variables';
|
||||
@import '@assets/vue-file-manager/_mixins';
|
||||
|
||||
.page-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 20px 30px;
|
||||
background: white;
|
||||
z-index: 9;
|
||||
width: 100%;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 9;
|
||||
padding: 20px 0;
|
||||
|
||||
.title {
|
||||
@include font-size(22);
|
||||
@include font-size(18);
|
||||
font-weight: 700;
|
||||
color: $text;
|
||||
}
|
||||
|
||||
.icon {
|
||||
@include font-size(16);
|
||||
margin-right: 15px;
|
||||
.go-back {
|
||||
margin-right: 10px;
|
||||
cursor: pointer;
|
||||
|
||||
svg {
|
||||
vertical-align: middle;
|
||||
margin-top: -4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 960px) {
|
||||
|
||||
.page-header {
|
||||
padding: 20px 15px;
|
||||
|
||||
.title {
|
||||
@include font-size(18);
|
||||
@@ -71,6 +64,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 690px) {
|
||||
.page-header {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
|
||||
.page-header {
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "@assets/app.scss";
|
||||
@import '@assets/vue-file-manager/_variables';
|
||||
@import '@assets/vue-file-manager/_mixins';
|
||||
|
||||
.actions {
|
||||
padding: 20px;
|
||||
@@ -24,7 +25,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
.small {
|
||||
@media only screen and (max-width: 690px) {
|
||||
.actions {
|
||||
padding: 15px;
|
||||
position: absolute;
|
||||
@@ -33,8 +34,4 @@
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -14,7 +14,8 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "@assets/app.scss";
|
||||
@import '@assets/vue-file-manager/_variables';
|
||||
@import '@assets/vue-file-manager/_mixins';
|
||||
|
||||
.popup-content {
|
||||
|
||||
@@ -24,8 +25,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
.small {
|
||||
|
||||
@media only screen and (max-width: 690px) {
|
||||
.popup-content {
|
||||
top: 57px;
|
||||
bottom: 72px;
|
||||
@@ -36,10 +36,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
|
||||
}
|
||||
|
||||
@keyframes popup-in {
|
||||
0% {
|
||||
opacity: 0;
|
||||
|
||||
@@ -1,26 +1,58 @@
|
||||
<template>
|
||||
<div class="popup-header">
|
||||
<h1 class="title">{{ title }}</h1>
|
||||
<div class="icon">
|
||||
<corner-down-right-icon v-if="icon === 'move'" size="15" class="title-icon"></corner-down-right-icon>
|
||||
<link-icon v-if="icon === 'share'" size="17" class="title-icon"></link-icon>
|
||||
</div>
|
||||
<div class="label">
|
||||
<h1 class="title">{{ title }}</h1>
|
||||
<x-icon @click="closePopup" size="22" class="close-icon"></x-icon>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { CornerDownRightIcon, LinkIcon, XIcon } from 'vue-feather-icons'
|
||||
import {events} from '@/bus'
|
||||
|
||||
export default {
|
||||
name: 'PopupHeader',
|
||||
props: [
|
||||
'title'
|
||||
]
|
||||
'title', 'icon'
|
||||
],
|
||||
components: {
|
||||
CornerDownRightIcon,
|
||||
LinkIcon,
|
||||
XIcon,
|
||||
},
|
||||
methods: {
|
||||
closePopup() {
|
||||
events.$emit('popup:close')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "@assets/app.scss";
|
||||
@import '@assets/vue-file-manager/_variables';
|
||||
@import '@assets/vue-file-manager/_mixins';
|
||||
|
||||
.popup-header {
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.icon {
|
||||
margin-right: 10px;
|
||||
line-height: 0;
|
||||
|
||||
path, line, polyline, rect, circle {
|
||||
stroke: $theme;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
@include font-size(18);
|
||||
@include font-size(17);
|
||||
font-weight: 700;
|
||||
color: $text;
|
||||
}
|
||||
@@ -30,9 +62,30 @@
|
||||
color: #8b8f9a;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.label {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
|
||||
.close-icon {
|
||||
padding: 1px 4px;
|
||||
border-radius: 6px;
|
||||
|
||||
&:hover {
|
||||
background: $light_background;
|
||||
|
||||
line {
|
||||
stroke: $theme;
|
||||
}
|
||||
}
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.small {
|
||||
@media only screen and (max-width: 690px) {
|
||||
.popup-header {
|
||||
padding: 15px;
|
||||
}
|
||||
@@ -40,6 +93,17 @@
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.popup-header {
|
||||
|
||||
.label {
|
||||
.close-icon {
|
||||
|
||||
&:hover {
|
||||
background: $dark_mode_foreground;
|
||||
}
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
color: $dark_mode_text_primary;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<transition name="popup">
|
||||
<div class="popup" @click.self="closePopup" v-show="isVisibleWrapper">
|
||||
<div class="popup" @click.self="closePopup" v-if="isVisibleWrapper">
|
||||
<div class="popup-wrapper">
|
||||
<slot></slot>
|
||||
</div>
|
||||
@@ -46,7 +46,8 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "@assets/app.scss";
|
||||
@import '@assets/vue-file-manager/_variables';
|
||||
@import '@assets/vue-file-manager/_mixins';
|
||||
|
||||
.popup {
|
||||
position: absolute;
|
||||
@@ -83,7 +84,27 @@
|
||||
}
|
||||
}
|
||||
|
||||
.small {
|
||||
@keyframes popup-in {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: scale(0.7);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
@keyframes popup-slide-in {
|
||||
0% {
|
||||
transform: translateY(100%);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 690px) {
|
||||
|
||||
.popup {
|
||||
overflow: hidden;
|
||||
}
|
||||
@@ -110,25 +131,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes popup-in {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: scale(0.7);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
@keyframes popup-slide-in {
|
||||
0% {
|
||||
transform: translateY(100%);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.popup-wrapper {
|
||||
background: $dark_mode_background;
|
||||
|
||||
34
resources/js/components/Others/SectionTitle.vue
Normal file
34
resources/js/components/Others/SectionTitle.vue
Normal file
@@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<b class="text-label">
|
||||
<slot></slot>
|
||||
</b>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'SectionTitle',
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '@assets/vue-file-manager/_variables';
|
||||
@import '@assets/vue-file-manager/_mixins';
|
||||
|
||||
.text-label {
|
||||
@include font-size(12);
|
||||
color: #AFAFAF;
|
||||
font-weight: 700;
|
||||
display: block;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1024px) {
|
||||
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.text-label {
|
||||
color: $theme;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<PopupWrapper name="share-create">
|
||||
<!--Title-->
|
||||
<PopupHeader :title="$t('popup_share_create.title', {item: itemTypeTitle})" />
|
||||
<PopupHeader :title="$t('popup_share_create.title', {item: itemTypeTitle})" icon="share" />
|
||||
|
||||
<!--Content-->
|
||||
<PopupContent>
|
||||
@@ -181,8 +181,6 @@
|
||||
|
||||
// Restore data
|
||||
setTimeout(() => {
|
||||
this.isGeneratedShared = false
|
||||
this.shareLink = undefined
|
||||
this.shareOptions = {
|
||||
permission: undefined,
|
||||
password: undefined,
|
||||
@@ -190,6 +188,8 @@
|
||||
type: undefined,
|
||||
unique_id: undefined,
|
||||
}
|
||||
this.isGeneratedShared = false
|
||||
this.shareLink = undefined
|
||||
}, 150)
|
||||
})
|
||||
}
|
||||
@@ -197,8 +197,8 @@
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "@assets/app.scss";
|
||||
@import "@assets/vue-file-manager/_inapp-forms.scss";
|
||||
@import '@assets/vue-file-manager/_forms';
|
||||
|
||||
.input-wrapper {
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<PopupWrapper name="share-edit">
|
||||
<!--Title-->
|
||||
<PopupHeader :title="$t('popup_share_edit.title')" />
|
||||
<PopupHeader :title="$t('popup_share_edit.title')" icon="share" />
|
||||
|
||||
<!--Content-->
|
||||
<PopupContent v-if="pickedItem && pickedItem.shared">
|
||||
@@ -243,8 +243,8 @@
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "@assets/app.scss";
|
||||
@import "@assets/vue-file-manager/_inapp-forms.scss";
|
||||
@import '@assets/vue-file-manager/_forms';
|
||||
|
||||
.input-wrapper {
|
||||
|
||||
|
||||
203
resources/js/components/Others/StorageItemDetail.vue
Normal file
203
resources/js/components/Others/StorageItemDetail.vue
Normal file
@@ -0,0 +1,203 @@
|
||||
<template>
|
||||
<article class="detail-storage-item" :class="type">
|
||||
<div class="header-storage-item">
|
||||
<div class="icon">
|
||||
<image-icon v-if="type == 'images'" size="23"></image-icon>
|
||||
<video-icon v-if="type == 'videos'" size="23"></video-icon>
|
||||
<music-icon v-if="type == 'audios'" size="23"></music-icon>
|
||||
<file-text-icon v-if="type == 'documents'" size="23"></file-text-icon>
|
||||
<file-icon v-if="type == 'others'" size="23"></file-icon>
|
||||
<hard-drive-icon v-if="type == 'disk'" size="23"></hard-drive-icon>
|
||||
</div>
|
||||
<div class="title">
|
||||
<b class="type">{{ title }}</b>
|
||||
<span class="total-size">{{ used }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<ProgressBar class="storage-progress" :progress="percentage" />
|
||||
</article>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ProgressBar from '@/components/FilesView/ProgressBar'
|
||||
import { ImageIcon, VideoIcon, FileTextIcon, FileIcon, HardDriveIcon, MusicIcon } from 'vue-feather-icons'
|
||||
|
||||
export default {
|
||||
name: 'StorageItemDetail',
|
||||
props: ['percentage', 'title', 'type', 'used'],
|
||||
components: {
|
||||
HardDriveIcon,
|
||||
FileTextIcon,
|
||||
ProgressBar,
|
||||
MusicIcon,
|
||||
VideoIcon,
|
||||
ImageIcon,
|
||||
FileIcon,
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '@assets/vue-file-manager/_variables';
|
||||
@import '@assets/vue-file-manager/_mixins';
|
||||
|
||||
.detail-storage-item {
|
||||
margin-bottom: 35px;
|
||||
|
||||
&.disk {
|
||||
|
||||
.icon {
|
||||
|
||||
path, line, polyline, rect, circle, polygon {
|
||||
stroke: $theme;
|
||||
}
|
||||
}
|
||||
|
||||
.storage-progress {
|
||||
|
||||
/deep/ span {
|
||||
background: $theme;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.images {
|
||||
|
||||
.icon {
|
||||
|
||||
path, line, polyline, rect, circle, polygon {
|
||||
stroke: $purple;
|
||||
}
|
||||
}
|
||||
|
||||
.storage-progress {
|
||||
|
||||
/deep/ span {
|
||||
background: $purple;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.videos {
|
||||
|
||||
.icon {
|
||||
|
||||
path, line, polyline, rect, circle, polygon {
|
||||
stroke: $yellow;
|
||||
}
|
||||
}
|
||||
|
||||
.storage-progress {
|
||||
|
||||
/deep/ span {
|
||||
background: $yellow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.audios {
|
||||
|
||||
.icon {
|
||||
|
||||
path, line, polyline, rect, circle, polygon {
|
||||
stroke: $pink;
|
||||
}
|
||||
}
|
||||
|
||||
.storage-progress {
|
||||
|
||||
/deep/ span {
|
||||
background: $pink;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.documents {
|
||||
|
||||
.icon {
|
||||
|
||||
path, line, polyline, rect, circle, polygon {
|
||||
stroke: $red;
|
||||
}
|
||||
}
|
||||
|
||||
.storage-progress {
|
||||
|
||||
/deep/ span {
|
||||
background: $red;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.others {
|
||||
|
||||
.icon {
|
||||
|
||||
path, line, polyline, rect, circle, polygon {
|
||||
stroke: $text;
|
||||
}
|
||||
}
|
||||
|
||||
.storage-progress {
|
||||
|
||||
/deep/ span {
|
||||
background: $text;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.header-storage-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 10px;
|
||||
|
||||
.icon {
|
||||
width: 35px;
|
||||
}
|
||||
|
||||
.type {
|
||||
@include font-size(15);
|
||||
color: $text;
|
||||
}
|
||||
|
||||
.total-size {
|
||||
@include font-size(10);
|
||||
display: block;
|
||||
color: $text-muted;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
|
||||
.header-storage-item {
|
||||
.type {
|
||||
color: $dark_mode_text_primary;
|
||||
}
|
||||
|
||||
.total-size {
|
||||
color: $dark_mode_text_secondary;
|
||||
}
|
||||
}
|
||||
|
||||
.detail-storage-item {
|
||||
|
||||
&.others {
|
||||
|
||||
.icon {
|
||||
|
||||
path, line, polyline, rect, circle, polygon {
|
||||
stroke: lighten($dark_mode_foreground, 15%);
|
||||
}
|
||||
}
|
||||
|
||||
.storage-progress {
|
||||
|
||||
/deep/ span {
|
||||
background: lighten($dark_mode_foreground, 15%);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
58
resources/js/components/Others/Tables/DatatableCell.vue
Normal file
58
resources/js/components/Others/Tables/DatatableCell.vue
Normal file
@@ -0,0 +1,58 @@
|
||||
<template>
|
||||
<tr class="table-row">
|
||||
<td
|
||||
class="table-cell"
|
||||
v-for="(collumn, index) in normalizedColumns"
|
||||
:key="index"
|
||||
>
|
||||
<span>{{ collumn }}</span>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ['data'],
|
||||
computed: {
|
||||
normalizedColumns() {
|
||||
// Remove ID from object
|
||||
if (this.data['id']) delete this.data['id']
|
||||
|
||||
// Return object
|
||||
return Object.values(this.data)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '@assets/vue-file-manager/_variables';
|
||||
@import '@assets/vue-file-manager/_mixins';
|
||||
|
||||
.table-row {
|
||||
border-radius: 8px;
|
||||
|
||||
&:hover {
|
||||
background: $light_background;
|
||||
}
|
||||
|
||||
.table-cell {
|
||||
padding-top: 15px;
|
||||
padding-bottom: 15px;
|
||||
|
||||
&:first-child {
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
padding-right: 15px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
span {
|
||||
@include font-size(16);
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user