Merge remote-tracking branch 'origin/version-1.8.3' into v2

# Conflicts:
#	app/FileManagerFolder.php
#	app/Http/Controllers/AppFunctionsController.php
#	app/Http/Controllers/Auth/AuthController.php
#	app/Http/Controllers/FileManager/BrowseController.php
#	app/Http/Controllers/General/SetupWizardController.php
#	app/Http/Controllers/General/UpgradeAppController.php
#	app/Http/Controllers/Sharing/FileSharingController.php
#	app/Http/helpers.php
#	app/Setting.php
#	composer.lock
#	public/mix-manifest.json
#	resources/js/App.vue
#	resources/js/components/Others/Forms/FormLabel.vue
#	resources/js/store/modules/app.js
#	resources/js/views/Admin.vue
#	resources/js/views/Mobile/AdminMobileMenu.vue
#	resources/js/views/Shared/SharedPage.vue
#	resources/views/index.blade.php
#	resources/views/vuefilemanager/crawler/og-view.blade.php
#	resources/views/vuefilemanager/invoice.blade.php
#	routes/api.php
#	routes/web.php
This commit is contained in:
Peter Papp
2021-03-27 12:12:42 +01:00
39 changed files with 3404 additions and 132 deletions

View File

View File

@@ -0,0 +1,146 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Setting;
use App\Language;
use App\LanguageString;
use App\Http\Tools\Demo;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Cache;
use App\Http\Requests\Languages\UpdateStringRequest;
use App\Http\Requests\Languages\CreateLanguageRequest;
use App\Http\Requests\Languages\UpdateLanguageRequest;
class LanguageController extends Controller
{
/**
* Get all languages for admin translate
*
* @return Collection
*/
public function get_languages()
{
return [
'languages' => Language::all(),
'set_language' => Setting::whereName('language')->first()->value
];
}
/**
* Get all language strings for admin translate
*
* @param Language $language
* @return Collection
*/
public function get_language_strings(Language $language)
{
$lang = Language::whereId($language->id);
$strings = $lang->with('languageStrings')->first();
// dd($language);
$license = get_setting('license') === 'Extended' ? 'extended' : 'regular';
$default_strings = collect(config('language_strings.' . $license));
return [
'translated_strings' => $strings,
'default_strings' => $default_strings
];
}
/**
* Create new language
*
* @param CreateLanguageRequest $request
* @return string
*/
public function create_language(CreateLanguageRequest $request)
{
// Check if is demo
if (env('APP_DEMO')) {
return Demo::response_204();
}
// Create languages & strings
$language = Language::create([
'name' => $request->name,
'locale' => $request->locale
]);
// Return created language
return $language;
}
/**
* Update language
*
* @param UpdateLanguageRequest $request
* @param Language $language
* @return $language
*/
public function update_language(UpdateLanguageRequest $request, Language $language)
{
// Check if is demo
if (env('APP_DEMO')) {
return Demo::response_204();
}
// Update language
$language->update(make_single_input($request));
// Return updated language
return $language;
}
/**
* Update string for language
*
* @param UpdateStringRequest $request
* @param Language $language
* @return ResponseFactory|\Illuminate\Http\Response
*/
public function update_string(UpdateStringRequest $request, Language $language)
{
// Check if is demo
if (env('APP_DEMO')) {
return Demo::response_204();
}
LanguageString::whereLangAndKey($language->locale, $request->name)
->update([
'key' => $request->name,
'lang' => $language->locale,
'value' => $request->value
]);
Cache::forget('language_strings-' . $language->locale);
return response('Done', 204);
}
/**
* Delete the language with all children strings
*
* @param Language $language
* @return ResponseFactory|\Illuminate\Http\Response
*/
public function delete_language(Language $language)
{
// Check if is demo
if (env('APP_DEMO')) {
return Demo::response_204();
}
$language->delete();
return response('Done', 204);
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace App\Http\Requests\Languages;
use Illuminate\Foundation\Http\FormRequest;
class CreateLanguageRequest 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',
'locale' => 'required|string'
];
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace App\Http\Requests\Languages;
use Illuminate\Foundation\Http\FormRequest;
class UpdateLanguageRequest 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',
'value' => 'required|string'
];
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace App\Http\Requests\Languages;
use Illuminate\Foundation\Http\FormRequest;
class UpdateStringRequest 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',
'value' => 'required|string'
];
}
}

View File

@@ -5,6 +5,8 @@ use App\Models\Folder;
use App\Models\Setting;
use App\Models\User;
use App\Models\Share;
use App\Models\Language;
use App\Models\LanguageString;
use ByteUnits\Metric;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model;
@@ -811,3 +813,54 @@ function set_time_by_user_timezone($time)
return Carbon::parse($time);
}
function __t($key, $values = null)
{
// Check if is in cache save default_language
if (Cache::has('default_language')) {
$locale = Cache::get('default_language');
} else {
$locale = Cache::rememberForever('default_language', function () {
return get_setting('language');
});
}
// Check if cash has string
if (Cache::has('language_strings-' . $locale)) {
$strings = Cache::get('language_strings-' . $locale)
->languageStrings;
// Find the string by key
$string = $strings->firstWhere('key', $key)->value;
}
// If cash dont have string return string from database
$string = LanguageString::whereLangAndKey($locale, $key)
->first()
->value;
if($values) {
return adjust_value($string, $values);
}
return $string;
}
function adjust_value($string, $values)
{
$search = [];
$replace = [];
if($values) {
foreach($values as $key => $variable) {
array_push($search, ':' . $key);
array_push($replace, $variable);
}
}
return str_ireplace($search, $replace, $string);
}

71
app/Language.php Normal file
View File

@@ -0,0 +1,71 @@
<?php
namespace App;
use App\LanguageString;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Cache;
use Illuminate\Database\Eloquent\Model;
class Language extends Model
{
protected $guarded = ['id'];
protected $keyType = 'string';
protected $primaryKey = 'id';
public $incrementing = false ;
public $timestamps = false;
protected static function boot()
{
parent::boot();
static::creating(function ($language) {
$language->id = Str::uuid();
});
static::deleting(function ($language) {
DB::table('language_strings')
->where('lang', $language->locale)
->delete();
Cache::forget('language_strings-' . $language->locale );
});
static::updated(function($language) {
Cache::forget('language_strings-' . $language->locale );
});
static::created(function ($language) {
$license = get_setting('license') === 'Extended' ? 'extended' : 'regular';
$language_strings = collect(config('language_strings.' . $license));
$strings = $language_strings->map(function ($value , $key) use($language) {
return [
'key' => $key,
'lang' => $language->locale,
'value' => $value
];
})->toArray();
DB::table('language_strings')
->insert($strings);
});
}
public function languageStrings()
{
return $this->hasMany('App\LanguageString', 'lang', 'locale');
}
}

28
app/LanguageString.php Normal file
View File

@@ -0,0 +1,28 @@
<?php
namespace App;
use Illuminate\Support\Facades\Cache;
use Illuminate\Database\Eloquent\Model;
class LanguageString extends Model
{
public $timestamps = false;
public $primaryKey = null;
public $incrementing = false;
protected $fillable = ['value' ,'key', 'lang'];
protected static function boot()
{
parent::boot();
static::updated(function() {
dd('test');
// Cache::forget('language_strings');
});
}
}

View File

@@ -69,7 +69,7 @@ class File extends Model
*/
public function getCreatedAtAttribute()
{
return format_date(set_time_by_user_timezone($this->attributes['created_at']), __('vuefilemanager.time'));
return format_date(set_time_by_user_timezone($this->attributes['created_at']), __t('time'));
}
/**
@@ -81,7 +81,7 @@ class File extends Model
{
if (!$this->attributes['deleted_at']) return null;
return format_date(set_time_by_user_timezone($this->attributes['deleted_at']), __('vuefilemanager.time'));
return format_date(set_time_by_user_timezone($this->attributes['deleted_at']), __t('time'));
}
/**

View File

@@ -44,12 +44,12 @@ class ResetPassword extends Notification
$app_name = get_setting('app_title') ?? 'VueFileManager';
return (new MailMessage)
->subject(__('vuefilemanager.reset_password_subject') . $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') . ', ' . $app_name);
->subject(__t('reset_password_subject') . $app_name)
->greeting(__t('reset_password_greeting'))
->line(__t('reset_password_line_1'))
->action(__t('reset_password_action'), $reset_url)
->line(__t('reset_password_line_2'))
->salutation(__t('salutation') . ', ' . $app_name);
}
/**

View File

@@ -43,11 +43,11 @@ class SharedSendViaEmail extends Notification
public function toMail($notifiable)
{
return (new MailMessage)
->subject(__('vuefilemanager.shared_link_email_subject' , ['user' => $this->user->name]))
->greeting(__('vuefilemanager.shared_link_email_greeting'))
->line(__('vuefilemanager.shared_link_email_user', ['user' => $this->user->name, 'email' => $this->user->email]))
->action(__('vuefilemanager.shared_link_email_link'), url('/shared', ['token' => $this->token]))
->salutation(__('vuefilemanager.shared_link_email_salutation', ['app_name' => get_setting('app_title') ?? 'VueFileManager']));
->subject(__t('shared_link_email_subject' , ['user' => $this->user->name]))
->greeting(__t('shared_link_email_greeting'))
->line(__t('shared_link_email_user', ['user' => $this->user->name, 'email' => $this->user->email]))
->action(__t('shared_link_email_link'), url('/shared', ['token' => $this->token]))
->salutation(__t('shared_link_email_salutation', ['app_name' => get_setting('app_title') ?? 'VueFileManager']));
}
/**

1222
config/language_strings.php Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateLanguagesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('languages', function (Blueprint $table) {
$table->uuid('id')->primary();
$table->string('name');
$table->string('locale')->unique();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('languages');
}
}

View File

@@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateLanguageStrings extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('language_strings', function (Blueprint $table) {
$table->string('key');
$table->longText('value');
$table->string('lang');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('language_strings');
}
}

View File

@@ -17,6 +17,7 @@
<cloud-icon v-if="link.icon === 'cloud'" size="17"></cloud-icon>
<monitor-icon v-if="link.icon === 'monitor'" size="17"></monitor-icon>
<box-icon v-if="link.icon === 'box'" size="17"></box-icon>
<globe-icon v-if="link.icon === 'language'" size="17"></globe-icon>
</div>
<b class="menu-link">
<span>{{ link.title }}</span>
@@ -39,6 +40,7 @@
Trash2Icon,
CloudIcon,
PowerIcon,
GlobeIcon,
ShareIcon,
UsersIcon,
UserIcon,
@@ -61,6 +63,7 @@
Trash2Icon,
CloudIcon,
PowerIcon,
GlobeIcon,
UsersIcon,
ShareIcon,
LockIcon,

View File

@@ -0,0 +1,867 @@
<template>
<PopupWrapper name="create-language">
<!--Title-->
<PopupHeader :title="'Create Language'" icon="edit" />
<!--Content-->
<PopupContent>
<!--Form to set sharing-->
<ValidationObserver @submit.prevent="createFolder" ref="createForm" v-slot="{ invalid }" tag="form" class="form-wrapper">
<!--Set password-->
<ValidationProvider tag="div" mode="passive" class="input-wrapper password" name="Language Name" rules="required" v-slot="{ errors }">
<label class="input-label">Type Name:</label>
<input v-model="name" :class="{'is-error': errors[0]}" type="text" ref="input" placeholder="Type Language Name">
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
<!--Set password-->
<ValidationProvider tag="div" mode="passive" class="input-wrapper password" name="Language Locale" rules="required" v-slot="{ errors }">
<label class="input-label">Select Locale:</label>
<SelectInput v-model="locale" :options="allLocals" placeholder="Select Language Locale" :isError="errors[0]"/>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</ValidationObserver>
</PopupContent>
<!--Actions-->
<PopupActions>
<ButtonBase
class="popup-button"
@click.native="$closePopup()"
button-style="secondary"
>Cancel
</ButtonBase>
<ButtonBase
class="popup-button"
@click.native="createLanguage"
button-style="theme"
:loading="isLoading"
:disabled="isLoading"
>Create Language
</ButtonBase>
</PopupActions>
</PopupWrapper>
</template>
<script>
import {ValidationProvider, ValidationObserver} from 'vee-validate/dist/vee-validate.full'
import PopupWrapper from '@/components/Others/Popup/PopupWrapper'
import PopupActions from '@/components/Others/Popup/PopupActions'
import PopupContent from '@/components/Others/Popup/PopupContent'
import PopupHeader from '@/components/Others/Popup/PopupHeader'
import SelectInput from '@/components/Others/Forms/SelectInput'
import ButtonBase from '@/components/FilesView/ButtonBase'
import {required} from 'vee-validate/dist/rules'
import {events} from '@/bus'
import axios from 'axios'
export default {
name: 'CreateLanguage',
components: {
ValidationProvider,
ValidationObserver,
PopupWrapper,
PopupActions,
PopupContent,
PopupHeader,
SelectInput,
ButtonBase,
required,
},
data() {
return {
name: undefined,
locale: undefined,
isLoading: false,
allLocals: [
{
value: "ab",
label: "Abkhaz"
},
{
value: "aa",
label: "Afar"
},
{
value: "af",
label: "Afrikaans"
},
{
value: "ak",
label: "Akan"
},
{
value: "sq",
label: "Albanian"
},
{
value: "am",
label: "Amharic"
},
{
value: "ar",
label: "Arabic"
},
{
value: "an",
label: "Aragonese"
},
{
value: "hy",
label: "Armenian"
},
{
value: "as",
label: "Assamese"
},
{
value: "av",
label: "Avaric"
},
{
value: "ae",
label: "Avestan"
},
{
value: "ay",
label: "Aymara"
},
{
value: "az",
label: "Azerbaijani"
},
{
value: "bm",
label: "Bambara"
},
{
value: "ba",
label: "Bashkir"
},
{
value: "eu",
label: "Basque"
},
{
value: "be",
label: "Belarusian"
},
{
value: "bn",
label: "Bengali; Bangla"
},
{
value: "bh",
label: "Bihari"
},
{
value: "bi",
label: "Bislama"
},
{
value: "bs",
label: "Bosnian"
},
{
value: "br",
label: "Breton"
},
{
value: "bg",
label: "Bulgarian"
},
{
value: "my",
label: "Burmese"
},
{
value: "ca",
label: "Catalan; Valencian"
},
{
value: "ch",
label: "Chamorro"
},
{
value: "ce",
label: "Chechen"
},
{
value: "ny",
label: "Chichewa; Chewa; Nyanja"
},
{
value: "zh",
label: "Chinese"
},
{
value: "cv",
label: "Chuvash"
},
{
value: "kw",
label: "Cornish"
},
{
value: "co",
label: "Corsican"
},
{
value: "cr",
label: "Cree"
},
{
value: "hr",
label: "Croatian"
},
{
value: "cs",
label: "Czech"
},
{
value: "da",
label: "Danish"
},
{
value: "dv",
label: "Divehi; Dhivehi; Maldivian;"
},
{
value: "nl",
label: "Dutch"
},
{
value: "dz",
label: "Dzongkha"
},
{
value: "en",
label: "English"
},
{
value: "eo",
label: "Esperanto"
},
{
value: "et",
label: "Estonian"
},
{
value: "ee",
label: "Ewe"
},
{
value: "fo",
label: "Faroese"
},
{
value: "fj",
label: "Fijian"
},
{
value: "fi",
label: "Finnish"
},
{
value: "fr",
label: "French"
},
{
value: "ff",
label: "Fula; Fulah; Pulaar; Pular"
},
{
value: "gl",
label: "Galician"
},
{
value: "lg",
label: "Ganda"
},
{
value: "ka",
label: "Georgian"
},
{
value: "de",
label: "German"
},
{
value: "el",
label: "Greek, Modern"
},
{
value: "gn",
label: "Guaraní"
},
{
value: "gu",
label: "Gujarati"
},
{
value: "ht",
label: "Haitian; Haitian Creole"
},
{
value: "ha",
label: "Hausa"
},
{
value: "he",
label: "Hebrew (modern)"
},
{
value: "hz",
label: "Herero"
},
{
value: "hi",
label: "Hindi"
},
{
value: "ho",
label: "Hiri Motu"
},
{
value: "hu",
label: "Hungarian"
},
{
value: "ia",
label: "Interlingua"
},
{
value: "id",
label: "Indonesian"
},
{
value: "ie",
label: "Interlingue"
},
{
value: "ga",
label: "Irish"
},
{
value: "ig",
label: "Igbo"
},
{
value: "ik",
label: "Inupiaq"
},
{
value: "io",
label: "Ido"
},
{
value: "is",
label: "Icelandic"
},
{
value: "it",
label: "Italian"
},
{
value: "iu",
label: "Inuktitut"
},
{
value: "ja",
label: "Japanese"
},
{
value: "jv",
label: "Javanese"
},
{
value: "kl",
label: "Kalaallisut, Greenlandic"
},
{
value: "kn",
label: "Kannada"
},
{
value: "kr",
label: "Kanuri"
},
{
value: "ks",
label: "Kashmiri"
},
{
value: "kk",
label: "Kazakh"
},
{
value: "km",
label: "Khmer"
},
{
value: "ki",
label: "Kikuyu, Gikuyu"
},
{
value: "rw",
label: "Kinyarwanda"
},
{
value: "rn",
label: "Kirundi"
},
{
value: "ky",
label: "Kyrgyz"
},
{
value: "kv",
label: "Komi"
},
{
value: "kg",
label: "Kongo"
},
{
value: "ko",
label: "Korean"
},
{
value: "ku",
label: "Kurdish"
},
{
value: "kj",
label: "Kwanyama, Kuanyama"
},
{
value: "la",
label: "Latin"
},
{
value: "lb",
label: "Luxembourgish, Letzeburgesch"
},
{
value: "li",
label: "Limburgish, Limburgan, Limburger"
},
{
value: "ln",
label: "Lingala"
},
{
value: "lo",
label: "Lao"
},
{
value: "lt",
label: "Lithuanian"
},
{
value: "lu",
label: "Luba-Katanga"
},
{
value: "lv",
label: "Latvian"
},
{
value: "gv",
label: "Manx"
},
{
value: "mk",
label: "Macedonian"
},
{
value: "mg",
label: "Malagasy"
},
{
value: "ms",
label: "Malay"
},
{
value: "ml",
label: "Malayalam"
},
{
value: "mt",
label: "Maltese"
},
{
value: "mi",
label: "MÄori"
},
{
value: "mr",
label: "Marathi"
},
{
value: "mh",
label: "Marshallese"
},
{
value: "mn",
label: "Mongolian"
},
{
value: "na",
label: "Nauru"
},
{
value: "nv",
label: "Navajo, Navaho"
},
{
value: "nb",
label: "Norwegian"
},
{
value: "nd",
label: "North Ndebele"
},
{
value: "ne",
label: "Nepali"
},
{
value: "ng",
label: "Ndonga"
},
{
value: "nn",
label: "Norwegian Nynorsk"
},
{
value: "no",
label: "Norwegian"
},
{
value: "ii",
label: "Nuosu"
},
{
value: "oc",
label: "Occitan"
},
{
value: "oj",
label: "Ojibwe, Ojibwa"
},
{
value: "cu",
label: "Old Church Slavonic, Church Slavic, Church Slavonic, Old Bulgarian, Old Slavonic"
},
{
value: "om",
label: "Oromo"
},
{
value: "or",
label: "Oriya"
},
{
value: "os",
label: "Ossetian, Ossetic"
},
{
value: "pa",
label: "Panjabi, Punjabi"
},
{
value: "pi",
label: "Pali"
},
{
value: "fa",
label: "Persian (Farsi)"
},
{
value: "pl",
label: "Polish"
},
{
value: "ps",
label: "Pashto, Pushto"
},
{
value: "pt",
label: "Portuguese"
},
{
value: "qu",
label: "Quechua"
},
{
value: "rm",
label: "Romansh"
},
{
value: "ro",
label: "Romanian"
},
{
value: "ru",
label: "Russian"
},
{
value: "sa",
label: "Sanskrit"
},
{
value: "sc",
label: "Sardinian"
},
{
value: "sd",
label: "Sindhi"
},
{
value: "se",
label: "Northern Sami"
},
{
value: "sm",
label: "Samoan"
},
{
value: "sg",
label: "Sango"
},
{
value: "sr",
label: "Serbian"
},
{
value: "gd",
label: "Scottish Gaelic"
},
{
value: "sn",
label: "Shona"
},
{
value: "si",
label: "Sinhala, Sinhalese"
},
{
value: "sk",
label: "Slovak"
},
{
value: "sl",
label: "Slovene"
},
{
value: "so",
label: "Somali"
},
{
value: "st",
label: "Southern Sotho"
},
{
value: "az",
label: "South Azerbaijani"
},
{
value: "nr",
label: "South Ndebele"
},
{
value: "es",
label: "Spanish; Castilian"
},
{
value: "su",
label: "Sundanese"
},
{
value: "sw",
label: "Swahili"
},
{
value: "ss",
label: "Swati"
},
{
value: "sv",
label: "Swedish"
},
{
value: "ta",
label: "Tamil"
},
{
value: "te",
label: "Telugu"
},
{
value: "tg",
label: "Tajik"
},
{
value: "th",
label: "Thai"
},
{
value: "ti",
label: "Tigrinya"
},
{
value: "bo",
label: "Tibetan Standard, Tibetan, Central"
},
{
value: "tk",
label: "Turkmen"
},
{
value: "tl",
label: "Tagalog"
},
{
value: "tn",
label: "Tswana"
},
{
value: "to",
label: "Tonga (Tonga Islands)"
},
{
value: "tr",
label: "Turkish"
},
{
value: "ts",
label: "Tsonga"
},
{
value: "tt",
label: "Tatar"
},
{
value: "tw",
label: "Twi"
},
{
value: "ty",
label: "Tahitian"
},
{
value: "ug",
label: "Uyghur, Uighur"
},
{
value: "uk",
label: "Ukrainian"
},
{
value: "ur",
label: "Urdu"
},
{
value: "uz",
label: "Uzbek"
},
{
value: "ve",
label: "Venda"
},
{
value: "vi",
label: "Vielabele"
},
{
value: "vo",
label: "Volapük"
},
{
value: "wa",
label: "Walloon"
},
{
value: "cy",
label: "Welsh"
},
{
value: "wo",
label: "Wolof"
},
{
value: "fy",
label: "Western Frisian"
},
{
value: "xh",
label: "Xhosa"
},
{
value: "yi",
label: "Yiddish"
},
{
value: "yo",
label: "Yoruba"
},
{
value: "za",
label: "Zhuang, Chuang"
},
{
value: "zu",
label: "Zulu"
}
]
}
},
methods: {
async createLanguage() {
// Validate fields
const isValid = await this.$refs.createForm.validate();
if (isValid) {
this.isLoading = true
axios.post('/api/languages', {
name: this.name,
locale: this.locale
})
.then((response) => {
events.$emit('add-language', response.data)
})
.catch(() => Vue.prototype.$isSomethingWrong())
.finally(() => {
this.name = undefined
this.locale = undefined
this.isLoading = false
this.$closePopup()
})
}
},
},
mounted () {
this.name = undefined,
this.locale = undefined
}
}
</script>
<style scoped lang="scss">
@import "@assets/vue-file-manager/_inapp-forms.scss";
@import '@assets/vue-file-manager/_forms';
.item-thumbnail {
margin-bottom: 20px;
}
</style>

View File

@@ -1,6 +1,7 @@
<template>
<div class="form-label">
<edit-2-icon size="22" class="icon text-theme" />
<edit-2-icon v-if="!icon" size="22" class="icon text-theme" />
<settings-icon v-if="icon === 'settings'" size="22" class="icon text-theme" />
<b class="label">
<slot></slot>
</b>
@@ -8,12 +9,14 @@
</template>
<script>
import { Edit2Icon } from 'vue-feather-icons'
import { Edit2Icon, SettingsIcon } from 'vue-feather-icons'
export default {
name: 'FormLabel',
props: ['icon'],
components: {
Edit2Icon
Edit2Icon,
SettingsIcon
}
}
</script>
@@ -30,7 +33,7 @@
.icon {
margin-right: 10px;
path {
path, circle {
color: inherit;
}
}

View File

@@ -23,6 +23,29 @@ const Helpers = {
})
}, 150)
Vue.prototype.$loadLanguage = function (language) {
return new Promise((resolve, reject) => {
let locale = language ? language : this.$store.getters.config.language
axios.get(`/language/${locale}`)
.then((response) => {
let lang = response.data.language_strings
let obj = {}
lang.map(element => {
obj[element.key] = element.value
})
i18n.setLocaleMessage(locale, obj)
i18n.locale = locale
resolve(true)
})
})
}
Vue.prototype.$updateImage = function (route, name, image) {
// Create form

View File

@@ -8,10 +8,9 @@ import cn from './lang/cn.json'
Vue.use(VueI18n);
const i18n = new VueI18n({
locale: config.locale,
messages: Object.assign({
en
}),
locale: config.language,
silentTranslationWarn: true,
});
export default i18n;

View File

@@ -25,7 +25,8 @@
"pages": "Pages",
"plans": "Plans",
"settings": "Settings",
"users": "Users"
"users": "Users",
"language": "Language"
},
"admin_page_dashboard": {
"backer_button": "Help Us Improve",
@@ -672,7 +673,8 @@
"users_list": "User Management",
"users_password": "Password",
"users_storage_usage": "Storage Usage",
"users_user": "User"
"users_user": "User",
"language": "Language"
},
"rows": {
"card": {

View File

@@ -285,6 +285,16 @@ const routesAdmin = [
},
]
},
{
name: 'Language',
path: '/admin/language',
component: () =>
import(/* webpackChunkName: "chunks/app-language" */ './views/Admin/Languages/Language'),
meta: {
requiresAuth: true,
title: i18n.t('routes_title.language')
},
}
]
},
{

View File

@@ -41,6 +41,14 @@
{{ $t('admin_menu.pages') }}
</div>
</router-link>
<router-link :to="{name: 'Language'}" class="menu-list-item link">
<div class="icon text-theme">
<globe-icon size="17" />
</div>
<div class="label text-theme">
{{ $t('admin_menu.language') }}
</div>
</router-link>
</div>
</ContentGroup>
@@ -72,7 +80,7 @@
</template>
<script>
import { UsersIcon, SettingsIcon, FileTextIcon, CreditCardIcon, DatabaseIcon, BoxIcon, MonitorIcon } from 'vue-feather-icons'
import { UsersIcon, SettingsIcon, FileTextIcon, CreditCardIcon, DatabaseIcon, BoxIcon, MonitorIcon, GlobeIcon } from 'vue-feather-icons'
import ContentSidebar from '@/components/Sidebar/ContentSidebar'
import ContentGroup from '@/components/Sidebar/ContentGroup'
import MenuBar from '@/components/Sidebar/MenuBar'
@@ -94,6 +102,7 @@
UsersIcon,
MenuBar,
BoxIcon,
GlobeIcon
},
}
</script>

View File

@@ -0,0 +1,262 @@
<template>
<div id="single-page">
<div id="page-content">
<MobileHeader :title="$router.currentRoute.meta.title"/>
<div class="wrapper">
<Spinner v-if="! loadedLanguages"/>
<div v-if="loadedLanguages" class="side-content">
<PageHeader :can-back="true" :title="$router.currentRoute.meta.title"/>
<div class="languages-wrapper page-tab from-fixed-width">
<div class="language-label-wrapper">
<label class="language-label">Languages</label>
</div>
<!-- Languages -->
<div class="all-language-wrapper">
<div @click="openLanguage(language)" v-for="language in languages" :key="language.id">
<div class="language" >
<label class="name" :class="{'active' :activeLanguage.locale === language.locale}">
{{language.name}}
</label>
<x-icon v-if="language.locale !== 'en' && language.locale !== setLanguage"
@click.stop="deleteLanguageConfirm(language)"
class="icon" size="17"/>
</div>
</div>
</div>
</div>
<MobileActionButton @click.native="createLanguage" icon="plus" class="button-add-language">
Add Language
</MobileActionButton>
</div>
<!-- Strings -->
<LanguageStrings :active-language="activeLanguage" :set-language="setLanguage" />
</div>
</div>
</div>
</template>
<script>
import LanguageStrings from '@/views/Admin/Languages/LanguageStrings'
import MobileHeader from '@/components/Mobile/MobileHeader'
import ButtonBase from '@/components/FilesView/ButtonBase'
import MobileActionButton from '@/components/FilesView/MobileActionButton'
import PageHeader from '@/components/Others/PageHeader'
import Spinner from '@/components/FilesView/Spinner'
import { PlusIcon, XIcon } from 'vue-feather-icons'
import { events } from '@/bus'
export default {
name: 'Language',
components: {
MobileActionButton,
LanguageStrings,
MobileHeader,
ButtonBase,
PageHeader,
PlusIcon,
Spinner,
XIcon
},
data () {
return {
activeLanguage: undefined,
languagesStrings:undefined,
setLanguage: undefined,
languages:undefined,
loadedLanguages: false,
}
},
methods: {
deleteLanguageConfirm(language) {
events.$emit('confirm:open', {
title: `Delete ${language.name} language?`,
message: 'Your language will be permanently deleted.',
buttonColor: 'danger-solid',
action: language
})
},
deleteLanguage(language) {
axios.delete(`/api/languages/${language.id}`)
.then(() => { this.getLanguages() })
},
createLanguage() {
events.$emit('popup:open', {name: 'create-language'})
},
getLanguages() {
this.loadedStrings = false
axios
.get('/api/languages')
.then((response) => {
this.languages = response.data.languages
this.activeLanguage = response.data.languages[0]
this.setLanguage = response.data.set_language
})
.catch(() => Vue.prototype.$isSomethingWrong())
.finally(() => {
this.loadedLanguages = true
})
},
openLanguage(language) {
this.activeLanguage = language
}
},
mounted () {
this.getLanguages()
events.$on('add-language', () => {
this.getLanguages()
})
events.$on('action:confirmed', language => {
this.deleteLanguage(language)
})
events.$on('language-name:update', (language) => {
let index = _.findIndex(this.languages, function(item) { return item.id === language.id })
this.languages[index].name = language.name
})
events.$on('language:set-as-default', (locale) => {
this.setLanguage = locale
})
},
destroyed () {
events.$off('action:confirmed')
},
}
</script>
<style lang="scss" scoped>
@import '@assets/vue-file-manager/_variables';
@import '@assets/vue-file-manager/_mixins';
#single-page {
height: 100%;
#page-content {
height: 100%;
}
}
.wrapper {
display: flex;
height: 100%;
}
.side-content{
flex: 0 0 225px;
.button-add-language {
margin-top: 30px;
/deep/.content {
display: flex;
align-items: center;
@include font-size(14);
font-weight: 700;
.icon {
margin-right: 10px;
}
}
}
.languages-wrapper {
margin-top: 70px;
.language-label-wrapper {
margin-bottom: 5px;
.language-label {
color: $light_text;
font-weight: 700;
@include font-size(12);
margin-top: 20px;
}
}
.all-language-wrapper {
.language {
display: flex;
align-items: center;
padding: 12px 25px 12px 0px;
cursor: pointer;
&:hover {
.icon {
display: block;
}
.name {
color: $theme !important;
}
}
.name {
color: $text;
font-weight: 700;
@include font-size(13);
cursor: pointer;
}
.icon {
display: none;
margin-left: auto;
cursor: pointer;
}
.active {
color: $theme !important;
}
}
}
}
}
@media only screen and (max-width: 1024px) {
.wrapper {
flex-direction: column;
.side-content{
margin-bottom: 70px;
}
.languages-wrapper {
margin-top: 0px;
}
}
}
@media only screen and (max-width: 690px) {
.side-content{
margin-bottom: 35px !important;
flex: none;
}
}
@media (prefers-color-scheme: dark) {
.language{
.name {
color: $dark_mode_text_primary !important;
}
}
.language-label {
color: $dark_mode_text_secondary !important;
}
}
</style>

View File

@@ -0,0 +1,397 @@
<template>
<!-- Serach bar -->
<div v-if="strings" class="language-strings-wrapper form">
<div class="search-bar-wrapper">
<div class="search-bar">
<div v-if="!searchInput" class="icon" >
<search-icon size="19"></search-icon>
</div>
<div @click="resetInput" v-if="searchInput" class="icon">
<x-icon class="pointer" size="19"></x-icon>
</div>
<input
v-model="searchInput"
@input="searchStrings"
class="query"
type="text"
name="searchInput"
placeholder="Search Language Strings..."
/>
</div>
</div>
<Spinner v-if="!loadedStrings"/>
<!-- Set Language as default switch -->
<div v-if="loadedStrings" class="form block-form">
<FormLabel class="mt-70" icon="settings">Language Settings</FormLabel>
<div class="block-wrapper">
<div class="input-wrapper">
<div class="inline-wrapper">
<div class="switch-label">
<label class="input-label">
Set as Default Language:
</label>
</div>
<SwitchInput
@input="updateLanguageSetting"
class="switch"
:class="{'disable-switch': languageSettingHandle }"
:state="languageSettingHandle"
/>
</div>
</div>
</div>
<!-- Language name -->
<div class="block-wrapper">
<label> Language Name:</label>
<ValidationProvider tag="div" mode="passive" class="input-wrapper" name="Language Name" rules="required" v-slot="{ errors }">
<input type="text"
v-model="language.name"
@input="updateName"
:class="{'is-error': errors[0]}"
/>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</div>
<!-- Strings -->
<FormLabel class="mt-70">Language Strings</FormLabel>
<div v-if="!loadSearch || filteredStrings.length === 0 && !searchInput" class="spinner-wrapper">
<Spinner class="spinner" />
</div>
<div v-if="loadSearch && filteredStrings.length > 0">
<div class="block-wrapper string" v-for="(string,index) in filteredStrings" :key="index">
<label> {{string.value}}:</label>
<ValidationProvider tag="div" class="input-wrapper" name="Language string" rules="required" v-slot="{ errors }">
<input type="text"
:class="{'is-error': errors[0]}"
@input="updateString(string.key)"
v-model="strings[getIndex(string.key)].value"
/>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</div>
</div>
<!-- Not Fount -->
<div class="not-found-wrapper" v-if="loadSearch && filteredStrings.length === 0 && searchInput">
<span class="not-found">Not Found</span>
</div>
</div>
</div>
</template>
<script>
import { ValidationProvider, ValidationObserver } from 'vee-validate/dist/vee-validate.full'
import SwitchInput from '@/components/Others/Forms/SwitchInput'
import FormLabel from '@/components/Others/Forms/FormLabel'
import { SearchIcon, XIcon } from 'vue-feather-icons'
import Spinner from '@/components/FilesView/Spinner'
import { events } from '@/bus'
import lodash from 'lodash'
export default {
name: 'LanguageStrings',
props: [ 'activeLanguage', 'setLanguage' ],
components: {
ValidationProvider,
ValidationObserver,
SwitchInput,
SearchIcon,
FormLabel,
Spinner,
XIcon,
},
computed: {
languageSettingHandle() {
return this.language.locale == this.setLanguage ? true : false
},
},
data () {
return {
defaultStrings: [],
filteredStrings: [],
loadSearch: false,
loadedStrings: false,
strings: undefined,
language: undefined,
searchInput: '',
}
},
watch: {
activeLanguage () {
this.getLanguageStrings(this.activeLanguage)
this.searchInput = ''
}
},
methods: {
updateLanguageSetting() {
this.$updateText('/settings', 'language', this.language.locale)
events.$emit('language:set-as-default', this.language.locale)
this.$loadLanguage(this.language.locale)
},
resetInput(){
this.searchInput = ''
this.searchStrings()
},
getIndex(key){
if(this.strings)
return _.findIndex(this.strings, function(string) { return string.key === key })
},
updateName() {
this.$updateText(`/languages/${this.language.id}`, 'name', this.language.name)
events.$emit('language-name:update', this.language)
},
updateString (key) {
// Return if the input is empty
if(! this.strings[this.getIndex(key)].value) return
this.$updateText(
`/languages/${this.language.id}/string`, `${key}`, this.strings[this.getIndex(key)].value,
)
},
searchStrings() {
this.loadSearch = false
this.filteredStrings = []
this.filterStrings()
},
filterStrings: _.debounce(function () {
this.filteredStrings = this.defaultStrings.filter(string => string.value.toLowerCase().includes( this.searchInput.toLowerCase() ))
this.loadSearch = true
}, 200),
getLanguageStrings (language) {
this.loadedStrings = false
this.defaultStrings = []
this.filteredStrings= []
axios
.get(`/api/languages/${language.id}/strings`)
.then(response => {
this.strings = response.data.translated_strings.language_strings
this.language = {
'id': response.data.translated_strings.id,
'name': response.data.translated_strings.name,
'locale': response.data.translated_strings.locale,
}
// Make from JSON object array of objects
for (const [key, value] of Object.entries(response.data.default_strings)) {
this.defaultStrings.push({
'key': key,
'value': value,
})
}
this.filterStrings()
})
.catch(() => Vue.prototype.$isSomethingWrong())
.finally(() => {
this.loadedStrings = true
})
}
},
}
</script>
<style lang="scss" scoped>
@import '@assets/vue-file-manager/_variables';
@import '@assets/vue-file-manager/_mixins';
@import '@assets/vue-file-manager/_forms';
.spinner-wrapper {
position: relative;
height: 50%;
.spinner {
top: 60% !important;
}
}
.not-found-wrapper {
display: flex;
margin-top: 20%;
.not-found {
margin: auto;
font-weight: 700;
padding: 10px;
border-radius: 8px;
background: $light_background;
box-shadow: 0 1px 5px rgba(0, 0, 0, 0.12);
}
}
.string:last-child {
margin-bottom: 32px !important;
}
.block-form {
padding: 1px;
height: 100%;
}
.disable-switch {
cursor: not-allowed;
/deep/.text-right {
pointer-events: none;
}
}
.language-strings-wrapper {
width: 100%;
height: 100%;
margin: 0 auto;
display: flex;
flex-direction: column;
position: relative;
.block-form{
overflow-y: scroll;
overflow-x: hidden;
}
}
.search-bar-wrapper {
padding: 7px 0px;
}
.search-bar {
position: relative;
width: 100%;
border-radius: 8px;
input {
background: $light-background;
border-radius: 8px;
outline: 0;
padding: 9px 20px 9px 43px;
font-weight: 700;
@include font-size(16);
width: 100%;
height: 50px;
min-width: 175px;
transition: 0.15s all ease;
border: 1px solid transparent;
-webkit-appearance: none;
&::placeholder {
color: $light_text;
@include font-size(14);
font-weight: 700;
}
&:focus {
border: 1px solid $theme;
box-shadow: 0 0 7px rgba($theme, 0.3);
}
&:focus + .icon {
path {
fill: $theme;
}
}
}
.icon {
height: 100%;
position: absolute;
top: 0;
left: 0;
padding: 11px 15px;
display: flex;
align-items: center;
circle,
line {
color: $light_text;
}
.pointer {
cursor: pointer;
}
}
}
// @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 {
// box-shadow: none;
// }
// }
// .icon {
// padding: 11px 15px 11px 0;
// }
// }
}
@media (prefers-color-scheme: dark) {
.search-bar {
input {
background: $dark_mode_background ;
&::placeholder {
color: $dark_mode_text_secondary;
}
}
.icon {
circle,
line {
color: $dark_mode_text_secondary !important;
}
}
}
.not-found-wrapper {
.not-found {
background: $dark_mode_foreground !important;
}
}
}
</style>

View File

@@ -20,7 +20,7 @@
<!--Email-->
<div class="block-wrapper">
<label>{{ $t('page_registration.label_email') }}</label>
<label>{{ $t('page_registration.label_email') }}:</label>
<ValidationProvider tag="div" mode="passive" class="input-wrapper" name="email" rules="required" v-slot="{ errors }">
<input v-model="user.email" :placeholder="$t('admin_page_user.create_user.label_email')" type="email" class="focus-border-theme" :class="{'is-error': errors[0]}"/>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
@@ -29,7 +29,7 @@
<!--Name-->
<div class="block-wrapper">
<label>{{ $t('page_registration.label_name') }}</label>
<label>{{ $t('page_registration.label_name') }}:</label>
<ValidationProvider tag="div" mode="passive" class="input-wrapper" name="user name" rules="required" v-slot="{ errors }">
<input v-model="user.name" :placeholder="$t('admin_page_user.create_user.label_name')" type="text" class="focus-border-theme" :class="{'is-error': errors[0]}"/>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
@@ -39,7 +39,7 @@
<!--Password-->
<div class="wrapper-inline">
<div class="block-wrapper">
<label>{{ $t('page_registration.label_pass') }}</label>
<label>{{ $t('page_registration.label_pass') }}:</label>
<ValidationProvider tag="div" mode="passive" class="input-wrapper" name="password" rules="required" v-slot="{ errors }">
<input v-model="user.password" :placeholder="$t('page_registration.placeholder_pass')" type="password" class="focus-border-theme" :class="{'is-error': errors[0]}"/>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
@@ -47,7 +47,7 @@
</div>
<div class="block-wrapper">
<label>{{ $t('page_registration.label_confirm_pass') }}</label>
<label>{{ $t('page_registration.label_confirm_pass') }}:</label>
<ValidationProvider tag="div" mode="passive" class="input-wrapper" name="password confirm" rules="required" v-slot="{ errors }">
<input v-model="user.password_confirmation" :placeholder="$t('admin_page_user.create_user.label_conf_pass')" type="password" class="focus-border-theme" :class="{'is-error': errors[0]}"/>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>

View File

@@ -34,7 +34,7 @@
<!--Email-->
<div class="block-wrapper">
<label>{{ $t('page_registration.label_email') }}</label>
<label>{{ $t('page_registration.label_email') }}:</label>
<div class="input-wrapper">
<input :value="user.data.attributes.email"
:placeholder="$t('page_registration.placeholder_email')"
@@ -46,7 +46,7 @@
<!--Name-->
<div class="block-wrapper">
<label>{{ $t('page_registration.label_name') }}</label>
<label>{{ $t('page_registration.label_name') }}:</label>
<div class="input-wrapper">
<input :value="user.data.relationships.settings.data.attributes.name"
:placeholder="$t('page_registration.placeholder_name')"

View File

@@ -7,13 +7,13 @@
<b v-if="! config.app_logo" class="auth-logo-text">{{ config.app_name }}</b>
<h1>{{ $t('page_create_password.title') }}</h1>
<h2>{{ $t('page_create_password.subtitle') }}</h2>
<h2>{{ $t('page_create_password.subtitle') }}:</h2>
<ValidationObserver @submit.prevent="createNewPassword" ref="create_new_password" v-slot="{ invalid }"
tag="form" class="form block-form create-new-password">
<div class="block-wrapper">
<label>{{ $t('page_create_password.label_email') }}</label>
<label>{{ $t('page_create_password.label_email') }}:</label>
<ValidationProvider tag="div" mode="passive" class="input-wrapper" name="E-Mail" rules="required"
v-slot="{ errors }">
<input v-model="recoverPassword.email" :placeholder="$t('page_login.placeholder_email')" type="email"

View File

@@ -7,7 +7,7 @@
<b v-if="! config.app_logo" class="auth-logo-text">{{ config.app_name }}</b>
<h1>{{ $t('page_forgotten_password.title') }}</h1>
<h2>{{ $t('page_forgotten_password.subtitle') }}</h2>
<h2>{{ $t('page_forgotten_password.subtitle') }}:</h2>
<ValidationObserver @submit.prevent="forgottenPassword" ref="forgotten_password" v-slot="{ invalid }"
tag="form" class="form inline-form">

View File

@@ -7,7 +7,7 @@
<b v-if="! config.app_logo" class="auth-logo-text">{{ config.app_name }}</b>
<h1>{{ $t('page_login.title') }}</h1>
<h2>{{ $t('page_login.subtitle') }}</h2>
<h2>{{ $t('page_login.subtitle') }}:</h2>
<ValidationObserver @submit.prevent="logIn" ref="log_in" v-slot="{ invalid }" tag="form"
class="form inline-form">
@@ -37,7 +37,7 @@
<div class="user" v-if="checkedAccount">
<img class="user-avatar" :src="checkedAccount.avatar" :alt="checkedAccount.name">
<h1>{{ $t('page_sign_in.title', {name: checkedAccount.name}) }}</h1>
<h2>{{ $t('page_sign_in.subtitle') }}</h2>
<h2>{{ $t('page_sign_in.subtitle') }}:</h2>
</div>
<ValidationObserver @submit.prevent="singIn" ref="sign_in" v-slot="{ invalid }" tag="form"

View File

@@ -7,13 +7,13 @@
<b v-if="! config.app_logo" class="auth-logo-text">{{ config.app_name }}</b>
<h1>{{ $t('page_registration.title') }}</h1>
<h2>{{ $t('page_registration.subtitle') }}</h2>
<h2>{{ $t('page_registration.subtitle') }}:</h2>
<ValidationObserver @submit.prevent="signUp" ref="sign_up" v-slot="{ invalid }" tag="form"
class="form block-form">
<div class="block-wrapper">
<label>{{ $t('page_registration.label_email') }}</label>
<label>{{ $t('page_registration.label_email') }}:</label>
<ValidationProvider tag="div" mode="passive" class="input-wrapper" name="E-Mail" rules="required"
v-slot="{ errors }">
<input v-model="register.email" :placeholder="$t('page_registration.placeholder_email')" type="email"
@@ -24,7 +24,7 @@
</div>
<div class="block-wrapper">
<label>{{ $t('page_registration.label_name') }}</label>
<label>{{ $t('page_registration.label_name') }}:</label>
<ValidationProvider tag="div" mode="passive" class="input-wrapper" name="Full Name" rules="required"
v-slot="{ errors }">
<input v-model="register.name" :placeholder="$t('page_registration.placeholder_name')" type="text"
@@ -35,7 +35,7 @@
</div>
<div class="block-wrapper">
<label>{{ $t('page_registration.label_pass') }}</label>
<label>{{ $t('page_registration.label_pass') }}:</label>
<ValidationProvider tag="div" mode="passive" class="input-wrapper" name="Your New Password"
rules="required" v-slot="{ errors }">
<input v-model="register.password" :placeholder="$t('page_registration.placeholder_pass')" type="password"
@@ -46,7 +46,7 @@
</div>
<div class="block-wrapper">
<label>{{ $t('page_registration.label_confirm_pass') }}</label>
<label>{{ $t('page_registration.label_confirm_pass') }}:</label>
<ValidationProvider tag="div" mode="passive" class="input-wrapper" name="Confirm Your Password"
rules="required" v-slot="{ errors }">
<input v-model="register.password_confirmation" :placeholder="$t('page_registration.placeholder_confirm_pass')"

View File

@@ -63,6 +63,13 @@
routeName: 'Pages',
isVisible: true,
},
{
icon: 'language',
title: this.$t('admin_menu.language'),
routeName: 'Language',
isVisible: true,
}
],
SassNavigation: [
{

View File

@@ -4,7 +4,7 @@
<div class="form block-form">
<FormLabel>{{ $t('user_settings.title_account') }}</FormLabel>
<div class="block-wrapper">
<label>{{ $t('page_registration.label_email') }}</label>
<label>{{ $t('page_registration.label_email') }}:</label>
<div class="input-wrapper">
<input :value="userInfo.email"
:placeholder="$t('page_registration.placeholder_email')"
@@ -14,7 +14,7 @@
</div>
</div>
<div class="block-wrapper">
<label>{{ $t('page_registration.label_name') }}</label>
<label>{{ $t('page_registration.label_name') }}:</label>
<div class="input-wrapper">
<input @keyup="changeUserName"
v-model="userInfo.name"

View File

@@ -1,66 +1,66 @@
<?php
return [
'app_description' => 'Your self-hosted storage cloud software powered by Laravel and Vue',
'user_not_fount' => 'We can\'t find a user with that e-mail address.',
'incorrect_password' => 'Sorry, your password is incorrect.',
'time' => '%d. %B. %Y at %H:%M',
'home' => 'Home',
// return [
// 'app_description' => __t('app_description'),
// 'user_not_fount' => __t('user_not_fount'),
// 'incorrect_password' => __t('incorrect_password'),
// 'time' => __t('time'),
// 'home' => __t('home'),
//Shared link email message
'shared_link_email_subject' => '🙋 :user share some files with you. Look at it!',
'shared_link_email_greeting' => 'Hello!',
'shared_link_email_user' => ':user (:email) send you a link to shared files.',
'shared_link_email_link' => 'Open your files',
'shared_link_email_salutation' => 'Regards, :app_name',
// //Shared link email message
// 'shared_link_email_subject' => __t('shared_link_email_subject'),
// 'shared_link_email_greeting' => __t('shared_link_email_greeting'),
// 'shared_link_email_user' => __t('shared_link_email_user'),
// 'shared_link_email_link' => __t('shared_link_email_link'),
// 'shared_link_email_salutation' => __t('shared_link_email_salutation'),
// Reset password email
'reset_password_greeting' => 'Hello!',
'reset_password_subject' => 'Reset password for your account on ',
'reset_password_line_1' => 'You are receiving this email because we received a password reset request for your account.',
'reset_password_line_2' => 'If you did not request a password reset, no further action is required.',
'reset_password_action' => 'Reset Password',
// // Reset password email
// 'reset_password_greeting' => __t('reset_password_greeting'),
// 'reset_password_subject' => __t('reset_password_subject'),
// 'reset_password_line_1' => __t('reset_password_line_1'),
// 'reset_password_line_2' => __t('reset_password_line_2'),
// 'reset_password_action' => __t('reset_password_action'),
'salutation' => 'Regards',
// 'salutation' => __t('salutation'),
// Invoice
'print_button' => 'Print Document',
// 'print_button' => __t('print_button'),
'vat' => 'VAT',
'vat_included' => 'incl.',
'subtotal' => 'Subtotal',
// 'vat' => __t('vat'),
// 'vat_included' => __t('vat_included'),
// 'subtotal' => __t('subtotal'),
'tax_exempted' => 'Tax is exempted',
'tax_be_paid_reverse' => 'Tax to be paid on reverse charge basis',
// 'tax_exempted' => __t('tax_exempted'),
// 'tax_be_paid_reverse' => __t('tax_be_paid_reverse'),
'invoice_title' => 'Invoice',
'date' => 'Date',
'product' => 'Product',
'subscription' => 'Subscription',
'invoice_number' => 'Invoice Number',
// 'invoice_title' => __t('invoice_title'),
// 'date' => __t('date'),
// 'product' => __t('product'),
// 'subscription' => __t('subscription'),
// 'invoice_number' => __t('invoice_number'),
'seller' => 'Seller',
'client' => 'Client',
// 'seller' => __t('seller'),
// 'client' => __t('client'),
'seller_vat' => 'VAT number',
'seller_name' => 'Name',
'seller_phone' => 'Phone',
// 'seller_vat' => __t('seller_vat'),
// 'seller_name' => __t('seller_name'),
// 'seller_phone' => __t('seller_phone'),
'name' => 'Name',
'phone' => 'Phone',
'address' => 'Address',
'city' => 'City',
'state' => 'State',
'postal_code' => 'Postal code',
'country' => 'Country',
// 'name' => __t('name'),
// 'phone' => __t('phone'),
// 'address' => __t('address'),
// 'city' => __t('city'),
// 'state' => __t('state'),
// 'postal_code' => __t('postal_code'),
// 'country' => __t('country'),
'col_description' => 'Description',
'col_date' => 'Date',
'col_amount' => 'Amount',
// 'col_description' => __t('col_description'),
// 'col_date' => __t('col_date'),
// 'col_amount' => __t('col_amount'),
'total' => 'Total',
// 'total' => __t('total'),
// OG Page
'user_sending' => ':name is sending you this file',
'protected_file' => 'This link is protected by password',
];
// 'user_sending' => __t('user_sending'),
// 'protected_file' => __t('protected_file'),
// ];

View File

@@ -41,6 +41,7 @@
host: '{{ url('/') }}',
api: '{{ url('/api') }}',
locale: '{{ \Illuminate\Support\Facades\App::getLocale() }}',
language: '{{ isset($settings->language) ? $settings->language : en }}',
app_color: '{{ $settings->app_color ?? '#00BC7E' }}',
app_logo: '{{ $settings->app_logo ?? null }}',

View File

@@ -7,16 +7,16 @@
<meta name="fragment" content="!">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="{{ __('vuefilemanager.user_sending', ['name' => $metadata['user']]) }}">
<meta name="description" content="{{ __t('user_sending', ['name' => $metadata['user']]) }}">
{{--OG Public items--}}
<meta property="og:url" content="{{ $metadata['url'] }}">
<meta property="og:description" content="{{ __('vuefilemanager.user_sending', ['name' => $metadata['user']]) }}">
<meta property="og:description" content="{{ __t('user_sending', ['name' => $metadata['user']]) }}">
{{--Show protected og metadata--}}
@if($metadata['is_protected'])
<meta property="og:title" content="{{ __('vuefilemanager.protected_file') }} | {{ $settings->app_title ?? 'VueFileManager' }}">
<title>{{ __('vuefilemanager.protected_file') }} | {{ $settings->app_title ?? 'VueFileManager' }}</title>
<meta property="og:title" content="{{ __t('protected_file') }} | {{ $settings->app_title ?? 'VueFileManager' }}">
<title>{{ __t('protected_file') }} | {{ $settings->app_title ?? 'VueFileManager' }}</title>
@endif
{{--Show public og metadata--}}

View File

@@ -2,7 +2,7 @@
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<title>@lang('vuefilemanager.invoice_title')</title>
<title>{{ __t('invoice_title') }}</title>
<link href="https://fonts.googleapis.com/css2?family=Nunito:wght@200;300;400;600;700;900&display=swap"
rel="stylesheet">
@@ -30,7 +30,7 @@
<path d="M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"></path>
<rect x="6" y="14" width="12" height="8"></rect>
</svg>
<span class="label group-hover-text-theme">@lang('vuefilemanager.print_button')</span>
<span class="label group-hover-text-theme">{{ __t('print_button') }}</span>
</button>
</div>
<div id="invoice-wrapper">
@@ -44,28 +44,28 @@
@endif
</div>
<div class="title">
<h1 class="text-theme">@lang('vuefilemanager.invoice_title')</h1>
<h1 class="text-theme">{{ __t('invoice_title') }}</h1>
</div>
</header>
<section class="invoice-subject">
<ul class="list">
<li class="list-item">
<b>@lang('vuefilemanager.date'):</b>
<b>{{ __t('date') }}:</b>
<span>{{ format_date($invoice->date()) }}</span>
</li>
<li class="list-item">
<b>@lang('vuefilemanager.product'):</b>
<span>@lang('vuefilemanager.subscription')</span>
<b>{{ __t('product') }}:</b>
<span>{{ __t('subscription') }}</span>
</li>
<li class="list-item">
<b>@lang('vuefilemanager.invoice_number'):</b>
<b>{{ __t('invoice_number') }}:</b>
<span>{{ $invoice->number }}</span>
</li>
<!-- Extra / VAT Information -->
@if (isset($vat))
<li class="list-item">
<b>@lang('vuefilemanager.vat'):</b>
<b>{{ __t('vat') }}:</b>
<span>{{ $vat }}</span>
</li>
@endif
@@ -73,25 +73,25 @@
</section>
<div class="invoice-partners">
<div class="partner">
<h2 class="partner-title text-theme">@lang('vuefilemanager.seller'):</h2>
<h2 class="partner-title text-theme">{{ __t('seller') }}:</h2>
<ul class="list">
@isset($settings->billing_vat_number)
<li class="list-item">
<b>@lang('vuefilemanager.seller_vat'):</b>
<b>{{ __t('seller_vat') }}:</b>
<span>{{ $settings->billing_vat_number }}</span>
</li>
@endisset
@isset($settings->billing_name)
<li class="list-item">
<b>@lang('vuefilemanager.seller_name'):</b>
<b>{{ __t('seller_name') }}:</b>
<span>{{ $settings->billing_name }}</span>
</li>
@endisset
@isset($settings->billing_phone_number)
<li class="list-item">
<b>@lang('vuefilemanager.seller_phone'):</b>
<b>{{ __t('seller_phone') }}:</b>
<span>{{ $settings->billing_phone_number }}</span>
</li>
@endisset
@@ -99,54 +99,54 @@
<ul class="list">
@isset($settings->billing_address)
<li class="list-item">
<b>@lang('vuefilemanager.address'):</b>
<b>{{ __t('address') }}:</b>
<span>{{ $settings->billing_address }}</span>
</li>
@endisset
@isset($settings->billing_city)
<li class="list-item">
<b>@lang('vuefilemanager.city'):</b>
<b>{{ __t('city') }}:</b>
<span>{{ $settings->billing_city }}</span>
</li>
@endisset
@isset($settings->billing_state)
<li class="list-item">
<b>@lang('vuefilemanager.state'):</b>
<b>{{ __t('state') }}:</b>
<span>{{ $settings->billing_state }}</span>
</li>
@endisset
@isset($settings->billing_postal_code)
<li class="list-item">
<b>@lang('vuefilemanager.postal_code'):</b>
<b>{{ __t('postal_code') }}:</b>
<span>{{ $settings->billing_postal_code }}</span>
</li>
@endisset
@isset($settings->billing_country)
<li class="list-item">
<b>@lang('vuefilemanager.country'):</b>
<b>{{ __t('country') }}:</b>
<span>{{ $settings->billing_country }}</span>
</li>
@endisset
</ul>
</div>
<div class="partner">
<h2 class="partner-title text-theme">@lang('vuefilemanager.client'):</h2>
<h2 class="partner-title text-theme">{{ __t('client') }}:</h2>
<ul class="list">
@isset($invoice->customer_name)
<li class="list-item">
<b>@lang('vuefilemanager.name'):</b>
<b>{{ __t('name') }}:</b>
<span>{{ $invoice->customer_name }}</span>
</li>
@endisset
@isset($invoice->customer_phone)
<li class="list-item">
<b>@lang('vuefilemanager.phone'):</b>
<b>{{ __t('phone') }}:</b>
<span>{{ $invoice->customer_phone }}</span>
</li>
@endisset
@@ -154,35 +154,35 @@
<ul class="list">
@isset($invoice->customer_address['line1'])
<li class="list-item">
<b>@lang('vuefilemanager.address'):</b>
<b>{{ __t('address') }}:</b>
<span>{{ $invoice->customer_address['line1'] }}</span>
</li>
@endisset
@isset($invoice->customer_address['city'])
<li class="list-item">
<b>@lang('vuefilemanager.city'):</b>
<b>{{ __t('city') }}:</b>
<span>{{ $invoice->customer_address['city'] }}</span>
</li>
@endisset
@isset($invoice->customer_address['state'])
<li class="list-item">
<b>@lang('vuefilemanager.state'):</b>
<b>{{ __t('state') }}:</b>
<span>{{ $invoice->customer_address['state'] }}</span>
</li>
@endisset
@isset($invoice->customer_address['postal_code'])
<li class="list-item">
<b>@lang('vuefilemanager.postal_code'):</b>
<b>{{ __t('postal_code') }}:</b>
<span>{{ $invoice->customer_address['postal_code'] }}</span>
</li>
@endisset
@isset($invoice->customer_address['country'])
<li class="list-item">
<b>@lang('vuefilemanager.country'):</b>
<b>{{ __t('country') }}:</b>
<span>{{ $invoice->customer_address['country'] }}</span>
</li>
@endisset
@@ -248,12 +248,12 @@
<table width="100%" class="table" border="0">
<thead class="table-header">
<tr>
<td class="text-theme">@lang('vuefilemanager.col_description')</td>
<td class="text-theme">@lang('vuefilemanager.col_date')</td>
<td class="text-theme">{{ __t('col_description') }}</td>
<td class="text-theme">{{ __t('col_date') }}</td>
@if ($invoice->hasTax())
<td class="text-theme" align="right">@lang('vuefilemanager.vat')</td>
<td class="text-theme" align="right">{{ __t('vat')}}</td>
@endif
<td class="text-theme">@lang('vuefilemanager.col_amount')</td>
<td class="text-theme">{{ __t('col_amount') }}</td>
</tr>
</thead>
@@ -287,7 +287,7 @@
<!-- Display The Subscriptions -->
@foreach ($invoice->subscriptions() as $subscription)
<tr class="row">
<td>@lang('vuefilemanager.subscription') ({{ $subscription->quantity }})</td>
<td>{{ __t('subscription') }} ({{ $subscription->quantity }})</td>
<td>
{{ $subscription->startDateAsCarbon()->formatLocalized('%B %e, %Y') }} -
{{ $subscription->endDateAsCarbon()->formatLocalized('%B %e, %Y') }}
@@ -296,7 +296,7 @@
@if ($invoice->hasTax())
<td>
@if ($inclusiveTaxPercentage = $subscription->inclusiveTaxPercentage())
{{ $inclusiveTaxPercentage }}% @lang('vuefilemanager.vat_included')
{{ $inclusiveTaxPercentage }}% {{ __t('vat_included') }}
@endif
@if ($subscription->hasBothInclusiveAndExclusiveTax())
@@ -316,7 +316,7 @@
<!-- Display The Subtotal -->
@if ($invoice->hasDiscount() || $invoice->hasTax() || $invoice->hasStartingBalance())
<tr>
<td colspan="{{ $invoice->hasTax() ? 3 : 2 }}" style="text-align: right;">@lang('vuefilemanager.subtotal')</td>
<td colspan="{{ $invoice->hasTax() ? 3 : 2 }}" style="text-align: right;">{{ __t('subtotal')}}</td>
<td>{{ $invoice->subtotal() }}</td>
</tr>
@endif
@@ -326,9 +326,9 @@
<tr>
<td colspan="{{ $invoice->hasTax() ? 3 : 2 }}" style="text-align: right;">
@if ($invoice->isTaxExempt())
@lang('vuefilemanager.tax_exempted')
{{ __t('tax_exempted')}}
@else
@lang('vuefilemanager.tax_be_paid_reverse')
{{ __t('tax_be_paid_reverse') }}
@endif
</td>
<td></td>
@@ -338,7 +338,7 @@
<tr>
<td colspan="3" style="text-align: right;">
{{ $tax->display_name }} {{ $tax->country ? ' - '.$tax->country : '' }}
({{ $tax->percentage }}%{{ $tax->isInclusive() ? ' ' . __('vuefilemanager.vat_included') : '' }})
({{ $tax->percentage }}%{{ $tax->isInclusive() ? ' ' . __t('vat_included') : '' }})
</td>
<td>{{ $tax->amount() }}</td>
</tr>
@@ -348,7 +348,7 @@
<!-- Display The Final Total -->
<tr>
<td colspan="{{ $invoice->hasTax() ? 3 : 2 }}" style="text-align: right;">
<strong>@lang('vuefilemanager.total')</strong>
<strong>{{ __t('total') }}</strong>
</td>
<td>
<strong>{{ $invoice->total() }}</strong>
@@ -359,7 +359,7 @@
</table>
</div>
<div class="invoice-summary">
<b>@lang('vuefilemanager.total') {{ $invoice->total() }}</b>
<b>{{ __t('total') }} {{ $invoice->total() }}</b>
</div>
</div>
</body>

View File

@@ -54,6 +54,16 @@ Route::group(['middleware' => ['auth:sanctum']], function () {
Route::patch('/{token}', [ShareController::class, 'update']);
Route::post('/{id}', [ShareController::class, 'store']);
});
// Language
Route::group(['prefix' => 'languages'], function () {
Route::get('/{language}/strings', 'Admin\LanguageController@get_language_strings');
Route::patch('/{language}/string', 'Admin\LanguageController@update_string');
Route::delete('/{language}', 'Admin\LanguageController@delete_language');
Route::patch('/{language}', 'Admin\LanguageController@update_language');
Route::post('/', 'Admin\LanguageController@create_language');
Route::get('/', 'Admin\LanguageController@get_languages');
});
});
// User master,editor routes

View File

@@ -9,6 +9,8 @@ use App\Http\Controllers\Subscription\StripeWebhookController;
Route::post('/stripe/webhook', [StripeWebhookController::class, 'handleWebhook']);
Route::post('/admin-setup', [SetupWizardController::class, 'create_admin_account']);
Route::get('/language/{lang}', 'AppFunctionsController@get_translate');
// Get user invoice from stripe service
Route::get('/invoice/{customer}/{token}', [InvoiceController::class, 'show'])->middleware(['auth:sanctum']);