diff --git a/app/Enums/cruds.php b/app/Enums/cruds.php new file mode 100644 index 0000000..1980e2e --- /dev/null +++ b/app/Enums/cruds.php @@ -0,0 +1,11 @@ + [ + 10 => ['name' => 'Create'], + 20 => ['name' => 'Update'], + 30 => ['name' => 'Delete'], + ], + +]; diff --git a/app/Enums/tables.php b/app/Enums/tables.php index 95a3d03..273bf4c 100644 --- a/app/Enums/tables.php +++ b/app/Enums/tables.php @@ -2,55 +2,82 @@ return [ + 'inverse' => [ + 10 => ['name' => 'Businesses'], + 20 => ['name' => 'Projects'], + 30 => ['name' => 'Sprints'], + 40 => ['name' => 'Statuses'], + 50 => ['name' => 'Systems'], + 60 => ['name' => 'Workflows'], + 70 => ['name' => 'Tags'], + 80 => ['name' => 'Tasks'], + 90 => ['name' => 'Works'], + 210 => ['name' => 'BusinessUser'], + 220 => ['name' => 'ProjectUser'], + 230 => ['name' => 'TagTask'], + ], + //Main Table's 'businesses' => [ 'id' => 10, - 'name' => 'Businesses' + 'name' => 'Businesses', + 'singular_name' => 'Business', ], 'projects' => [ 'id' => 20, - 'name' => 'Projects' + 'name' => 'Projects', + 'singular_name' => 'Project', ], 'sprints' => [ 'id' => 30, - 'name' => 'Sprints' + 'name' => 'Sprints', + 'singular_name' => 'Sprint', ], 'statuses' => [ 'id' => 40, - 'name' => 'Statuses' + 'name' => 'Statuses', + 'singular_name' => 'Status', ], 'systems' => [ 'id' => 50, - 'name' => 'Systems' + 'name' => 'Systems', + 'singular_name' => 'System', ], 'workflows' => [ 'id' => 60, - 'name' => 'Workflows' + 'name' => 'Workflows', + 'singular_name' => 'Workflow', ], 'tags' => [ 'id' => 70, - 'name' => 'Tags' + 'name' => 'Tags', + 'singular_name' => 'Tag', ], 'tasks' => [ 'id' => 80, - 'name' => 'Tasks' + 'name' => 'Tasks', + 'singular_name' => 'Task', ], 'works' => [ 'id' => 90, - 'name' => 'Works' + 'name' => 'Works', + 'singular_name' => 'Work', ], //Relation Table's 'business_user' => [ 'id' => 210, - 'name' => 'BusinessUser' + 'name' => 'BusinessUser', + 'singular_name' => 'BusinessUser', ], 'project_user' => [ 'id' => 220, - 'name' => 'ProjectUser' + 'name' => 'ProjectUser', + 'singular_name' => 'ProjectUser', ], 'tag_task' => [ 'id' => 230, - 'name' => 'TagTask' + 'name' => 'TagTask', + 'singular_name' => 'TagTask', ], ]; diff --git a/app/Events/TagCreate.php b/app/Events/TagCreate.php new file mode 100644 index 0000000..0a86866 --- /dev/null +++ b/app/Events/TagCreate.php @@ -0,0 +1,38 @@ +message = $message; + } + + /** + * Get the channels the event should broadcast on. + * + * @return \Illuminate\Broadcasting\Channel|array + */ + public function broadcastOn() + { + return new PrivateChannel('channel-name'); + } +} diff --git a/app/Listeners/NotifHandler.php b/app/Listeners/NotifHandler.php new file mode 100644 index 0000000..1fed4db --- /dev/null +++ b/app/Listeners/NotifHandler.php @@ -0,0 +1,36 @@ +message); + $event_class = 'App\Events\\'.enum('tables.'.$message->data->table_name.'.singular_name').enum('cruds.inverse.'.$message->data->crud_id.'.name'); + if (class_exists($event_class)) { +// event(new ('App\Events\\'.$event_class($message))); + $event_class::dispatch($message); + } + } +} diff --git a/app/Listeners/TagCreateNotif.php b/app/Listeners/TagCreateNotif.php new file mode 100644 index 0000000..8222723 --- /dev/null +++ b/app/Listeners/TagCreateNotif.php @@ -0,0 +1,37 @@ +message; + $notif_line = 'tags.create'; + $users = User::where('id', '<', 10)->get(); + Notification::send($users, new MailNotification($notif_line)); + } +} diff --git a/app/Models/User.php b/app/Models/User.php index 857c7d3..b1549d3 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -6,6 +6,7 @@ use App\Models\File; use App\Models\Model; use App\Models\SoftDeletes; use App\Models\ReportableRelation; +use Illuminate\Notifications\Notifiable; use Illuminate\Validation\Rule; use Illuminate\Http\UploadedFile; use Spatie\MediaLibrary\HasMedia; @@ -18,7 +19,8 @@ use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract; class User extends Model implements AuthenticatableContract, AuthorizableContract, HasMedia { - use SoftDeletes, + use Notifiable, + SoftDeletes, Authorizable, Authenticatable, InteractsWithMedia; diff --git a/app/Notifications/MailNotification.php b/app/Notifications/MailNotification.php new file mode 100644 index 0000000..4122952 --- /dev/null +++ b/app/Notifications/MailNotification.php @@ -0,0 +1,87 @@ +message = $message; + } + + /** + * Get the notification's delivery channels. + * + * @param mixed $notifiable + * @return array + */ + public function via($notifiable) + { + return ['mail']; + } + + /** + * Get the mail representation of the notification. + * + * @param mixed $notifiable + * @return \Illuminate\Notifications\Messages\MailMessage + */ + public function toMail($notifiable) + { + return (new MailMessage) + ->line(__('notification.'.$this->message)) + ->action('Notification Action', url('/')) + ->line('Thank you for using our application!'); + } + + /** + * Get the array representation of the notification. + * + * @param mixed $notifiable + * @return array + */ + public function toArray($notifiable) + { + return [ + // + ]; + } +} diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index 4d702ef..c627395 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -3,7 +3,10 @@ namespace App\Providers; use App\Events\ModelSaved; +use App\Events\TagCreate; use App\Listeners\ActivityRegistration; +use App\Listeners\NotifHandler; +use App\Listeners\TagCreateNotif; use Illuminate\Auth\Events\Registered; use Illuminate\Auth\Listeners\SendEmailVerificationNotification; use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; @@ -21,7 +24,11 @@ class EventServiceProvider extends ServiceProvider SendEmailVerificationNotification::class, ], ModelSaved::class => [ - ActivityRegistration::class + ActivityRegistration::class, + NotifHandler::class, + ], + TagCreate::class => [ + TagCreateNotif::class, ] ]; diff --git a/config/mail.php b/config/mail.php index 54299aa..022418f 100644 --- a/config/mail.php +++ b/config/mail.php @@ -36,8 +36,8 @@ return [ 'mailers' => [ 'smtp' => [ 'transport' => 'smtp', - 'host' => env('MAIL_HOST', 'smtp.mailgun.org'), - 'port' => env('MAIL_PORT', 587), + 'host' => env('MAIL_HOST', 'localhost'), + 'port' => env('MAIL_PORT', 1025), 'encryption' => env('MAIL_ENCRYPTION', 'tls'), 'username' => env('MAIL_USERNAME'), 'password' => env('MAIL_PASSWORD'), diff --git a/docker-compose.yml b/docker-compose.yml index 96ea2ee..d606ce7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -100,6 +100,15 @@ services: - mysql networks: - sail + mailhog: + image: mailhog/mailhog + logging: + driver: 'none' # disable saving logs + ports: + - 1025:1025 # smtp server + - 8025:8025 # web ui + networks: + - sail networks: sail: driver: bridge diff --git a/resources/lang/fa/auth.php b/resources/lang/fa/auth.php new file mode 100644 index 0000000..a5831f3 --- /dev/null +++ b/resources/lang/fa/auth.php @@ -0,0 +1,19 @@ + 'اطلاعات وارد شده صحیح نمی باشد', + 'throttle' => 'درخواست بیش از حد مجاز! لطفا بعد از :seconds ثانیه دوباره امتحان کنید', + +]; diff --git a/resources/lang/fa/notification.php b/resources/lang/fa/notification.php new file mode 100644 index 0000000..e885bcc --- /dev/null +++ b/resources/lang/fa/notification.php @@ -0,0 +1,20 @@ + [ + 'create' => 'یک تگ ساخته شد.' + ] + +]; diff --git a/resources/lang/fa/pagination.php b/resources/lang/fa/pagination.php new file mode 100644 index 0000000..e00adbf --- /dev/null +++ b/resources/lang/fa/pagination.php @@ -0,0 +1,19 @@ + '« قبلی', + 'next' => 'بعدی »', + +]; diff --git a/resources/lang/fa/passwords.php b/resources/lang/fa/passwords.php new file mode 100644 index 0000000..671b232 --- /dev/null +++ b/resources/lang/fa/passwords.php @@ -0,0 +1,22 @@ + 'رمز عبور شما با موفقیت بازیابی شد', + 'sent' => 'ایمیلی برای بازیابی رمزعبور برای شما ارسال شد', + 'throttled' => 'لطفا اندکی صبر کنید', + 'token' => 'مشخصه بازیابی رمزعبور شما صحیح نمی باشد', + 'user' => "کاربری با این اطلاعات وجود ندارد", + +]; diff --git a/resources/lang/fa/validation.php b/resources/lang/fa/validation.php new file mode 100644 index 0000000..54b4e77 --- /dev/null +++ b/resources/lang/fa/validation.php @@ -0,0 +1,189 @@ + 'گزینه :attribute باید تایید شود', + 'active_url' => 'گزینه :attribute یک آدرس سایت معتبر نیست', + 'after' => 'گزینه :attribute باید تاریخی بعد از :date باشد', + 'after_or_equal' => 'گزینه :attribute باید تاریخی مساوی یا بعد از :date باشد', + 'alpha' => 'گزینه :attribute باید تنها شامل حروف باشد', + 'alpha_dash' => 'گزینه :attribute باید تنها شامل حروف، اعداد، خط تیره و زیر خط باشد', + 'alpha_num' => 'گزینه :attribute باید تنها شامل حروف و اعداد باشد', + 'array' => 'گزینه :attribute باید آرایه باشد', + 'before' => 'گزینه :attribute باید تاریخی قبل از :date باشد', + 'before_or_equal' => 'گزینه :attribute باید تاریخی مساوی یا قبل از :date باشد', + 'between' => [ + 'numeric' => 'گزینه :attribute باید بین :min و :max باشد', + 'file' => 'گزینه :attribute باید بین :min و :max کیلوبایت باشد', + 'string' => 'گزینه :attribute باید بین :min و :max کاراکتر باشد', + 'array' => 'گزینه :attribute باید بین :min و :max آیتم باشد', + ], + 'boolean' => 'گزینه :attribute تنها می تواند صحیح یا غلط باشد', + 'confirmed' => 'تایید مجدد گزینه :attribute صحیح نمی باشد', + 'date' => 'گزینه :attribute یک تاریخ صحیح نمی باشد', + 'date_equals' => 'گزینه :attribute باید تاریخی مساوی با :date باشد', + 'date_format' => 'گزینه :attribute با فرمت :format همخوانی ندارد', + 'different' => 'گزینه :attribute و :other باید متفاوت باشند', + 'digits' => 'گزینه :attribute باید :digits عدد باشد', + 'digits_between' => 'گزینه :attribute باید بین :min و :max عدد باشد', + 'dimensions' => 'ابعاد تصویر گزینه :attribute مجاز نمی باشد', + 'distinct' => 'گزینه :attribute دارای افزونگی داده می باشد', + 'email' => 'گزینه :attribute باید یک آدرس ایمیل صحیح باشد', + 'ends_with' => 'گزینه :attribute باید با یکی از این مقادیر پایان یابد، :values', + 'exists' => 'گزینه انتخاب شده :attribute صحیح نمی باشد', + 'file' => 'گزینه :attribute باید یک فایل باشد', + 'filled' => 'گزینه :attribute نمی تواند خالی باشد', + 'gt' => [ + 'numeric' => 'گزینه :attribute باید بزرگتر از :value باشد', + 'file' => 'گزینه :attribute باید بزرگتر از :value کیلوبایت باشد', + 'string' => 'گزینه :attribute باید بزرگتر از :value کاراکتر باشد', + 'array' => 'گزینه :attribute باید بیشتر از :value آیتم باشد', + ], + 'gte' => [ + 'numeric' => 'گزینه :attribute باید بزرگتر یا مساوی :value باشد', + 'file' => 'گزینه :attribute باید بزرگتر یا مساوی :value کیلوبایت باشد', + 'string' => 'گزینه :attribute باید بزرگتر یا مساوی :value کاراکتر باشد', + 'array' => 'گزینه :attribute باید :value آیتم یا بیشتر داشته باشد', + ], + 'image' => 'گزینه :attribute باید از نوع تصویر باشد', + 'in' => 'گزینه انتخابی :attribute صحیح نمی باشد', + 'in_array' => 'گزینه :attribute در :other وجود ندارد', + 'integer' => 'گزینه :attribute باید از نوع عددی باشد', + 'ip' => 'گزینه :attribute باید آی پی آدرس باشد', + 'ipv4' => 'گزینه :attribute باید آی پی آدرس ورژن 4 باشد', + 'ipv6' => 'گزینه :attribute باید آی پی آدرس ورژن 6 باشد', + 'json' => 'گزینه :attribute باید از نوع رشته جیسون باشد', + 'lt' => [ + 'numeric' => 'گزینه :attribute باید کمتر از :value باشد', + 'file' => 'گزینه :attribute باید کمتر از :value کیلوبایت باشد', + 'string' => 'گزینه :attribute باید کمتر از :value کاراکتر باشد', + 'array' => 'گزینه :attribute باید کمتر از :value آیتم داشته باشد', + ], + 'lte' => [ + 'numeric' => 'گزینه :attribute باید مساوی یا کمتر از :value باشد', + 'file' => 'گزینه :attribute باید مساوی یا کمتر از :value کیلوبایت باشد', + 'string' => 'گزینه :attribute باید مساوی یا کمتر از :value کاراکتر باشد', + 'array' => 'گزینه :attribute نباید کمتر از :value آیتم داشته باشد', + ], + 'max' => [ + 'numeric' => 'گزینه :attribute نباید بزرگتر از :max باشد', + 'file' => 'گزینه :attribute نباید بزرگتر از :max کیلوبایت باشد', + 'string' => 'گزینه :attribute نباید بزرگتر از :max کاراکتر باشد', + 'array' => 'گزینه :attribute نباید بیشتر از :max آیتم داشته باشد', + ], + 'mimes' => 'گزینه :attribute باید دارای یکی از این فرمت ها باشد: :values', + 'mimetypes' => 'گزینه :attribute باید دارای یکی از این فرمت ها باشد: :values', + 'min' => [ + 'numeric' => 'گزینه :attribute باید حداقل :min باشد', + 'file' => 'گزینه :attribute باید حداقل :min کیلوبایت باشد', + 'string' => 'گزینه :attribute باید حداقل :min کاراکتر باشد', + 'array' => 'گزینه :attribute باید حداقل :min آیتم داشته باشد', + ], + 'not_in' => 'گزینه انتخابی :attribute صحیح نمی باشد', + 'not_regex' => 'فرمت گزینه :attribute صحیح نمی باشد', + 'numeric' => 'گزینه :attribute باید از نوع عددی باشد', + 'password' => 'رمزعبور صحیح نمی باشد', + 'present' => 'گزینه :attribute باید از نوع درصد باشد', + 'regex' => 'فرمت گزینه :attribute صحیح نمی باشد', + 'required' => 'تکمیل گزینه :attribute الزامی است', + 'required_if' => 'تکمیل گزینه :attribute زمانی که :other دارای مقدار :value است الزامی می باشد', + 'required_unless' => 'تکمیل گزینه :attribute الزامی می باشد مگر :other دارای مقدار :values باشد', + 'required_with' => 'تکمیل گزینه :attribute زمانی که مقدار :values درصد است الزامی است', + 'required_with_all' => 'تکمیل گزینه :attribute زمانی که مقادیر :values درصد است الزامی می باشد', + 'required_without' => 'تکمیل گزینه :attribute زمانی که مقدار :values درصد نیست الزامی است', + 'required_without_all' => 'تکمیل گزینه :attribute زمانی که هیچ کدام از مقادیر :values درصد نیست الزامی است', + 'same' => 'گزینه های :attribute و :other باید یکی باشند', + 'size' => [ + 'numeric' => 'گزینه :attribute باید :size باشد', + 'file' => 'گزینه :attribute باید :size کیلوبایت باشد', + 'string' => 'گزینه :attribute باید :size کاراکتر باشد', + 'array' => 'گزینه :attribute باید شامل :size آیتم باشد', + ], + 'starts_with' => 'گزینه :attribute باید با یکی از این مقادیر شروع شود، :values', + 'string' => 'گزینه :attribute باید تنها شامل حروف باشد', + 'timezone' => 'گزینه :attribute باید از نوع منطقه زمانی صحیح باشد', + 'unique' => 'این :attribute از قبل ثبت شده است', + 'uploaded' => 'آپلود گزینه :attribute شکست خورد', + 'url' => 'فرمت :attribute اشتباه است', + 'uuid' => 'گزینه :attribute باید یک UUID صحیح باشد', + + /* + |-------------------------------------------------------------------------- + | Custom Validation Language Lines + |-------------------------------------------------------------------------- + | + | Here you may specify custom validation messages for attributes using the + | convention "attribute.rule" to name the lines. This makes it quick to + | specify a specific custom language line for a given attribute rule. + | + */ + + 'custom' => [ + 'attribute-name' => [ + 'rule-name' => 'custom-message', + ], + ], + + /* + |-------------------------------------------------------------------------- + | Custom Validation Attributes + |-------------------------------------------------------------------------- + | + | The following language lines are used to swap our attribute placeholder + | with something more reader friendly such as "E-Mail Address" instead + | of "email". This simply helps us make our message more expressive. + | + */ + + 'attributes' => [ + 'name' => 'نام', + 'username' => 'نام کاربری', + 'email' => 'ایمیل', + 'first_name' => 'نام', + 'last_name' => 'نام خانوادگی', + 'password' => 'رمز عبور', + 'password_confirmation' => 'تاییدیه رمز عبور', + 'city' => 'شهر', + 'state' => 'استان', + 'country' => 'کشور', + 'address' => 'آدرس', + 'phone' => 'تلفن', + 'mobile' => 'تلفن همراه', + 'age' => 'سن', + 'sex' => 'جنسیت', + 'gender' => 'جنسیت', + 'day' => 'روز', + 'month' => 'ماه', + 'year' => 'سال', + 'hour' => 'ساعت', + 'minute' => 'دقیقه', + 'second' => 'ثانیه', + 'title' => 'عنوان', + 'text' => 'متن', + 'content' => 'محتوا', + 'description' => 'توضیحات', + 'date' => 'تاریخ', + 'time' => 'زمان', + 'available' => 'موجود', + 'type' => 'نوع', + 'img' => 'تصویر', + 'image' => 'تصویر', + 'size' => 'اندازه', + 'color' => 'رنگ', + 'captcha' => 'کد امنیتی', + 'price' => 'قیمت', + 'pic' => 'تصویر', + 'link' => 'لینک', + ], +];