diff --git a/.env.example b/.env.example
index c3ed2a9..fde9c84 100644
--- a/.env.example
+++ b/.env.example
@@ -1,8 +1,11 @@
-APP_NAME=Laravel
+CONTAINER_NAME=
+
+APP_NAME=Liwo
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_URL=http://localhost
+APP_TIMEZONE="Asia/Tehran"
LOG_CHANNEL=stack
LOG_LEVEL=debug
@@ -22,8 +25,9 @@ SESSION_LIFETIME=120
MEMCACHED_HOST=127.0.0.1
-REDIS_HOST=127.0.0.1
-REDIS_PASSWORD=null
+REDIS_HOST=redis
+REDIS_PASSWORD=root
+REDIS_USER=root
REDIS_PORT=6379
MAIL_MAILER=smtp
@@ -47,3 +51,4 @@ PUSHER_APP_CLUSTER=mt1
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
+
diff --git a/.gitignore b/.gitignore
index 0ae59f0..0796583 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,3 +11,5 @@ Homestead.json
Homestead.yaml
npm-debug.log
yarn-error.log
+.idea/
+
diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml
deleted file mode 100644
index a55e7a1..0000000
--- a/.idea/codeStyles/codeStyleConfig.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/liwo.iml b/.idea/liwo.iml
deleted file mode 100644
index c956989..0000000
--- a/.idea/liwo.iml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
deleted file mode 100644
index f1e8f43..0000000
--- a/.idea/modules.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/Channels/FcmChannel.php b/app/Channels/FcmChannel.php
new file mode 100644
index 0000000..382bfcf
--- /dev/null
+++ b/app/Channels/FcmChannel.php
@@ -0,0 +1,93 @@
+http = $http;
+ $this->apiKey = $apiKey;
+ }
+
+ /**
+ * Send the given notification.
+ *
+ * @param mixed $notifiable
+ * @param \Illuminate\Notifications\Notification $notification
+ * @return void
+ */
+ public function send($notifiable, Notification $notification)
+ {
+ $message = $notification->toFcm($notifiable);
+
+ $message->to($notifiable->routeNotificationFor('fcm', $notification));
+
+ if (! $this->apiKey || (! $message->topic && ! $message->to)) {
+ return;
+ }
+
+ $this->http->post(self::API_URI, [
+ 'headers' => [
+ 'Authorization' => "key={$this->apiKey}",
+ 'Content-Type' => 'application/json',
+ ],
+ 'json' => $this->buildJsonPayload($message),
+ ]);
+ }
+
+ protected function buildJsonPayload(FcmMessage $message)
+ {
+ $payload = array_filter([
+ 'priority' => $message->priority,
+ 'data' => $message->data,
+ 'notification' => $message->notification,
+ 'condition' => $message->condition,
+ ]);
+
+ if ($message->topic) {
+ $payload['to'] = "/topics/{$message->topic}";
+ } else {
+ if (is_array($message->to)) {
+ $payload['registration_ids'] = $message->to;
+ } else {
+ $payload['to'] = $message->to;
+ }
+ }
+
+ return $payload;
+ }
+}
diff --git a/app/Channels/Messages/FcmMessage.php b/app/Channels/Messages/FcmMessage.php
new file mode 100644
index 0000000..dc81a8a
--- /dev/null
+++ b/app/Channels/Messages/FcmMessage.php
@@ -0,0 +1,132 @@
+to = $to[0];
+ } else {
+ $this->to = $to;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Set the topic of the FCM message.
+ *
+ * @param string $topic
+ * @return $this
+ */
+ public function topic(string $topic)
+ {
+ $this->topic = $topic;
+
+ return $this;
+ }
+
+ /**
+ * Set the data of the FCM message.
+ *
+ * @param array $data
+ * @return $this
+ */
+ public function data(array $data)
+ {
+ $this->data = $data;
+
+ return $this;
+ }
+
+ /**
+ * Set the notification of the FCM message.
+ *
+ * @param array $notification
+ * @return $this
+ */
+ public function notification(array $notification)
+ {
+ $this->notification = $notification;
+
+ return $this;
+ }
+
+ /**
+ * Set the condition for receive the FCM message.
+ *
+ * @param string $condition
+ * @return $this
+ */
+ public function condition(string $condition)
+ {
+ $this->condition = $condition;
+
+ return $this;
+ }
+
+ /**
+ * Set the priority of the FCM message.
+ *
+ * @param string $priority
+ * @return $this
+ */
+ public function priority(string $priority)
+ {
+ $this->priority = $priority;
+
+ return $this;
+ }
+}
diff --git a/app/Console/Commands/CostCommand.php b/app/Console/Commands/CostCommand.php
new file mode 100644
index 0000000..949442b
--- /dev/null
+++ b/app/Console/Commands/CostCommand.php
@@ -0,0 +1,169 @@
+info('There is no business for auditing.');
+ continue;
+ }
+
+ $business = Business::orderBy('calculated_at')->first();
+ if ($business === null) {
+ continue;
+ }
+
+ if ($business->calculated_at->isFuture()) {
+ continue;
+ }
+
+ $next_month = jdate($business->calculated_at)->addMonths();
+ [$year, $month, $day] = CalendarUtils::toGregorian(
+ $next_month->getYear(), $next_month->getMonth(), 1
+ );
+ $now = Carbon::createFromDate($year, $month, $day);
+ $now->setTime(0, 0, 0);
+
+ if ($now->isFuture()) {
+ $now = Carbon::now();
+ }
+
+ // if calculated_at less than an hour stop
+ if ($now->diffInMinutes($business->calculated_at) <= 59) {
+ $this->info('Must be one hour after the last audit.');
+ $this->ensureLockIsWritten();
+ continue;
+ }
+
+ try {
+ DB::beginTransaction();
+ // Fixed amounts of expenses
+ $business->load('users', 'files');
+
+ $costs = 0;
+ $costs += $this->calculateCostOfBusinessUsers($business, $now);
+ $costs += $this->calculateCostOfBusinessFiles($business, $now);
+
+ // increment and decrement of wallet in php
+ // deduct costs from your business wallet
+ // make sure save the calculated_at
+ $business->update([
+ 'wallet' => $business->wallet - $costs,
+ 'calculated_at' => $now,
+ ]);
+
+ DB::commit();
+ $this->info("The business #{$business->id} was audited.");
+ } catch (Throwable $throwable) {
+ throw $throwable;
+ DB::rollback();
+ report($throwable);
+ continue;
+ }
+ }
+ }
+
+ public function calculateCostOfBusinessUsers($business, $now)
+ {
+ $user_fee = enum('business.fee.user');
+ $recorded_month = jdate($business->calculated_at)->format("Y-m-01");
+
+ if ($business->users->isEmpty()) {
+ return 0;
+ }
+
+ // get business employee
+ $users_cost = $business->cost
+ ->where('type', '=', Cost::USER_TYPE)
+ ->where('fee', '=', $user_fee)
+ ->where('month', '=', $recorded_month)
+ ->where('amount', '=', $business->users->count())
+ ->first();
+
+ if ($users_cost === null) {
+ $business->cost()->create([
+ 'type' => Cost::USER_TYPE,
+ 'month' => $recorded_month,
+ 'amount' => $business->users->count(),
+ 'fee' => $user_fee,
+ 'duration' => $duration = $now->diffInMinutes($business->calculated_at), // from the created_at time of the newset fifth user
+ 'additional' => $business->users->pluck('id')->toArray(),
+ ]);
+ } else {
+ $users_cost->update([
+ 'duration' => $duration = $now->diffInMinutes($business->calculated_at) + $users_cost->duration, // last calc - (current month - now else last calc - end of the past month),
+ 'additional' => $business->users->pluck('id')->toArray(),
+ ]);
+ }
+
+ return $user_fee * $duration;
+ }
+
+ public function calculateCostOfBusinessFiles($business, $now)
+ {
+ $file_fee = enum('business.fee.file');
+ $calculated_at = $business->calculated_at;
+ $recorded_month = jdate($business->calculated_at)->format("Y-m-01");
+
+
+ // do the math in php
+ $packs = intdiv($business->files_volume, 200);
+ if ($packs === 0) {
+ return 0;
+ } else {
+ $packs--;
+ }
+
+ $files = $business->cost
+ ->where('type', '=', Cost::FILE_TYPE)
+ ->where('fee', '=', $file_fee)
+ ->where('month', '=', $recorded_month)
+ ->where('amount', '=', $packs)
+ ->first();
+
+ if ($files === null) {
+ $business->cost()->create([
+ 'type' => Cost::FILE_TYPE,
+ 'month' => $recorded_month,
+ 'amount' => $packs,
+ 'fee' => $file_fee,
+ 'duration' => $duration = $now->diffInMinutes($calculated_at), // how to determine the file?,
+ 'additional' => ['volume' => $business->files_volume],
+ ]);
+ } else {
+ $files->update([
+ 'duration' => $duration = $now->diffInMinutes($calculated_at) + $files->duration, // last calc - (current month - now else last calc - end of the past month),,
+ 'additional' => ['volume' => $business->files_volume],
+ ]);
+ }
+
+ return $file_fee * $duration;
+ }
+
+ public function ensureLockIsWritten(): void
+ {
+ Cache::put('lock', true, 3600);
+ while (Cache::get('lock', false) === false) {
+ // prevent
+ }
+ }
+}
diff --git a/app/Enums/business.php b/app/Enums/business.php
new file mode 100644
index 0000000..5690874
--- /dev/null
+++ b/app/Enums/business.php
@@ -0,0 +1 @@
+ [
'user' => 100,
'file' => 200,
]
];
\ No newline at end of file
diff --git a/app/Enums/comment.php b/app/Enums/comment.php
new file mode 100644
index 0000000..e516e01
--- /dev/null
+++ b/app/Enums/comment.php
@@ -0,0 +1 @@
+ [
'reject' => [
'id' => 10,
'label' => 'رد'
],
'approve' => [
'id' => 20,
'label' => 'تایید'
],
]
];
\ No newline at end of file
diff --git a/app/Enums/cruds.php b/app/Enums/cruds.php
new file mode 100644
index 0000000..283ea3b
--- /dev/null
+++ b/app/Enums/cruds.php
@@ -0,0 +1,11 @@
+ [
+ 10 => ['name' => 'Create', 'singular_name' => 'create'],
+ 20 => ['name' => 'Update', 'singular_name' => 'update'],
+ 30 => ['name' => 'Delete', 'singular_name' => 'delete'],
+ ],
+
+];
diff --git a/app/Enums/levels.php b/app/Enums/levels.php
new file mode 100644
index 0000000..8d1b627
--- /dev/null
+++ b/app/Enums/levels.php
@@ -0,0 +1 @@
+ [
'id' => 4,
'name' => 'Owner',
'label' => 'صاحب'
],
'admin' => [
'id' => 3,
'name' => 'Admin',
'label' => 'مدیر'
],
'colleague' => [
'id' => 2,
'name' => 'Colleague',
'label' => 'همکار'
],
'guest' => [
'id' => 1,
'name' => 'Guest',
'label' => 'مهمان'
],
'inactive' => [
'id' => 0,
'name' => 'Inactive',
'label' => 'غیر فعال'
],
];
\ No newline at end of file
diff --git a/app/Enums/log.php b/app/Enums/log.php
new file mode 100644
index 0000000..3157502
--- /dev/null
+++ b/app/Enums/log.php
@@ -0,0 +1,25 @@
+ [
+ User::class => 10,
+ Business::class => 20,
+ Project::class => 30,
+ Task::class => 40,
+ Spenthour::class => 50,
+ ],
+
+ 'actions' => [
+ 'created' => 10,
+ 'updated' => 20,
+ 'deleted' => 30,
+ 'restored' => 40,
+ ],
+];
diff --git a/app/Enums/post.php b/app/Enums/post.php
new file mode 100644
index 0000000..0e8190c
--- /dev/null
+++ b/app/Enums/post.php
@@ -0,0 +1 @@
+ [
'draft' => [
'id' => 10,
'label' => 'پیش نویس'
],
'review' => [
'id' => 20,
'label' => 'در حال بررسی'
],
'published' => [
'id' => 30,
'label' => 'منتشر'
],
'trashed' => [
'id' => 40,
'label' => 'حذف'
],
]
];
\ No newline at end of file
diff --git a/app/Enums/roles.php b/app/Enums/roles.php
new file mode 100644
index 0000000..6dd5eda
--- /dev/null
+++ b/app/Enums/roles.php
@@ -0,0 +1 @@
+ [
'id' => 0,
'label' => 'غیر فعال'
],
'guest' => [
'id' => 1,
'label' => 'میهمان'
],
'Colleague' => [
'id' => 2,
'label' => 'همکار'
],
'senior' => [
'id' => 3,
'label' => 'معاون'
],
'manager' => [
'id' => 4,
'label' => 'مدیر'
],
];
\ No newline at end of file
diff --git a/app/Enums/service.php b/app/Enums/service.php
new file mode 100644
index 0000000..39ab78f
--- /dev/null
+++ b/app/Enums/service.php
@@ -0,0 +1 @@
+ [
'file' => [
'orphanage' => [
'id' => 'orphanage',
'label' => 'دادههای موقت',
]
]
]
];
\ No newline at end of file
diff --git a/app/Enums/status.php b/app/Enums/status.php
new file mode 100644
index 0000000..8bd99c4
--- /dev/null
+++ b/app/Enums/status.php
@@ -0,0 +1 @@
+ [
'inactive' => [
'id' => 0,
'label' => 'غیر فعال',
'name' => 'Inactive'
],
'active' => [
'id' => 1,
'label' => 'فعال',
'name' => 'Active'
],
'close' => [
'id' => 2,
'label' => 'بسته',
'name' => 'Close'
],
'done' => [
'id' => 3,
'label' => 'انجام شده',
'name' => 'Done'
],
],
];
\ No newline at end of file
diff --git a/app/Enums/tables.php b/app/Enums/tables.php
new file mode 100644
index 0000000..273bf4c
--- /dev/null
+++ b/app/Enums/tables.php
@@ -0,0 +1,83 @@
+ [
+ 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',
+ 'singular_name' => 'Business',
+ ],
+ 'projects' => [
+ 'id' => 20,
+ 'name' => 'Projects',
+ 'singular_name' => 'Project',
+ ],
+ 'sprints' => [
+ 'id' => 30,
+ 'name' => 'Sprints',
+ 'singular_name' => 'Sprint',
+ ],
+ 'statuses' => [
+ 'id' => 40,
+ 'name' => 'Statuses',
+ 'singular_name' => 'Status',
+ ],
+ 'systems' => [
+ 'id' => 50,
+ 'name' => 'Systems',
+ 'singular_name' => 'System',
+ ],
+ 'workflows' => [
+ 'id' => 60,
+ 'name' => 'Workflows',
+ 'singular_name' => 'Workflow',
+ ],
+ 'tags' => [
+ 'id' => 70,
+ 'name' => 'Tags',
+ 'singular_name' => 'Tag',
+ ],
+ 'tasks' => [
+ 'id' => 80,
+ 'name' => 'Tasks',
+ 'singular_name' => 'Task',
+ ],
+ 'works' => [
+ 'id' => 90,
+ 'name' => 'Works',
+ 'singular_name' => 'Work',
+ ],
+
+ //Relation Table's
+ 'business_user' => [
+ 'id' => 210,
+ 'name' => 'BusinessUser',
+ 'singular_name' => 'BusinessUser',
+ ],
+ 'project_user' => [
+ 'id' => 220,
+ 'name' => 'ProjectUser',
+ 'singular_name' => 'ProjectUser',
+ ],
+ 'tag_task' => [
+ 'id' => 230,
+ 'name' => 'TagTask',
+ 'singular_name' => 'TagTask',
+ ],
+];
diff --git a/app/Enums/ticket.php b/app/Enums/ticket.php
new file mode 100644
index 0000000..5899fbf
--- /dev/null
+++ b/app/Enums/ticket.php
@@ -0,0 +1 @@
+ [
'sale' => [
'id' => 1,
'label' => 'فروش',
],
'support' => [
'id' => 2,
'label' => 'پشتیبانی'
]
],
'category' => [
'webdesign-sale' => [
'id' => 1,
'label' => 'طراحی سایت',
'type' => 1
],
'seo-sale' => [
'id' => 2,
'label' => 'فروش سئو',
'type' => 1
],
'webdesign-support' => [
'id' => 3,
'label' => 'پشتیبانی طراحی سایت',
'type' => 2
]
],
'status' => [
'active' => [
'id' => 1,
'label' => 'فعال'
],
'close' => [
'id' => 2,
'label' => 'بسته'
]
]
];
\ No newline at end of file
diff --git a/app/Enums/user.php b/app/Enums/user.php
new file mode 100644
index 0000000..e639578
--- /dev/null
+++ b/app/Enums/user.php
@@ -0,0 +1 @@
+ [
'guest' => [
'id' => 1,
'label' => 'مهمان'
],
'user' => [
'id' => 2,
'label' => 'کاربر'
],
'service' => [
'id' => 3,
'label' => 'سرویس'
]
],
'status' => [
'desable' => [
'id' => 0,
'label' => 'غیر فعال'
],
'active' => [
'id' => 1,
'label' => 'فعال'
]
],
'permissions' => [
// user
'user-user-create',
'user-user-view-any',
'user-user-update-own',
'user-user-update-any',
'user-user-role-own',
'user-user-role-any',
'user-role-create',
'user-role-view-any',
// ticket
'ticket-ticket-create',
'ticket-ticket-view-any',
'ticket-ticket-reply-any',
// post
'post-post-create',
'post-post-view-publish',
'post-post-view-any',
'post-post-view-own',
'post-post-update-own',
'post-post-update-any',
'post-post-delete-own',
'post-post-delete-any',
// tag
'post-tag-update-any',
'post-tag-create',
'post-tag-delete-any',
// taxonomies
'post-taxonomy-view-any',
'post-taxonomy-update-any',
'post-taxonomy-create',
'post-taxonomy-delete-any',
// comments
'post-comment-view-any',
'post-comment-view-published',
'post-comment-view-own',
'post-comment-create',
'post-comment-update-any',
'post-comment-delete-any',
]
];
\ No newline at end of file
diff --git a/app/Events/BusinessUpdate.php b/app/Events/BusinessUpdate.php
new file mode 100644
index 0000000..f2ffa55
--- /dev/null
+++ b/app/Events/BusinessUpdate.php
@@ -0,0 +1,20 @@
+message = $message;
+ }
+}
diff --git a/app/Events/BusinessUserCreate.php b/app/Events/BusinessUserCreate.php
new file mode 100644
index 0000000..b150a8d
--- /dev/null
+++ b/app/Events/BusinessUserCreate.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/Events/ModelSaved.php b/app/Events/ModelSaved.php
new file mode 100644
index 0000000..32a28cc
--- /dev/null
+++ b/app/Events/ModelSaved.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/Events/ProjectUserCreate.php b/app/Events/ProjectUserCreate.php
new file mode 100644
index 0000000..fa5e385
--- /dev/null
+++ b/app/Events/ProjectUserCreate.php
@@ -0,0 +1,24 @@
+message = $message;
+ }
+}
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/Events/TaskCreate.php b/app/Events/TaskCreate.php
new file mode 100644
index 0000000..e51dff2
--- /dev/null
+++ b/app/Events/TaskCreate.php
@@ -0,0 +1,28 @@
+message = $message;
+ }
+}
diff --git a/app/Events/TaskUpdate.php b/app/Events/TaskUpdate.php
new file mode 100644
index 0000000..91e18bd
--- /dev/null
+++ b/app/Events/TaskUpdate.php
@@ -0,0 +1,28 @@
+message = $message;
+ }
+}
diff --git a/app/Http/Controllers/ActivityController.php b/app/Http/Controllers/ActivityController.php
new file mode 100644
index 0000000..870279c
--- /dev/null
+++ b/app/Http/Controllers/ActivityController.php
@@ -0,0 +1,110 @@
+indexValidation($request);
+ $per_page = $request->limit > 100 ? 10 : $request->limit;
+ return $this->indexFiltering($business)->paginate($per_page);
+ }
+
+ public function indexValidation($request)
+ {
+ $bound = 10;
+ $this->validate($request, [
+ 'filter.project_id' => [new MaxBound($bound)] ,
+ 'filter.system_id' => [new MaxBound($bound)] ,
+ 'filter.workflow_id' => [new MaxBound($bound)] ,
+ 'filter.status_id' => [new MaxBound($bound)] ,
+ 'filter.sprint_id' => [new MaxBound($bound)] ,
+ 'filter.actor_id' => [new MaxBound($bound)] ,
+ 'filter.user_id' => [new MaxBound($bound)] ,
+ 'filter.subject_id' => [new MaxBound($bound)] ,
+ //todo: validation for crud_id and table_id
+ 'filter.creates_before' => 'bail|nullable|date|date_format:Y-m-d' ,
+ 'filter.creates_after' => 'bail|nullable|date|date_format:Y-m-d' ,
+ 'filter.creates_in' => 'bail|nullable|numeric|max:90' ,
+ ]);
+ }
+ public function indexFiltering($business)
+ {
+ $query = Activity::where('business_id', $business);
+ $activityQ = QueryBuilder::for($query)
+ ->allowedFilters([
+ AllowedFilter::exact('project_id'),
+ AllowedFilter::exact('system_id'),
+ AllowedFilter::exact('workflow_id'),
+ AllowedFilter::exact('status_id'),
+ AllowedFilter::exact('sprint_id'),
+ AllowedFilter::exact('task_id'),
+ AllowedFilter::exact('actor_id'),
+ AllowedFilter::exact('user_id'),
+ AllowedFilter::exact('crud_id'),
+ AllowedFilter::exact('table_id'),
+ AllowedFilter::exact('subject_id'),
+ AllowedFilter::scope('creates_before'),
+ AllowedFilter::scope('creates_after'),
+ AllowedFilter::scope('creates_in'),
+ ])
+ ->defaultSort('-id')
+ ->allowedSorts('id', 'created_at');
+ if (\request('_business_info')['info']['users'][\auth()->id()]['level'] != enum('levels.owner.id')) {
+ $requested_projects = isset(\request('filter')['project_id']) ?
+ array_unique(explode(',',\request('filter')['project_id'] ?? null )) :
+ null;
+ $requested_projects = collect($requested_projects)->keyBy(null)->toArray();
+ $project_ids = $this->myStateProjects($requested_projects);
+ $activityQ->where(function ($q) use ($project_ids) {
+ $q->whereIn('project_id', $project_ids['non_guest_ids'])
+ ->orWhere(function ($q) use ($project_ids) {
+ $q->whereIn('project_id', $project_ids['guest_ids'])
+ ->where('user_id', auth()->id());
+ });
+ });
+ }
+ return $activityQ;
+ }
+
+ public function myStateProjects($requested_projects)
+ {
+ $non_guest_ids = [];
+ $guest_ids = [];
+ $is_empty = empty($requested_projects);
+
+ foreach (\request('_business_info')['info']['projects'] as $p_id => $p) {
+
+ $level = \request('_business_info')['info']['projects'][$p_id]['members'][\auth()->id()]['level'];
+
+ if (( $is_empty || isset($requested_projects[$p_id]))
+ && $level > enum('levels.guest.id')) {
+ array_push($non_guest_ids, $p_id);
+ }
+ if (( $is_empty || isset($requested_projects[$p_id]))
+ && $level == enum('levels.guest.id')) {
+ array_push($guest_ids, $p_id);
+ }
+ }
+
+ return ['non_guest_ids' => $non_guest_ids, 'guest_ids' => $guest_ids];
+ }
+
+ public function store($business, Request $request)
+ {
+ return Activity::create($request->merge(['business_id' => $business])->all());
+ }
+
+ public function delete()
+ {
+
+ }
+}
diff --git a/app/Http/Controllers/AuthController.php b/app/Http/Controllers/AuthController.php
new file mode 100644
index 0000000..55fe6f7
--- /dev/null
+++ b/app/Http/Controllers/AuthController.php
@@ -0,0 +1,268 @@
+stateless()->redirect();
+ }
+
+ public function handleGoogleCallback(Request $request)
+ {
+ try {
+
+ $user = Socialite::driver('google')->stateless()->user();
+ $find_user = User::where('email', $user->email)->first();
+
+ if ($find_user) {
+
+ $find_user->update([
+ 'active' => true
+ ]);
+
+ Auth::setUser($find_user);
+
+ } else {
+
+ $user = User::create($user->user + [
+ 'password' => Hash::make('google-login-user'),
+ 'username' => $user->email,
+ 'active' => true
+ ]);
+
+ Auth::setUser($user);
+
+ }
+ $finger_print = $this->createFingerPrint();
+ return redirect('http://localhost:3000/login?token='.$finger_print->token);
+
+ } catch (Exception $e) {
+ dd($e->getMessage());
+ }
+ }
+
+ public function login(Request $request)
+ {
+ // todo: Logging in from a new device will result in sending a notification
+ $this->validate($request, [
+ 'email' => 'required|email|exists:users,email',
+ 'password' => 'required|string|min:6'
+ ]);
+
+ $user = User::where('email', $request->email)->first();
+ if ($user && Hash::check($request->password, $user->password)) {
+ Auth::setUser($user);
+
+ return [
+ 'auth' => $this->createFingerPrint(),
+ 'businesses' => Auth::user()->businesses->keyBy('id')->map(fn($b, $bid) => Business::info($bid))
+ ];
+ }
+
+ return new JsonResponse([
+ 'message' => trans('auth.failed'),
+ 'status' => Response::HTTP_NOT_FOUND,
+ ], Response::HTTP_NOT_FOUND);
+ }
+
+
+ public function register(Request $request)
+ {
+ $this->validate($request, [
+ 'name' => 'required|string|max:225|min:2',
+ 'username' => ['required', Rule::unique('users', 'username')],
+ 'email' => ['required', 'email', Rule::unique('users', 'email')],
+ 'password' => 'required|string|min:8'
+ ]);
+
+ $request->merge(['password' => Hash::make($request->password)]);
+
+ $code_data = ['verification_code' => $this->sendVerificationCode()];
+ $method_data = ['method' => 'registerMain'];
+
+ Cache::put($request->email, $request->all() + $code_data + $method_data, 3600); // remain one hour
+
+ return \response()->json([
+ 'message' => 'Code send for user and user must be verified.'],
+ Response::HTTP_OK);
+
+ }
+
+ public function registerMain($user_info)
+ {
+ $user = User::create($user_info);
+
+ Auth::setUser($user);
+
+ return $this->createFingerPrint();
+ }
+
+ public function sendVerificationCode($contact_way = null)
+ {
+ $verification_code = 1234; // rand(10001, 99999)
+
+ //send code for user with contact way
+
+ return $verification_code;
+ }
+
+ public function verification(Request $request)
+ {
+ if (!Cache::has($request->email)) {
+ return \response()->json(['message' => 'Code expired.'], Response::HTTP_BAD_REQUEST);
+ }
+
+ $user_info = Cache::get($request->email);
+
+ $this->validate($request, [
+ 'email' => 'required|email',
+ 'verification_code' => 'required|string|min:4|max:4|in:'.$user_info['verification_code']
+ ]);
+
+ Cache::forget($request->email);
+
+ return isset($user_info['method']) ?
+ call_user_func('self::'.$user_info['method'], $user_info) :
+ \response()->json(['message' => 'Code verified successfully.'], Response::HTTP_OK,);
+ }
+
+ public function forgetPassword(Request $request)
+ {
+ $this->validate($request, [
+ 'email' => 'required|email|exists:users,email'
+ ]);
+
+ $code_data = ['verification_code' => $this->sendVerificationCode()];
+
+ Cache::put($request->email, $request->all() + $code_data, 3600); // remain one hour
+
+ return \response()->json([
+ 'message' => 'Code send for user and user must be verified.'],
+ Response::HTTP_OK);
+ }
+
+ public function updatePassword(Request $request)
+ {
+ if (!Cache::has($request->email)) {
+ return \response()->json(['message' => 'Code expired.'], Response::HTTP_BAD_REQUEST);
+ }
+
+ $this->validate($request, [
+ 'email' => 'required|email',
+ 'password' => 'required|string|min:8|confirmed',
+ 'verification_code' => 'required|string|min:4|max:4|in:'.Cache::get($request->email)['verification_code']
+ ]);
+
+ $user = User::where('email', $request->email)->first();
+
+ $user->update([
+ 'password' => Hash::make($request->password)
+ ]);
+
+ Auth::setUser($user);
+
+ return $this->createFingerPrint();
+ }
+
+ /**
+ * @param Request $request
+ * @return mixed
+ * @throws TokenMismatchException
+ */
+ public function logout(Request $request)
+ {
+ $token = $request->bearerToken();
+
+ if (blank($token)) {
+ return new JsonResponse([
+ 'message' => 'Not authorized request.',
+ 'status' => Response::HTTP_UNAUTHORIZED
+ ]);
+ }
+
+ /** @var Fingerprint $token */
+ $token = Auth::user()->fingerprints()->firstWhere([
+ 'token' => $token,
+ ]);
+
+ if ($token) {
+ return $token->delete();
+ }
+
+ throw new TokenMismatchException('Invalid token!');
+ }
+
+ /**
+ * @param string $token
+ * @throws TokenMismatchException
+ */
+ public function revoke(string $token)
+ {
+ /** @var Fingerprint $token */
+ $token = Fingerprint::firstWhere([
+ 'token' => $token,
+ ]);
+
+ if ($token) {
+ return $token->delete();
+ }
+
+ throw new TokenMismatchException();
+ }
+
+ public function auth()
+ {
+ return new UserResource(Auth::user());
+ }
+
+ public function authWithInfo()
+ {
+ return [
+ 'auth' => new UserResource(Auth::user()),
+ 'businesses' => Auth::user()->businesses->keyBy('id') ->map(fn($b, $bid) => Business::info($bid))
+ ];
+ }
+
+ public function delete(Request $request)
+ {
+ Auth::user()->fingerprints()->delete();
+ unset(Auth::user()->token);
+ Auth::user()->delete();
+
+ return 'success';
+ }
+
+ public function createFingerPrint()
+ {
+ $attributes = [
+ 'agent' => request()->getAgent(),
+ 'ip' => request()->getClientIp(),
+ 'os' => request()->getOS(),
+ 'latitude' => \request()->getLocation()->getAttribute('lat'),
+ 'longitude' => \request()->getLocation()->getAttribute('lon'),
+ ];
+
+ $values = [
+ 'token' => Str::random(60)
+ ];
+
+ return Auth::user()->fingerprints()->firstOrCreate($attributes, $attributes + $values);
+ }
+}
diff --git a/app/Http/Controllers/BusinessController.php b/app/Http/Controllers/BusinessController.php
new file mode 100644
index 0000000..aafdd41
--- /dev/null
+++ b/app/Http/Controllers/BusinessController.php
@@ -0,0 +1,195 @@
+user()->businesses
+ ->keyBy('id')
+ ->map(fn($b, $bid) => Business::info($bid));
+ }
+
+ public function store(Request $request)
+ {
+// $users = [];
+// foreach ($request->users ?? [] as $key => $value) {
+// $users[$value] = [];
+// }
+
+// $owner = [
+// Auth::id() => [
+// 'level' => enum('levels.owner.id'),
+// ]
+// ];
+//
+// $users = $users + $owner;
+//
+// $request->merge(['users' => $users]);
+
+ $business = Business::create($request->all());
+ $business->users()->sync([Auth::id() => [
+ 'level' => enum('levels.owner.id'),
+ ]
+ ], false);
+
+ return Business::info($business->id);
+ }
+
+ public function show(string $business)
+ {
+ permit('businessAccess');
+ return Business::info($business);
+ }
+
+ public function update(Request $request, string $business)
+ {
+ // permit('businessEdit');
+ $business = Business::findOrFail($business);
+ $business->fill($request->all())->save();
+
+ return Business::info($business->id);
+ }
+
+ public function setAvatar(Request $request, string $business)
+ {
+ $business = Business::findOrFail($business);
+ if ($request->hasFile('avatar')) {
+ $business->saveAsAvatar($request->file('avatar'));
+ }
+
+ return Business::info($business->id);
+ }
+
+ public function unSetAvatar(Request $request, string $business)
+ {
+ $business = Business::findOrFail($business);
+ $business->deleteAvatar();
+
+ return Business::info($business->id);
+ }
+
+
+ public function info(string $business)
+ {
+ return request('_business_info');
+ }
+
+ public function restore(string $business)
+ {
+ $business = Business::onlyTrashed()->findOrFail($business);
+ $business->restore();
+
+ return response(['message' => 'business successfully restored.']);
+ }
+
+ public function storeOrUpdateUser($business, Request $request)
+ {
+ permit('businessUsers');
+ $validatedData = $this->validate($request, [
+ 'level' => 'required|numeric|between:0,4',
+ 'user_id' => 'required|numeric|not_in:'.auth()->id(),
+ ]);
+
+ DB::transaction(function () use ($validatedData, $request, $business) {
+ $this->addUser($business, $request->user_id, $validatedData);
+ if (can('businessAccess', ['user_id'=> $request->user_id])) {
+ //update
+ $this->relatedUpdateChanges($request->user_id, $request->level);
+ }
+ }, 3);
+
+ return Business::info($business, true);
+ }
+
+ public function relatedUpdateChanges($user_id, $level)
+ {
+ if ($level == enum('levels.owner.id')) {
+ // user up level to owner
+ $this->removeProjectDirectRelation($user_id);
+ }
+ if ($level != enum('levels.owner.id') &&
+ $level > request('_business_info')['info']['users'][$user_id]['level']) {
+ // user at least up level to $request->level
+ $this->updateProjectAccessLevel($level, $user_id);
+ }
+ }
+
+ public function addUser($business, $user, $validatedData)
+ {
+ $businessModel = Business::findOrFail($business);
+ $businessModel->users()->sync([$user => $validatedData], false);
+ }
+
+ public function removeProjectDirectRelation($user)
+ {
+ $userModel = User::findOrFail($user);
+ return $userModel->projects()->sync([], true);
+ }
+
+ public function updateProjectAccessLevel($level, $user)
+ {
+ $ids = [];
+ foreach (request('_business_info')['projects'] as $project_id => $item) {
+ foreach ($item['members'] as $idx => $member) {
+ if ($member['id'] == $user && $member['level'] != enum('levels.inactive.id') && $member['level'] < $level) {
+ $ids[$project_id] = ['level' => $level];
+ break;
+ }
+ }
+ }
+ $userModel = User::findOrFail($user);
+ return $userModel->projects()->sync($ids, false);
+ }
+
+ public function deleteUser($business, $user)
+ {
+ permit('businessAccess');
+ $this->checkDeleteUserPolicy($user);
+ $businessModel = Business::findOrFail($business);
+
+ DB::transaction(function () use ($user, $businessModel) {
+ $this->detachUser($businessModel, $user);
+ $this->removeProjectDirectRelation($user);
+ }, 3);
+
+ return Business::info($business, true);
+ }
+
+ public function haveAnotherOwner($user)
+ {
+ foreach (request('_business_info')['info']['users'] as $id => $item) {
+ if ($item['level'] == enum('levels.owner.id') && $id != $user) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public function detachUser($business, $user)
+ {
+ return $business->users()->sync(
+ $business->users->except($user)->pluck('id')->toArray()
+ );
+ }
+
+ public function checkDeleteUserPolicy($user)
+ {
+ if (!can('isBusinessOwner') && auth()->id() != $user ) {
+ // Non owner user remove another owner
+ abort(405);
+ }
+ if (can('isBusinessOwner') && auth()->id() == $user && !$this->haveAnotherOwner($user)) {
+ // Owner remove self but business haven't another owner
+ abort(405);
+ }
+ }
+
+}
diff --git a/app/Http/Controllers/CommentController.php b/app/Http/Controllers/CommentController.php
new file mode 100644
index 0000000..898d232
--- /dev/null
+++ b/app/Http/Controllers/CommentController.php
@@ -0,0 +1,90 @@
+ $project]);
+ $taskModel = Task::where([['project_id', $project ], ['id', $task]])->firstOrFail();
+ if (can('isDefiniteGuestInProject', ['project_id' => $project])){ // is guest in project (only guest)
+ return $taskModel->assignee_id == \auth()->id() ?
+ Comment::where([
+ ['business_id', $business],
+ ['project_id', $project],
+ ['task_id', $task],
+ ])->get():
+ abort(Response::HTTP_FORBIDDEN); // not allowed
+ } else {
+ return Comment::where([
+ ['business_id', $business],
+ ['project_id', $project],
+ ['task_id', $task],
+ ])->get();
+ }
+ }
+
+ public function store($business, $project, $task, Request $request)
+ {
+ permit('projectAccess', ['project_id' => $project]);
+ $taskModel = Task::where([['project_id', $project ], ['id', $task]])->firstOrFail();
+ if (can('isDefiniteGuestInProject', ['project_id' => $project])){ // is guest in project (only guest)
+ return $taskModel->assignee_id == \auth()->id() ?
+ Comment::create($request->merge([
+ 'business_id' => $business,
+ 'project_id' => $project,
+ 'task_id' => $task,
+ 'user_id' => \auth()->id(),
+ ])->except('_business_info')) :
+ abort(Response::HTTP_FORBIDDEN); // not allowed
+ } else {
+ return Comment::create($request->merge([
+ 'business_id' => $business,
+ 'project_id' => $project,
+ 'task_id' => $task,
+ 'user_id' => \auth()->id(),
+ ])->except('_business_info'));
+ }
+ }
+
+ public function show($business, $project, $task, $comment)
+ {
+ permit('projectAccess', ['project_id' => $project]);
+ $taskModel = Task::where([['project_id', $project ], ['id', $task]])->firstOrFail();
+ if (can('isDefiniteGuestInProject', ['project_id' => $project])){ // is guest in project (only guest)
+ return $taskModel->assignee_id == \auth()->id() ?
+ Comment::findOrFail($comment) :
+ abort(Response::HTTP_FORBIDDEN); // not allowed
+ } else {
+ return Comment::findOrFail($comment);
+ }
+ }
+
+ public function update($business, $project, $task, $comment, Request $request)
+ {
+ permit('projectAccess', ['project_id' => $project]);
+ $comment = Comment::findOrFail($comment);
+ if ($comment->user_id == \auth()->id()) {
+ $comment->update($request->except('_business_info'));
+ return $comment;
+ }
+ return abort(Response::HTTP_FORBIDDEN); // not allowed
+ }
+
+ public function destroy($business, $project, $task, $comment)
+ {
+ permit('projectAccess', ['project_id' => $project]);
+ $comment = Comment::findOrFail($comment);
+ if ($comment->user_id == \auth()->id()) {
+ $comment->delete();
+ return \response()->json(['message' => 'comment deleted successfully.'], Response::HTTP_OK);
+ }
+ return abort(Response::HTTP_FORBIDDEN); // not allowed
+ }
+}
diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php
index a0a2a8a..03e02a2 100644
--- a/app/Http/Controllers/Controller.php
+++ b/app/Http/Controllers/Controller.php
@@ -2,10 +2,10 @@
namespace App\Http\Controllers;
-use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Bus\DispatchesJobs;
-use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController;
+use Illuminate\Foundation\Validation\ValidatesRequests;
+use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
class Controller extends BaseController
{
diff --git a/app/Http/Controllers/CreditController.php b/app/Http/Controllers/CreditController.php
new file mode 100644
index 0000000..d1f311b
--- /dev/null
+++ b/app/Http/Controllers/CreditController.php
@@ -0,0 +1,69 @@
+allowedSorts([
+ 'amount',
+ 'succeeded',
+ 'created_at',
+ ])
+ ->allowedFilters([
+ AllowedFilter::exact('user_id'),
+ AllowedFilter::exact('succeeded'),
+ ]);
+
+ return TransactionResource::collection(
+ $builder->paginate($request->per_page)
+ );
+ }
+
+ public function pay(Request $request, int $business)
+ {
+ \permit('isBusinessOwner');
+
+ return Transaction::create([
+ 'user_id'=> Auth::id(),
+ 'business_id'=> $business,
+ 'amount'=> $request->amount,
+ ]);
+ }
+
+ public function redirection($transaction)
+ {
+ $transaction = Transaction::findOrFail($transaction);
+ if ($transaction->isWentToPaymentGateway()) {
+ throw new \Exception("Siktir baba ye bar ghablan rafti.");
+ }
+
+ return $transaction->prepare()->redirect();
+ }
+
+ public function callback(Request $request)
+ {
+ $transaction = Transaction::findByAuthority($request->get('Authority'))->verify();
+ if (!$transaction->hasBeenAppliedToWallet() && $transaction->succeeded) {
+ $transaction->business->increment("wallet", $transaction->amount);
+ $transaction->amountWasAppliedToWallet();
+
+ return true;
+ }
+
+ throw new \Exception("تراکنش تایید نشد");
+ }
+}
diff --git a/app/Http/Controllers/FileController.php b/app/Http/Controllers/FileController.php
new file mode 100644
index 0000000..23b30ca
--- /dev/null
+++ b/app/Http/Controllers/FileController.php
@@ -0,0 +1,207 @@
+indexValidation($request);
+
+ $per_page = $request->limit > 100 ? 10 : $request->limit;
+
+ return $this->indexFiltering($business)->paginate($per_page);
+ }
+
+ public function indexValidation($request)
+ {
+ $bound = 10;
+ $this->validate($request, [
+ 'filter.project_id' => [new MaxBound($bound)] ,
+ 'filter.user_id' => [new MaxBound($bound)] ,
+ ]);
+ }
+
+ public function indexFiltering($business)
+ {
+ $query = File::where('business_id', $business);
+ $fileQ = QueryBuilder::for($query)
+ ->allowedFilters([
+ AllowedFilter::exact('user_id'),
+ AllowedFilter::exact('project_id'),
+ AllowedFilter::exact('extension'),
+ ]);
+ if (\request('_business_info')['info']['users'][\auth()->id()]['level'] != enum('levels.owner.id')) {
+ $requested_projects = isset(\request('filter')['project_id']) ?
+ array_unique(explode(',',\request('filter')['project_id'] ?? null )) :
+ null;
+ $requested_projects = collect($requested_projects)->keyBy(null)->toArray();
+ $project_ids = $this->myStateProjects($requested_projects);
+ $fileQ->where(function ($q) use ($project_ids) {
+ $q->whereIn('project_id', $project_ids['non_guest_ids'])
+ ->orWhere(function ($q) use ($project_ids) {
+ $q->whereIn('project_id', $project_ids['guest_ids'])
+ ->where('user_id', auth()->id());
+ });
+ });
+ }
+
+ if (request()->filled('group')) {
+ $fileQ->selectRaw("files.group, count(files.id) as file_count, sum(files.size) as file_size")->groupBy('group');
+ }
+
+ return $fileQ;
+ }
+
+ public function myStateProjects($requested_projects)
+ {
+ $non_guest_ids = [];
+ $guest_ids = [];
+ $is_empty = empty($requested_projects);
+
+ foreach (\request('_business_info')['info']['projects'] as $p_id => $p) {
+
+ $level = \request('_business_info')['info']['projects'][$p_id]['members'][\auth()->id()]['level'];
+
+ if (( $is_empty || isset($requested_projects[$p_id]))
+ && $level > enum('levels.guest.id')) {
+ array_push($non_guest_ids, $p_id);
+ }
+ if (( $is_empty || isset($requested_projects[$p_id]))
+ && $level == enum('levels.guest.id')) {
+ array_push($guest_ids, $p_id);
+ }
+ }
+
+ return ['non_guest_ids' => $non_guest_ids, 'guest_ids' => $guest_ids];
+ }
+
+ public function store(Request $request, int $business, int $project)
+ {
+ // different size and different validation
+ // validate
+ // validate the wallet is not so much in debt
+ // create record in the db
+ // put file in s3
+ // return file resource
+ $business = Business::findOrFail($business);
+ $project = Project::findOrFail($project);
+
+ $this->validate($request, ['file' => 'required|file',]);
+
+ $file = $request->file('file');
+ $file_extension = $file->getClientOriginalExtension();
+ $file_name = Str::random(40).'.'.$file_extension;
+ $file->storeAs(
+ $business->id.\DIRECTORY_SEPARATOR.$project->id,
+ $file_name,
+ 's3'
+ );
+
+ $file_record = File::create([
+ 'user_id' => Auth::id(),
+ 'business_id' => $business->id,
+ 'project_id' => $project->id,
+ 'disk' => 's3', // default disk
+ 'original_name' => $file->getClientOriginalName(),
+ 'extension' => $file_extension,
+ 'name' => $file_name,
+ 'mime' => $file->getClientMimeType(),
+ 'group' => $this->groupDetection($file),
+ 'size' => $file->getSize(),
+ 'description' => $request->description
+ ]);
+
+ $business->update([
+ 'files_volume' => $business->files_volume + $file_record->size
+ ]);
+
+ return new FileResource($file_record);
+ }
+
+ public function groupDetection(UploadedFile $file)
+ {
+ // Media files like mp4, mp3, wma and png or jpeg
+ [$type, $subtype] = Str::of($file->getMimeType())->explode("/",2)->pad(2, null);
+
+ if (in_array($type, ['audio', 'video', 'image'])) {
+ return $type;
+ }
+
+ // Covert string to \Illuminate\Support\Stringable object
+ $subtype = Str::of($subtype);
+
+ // PDF files
+ if ($subtype->contains(["pdf"])) {
+ return "pdf";
+ }
+
+ // Compressed files like zip, cab, rar, etc.
+ if ($subtype->contains(['compressed']) || in_array($file->getClientOriginalExtension(), ['zip', 'rar','7z','cab'])) {
+ return "compressed";
+ }
+
+ // Office files like xls, xlsx, doc, docx, etc.
+ if ($subtype->contains(['vnd.ms', 'vnd.sealed', 'officedocument', 'opendocument'])) {
+ return "office";
+ }
+
+ // Non of the above files
+ return "other";
+ }
+
+ public function download(int $business, int $project, int $file)
+ {
+ // requested file belongs to this project and this business
+ // check permisson
+ // create perma link or temp link
+ // return the file resource or stream it
+ return File::findOrFail($file)->getTemporaryLink();
+ }
+
+ public function rename(Request $request, int $business, int $project, int $file)
+ {
+ // requested file belongs to this project and this business
+ // check permisson
+ // update original name
+ // return the file resource
+ // sanitize the name for slashs and back slashes
+ $this->validate($request, [
+ 'name' => 'required|string'
+ ]);
+
+ $file = File::findOrFail($file);
+
+ $file->update(['original_name' => $request->name.".".$file->extension]);
+
+ return new FileResource($file);
+ }
+
+
+ public function delete(Request $request, int $business, int $project, int $file)
+ {
+ // requested file belongs to this project and this business
+ // check permisson
+ // check it's relations
+ // delete the file form File table
+ // delete file from s3
+ $file = File::findOrFail($file);
+ Storage::disk('s3')->delete($file->getPath());
+ return $file->delete();
+ }
+}
diff --git a/app/Http/Controllers/InvoiceController.php b/app/Http/Controllers/InvoiceController.php
new file mode 100644
index 0000000..b6ee5ac
--- /dev/null
+++ b/app/Http/Controllers/InvoiceController.php
@@ -0,0 +1,80 @@
+selectRaw("concat_ws('-',business_id,month) as factor_id")
+ ->selectRaw("MIN(created_at) as begin")
+ ->selectRaw("MAX(updated_at) as end")
+ ->selectRaw("sum(cost) as cost")
+ ->selectRaw("sum(tax) as tax")
+ ->where('business_id','=',$business->id)
+ ->groupBy('month','factor_id');
+
+ $costs = QueryBuilder::for($builder)
+ ->allowedSorts([
+ 'factor_id',
+ 'begin',
+ 'end',
+ 'cost',
+ ])
+ ->allowedFilters([
+ AllowedFilter::exact('month'),
+ AllowedFilter::exact('type'),
+ ]);
+
+ return $costs->paginate($request->per_page);
+ }
+
+ public function indexFiltering($business)
+ {
+ $query = File::where('business_id', $business);
+ $fileQ = QueryBuilder::for($query)
+ ->allowedFilters([
+ AllowedFilter::exact('user_id'),
+ AllowedFilter::exact('project_id'),
+ AllowedFilter::exact('extension'),
+ ]);
+ if (\request('_business_info')['info']['users'][\auth()->id()]['level'] != enum('levels.owner.id')) {
+ $requested_projects = isset(\request('filter')['project_id']) ?
+ array_unique(explode(',', \request('filter')['project_id'] ?? null)) :
+ null;
+ $requested_projects = collect($requested_projects)->keyBy(null)->toArray();
+ $project_ids = $this->myStateProjects($requested_projects);
+ $fileQ->where(function ($q) use ($project_ids) {
+ $q->whereIn('project_id', $project_ids['non_guest_ids'])
+ ->orWhere(function ($q) use ($project_ids) {
+ $q->whereIn('project_id', $project_ids['guest_ids'])
+ ->where('user_id', auth()->id());
+ });
+ });
+ }
+
+ if (request()->filled('group')) {
+ $fileQ->selectRaw("files.group, count(files.id) as file_count, sum(files.size) as file_size")->groupBy('group');
+ }
+
+ return $fileQ;
+ }
+
+ public function show(Request $request, int $business, string $date)
+ {
+ return Cost::where('business_id', '=', $business)
+ ->where("month","=",$date)
+ ->get();
+ }
+}
diff --git a/app/Http/Controllers/ProjectController.php b/app/Http/Controllers/ProjectController.php
new file mode 100644
index 0000000..00a20cb
--- /dev/null
+++ b/app/Http/Controllers/ProjectController.php
@@ -0,0 +1,159 @@
+get();
+ }
+
+ public function store(Request $request, string $business)
+ {
+ permit('businessProjects');
+ Project::create($request->merge(['business_id' => $business])->all());
+ return Business::info($request->route('business'), true);
+ }
+
+ public function update(Request $request, int $business, string $project)
+ {
+ permit('projectEdit', ['project_id' => $project]);
+ $project = Project::findOrFail($project);
+ $project->update($request->except('business_id'));
+ return Business::info($request->route('business'), true);
+ }
+
+
+ public function delete(Request $request, int $business, string $project)
+ {
+ permit('businessProjects');
+ $project = Project::findOrFail($project);
+ $project->delete();
+ return Business::info($request->route('business'));
+ }
+
+ public function restore(Request $request, int $business, string $project)
+ {
+ $project = Project::onlyTrashed()->findOrFail($project);
+ $project->restore();
+
+ return response(['message' => 'project successfully restored.']);
+ }
+
+ public function storeOrUpdateUser($business, $project, Request $request)
+ {
+ permit('projectUsers', ['project_id' => $project]);
+ $validatedData = $this->validate($request, [
+ 'level' => 'required|numeric|between:1,3',
+ 'user_id' => 'required|numeric|not_in:'.auth()->id(),
+ ]);
+
+ $this->checkAddUserPolicy($request->user_id, $request->level);
+
+ $projectModel = Project::findOrFail($project);
+ DB::transaction(function () use ($business, $validatedData, $request, $projectModel) {
+ $projectModel->members()->sync([$request->user_id => $validatedData], false);
+
+ if (!can('businessAccess', ['user_id' => $request->user_id])) {
+
+ // Register user to business with zero level
+ //User not exist in the business before
+
+ $this->addUserWithZeroLevel($request->user_id, $business);
+ }
+ }, 3);
+
+ return Business::info($projectModel->business_id, true);
+ }
+
+ public function checkAddUserPolicy($user, $level)
+ {
+ if (can('businessAccess', ['user_id' => $user])
+ && $level < request('_business_info')['info']['users'][$user]['level']) {// before in business
+ abort(405);
+ }
+ }
+
+ public function addUserWithZeroLevel($user_id, $business)
+ {
+ $businessModel = Business::findOrFail($business);
+ return $businessModel->users()->sync([$user_id => [
+ 'level' => 0,
+ 'user_id' => $user_id
+ ]], false);
+ }
+
+ public function deleteUser($business, $project, $user)
+ {
+ permit('projectAccess', ['project_id' => $project]);
+ $this->checkDeleteUserPolicy($user, $project);
+
+ $projectModel = Project::findOrFail($project);
+
+ DB::transaction(function () use ($project, $business, $user, $projectModel) {
+ $this->detachMember($projectModel, $user);
+ if (!can('isActiveUser', ['user_id' => $user]) && !$this->haveOneProject($user, $project)) {
+
+ // User level in business is zero
+ // And haven't another project then remove it form business
+
+ $businessModel = Business::findOrFail($business);
+ $this->detachUser($businessModel, $user);
+ }
+ }, 3);
+
+ return Business::info($projectModel->business_id, true);
+ }
+
+ public function detachMember($project, $user)
+ {
+ return $project->members()->detach($user) ? true : abort(404);
+ }
+
+ public function detachUser($business, $user)
+ {
+ return $business->users()->detach($user) ? true : abort(404);
+ }
+
+ public function haveOneProject($user, $project)
+ {
+ foreach (request('_business_info')['info']['projects'] as $id => $item) {
+ if ($item['members'][$user]['level'] > enum('levels.inactive.id') && $id != $project) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public function checkDeleteUserPolicy($user, $project)
+ {
+ if (!can('isProjectOwner', ['project_id' => $project]) && (auth()->id() != $user) ) {
+ abort(405);
+ }
+ }
+
+ public function setAvatar(Request $request, int $business, string $project)
+ {
+ $project = Project::findOrFail($project);
+ if ($request->hasFile('avatar')) {
+ $project->saveAsAvatar($request->file('avatar'));
+ }
+
+ return $project;
+ }
+
+ public function unSetAvatar(Request $request,int $business ,string $project)
+ {
+ $project = Project::findOrFail($project);
+ $project->deleteAvatar();
+
+ return $project;
+ }
+}
diff --git a/app/Http/Controllers/SprintController.php b/app/Http/Controllers/SprintController.php
new file mode 100644
index 0000000..69d560c
--- /dev/null
+++ b/app/Http/Controllers/SprintController.php
@@ -0,0 +1,45 @@
+ $project]);
+
+ Sprint::create($request->merge(
+ ['business_id' => $business, 'project_id' => $project]
+ )->except('_business_info'));
+
+ return Business::info($business, true);
+ }
+
+ public function update($business, $project, $sprint, Request $request)
+ {
+ permit('projectSprints', ['project_id' => $project]);
+
+ $sprint = Sprint::findOrFail($sprint);
+
+ $sprint->update($request->except('_business_info'));
+
+ return Business::info($business, true);
+ }
+
+ public function delete($business, $project, $sprint)
+ {
+ permit('projectSprints', ['project_id' => $project]);
+
+ $sprint = Sprint::findOrFail($sprint);
+
+ $sprint->delete();
+
+ return Business::info($business, true);
+ }
+
+}
diff --git a/app/Http/Controllers/StatisticController.php b/app/Http/Controllers/StatisticController.php
new file mode 100644
index 0000000..ae65111
--- /dev/null
+++ b/app/Http/Controllers/StatisticController.php
@@ -0,0 +1,112 @@
+ 0, // The sum of all tasks that are under review
+ 'total' => 0, // Sum of all tasks
+ 'overdue' => 0, // The sum of all overworked tasks
+ 'spent_time' => 0, // The sum of all the minutes spent on tasks
+ 'estimated_time' => 0, // The sum of all the minutes spent performing tasks is estimated
+ 'over_spent_time' => 0, // The sum of all the minutes spent overworking tasks
+ 'under_spent_time' => 0, // The sum of all the minutes left until estimated time
+ ];
+
+ public function index(Request $request, int $business, ?int $project = null)
+ {
+ $builder = DB::table('tasks')->where('business_id','=',$business);
+ if ($project) {
+ $builder->where('project_id', '=', $project);
+ }
+ $tasks = $builder->get();
+
+ $tags = DB::table('tag_task')
+ ->whereIn('task_id', $tasks->pluck('id')->toArray())
+ ->get()
+ ->groupBy('task_id')
+ ->toArray();
+
+
+ $result = [];
+
+ foreach ($tasks as $task) {
+
+ $this->addProjects($result, $task);
+
+ $this->addSprints($result, $task);
+
+ $this->addSystems($result, $task);
+
+ $this->addTags($result, $task, $tags[$task->id] ?? []);
+ }
+
+ return $result;
+ }
+
+
+ public function addProjects(&$result, $task)
+ {
+ $key = "projects.{$task->project_id}";
+
+ $this->subset($key, $result, $task);
+ }
+
+ public function addSprints(&$result, $task)
+ {
+ $key = "projects.{$task->project_id}.";
+ $key .= 'sprints.';
+ $key .= ($task->sprint_id ?? 0) . '.';
+ $key .= ($task->assignee_id ?? 0) . '.';
+ $key .= $task->workflow_id . '.';
+ $key .= $task->status_id;
+
+ $this->subset($key, $result, $task);
+ }
+
+ public function addSystems(&$result, $task)
+ {
+ $key = "projects.{$task->project_id}.";
+ $key .= 'systems.';
+ $key .= ($task->system_id ?? 0) . '.';
+ $key .= ($task->assignee_id ?? 0) . '.';
+ $key .= $task->workflow_id . '.';
+ $key .= $task->status_id;
+
+ $this->subset($key, $result, $task);
+ }
+
+ public function addTags(&$result, $task, $tags)
+ {
+ foreach ($tags as $tag) {
+ $key = "projects.{$task->project_id}.";
+ $key .= 'tags.';
+ $key .= $tag->id . '.';
+ $key .= ($task->assignee_id ?? 0) . '.';
+ $key .= $task->workflow_id . '.';
+ $key .= $task->status_id;
+
+ $this->subset($key, $result, $task);
+ }
+ }
+
+ public function subset($key , &$result, $task)
+ {
+ $node = Arr::get($result, $key, $this->map);
+
+ $node['test'] += $task->ready_to_test;
+ $node['total'] += 1;
+ $node['overdue'] += !$task->on_time;
+ $node['spent_time'] += $task->spent_time;
+ $node['estimated_time'] += $task->estimated_time;
+ $node['over_spent_time'] += $task->estimated_time < $task->spent_time ? $task->spent_time - $task->estimated_time : 0;
+ $node['under_spent_time'] += $task->estimated_time > $task->spent_time ? $task->estimated_time - $task->spent_time : 0;
+
+ Arr::set($result, $key, $node);
+ }
+}
diff --git a/app/Http/Controllers/StatusController.php b/app/Http/Controllers/StatusController.php
new file mode 100644
index 0000000..73cb572
--- /dev/null
+++ b/app/Http/Controllers/StatusController.php
@@ -0,0 +1,35 @@
+merge(['business_id' => $business, 'workflow_id' => $workflow])->except('_business_info'));
+ return Business::info($business, true);
+ }
+
+ public function update($business, $workflow, $status, Request $request)
+ {
+ permit('businessStatuses');
+ $status = Status::findOrFail($status);
+ $status->update($request->except('_business_info'));
+ return Business::info($business, true);
+ }
+
+ public function delete($business, $workflow, $status)
+ {
+ permit('businessStatuses');
+ $status = Status::findOrFail($status);
+ $status->delete();
+ return Business::info($business, true);
+ }
+}
diff --git a/app/Http/Controllers/SystemController.php b/app/Http/Controllers/SystemController.php
new file mode 100644
index 0000000..bb41f82
--- /dev/null
+++ b/app/Http/Controllers/SystemController.php
@@ -0,0 +1,40 @@
+ $project]);
+ System::create([
+ 'business_id' => $business,
+ 'project_id' => $project,
+ 'name' => $request->name
+ ]);
+ return Business::info($business, true);
+ }
+
+ public function update($business, $project, $system, Request $request)
+ {
+ permit('projectSystems', ['project_id' => $project]);
+ $system = System::findOrFail($system);
+ $system->update($request->except('_business_info'));
+ return Business::info($business, true);
+ }
+
+
+ public function delete($business, $project, $system)
+ {
+ permit('projectSystems', ['project_id' => $project]);
+ $system = System::findOrFail($system);
+ $system->delete();
+ return Business::info($business, true);
+ }
+
+
+}
diff --git a/app/Http/Controllers/TagController.php b/app/Http/Controllers/TagController.php
new file mode 100644
index 0000000..2667a2b
--- /dev/null
+++ b/app/Http/Controllers/TagController.php
@@ -0,0 +1,34 @@
+merge(['business_id' => $business])->except('_business_info'));
+ return Business::info($business, true);
+ }
+
+ public function update($business, $tag, Request $request)
+ {
+ permit('businessTags');
+ $tag = Tag::findOrFail($tag);
+ $tag->update($request->except('_business_info'));
+ return Business::info($business, true);
+ }
+
+ public function delete($business, $tag)
+ {
+ permit('businessTags');
+ $tag = Tag::findOrFail($tag);
+ $tag->delete();
+ return Business::info($business, true);
+ }
+
+}
diff --git a/app/Http/Controllers/TaskController.php b/app/Http/Controllers/TaskController.php
new file mode 100644
index 0000000..02ef09b
--- /dev/null
+++ b/app/Http/Controllers/TaskController.php
@@ -0,0 +1,295 @@
+limit > 100 ? 10 : $request->limit;
+ $this->indexValidation($request);
+ $tasks = $this->indexFiltering($business)
+ ->when($request->filled('group'), function ($q) use ($request) {
+ return $q->report($request->group);
+ });
+
+ return $request->filled('group') ?
+ $tasks->get()->groupBy($request->group)
+ ->map(function ($q) { return $q->keyBy('status_id'); })
+ : new TaskCollection($tasks->paginate($per_page));
+ }
+
+ public function indexValidation($request)
+ {
+ $bound = 10;
+ $this->validate($request, [
+ 'filter.project_id' => [new MaxBound($bound)] ,
+ 'filter.creator_id' => [new MaxBound($bound)] ,
+ 'filter.assignee_id' => [new MaxBound($bound)] ,
+ 'filter.system_id' => [new MaxBound($bound)] ,
+ 'filter.workflow_id' => [new MaxBound($bound)] ,
+ 'filter.status_id' => [new MaxBound($bound)] ,
+ 'filter.approver_id' => [new MaxBound($bound)] ,
+ 'filter.priority_min' => 'nullable|numeric|between:1,10' ,
+ 'filter.priority_max' => 'nullable|numeric|between:1,10' ,
+ 'filter.ready_to_test' => 'nullable|boolean' ,
+ 'filter.on_time' => 'nullable|boolean' ,
+ ]);
+ }
+
+ public function indexFiltering($business)
+ {
+ $query = Task::where('business_id', $business);
+ $taskQ = QueryBuilder::for($query)
+// ->with('tags')
+ ->select(DB::raw('tasks.* , (spent_time - estimated_time) as over_spent'))
+ ->allowedFilters([
+ AllowedFilter::exact('project_id'),
+ AllowedFilter::exact('system_id'),
+ AllowedFilter::exact('creator_id'),
+ AllowedFilter::exact('assignee_id'),
+ AllowedFilter::exact('approver_id'),
+ AllowedFilter::exact('sprint_id'),
+ AllowedFilter::exact('workflow_id'),
+ AllowedFilter::exact('status_id'),
+ AllowedFilter::exact('on_time'),
+ AllowedFilter::exact('ready_to_test'),
+ AllowedFilter::exact('tagTask.tag_id'),
+ AllowedFilter::scope('priority_min'),
+ AllowedFilter::scope('priority_max'),
+ AllowedFilter::scope('creates_before'),
+ AllowedFilter::scope('creates_after'),
+ AllowedFilter::scope('creates_in'),
+ AllowedFilter::scope('updates_before'),
+ AllowedFilter::scope('updates_after'),
+ AllowedFilter::scope('updates_in'),
+ AllowedFilter::scope('spent_from'),
+ AllowedFilter::scope('spent_to'),
+ AllowedFilter::scope('estimated_from'),
+ AllowedFilter::scope('estimated_to'),
+ AllowedFilter::scope('starts_before'),
+ AllowedFilter::scope('starts_after'),
+ AllowedFilter::scope('starts_in'),
+ AllowedFilter::scope('finish_before'),
+ AllowedFilter::scope('finish_after'),
+ AllowedFilter::scope('finish_in'),
+ AllowedFilter::scope('completes_before'),
+ AllowedFilter::scope('completes_after'),
+ AllowedFilter::scope('completes_in'),
+ AllowedFilter::scope('over_spent_from'),
+ AllowedFilter::scope('over_spent_to'),
+ AllowedFilter::scope('due_date_before'),
+ AllowedFilter::scope('due_date_after'),
+ AllowedFilter::scope('due_date_in'),
+ AllowedFilter::scope('my_watching'),
+ AllowedFilter::scope('over_due'),
+ ]);
+ if (\request('_business_info')['info']['users'][\auth()->id()]['level'] != enum('levels.owner.id')) {
+ $requested_projects = isset(\request('filter')['project_id']) ?
+ array_unique(explode(',',\request('filter')['project_id'] ?? null )) :
+ null;
+ $requested_projects = collect($requested_projects)->keyBy(null)->toArray();
+ $project_ids = $this->myStateProjects($requested_projects);
+ $taskQ->where(function ($q) use ($project_ids) {
+ $q->whereIn('project_id', $project_ids['non_guest_ids'])
+ ->orWhere(function ($q) use ($project_ids) {
+ $q->whereIn('project_id', $project_ids['guest_ids'])
+ ->where('assignee_id', auth()->id());
+ });
+ });
+ }
+ return $taskQ;
+ }
+
+ public function myStateProjects($requested_projects)
+ {
+ $non_guest_ids = [];
+ $guest_ids = [];
+ $is_empty = empty($requested_projects);
+
+ foreach (\request('_business_info')['info']['projects'] as $p_id => $p) {
+
+ $level = \request('_business_info')['info']['projects'][$p_id]['members'][\auth()->id()]['level'];
+
+ if (( $is_empty || isset($requested_projects[$p_id]))
+ && $level > enum('levels.guest.id')) {
+ array_push($non_guest_ids, $p_id);
+ }
+ if (( $is_empty || isset($requested_projects[$p_id]))
+ && $level == enum('levels.guest.id')) {
+ array_push($guest_ids, $p_id);
+ }
+ }
+
+ return ['non_guest_ids' => $non_guest_ids, 'guest_ids' => $guest_ids];
+ }
+
+ public function store($business, $project, Request $request)
+ {
+ permit('projectTasks', ['project_id' => $project]);
+
+ $task = Task::create($request->merge(
+ ['business_id' => $business, 'project_id' => $project, 'creator_id' => \auth()->id()]
+ )->except(['_business_info', 'completed_at', 'on_time', 'ready_to_test']));
+
+
+ return new TaskResource($task);
+ }
+
+ public function storeTagTasks($tags, $task) {
+ $tagModels = [];
+ if (isset($tags)) {
+ foreach ($tags as $tag) {
+ array_push($tagModels,
+ new TagTask(['tag_id' => $tag, 'task_id' => $task->id])
+ );
+ }
+ $task->tags()->saveMany($tagModels);
+ }
+ }
+
+ /**
+ * Rule's:
+ * 1) guest's only can see self task
+ * 2) user is active in project
+ */
+ public function show($business, $project, $task)
+ {
+ $task = Task::findOrFail($task);
+ $project = $task->project_id;
+ permit('projectAccess', ['project_id' => $project]);
+ if (can('isDefiniteGuestInProject', ['project_id' => $project])){ // is guest in project (only guest)
+ return $task->assignee_id == \auth()->id() ?
+ new TaskResource($task) :
+ abort(Response::HTTP_METHOD_NOT_ALLOWED); // not allowed
+ } else {
+ return new TaskResource($task);
+ }
+ }
+
+ /**
+ * Rule's:
+ * 1) update assignee_id when not exist work time and active user
+ * 2) update approver_id when atLeast colleague
+ * 3) update ready_to_test when assignee_id == auth xor assignee_id == null and isAdmin
+ * 4) update tags
+ * 5) update completed_at when status in [done, close]
+ * 6) due_date before sprint end_time
+ */
+ public function update($business, $project, $task, Request $request)
+ {
+ permit('isProjectGuest', ['project_id' => $project]);
+ $taskModel = Task::where([['project_id', $project ], ['id', $task]])->firstOrFail();
+
+ if ($taskModel->assignee_id != \auth()->id() && can('isDefiniteGuestInProject', ['project_id' => $project])) {
+ permit('isDefiniteGuestInProject', ['project_id' => $project]);
+ }
+
+ if($request->filled('watchers')) {
+ $watchers = $taskModel->watchers ?? [];
+ if(!can('isDefiniteGuestInProject', ['project_id' => $project]) && !can('projectTasks', ['project_id' => $project])) {
+ if (array_search(auth()->id(), $watchers) !== false) {
+ // remove auth from watchers
+ $watchers = array_values(\array_diff($watchers, [auth()->id()]));
+ } else {
+ // add auth to watchers
+ $watchers = array_merge($watchers, [auth()->id()]);
+ }
+ }
+ if(can('projectTasks', ['project_id' => $project])) {
+ $watchers = array_intersect($watchers ?? [], $request->watchers);
+ }
+ $request->merge(['watchers' => $watchers]);
+ }
+
+ if (!can('projectTasks', ['project_id' => $project])) {
+ $request->replace($request->only(['_business_info', 'ready_to_test', 'status_id', 'watchers']));
+ }
+ if ($taskModel->assignee_id != \auth()->id()) {
+ $request->request->remove('ready_to_test');
+ }
+
+
+ if (isset(\request('_business_info')['workflows'][$request->workflow_id]['statuses'][$request->status_id]['state'])) {
+ $state = \request('_business_info')['workflows'][$request->workflow_id]['statuses'][$request->status_id]['state'];
+ if ($state == enum('status.states.close.id') || $state == enum('status.states.done.id')) {
+ //ToDo: is needed to check before state is done or close?
+ $request->merge([
+ 'completed_at' => Carbon::now(),
+ 'work_finish' => Work::where('task_id', $task)->latest()->first()->ended_at ?? null
+ ]);
+ if (isset($taskModel->due_date) && $taskModel->due_date < date('yy-m-d')) {
+ //check if before set due date and miss, we change on time flag
+ $request->merge(['on_time' => false]);
+ }
+ }
+ }
+ if (!$request->has('tags')) {
+ $request->merge(['tags' => []]);
+ }
+ $taskModel->update($request->except('_business_info'));
+ return new TaskResource($taskModel);
+ }
+
+ public function updateReadyToTest($business, $project, $task)
+ {
+ permit('isProjectGuest', ['project_id' => $project]);
+ $task = Task::where([['project_id', $project ], ['id', $task]])->firstOrFail();
+ if ($task->assignee_id == \auth()->id()) {
+ $task->update([
+ 'ready_to_test' => 1
+ ]);
+ } else {
+ return abort(Response::HTTP_FORBIDDEN); // not allowed
+ }
+ return $task->load(['tagTask'=> fn($q) => $q->select('id', 'tag_id', 'task_id'), 'works', 'comments']);
+ }
+
+ /**
+ * Rule's:
+ * 1) delete only not work time (soft delete)
+ */
+ public function destroy($task)
+ {
+ $taskModel = Task::findOrFail($task);
+ if (Work::where('task_id', $task)->exists()) {
+ return \response()->json(['task_id' => 'The task id cannot be deleted.'], Response::HTTP_UNPROCESSABLE_ENTITY);
+ }
+ $taskModel->delete();
+ return \response()->json(['message' => 'task deleted successfully.'], Response::HTTP_OK);
+ }
+
+ public function toggleWatcher($business, $task, $project =null)
+ {
+ permit('isProjectGuest', ['project_id' => $project]);
+ $taskModel = Task::findOrFail($task);
+ $watchers = $taskModel->watchers ?? [];
+
+ if (array_search(auth()->id(), $watchers) !== false) {
+ // remove auth from watchers
+ $new_watchers = array_values(\array_diff($watchers, [auth()->id()]));
+ } else {
+ // add auth to watchers
+ $new_watchers = array_merge($watchers, [auth()->id()]);
+ }
+
+ $taskModel->update([
+ 'watchers' => $new_watchers
+ ]);
+ return new TaskResource($taskModel);
+ }
+}
diff --git a/app/Http/Controllers/TaskFileController.php b/app/Http/Controllers/TaskFileController.php
new file mode 100644
index 0000000..b8bd77c
--- /dev/null
+++ b/app/Http/Controllers/TaskFileController.php
@@ -0,0 +1,87 @@
+id !== $project->business_id
+ || $project->id !== $task['project_id']
+// || $task['user_id']!== Auth::id()
+ ) {
+ \abort(Response::HTTP_UNAUTHORIZED);
+ }
+
+ return [$business, $project, $task];
+ }
+
+ public function index(int $business, int $project, int $task)
+ {
+ // check permissions
+ // owner project
+ // admin project
+ // colleague project
+ // guest or de active
+ // return files as file resource
+ [$business, $project, $task] = $this->checkBelonging($business, $project, $task);
+ return FileResource::collection($task->files ?? []);
+ }
+
+ public function sync(Request $request,int $business, int $project, int $task)
+ {
+ // different size and different validation
+ // validate
+ // validate the wallet is not so much in debt
+ // create record in the db
+ // put file in s3
+ // return file resource
+ [$business, $project, $task] = $this->checkBelonging($business,$project,$task);
+
+ $this->validate($request, [
+ 'files' => 'required|array',
+ 'files.*' => 'int',
+ ]);
+
+ $files = File::find($request->files)->each(function (File $file) {
+ if ($file->user_id !== Auth::id()) {
+ abort(Response::HTTP_UNAUTHORIZED);
+ }
+ });
+
+ // sync
+
+ return FileResource::collection($files);
+ }
+
+ public function download(int $business, int $project, int $task, int $file)
+ {
+ // requested file belongs to this project and this business
+ // check permisson
+ // create perma link or temp link
+ // return the file resource or stream it
+ [$business, $project, $task] = $this->checkBelonging($business, $project, $task);
+
+ $file = File::findOrFail($file);
+ if ($file->user_id !== Auth::id()) {
+ abort(Response::HTTP_UNAUTHORIZED);
+ }
+
+ return $file->getTemporaryLink();
+ }
+}
diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php
new file mode 100644
index 0000000..b8a4135
--- /dev/null
+++ b/app/Http/Controllers/UserController.php
@@ -0,0 +1,70 @@
+filled('search')){
+ $userQ = $userQ->where(function($query) use ($request) {
+ $query->where('email', 'like', '%'.$request->search.'%')
+ ->orWhere('username', 'like', '%'.$request->search.'%');
+ });
+ }
+
+ return $userQ->where('id', '!=', auth()->id())
+ ->whereNotIn('id', request('_business_info')['info']['users']->keys())
+ ->select('id', 'name', 'email', 'username')->take($limit)->get();
+ }
+
+ public function index(Request $request)
+ {
+ $userQ = QueryBuilder::for(User::class)
+ ->allowedFilters([
+ AllowedFilter::exact('id'),
+ ]);
+ return $userQ->select('id', 'name', 'email', 'username')->get();
+ }
+
+ public function show($user)
+ {
+ return User::select('id', 'name', 'email', 'username')->findOrFail($user);
+ }
+
+ public function update(Request $request, string $user)
+ {
+ $user = User::findOrFail($user);
+ $user->update($request->all());
+
+ return $user;
+ }
+
+ public function setAvatar(Request $request, string $user)
+ {
+ $user = User::findOrFail($user);
+ if ($request->hasFile('avatar')) {
+ $user->saveAsAvatar($request->file('avatar'));
+ }
+
+ return $user;
+ }
+
+ public function unSetAvatar(Request $request, string $user)
+ {
+ $user = User::findOrFail($user);
+ $user->deleteAvatar();
+ $user->refresh();
+
+ return $user;
+ }
+}
diff --git a/app/Http/Controllers/WorkController.php b/app/Http/Controllers/WorkController.php
new file mode 100644
index 0000000..d7f6d1f
--- /dev/null
+++ b/app/Http/Controllers/WorkController.php
@@ -0,0 +1,242 @@
+limit > 100 ? 10 : $request->limit;
+ $workQ = $this->indexFiltering($business)
+ ->when($request->filled('group'), function ($q) use ($request) {
+ return $request->group == 'user' ? $q->report() : $q->reportByDate();
+ });
+
+ return $request->filled('group') ? $workQ->get() :
+ $workQ->defaultSort('-id')
+ ->allowedSorts('id', 'started_at')->paginate($per_page);
+ }
+
+ public function indexFiltering($business)
+ {
+ $query = Work::where('works.business_id', $business);
+ $workQ = queryBuilder::for($query)
+ ->join('tasks', 'tasks.id', 'works.task_id')
+ ->select('works.*', 'tasks.title', 'tasks.sprint_id', 'tasks.system_id')
+ ->allowedFilters([
+ AllowedFilter::exact('project_id'),
+ AllowedFilter::exact('tasks.sprint_id', null, false),
+ AllowedFilter::exact('tasks.system_id', null, false),
+ AllowedFilter::exact('user_id'),
+ AllowedFilter::scope('started_at_in'),
+ AllowedFilter::scope('started_at'),
+ AllowedFilter::scope('spent_time_from'),
+ AllowedFilter::scope('spent_time_to'),
+ ]);
+ if (\request('_business_info')['info']['users'][\auth()->id()]['level'] != enum('levels.owner.id')) {
+ $requested_projects = isset(\request('filter')['project_id']) ?
+ array_unique(explode(',',\request('filter')['project_id'] ?? null )) :
+ null;
+ $requested_projects = collect($requested_projects)->keyBy(null)->toArray();
+ $project_ids = $this->myStateProjects($requested_projects);
+ $workQ->where(function ($q) use ($project_ids) {
+ $q->whereIn('project_id', $project_ids['non_guest_ids'])
+ ->orWhere(function ($q) use ($project_ids) {
+ $q->whereIn('project_id', $project_ids['guest_ids'])
+ ->where('assignee_id', auth()->id());
+ });
+ });
+ }
+ return $workQ;
+ }
+
+ public function myStateProjects($requested_projects)
+ {
+ $non_guest_ids = [];
+ $guest_ids = [];
+ $is_empty = empty($requested_projects);
+
+ foreach (\request('_business_info')['info']['projects'] as $p_id => $p) {
+
+ $level = \request('_business_info')['info']['projects'][$p_id]['members'][\auth()->id()]['level'];
+
+ if (( $is_empty || isset($requested_projects[$p_id]))
+ && $level > enum('levels.guest.id')) {
+ array_push($non_guest_ids, $p_id);
+ }
+ if (( $is_empty || isset($requested_projects[$p_id]))
+ && $level == enum('levels.guest.id')) {
+ array_push($guest_ids, $p_id);
+ }
+ }
+
+ return ['non_guest_ids' => $non_guest_ids, 'guest_ids' => $guest_ids];
+ }
+
+ public function show($business, $project, $task, $work)
+ {
+ permit('projectAccess', ['project_id' => $project]);
+ $work = Work::where([['project_id', $project ], ['task_id', $task], ['id', $work]])->firstOrFail();
+ if (can('isDefiniteGuestInProject', ['project_id' => $project])){ // is guest in project (only guest)
+ return $work->user_id == \auth()->id() ? $work : abort(Response::HTTP_FORBIDDEN); // not allowed
+ } else {
+ return $work;
+ }
+ }
+ /**
+ * Rule's:
+ * 1) only assignee_id can store work
+ * 2) started_at after task created_at
+ * 3) ended_at after started_at
+ * 4) not any work before in this work
+ */
+ public function store($business, $project, $task, Request $request)
+ {
+ $taskModel = Task::findOrFail($task);
+ if ($taskModel->assignee_id != auth()->id()) {
+ abort(Response::HTTP_FORBIDDEN); // not allowed
+ }
+
+ $end = Carbon::createFromFormat('Y-m-d H:i', $request->ended_at);
+ $start = Carbon::createFromFormat('Y-m-d H:i', $request->started_at);
+ $diff_in_min = $end->diffInMinutes($start);
+ $work = Work::create($request->merge([
+ 'business_id' => $business,
+ 'project_id' => $project,
+ 'task_id' => $task,
+ 'user_id' => auth()->id(),
+ 'minute_sum' => $diff_in_min,
+ 'task' => [
+ 'spent_time' => $taskModel->spent_time + $diff_in_min
+ ]
+ ])->except('_business_info'));
+ $taskModel->refresh();
+// $taskModel->update([
+// 'work_start' => Work::where('task_id', $taskModel->id)->orderBy('started_at')->first()->started_at ?? null,
+// 'spent_time' => $taskModel->spent_time + $diff_in_min
+// ]);
+ return $taskModel->load(['tags', 'works', 'comments']);
+ }
+
+ public function storeValidation($request, $taskModel)
+ {
+ $this->validate($request, [
+ 'message' => 'nullable|string|min:3|max:225',
+ 'started_at' => 'required|date_format:Y-m-d H:i|after:'.$taskModel->created_at,
+ 'ended_at' => 'required|date_format:Y-m-d H:i|after:started_at',
+ ]);
+ $state = \request('_business_info')['workflows'][$taskModel->workflow_id]['statuses'][$taskModel->status_id]['state'] ?? null;
+ if ($state == enum('status.states.close.id') || $state == enum('status.states.done.id')) {
+ throw ValidationException::withMessages(['task' => 'The selected task is invalid.']);
+ }
+ $works = Work::where([
+ ['ended_at', '>', $request->started_at],
+ ['ended_at', '<', $request->ended_at],
+ ])->orWhere([
+ ['started_at', '>', $request->started_at],
+ ['started_at', '<', $request->ended_at],
+ ])->orWhere([
+ ['started_at', '>=', $request->started_at],
+ ['ended_at', '<=', $request->ended_at],
+ ])->exists();
+ if ($works) {
+ throw ValidationException::withMessages(['work' => 'The selected work is invalid.']);
+ }
+ }
+
+ /**
+ * Rule's:
+ * 1) only assignee_id can store work
+ * 2) started_at after task created_at
+ * 3) ended_at after started_at
+ * 4) not any work before in this work
+ */
+ public function update($business, $project, $task, $work, Request $request)
+ {
+ $taskModel = Task::findOrFail($task);
+ $workModel = Work::findOrFail($work);
+ if ($taskModel->assignee_id != auth()->id()) {
+ abort(Response::HTTP_FORBIDDEN); // not allowed
+ }
+
+ $end = Carbon::createFromFormat('Y-m-d H:i', $request->ended_at);
+ $start = Carbon::createFromFormat('Y-m-d H:i', $request->started_at);
+ $new_diff_in_min = $end->diffInMinutes($start);
+ $old_diff_in_min = $workModel->minute_sum;
+ $workModel->update($request->merge([
+ 'business_id' => $business,
+ 'project_id' => $project,
+ 'task_id' => $task,
+ 'user_id' => auth()->id(),
+ 'minute_sum' => $new_diff_in_min,
+ 'task' => [
+ 'spent_time' => ($taskModel->spent_time - $old_diff_in_min) + $new_diff_in_min
+ ]
+ ])->except('_business_info'));
+ $taskModel->refresh();
+// $taskModel->update([
+// 'work_start' => Work::where('task_id', $taskModel->id)->orderBy('started_at')->first()->started_at ?? null,
+// 'spent_time' => ($taskModel->spent_time - $old_diff_in_min) + $new_diff_in_min
+// ]);
+ return $taskModel->load(['tags', 'works', 'comments']);
+ }
+
+ public function updateValidation($request, $taskModel, $workModel)
+ {
+ $this->validate($request, [
+ 'message' => 'nullable|string|min:3|max:225',
+ 'started_at' => 'nullable|date_format:Y-m-d H:i|after:'.$taskModel->created_at,
+ 'ended_at' => 'nullable|date_format:Y-m-d H:i|after:started_at',
+ ]);
+ //ToDo: is needed to check status is active or idea??
+ $works = false;
+ if ($request->filled('started_at') || $request->filled('ended_at')) {
+ $started_at = $request->started_at ?? $workModel->started_at->format('Y-m-d H:i');
+ $ended_at = $request->ended_at ?? $workModel->ended_at->format('Y-m-d H:i');
+ if (strtotime($ended_at) <= strtotime($started_at)) {
+ throw ValidationException::withMessages(['ended_at' => 'The ended at must be a date after started at.']);
+ }
+ $works = Work::where([
+ ['ended_at', '>', $started_at],
+ ['ended_at', '<', $ended_at],
+ ])->orWhere([
+ ['started_at', '>', $started_at],
+ ['started_at', '<', $ended_at],
+ ])->orWhere([
+ ['started_at', '>=', $started_at],
+ ['ended_at', '<=', $ended_at],
+ ])->where('id', '!=', $workModel->id)->exists();
+ $end = Carbon::createFromFormat('Y-m-d H:i', $ended_at);
+ $start = Carbon::createFromFormat('Y-m-d H:i', $started_at);
+ \request()->merge(['minute_sum' => $end->diffInMinutes($start)]);
+ }
+ if ($works) {
+ throw ValidationException::withMessages(['work' => 'The selected work is invalid.']);
+ }
+ }
+
+ public function destroy($business, $project, $task, $work)
+ {
+ $taskModel = Task::findOrFail($task);
+ $workModel = Work::findOrFail($work);
+ if ($taskModel->assignee_id != auth()->id()) {
+ abort(Response::HTTP_FORBIDDEN); // not allowed
+ }
+ $diff_in_min = $workModel->minute_sum;
+ $workModel->delete();
+ $taskModel->update([
+ 'work_start' => Work::where('task_id', $taskModel->id)->orderBy('started_at')->first()->started_at ?? null,
+ 'spent_time' => ($taskModel->spent_time - $diff_in_min)
+ ]);
+ return $taskModel->load(['tags', 'works','comments']);
+ }
+}
diff --git a/app/Http/Controllers/WorkflowController.php b/app/Http/Controllers/WorkflowController.php
new file mode 100644
index 0000000..beff817
--- /dev/null
+++ b/app/Http/Controllers/WorkflowController.php
@@ -0,0 +1,67 @@
+merge(['business_id' => $business])->except('_business_info'));
+// $statuses = collect($validatedData['statuses'])->map(function($status) use ($workflow, $business) {
+// $status['business_id'] = $business;
+// $status['workflow_id'] = $workflow->id;
+// return $status;
+// });
+// $statuses = $workflow->statuses()->createMany($statuses->toArray());
+ return Business::info($business, true);
+ }
+
+ public function update($business, $workflow, Request $request)
+ {
+ permit('businessWorkFlows');
+ $workflowModel = Workflow::findOrFail($workflow);
+ $workflowModel->update($request->except('_business_info'));
+ return Business::info($business, true);
+ }
+
+ public function syncStatus($business, $workflowModel)
+ {
+ $old_statuses_name = array_keys(collect(\request('_business_info')['workflows'][$workflowModel->id]['statuses'])->keyBy('name')->toArray());
+ $new_statuses_name = array_keys(collect(\request('statuses'))->keyBy('name')->toArray());
+ $removed_statuses_name = array_diff(array_merge($old_statuses_name, $new_statuses_name), $new_statuses_name);
+
+ foreach ($removed_statuses_name as $status_name) {
+ //delete all statuses that removed name's from request->statuses
+ $workflowModel->statuses()->where('name', $status_name)->first()->delete();
+ }
+
+ foreach (request('statuses') as $status) {
+ //sync another statuses
+ $workflowModel->statuses()
+ ->updateOrCreate(
+ ['name' => $status['name'], 'business_id' => $business, 'workflow_id' => $workflowModel->id],
+ ['state' => $status['state'], 'order' => $status['order']]
+ );
+ }
+
+ return $workflowModel;
+ }
+
+ public function delete($business, $workflow)
+ {
+ permit('businessWorkFlows');
+ $workflow = Workflow::findOrFail($workflow);
+ foreach ($workflow->statuses as $status) {
+ //delete all statuses related to this workflow
+ $status->delete();
+ }
+ $workflow->delete();
+ return Business::info($business, true);
+ }
+
+}
diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php
index 30020a5..4bd9d18 100644
--- a/app/Http/Kernel.php
+++ b/app/Http/Kernel.php
@@ -2,6 +2,7 @@
namespace App\Http;
+use App\Utilities\Middlewares\BindBusinessInfo;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
class Kernel extends HttpKernel
@@ -62,5 +63,6 @@ class Kernel extends HttpKernel
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
+ 'bindBusiness' => BindBusinessInfo::class,
];
}
diff --git a/app/Http/Resources/BusinessResource.php b/app/Http/Resources/BusinessResource.php
new file mode 100644
index 0000000..b448dc5
--- /dev/null
+++ b/app/Http/Resources/BusinessResource.php
@@ -0,0 +1,34 @@
+ 'user',
+ '_resource' => 'businesses',
+ ];
+
+ foreach ($this->getAttributes() as $attribute => $value) {
+ switch ($attribute) {
+ case 'name':
+ case 'slug':
+ case 'wallet':
+ case 'files_volume':
+ case 'cache':
+ case 'calculated_at':
+ case 'created_at':
+ case 'updated_at':
+ case 'deleted_at':
+ $resource[$attribute] = $value;
+ break;
+ }
+ }
+
+ return $resource;
+ }
+}
diff --git a/app/Http/Resources/CommentResource.php b/app/Http/Resources/CommentResource.php
new file mode 100644
index 0000000..eecc44a
--- /dev/null
+++ b/app/Http/Resources/CommentResource.php
@@ -0,0 +1,34 @@
+ 'task',
+ '_resource' => 'comment',
+ ];
+
+ foreach ($this->getAttributes() as $attribute => $value) {
+ switch ($attribute) {
+ case 'task_id' :
+ case 'user_id' :
+ case 'body' :
+ $resource[$attribute] = $value;
+ break;
+ }
+ }
+
+ return $resource;
+ }
+}
diff --git a/app/Http/Resources/FileResource.php b/app/Http/Resources/FileResource.php
new file mode 100644
index 0000000..bc99db1
--- /dev/null
+++ b/app/Http/Resources/FileResource.php
@@ -0,0 +1,35 @@
+ 'user',
+ '_resource' => 'files',
+ ];
+
+ foreach ($this->getAttributes() as $attribute => $value) {
+ switch ($attribute) {
+ case 'user_id':
+ case 'business_id':
+ case 'project_id':
+ case 'disk':
+ case 'original_name':
+ case 'name':
+ case 'extension':
+ case 'mime':
+ case 'size':
+ case 'description':
+ $resource[$attribute] = $value;
+ break;
+ }
+ }
+
+ return $resource;
+ }
+}
diff --git a/app/Http/Resources/FingerprintResource.php b/app/Http/Resources/FingerprintResource.php
new file mode 100644
index 0000000..2c2eded
--- /dev/null
+++ b/app/Http/Resources/FingerprintResource.php
@@ -0,0 +1,34 @@
+ 'user',
+ '_resource' => 'fingerprints',
+ ];
+
+ foreach ($this->getAttributes() as $attribute => $value) {
+ switch ($attribute) {
+ case 'id':
+ case 'agent':
+ case 'ip':
+ case 'os':
+ case 'latitude':
+ case 'longitude':
+ case 'token':
+ $resource[$attribute] = $value;
+ break;
+ }
+ }
+
+ return $resource;
+ }
+}
diff --git a/app/Http/Resources/ProjectResource.php b/app/Http/Resources/ProjectResource.php
new file mode 100644
index 0000000..8800864
--- /dev/null
+++ b/app/Http/Resources/ProjectResource.php
@@ -0,0 +1,38 @@
+ 'user',
+ '_resource' => 'project',
+ ];
+
+ foreach ($this->getAttributes() as $attribute => $value) {
+ switch ($attribute) {
+ case 'name':
+ case 'business_id':
+ case 'slug':
+ case 'private':
+ case 'budget':
+ case 'start':
+ case 'finish':
+ case 'created_at':
+ case 'updated_at':
+ case 'archived_at':
+ case 'deleted_at':
+ $resource[$attribute] = $value;
+ break;
+ }
+ }
+
+ return $resource;
+ }
+}
diff --git a/app/Http/Resources/TaskCollection.php b/app/Http/Resources/TaskCollection.php
new file mode 100644
index 0000000..be5944b
--- /dev/null
+++ b/app/Http/Resources/TaskCollection.php
@@ -0,0 +1,23 @@
+ TaskResource::collection($this->collection),
+ 'now' => Carbon::now()->toDateString()
+ ];
+ }
+}
diff --git a/app/Http/Resources/TaskResource.php b/app/Http/Resources/TaskResource.php
new file mode 100644
index 0000000..94491fb
--- /dev/null
+++ b/app/Http/Resources/TaskResource.php
@@ -0,0 +1,30 @@
+getAttributes() as $attribute => $value) {
+ $resource[$attribute] = $value;
+ if ($attribute == 'watchers') {
+ $resource[$attribute] = json_decode($value);
+ }
+ }
+
+ $resource['tags'] = $this->tags()->pluck('tag_id')->toArray();
+ $resource['works'] = $this->works;
+ $resource['comments'] = $this->comments;
+
+ return $resource;
+ }
+}
diff --git a/app/Http/Resources/TransactionResource.php b/app/Http/Resources/TransactionResource.php
new file mode 100644
index 0000000..89b07df
--- /dev/null
+++ b/app/Http/Resources/TransactionResource.php
@@ -0,0 +1,32 @@
+ 'user',
+ '_resource' => 'transactions',
+ ];
+
+ foreach ($this->getAttributes() as $attribute => $value) {
+ switch ($attribute) {
+ case 'user_id':
+ case 'business_id':
+ case 'amount':
+ case 'succeeded':
+ case 'options':
+ case 'created_at':
+ case 'updated_at':
+ $resource[$attribute] = $value;
+ break;
+ }
+ }
+
+ return $resource;
+ }
+}
diff --git a/app/Http/Resources/UserResource.php b/app/Http/Resources/UserResource.php
new file mode 100644
index 0000000..5bf3f1f
--- /dev/null
+++ b/app/Http/Resources/UserResource.php
@@ -0,0 +1,34 @@
+ 'user',
+ '_resource' => 'user',
+ ];
+ foreach ($this->getAttributes() as $attribute => $value) {
+ switch ($attribute) {
+ case 'id':
+ case 'name':
+ case 'mobile':
+ case 'email':
+ case 'created_at':
+ case 'updated_at':
+ $resource[$attribute] = $value;
+ break;
+ }
+ }
+
+
+ $resource['includes']['fingerprints'] = $this->whenLoaded('fingerprints', function () {
+ return FingerprintResource::collection($this->fingerprints);
+ });
+
+ return $resource;
+ }
+}
diff --git a/app/Listeners/ActivityRegistration.php b/app/Listeners/ActivityRegistration.php
new file mode 100644
index 0000000..90b97b8
--- /dev/null
+++ b/app/Listeners/ActivityRegistration.php
@@ -0,0 +1,73 @@
+message));
+ $message = json_decode($event->message);
+ Activity::create([
+ 'business_id' => $message->business,
+ 'project_id' => $message->project,
+ 'actor_id' => $message->auth,
+ 'system_id' => $message->data->system_id,
+ 'workflow_id' => $message->data->workflow_id,
+ 'status_id' => $message->data->status_id,
+ 'sprint_id' => $message->data->sprint_id,
+ 'task_id' => $message->data->task_id ?? null,
+ 'subject_id' => $message->data->subject_id ?? null,
+ 'user_id' => $message->data->user_id,
+ 'crud_id' => $message->data->crud_id,
+ 'table_id' => enum('tables.'.$message->data->table_name.'.id'),
+ 'original' => $message->data->original,
+ 'diff' => $message->data->diff,
+ ]);
+ }
+}
diff --git a/app/Listeners/BusinessUpdateListener.php b/app/Listeners/BusinessUpdateListener.php
new file mode 100644
index 0000000..3ef51cb
--- /dev/null
+++ b/app/Listeners/BusinessUpdateListener.php
@@ -0,0 +1,32 @@
+message;
+
+ $wallet = $payload?->data?->diff?->wallet;
+ $owners = Business::findOrFail($payload->business)->owners;
+ $message = ['body' => 'Test'];
+
+ if ($wallet < 0) {
+ Notification::send($owners, new MailNotification($message));
+ Notification::send($owners, new DBNotification($message));
+ }
+ }
+}
diff --git a/app/Listeners/BusinessUserCreateNotif.php b/app/Listeners/BusinessUserCreateNotif.php
new file mode 100644
index 0000000..29f4456
--- /dev/null
+++ b/app/Listeners/BusinessUserCreateNotif.php
@@ -0,0 +1,93 @@
+message;
+ if ($payload->data->original->level === enum('levels.inactive.id')) {
+ // When user level in business is zero, probably user added to business by system
+ // And not necessary send notification to stockholders
+ return;
+ }
+ $new_user = User::findOrFail($payload->data->original->user_id);
+ $owners = Business::findOrFail($payload->business)->owners()->where('id', '!=', $new_user->id)->get();
+
+ $notif = $this->makeNotif($payload,['business' => request('_business_info')['name'], 'user' => $new_user->name]);
+
+ $users = $owners->prepend($new_user);
+
+ $this->sendNotifications($users, $notif);
+ }
+
+ /**
+ * Make notification object
+ *
+ * @param $payload
+ * @param array $options
+ * @return array
+ */
+ public function makeNotif($payload, $options = []) {
+ return [
+ 'greeting' => $this->getMessageLine($payload, 'greeting'),
+ 'subject' => $this->getMessageLine($payload, 'subject'),
+ 'title' => $this->getMessageLine($payload, 'title'),
+ 'body' => $this->getMessageLine($payload, 'body', $options)
+ ];
+ }
+
+ /**
+ * Fetch message from notifications lang file
+ *
+ * @param $payload
+ * @param $key
+ * @param null $options
+ * @return array|\Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\Translation\Translator|string|null
+ *
+ */
+ public function getMessageLine($payload, $key, $options = [])
+ {
+ return __('notification.'.$payload->data->table_name.'.'.enum('cruds.inverse.'.$payload->data->crud_id.'.singular_name').'.'.$key, $options);
+ }
+
+ /**
+ * Call notifications
+ *
+ * @param $users
+ * @param $notif
+ */
+ public function sendNotifications($users, $notif) {
+ Notification::send($users, new MailNotification($notif));
+ Notification::send($users, new DBNotification($notif));
+ Notification::send($users, new FcmNotification($notif));
+ }
+}
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/ProjectUserCreateNotif.php b/app/Listeners/ProjectUserCreateNotif.php
new file mode 100644
index 0000000..add26cb
--- /dev/null
+++ b/app/Listeners/ProjectUserCreateNotif.php
@@ -0,0 +1,93 @@
+message;
+
+ $new_user = User::findOrFail($payload->data->original->user_id);
+ $project = Project::findOrFail($payload->data->original->project_id);
+ $owners_id = request('_business_info')['info']['projects'][$project->id]['members']->reject(function ($item, $key) use ($new_user) {
+ return $item['level'] < enum('levels.owner.id') || $key === $new_user->id;
+ })->toArray();
+
+ $owners = User::whereIn('id', array_keys($owners_id))->get();
+
+ $notif = $this->makeNotif($payload,['project' => $project->name, 'user' => $new_user->name]);
+
+ $users = $owners->prepend($new_user);
+
+ $this->sendNotifications($users, $notif);
+ }
+
+ /**
+ * Make notification object
+ *
+ * @param $payload
+ * @param array $options
+ * @return array
+ */
+ public function makeNotif($payload, $options = []) {
+ return [
+ 'greeting' => $this->getMessageLine($payload, 'greeting'),
+ 'subject' => $this->getMessageLine($payload, 'subject'),
+ 'title' => $this->getMessageLine($payload, 'title'),
+ 'body' => $this->getMessageLine($payload, 'body', $options)
+ ];
+ }
+
+ /**
+ * Fetch message from notifications lang file
+ *
+ * @param $payload
+ * @param $key
+ * @param null $options
+ * @return array|\Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\Translation\Translator|string|null
+ *
+ */
+ public function getMessageLine($payload, $key, $options = [])
+ {
+ return __('notification.'.$payload->data->table_name.'.'.enum('cruds.inverse.'.$payload->data->crud_id.'.singular_name').'.'.$key, $options);
+ }
+
+ /**
+ * Call notifications
+ *
+ * @param $users
+ * @param $notif
+ */
+ public function sendNotifications($users, $notif) {
+ Notification::send($users, new MailNotification($notif));
+ Notification::send($users, new DBNotification($notif));
+ Notification::send($users, new FcmNotification($notif));
+ }
+}
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/Listeners/TaskCreateNotif.php b/app/Listeners/TaskCreateNotif.php
new file mode 100644
index 0000000..3bba2d8
--- /dev/null
+++ b/app/Listeners/TaskCreateNotif.php
@@ -0,0 +1,104 @@
+message;
+
+ if ($payload->data->original->assignee_id !== null) {
+ $this->assigneeNotifHandler($payload);
+ }
+ if ($payload->data->original->approver_id !== null) {
+ $this->approverNotifHandler($payload);
+ }
+ }
+
+ public function assigneeNotifHandler($payload) {
+ $user = User::findOrFail($payload->data->original->assignee_id);
+ $task = Task::findOrFail($payload->data->original->id);
+
+ $notif = $this->makeNotif($payload, 'assignee', ['task' => $task->title]);
+
+ $this->sendNotifications($user, $notif);
+ }
+
+ public function approverNotifHandler($payload) {
+ $user = User::findOrFail($payload->data->original->approver_id);
+ $task = Task::findOrFail($payload->data->original->id);
+
+ $notif = $this->makeNotif($payload, 'approver', ['task' => $task->title]);
+
+ $this->sendNotifications($user, $notif);
+ }
+
+ /**
+ * Make notification object
+ *
+ * @param $payload
+ * @param null $route
+ * @param array $options
+ * @return array
+ */
+ public function makeNotif($payload, $route = null, $options = []) {
+ $route = $route == null ? "" : $route.'.';
+ return [
+ 'greeting' => $this->getMessageLine($payload, $route.'greeting'),
+ 'subject' => $this->getMessageLine($payload, $route.'subject'),
+ 'title' => $this->getMessageLine($payload, $route.'title'),
+ 'body' => $this->getMessageLine($payload, $route.'body', $options)
+ ];
+ }
+
+ /**
+ * Fetch message from notifications lang file
+ *
+ * @param $payload
+ * @param $key
+ * @param null $options
+ * @return array|\Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\Translation\Translator|string|null
+ *
+ */
+ public function getMessageLine($payload, $key, $options = [])
+ {
+ return __('notification.'.$payload->data->table_name.'.'.enum('cruds.inverse.'.$payload->data->crud_id.'.singular_name').'.'.$key, $options);
+ }
+
+ /**
+ * Call notifications
+ *
+ * @param $users
+ * @param $notif
+ */
+ public function sendNotifications($users, $notif) {
+ Notification::send($users, new MailNotification($notif));
+ Notification::send($users, new DBNotification($notif));
+ Notification::send($users, new FcmNotification($notif));
+ }
+}
diff --git a/app/Listeners/TaskUpdateNotif.php b/app/Listeners/TaskUpdateNotif.php
new file mode 100644
index 0000000..118036c
--- /dev/null
+++ b/app/Listeners/TaskUpdateNotif.php
@@ -0,0 +1,155 @@
+message;
+ if (isset($payload->data->diff->assignee_id)) {
+ $this->assigneeNotifHandler($payload);
+ }
+ if (isset($payload->data->diff->approver_id)) {
+ $this->approverNotifHandler($payload);
+ }
+ if (isset($payload->data->diff->completed_at)) {
+ $this->completedNotifHandler($payload);
+ }
+ if (isset($payload->data->diff->ready_to_test)) {
+ $this->readyNotifHandler($payload);
+ }
+ }
+
+ public function assigneeNotifHandler($payload) {
+ $user = User::findOrFail($payload->data->diff->assignee_id);
+ $task = Task::findOrFail($payload->data->original->id);
+
+ $notif = $this->makeNotif($payload, 'assignee', ['task' => $task->title]);
+
+ $this->sendNotifications($user, $notif);
+ }
+
+ public function approverNotifHandler($payload) {
+ $user = User::findOrFail($payload->data->diff->approver_id);
+ $task = Task::findOrFail($payload->data->original->id);
+
+ $notif = $this->makeNotif($payload, 'approver', ['task' => $task->title]);
+
+ $this->sendNotifications($user, $notif);
+ }
+
+ public function completedNotifHandler($payload){
+ $task = Task::findOrFail($payload->data->original->id);
+ $user_ids = array_filter([
+ $task->approver_id,
+ $task->assignee_id,
+ $task->creator_id
+ ]);
+ $user_ids = array_unique(array_merge($user_ids, $task->watchers));
+
+ $users = User::whereIn('id', $user_ids)->where('id', '!=', auth()->id())->get();
+
+ $notif = $this->makeNotif($payload, 'completed', ['task' => $task->title]);
+
+ $this->sendNotifications($users, $notif);
+ }
+
+ public function readyNotifHandler($payload)
+ {
+ $task = Task::findOrFail($payload->data->original->id);
+ $user_ids = array_unique(array_filter([
+ $task->approver_id,
+ $task->assignee_id,
+ $task->creator_id
+ ]));
+
+ $users = User::whereIn('id', $user_ids)->where('id', '!=', auth()->id())->get();
+
+ $notif = $this->makeNotif($payload, 'ready', ['task' => $task->title]);
+
+ $this->sendNotifications($users, $notif);
+ }
+
+ /**
+ * Make notification object
+ *
+ * @param $payload
+ * @param null $route
+ * @param array $options
+ * @return array
+ */
+ public function makeNotif($payload, $route = null, $options = []) {
+ $route = $route == null ? "" : $route.'.';
+ return [
+ 'greeting' => $this->getMessageLine($payload, $route.'greeting'),
+ 'subject' => $this->getMessageLine($payload, $route.'subject'),
+ 'title' => $this->getMessageLine($payload, $route.'title'),
+ 'body' => $this->getMessageLine($payload, $route.'body', $options)
+ ];
+ }
+
+ /**
+ * Fetch message from notifications lang file
+ *
+ * @param $payload
+ * @param $key
+ * @param null $options
+ * @return array|\Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\Translation\Translator|string|null
+ *
+ */
+ public function getMessageLine($payload, $key, $options = [])
+ {
+ return __('notification.'.$payload->data->table_name.'.'.enum('cruds.inverse.'.$payload->data->crud_id.'.singular_name').'.'.$key, $options);
+ }
+
+ /**
+ * Call notifications
+ *
+ * @param $users
+ * @param $notif
+ */
+ public function sendNotifications($users, $notif) {
+// switch ($level) {
+// case "emergency":
+//// Notification::send($users, new SmsNotification($notif));
+// case "critical":
+// Notification::send($users, new MailNotification($notif));
+// case "high":
+// Notification::send($users, new DBNotification($notif));
+// case "medium":
+// Notification::send($users, new FcmNotification($notif));
+// case "low":
+//// Notification::send($users, new SocketNotification($notif));
+// default:
+//// Notification::send($users, new SocketNotification($notif));
+// }
+ Notification::send($users, new MailNotification($notif));
+ Notification::send($users, new DBNotification($notif));
+ Notification::send($users, new FcmNotification($notif));
+ }
+}
diff --git a/app/Models/Activity.php b/app/Models/Activity.php
new file mode 100644
index 0000000..ed20699
--- /dev/null
+++ b/app/Models/Activity.php
@@ -0,0 +1,38 @@
+ 'array',
+ 'diff' => 'array',
+ ];
+
+ public function scopeCreatesBefore($query, $date)
+ {
+ return $query->whereDate('created_at', '<=', Carbon::parse($date));
+ }
+ public function scopeCreatesAfter($query, $date)
+ {
+ return $query->whereDate('created_at', '>=', Carbon::parse($date));
+ }
+ public function scopeCreatesIn($query, $days)
+ {
+ return $days != "" ?
+ $query->whereDate('created_at', '>=', Carbon::now()->modify('-'.$days.' day')->toDate()) :
+ $query;
+ }
+
+}
diff --git a/app/Models/Business.php b/app/Models/Business.php
new file mode 100644
index 0000000..dc03aef
--- /dev/null
+++ b/app/Models/Business.php
@@ -0,0 +1,487 @@
+ 'business_user'] // relations [ name => pivot ]
+ ];
+
+ public $detach_relation = false;
+
+ protected $casts = [
+ 'has_avatar' => 'boolean',
+ 'calculated_at' => 'datetime',
+ ];
+
+ public function getValueOf(?string $key)
+ {
+ $values = [
+ 'business_id' => $this->id,
+ 'workflow_id' => null,
+ 'project_id' => null,
+ 'sprint_id' => null,
+ 'status_id' => null,
+ 'system_id' => null,
+ 'user_id' => null,
+ 'task_id' => null,
+ 'subject_id' => $this->id,
+ ];
+
+ if ($key && isset($values, $key)) {
+ return $values[$key];
+ }
+
+ return $values;
+ }
+
+ public function rules()
+ {
+ return [
+ 'name' => 'bail|required|string|min:2|max:255',
+ 'color' => 'nullable|string|min:2|max:255',
+ 'slug' => ['bail', 'required', 'string', 'min:2', 'max:255', 'regex:/^[a-zA-Z]+[a-zA-Z\d-]*(.*[a-zA-Z\d])+$/',
+ Rule::unique($this->table, 'slug')->ignore($this->id)],
+ 'description' => 'nullable|string|min:2|max:1000',
+ ];
+ }
+
+ public function cost()
+ {
+ return $this->hasMany(Cost::class, 'business_id','id');
+ }
+
+ public function owners()
+ {
+ return $this->users()->wherePivot('level', '=', enum('levels.owner.id'));
+ }
+
+ public function members()
+ {
+ return $this->users()->wherePivot('owner', '!=', enum('levels.owner.id'));
+ }
+
+ public function users()
+ {
+ return $this->belongsToMany(
+ User::class, 'business_user', 'business_id', 'user_id',
+ 'id', 'id', __FUNCTION__
+ )
+ ->using(ReportableRelation::class)
+ ->withPivot(self::$permissions);
+ }
+
+ public function tags()
+ {
+ return $this->hasMany(Tag::class, 'business_id', 'id');
+ }
+
+ public function projects()
+ {
+ return $this->hasMany(Project::class, 'business_id', 'id');
+ }
+
+ public function systems()
+ {
+ return $this->hasMany(System::class, 'business_id', 'id');
+ }
+
+ public function workflows()
+ {
+ return $this->hasMany(Workflow::class, 'business_id', 'id');
+ }
+ public function sprints()
+ {
+ return $this->hasMany(Sprint::class, 'business_id', 'id');
+ }
+
+ public function statuses()
+ {
+ return $this->hasMany(Status::class, 'business_id', 'id');
+ }
+
+ public function files()
+ {
+ return $this->hasMany(File::class, 'user_id', 'id');
+ }
+
+ public function transactions()
+ {
+ return $this->hasMany(Transaction::class, 'business_id', 'id');
+ }
+
+ public function updateRelations()
+ {
+ // users relations
+ if (!empty($this->filled_relations['users']) || $this->detach_relation) {
+ $this->dirties['users'] = $this->users()->sync($this->filled_relations['users'], $this->detach_relation);
+ }
+ }
+
+
+ public static function info($businessId, $fresh = false)
+ {
+
+ $info = [];
+
+ $fresh = true;
+ if ($fresh){
+ Cache::forget('business_info'.$businessId);
+ }
+
+ if (Cache::has('business_info'.$businessId)) {
+ return Cache::get('business_info'.$businessId);
+ } else {
+ $business = self::findOrFail($businessId);
+
+ $tags = $business->tags()->select('id', 'label', 'color')->get()->keyBy('id');
+ $info['tags'] = $tags->pluck('id');
+
+ $workflows = $business->workflows()->select ('id', 'business_id', 'name','desc')->get()->keyBy('id')
+ ->load(['statuses'=> fn($q) => $q->select('id', 'business_id', 'workflow_id', 'name', 'state', 'order')])
+ ->map(fn($q) => [
+ 'id' => $q->id,
+ 'business_id' => $q->business_id,
+ 'name' => $q->name,
+ 'desc' => $q->desc,
+ 'statuses' => $q['statuses']->keyBy('id'),
+ ]);
+
+ $info['workflows'] = $workflows->map(fn($q) => ['statuses' => $q['statuses']->pluck('id')]);
+
+
+ $users = $business->users()->select('id', 'name', 'email', 'mobile', 'username')->with('media')->get()
+ ->keyBy('id')
+ ->map(fn($u) => [
+ 'id' => $u->id,
+ 'name' => $u->name,
+ 'email' => $u->email,
+ 'mobile' => $u->mobile,
+ 'username' => $u->get,
+ 'avatar' => $u->has_avatar,
+ 'level' => $u->pivot['level'],
+ ]);
+
+ $info['users'] = $users->map(fn($u) => [
+ 'level' => $u['level']
+ ]);
+
+ $projects = $business->projects()->get()->keyBy('id')->load([
+ 'members' => fn($q) => $q->select('id', 'level'),
+ 'systems' => fn($q) => $q->select('id', 'project_id', 'name'),
+ 'sprints' => fn($q) => $q->select('id', 'project_id', 'name', 'description', 'started_at', 'ended_at', 'active'),
+ 'media',
+ ]);
+
+ $info['projects'] = $projects->map(function($q) use($users){
+ return [
+ 'avatar' => $q->has_avatar,
+ 'systems' => $q->systems->pluck('id'),
+ 'sprints' => $q->sprints->pluck('id'),
+ 'members' => $users->keyBy('id')->map(function($u, $uid) use ($q){
+ $project_user = $q->members->firstWhere('id', $uid);
+ return $project_user? ['level' => $project_user->pivot->level, 'is_direct' => true]:
+ ['level' => $q->private && $u['level'] != enum('levels.owner.id') ? enum('levels.inactive.id') : $u['level'],
+ 'is_direct'=> $project_user ? true : false];
+ })
+ ];
+ });
+
+ $business_info = array_merge(
+ $business->only('id', 'name', 'slug', 'color','wallet', 'files_volume'),
+ ['avatar' => $business->has_avatar],
+ compact(
+ 'info',
+ 'tags',
+ 'workflows',
+ 'users',
+ 'projects'
+ )
+ );
+
+ Cache::put('business_info'.$businessId , $business_info, config('app.cache_ttl'));
+
+ return $business_info;
+ }
+
+
+ }
+
+ public static function stats()
+ {
+ return [
+ 'users' => [
+ 10 => [
+ 'statuses' => [
+ 10 => 20,
+ 30 => 40
+ ],
+ 'workflows' => [
+ 10 => 20,
+ 30 => 40
+ ],
+ 'tags' => [
+ 10 => 20,
+ 30 => 40
+ ],
+ 'project' => [
+ 10 => 20,
+ 30 => 40
+ ],
+
+ 'sprints' => [
+ 10 => 20,
+ 30 => 40
+ ],
+ 'works' => [
+ ],
+ '__subsystems' => [
+ 10 => 20,
+ 30 => 40
+ ],
+ ]
+ ],
+ 'workflows' => [
+ 50 => [
+ 'statuses' => [
+ 20 => 50
+ ],
+
+ ]
+ ],
+ 'statuses' => [
+ 10 => [
+ 'users' => [
+
+ ],
+ 'projects' => [
+
+ ]
+ ]
+ ],
+ 'sprints' => [
+ 10 => [
+ 'statuses' => [
+ 10 => [
+ 10 => 1
+ ]
+ ]
+ ]
+ ]
+ ];
+ }
+
+ public function reportActivity()
+ {
+
+ }
+
+
+ public static function nuxtInfo($businessId)
+ {
+ if (empty($businessId)){
+ return null;
+ }
+
+ Cache::forget('business_nuxt_info' . $businessId);
+ return Cache::rememberForever('business_nuxt_info' . $businessId, function () use ($businessId) {
+
+ $business = self::with([
+ 'projects.members' => fn($q) => $q->select('id', 'name'),
+ 'projects',
+ 'tags',
+ 'workflows.statuses',
+ 'workflows',
+ 'statuses',
+ 'users',
+ ])->findOrFail($businessId);
+
+
+ $globals = [];
+ $business->users->each(function ($u) use (&$globals) {
+ $globals[$u->id] = $u->pivot->owner == true ? ['owner' => true] : $u->pivot->only(self::$permissions);
+ });
+
+
+ $projects = [];
+ $business->projects->each(function ($p) use (&$projects, &$globals) {
+
+ });
+
+
+ $business->setRelation('projects', collect($projects));
+ $business->setRelation('users', collect($globals));
+
+ return $business;
+ });
+
+ }
+
+
+ public static function infoOld($businessId)
+ {
+
+ if (empty($businessId))
+ return null;
+
+
+ Cache::forget('business_info' . $businessId);
+ return Cache::rememberForever('business_info' . $businessId, function () use ($businessId) {
+ $ob = [];
+ $business = self::with([
+ 'projects.members' => fn($q) => $q->select('id', 'name'),
+ 'projects' => fn($q) => $q->select('id', 'business_id', 'private'),
+ 'tags' => fn($q) => $q->select('id', 'business_id', 'label'),
+ 'sprints' => fn($q) => $q->select('id','business_id','name', 'active'),
+ 'workflows.statuses' => fn($q) => $q->select('id', 'name'),
+ 'workflows' => fn($q) => $q->select('id', 'business_id', 'name'),
+ 'statuses' => fn($q) => $q->select('id', 'business_id', 'name', 'state'),
+ 'users' => fn($q) => $q->select('id', 'name'),
+ ])->findOrFail($businessId);
+
+
+ // permissions in business
+ $permissions = [];
+ $business->users->each(function ($user) use (&$permissions) {
+ $permissions[$user->id] = $user->pivot->only(['owner']);
+ $permissions[$user->id]['global_PA'] = $permissions[$user->id]['owner'] == true ?
+ enum('roles.manager.id') : $user->pivot->PA;
+ $permissions[$user->id]['global_FA'] = $permissions[$user->id]['owner'] == true ?
+ enum('roles.manager.id') : $user->pivot->FA;
+ $permissions[$user->id]['PA'] = [];
+ $permissions[$user->id]['FA'] = [];
+ });
+
+
+ //projects
+ $projects = [];
+ $business->projects->each(function ($p) use (&$permissions, $business, &$projects) {
+
+ $business->users->each(function ($user) use (&$permissions, $p) {
+ $PA = null;
+ $FA = null;
+
+ if ($permissions[$user->id]['owner'] == true) {
+ $PA = enum('roles.manager.id');
+ $FA = enum('roles.manager.id');
+ } else if (!empty($project_user = $p->getPermissions($user->id))) {
+ $PA = $project_user->pivot->PA;
+ $FA = $project_user->pivot->FA;
+ } else if (empty($p->getPermissions($user->id))) {
+ $PA = $p->private == false ? $permissions[$user->id]['global_PA'] : enum('roles.hidden.id') ;
+ $FA = $p->private == false ? $permissions[$user->id]['global_FA'] : enum('roles.hidden.id');
+ }
+
+
+ if ($PA !== null && $FA !== null) {
+ $permissions[$user->id]['PA'][$p->id] = $PA;
+ $permissions[$user->id]['FA'][$p->id] = $FA;
+ }
+ });
+
+ $projects[$p->id] ='';
+ });
+
+
+ //workflow
+ $workflows = [];
+ $business->workflows->each(function ($w) use (&$workflows) {
+ $workflows[$w->id] = $w->statuses->pluck('id');
+ });
+
+
+ $ob['tags'] = $business->tags->pluck('id');
+ $ob['statuses'] = $business->statuses;
+ $ob['sprints'] = $business->sprints;
+ $ob['workflows'] = $workflows;
+ $ob['users'] = $permissions;
+ $ob['projects'] = $projects;
+
+ return collect($ob);
+ });
+
+ }
+
+ public function registerMediaCollections(): void
+ {
+ $this->addMediaCollection(static::COLLECTION_NAME)
+ ->acceptsMimeTypes([
+ 'image/jpeg',
+ 'image/png',
+ 'image/tiff',
+ 'image/gif',
+ ])
+ ->useDisk('public')
+ ->singleFile();
+ }
+
+ public function registerMediaConversions(Media $media = null): void
+ {
+ $this->addMediaConversion(static::CONVERSION_NAME)
+ ->width(200)
+ ->height(200)
+ ->queued()
+ ->nonOptimized()
+ ->performOnCollections(static::COLLECTION_NAME);
+ }
+
+
+ public function saveAsAvatar(UploadedFile $avatar): void
+ {
+ $this->addMedia($avatar)->toMediaCollection(static::COLLECTION_NAME);
+ $this->update([
+ 'has_avatar' => true,
+ ]);
+ @unlink($this->getFirstMedia(static::COLLECTION_NAME)->getPath());
+ }
+
+ public function deleteAvatar(): void
+ {
+ $path = $this->getFirstMedia(static::COLLECTION_NAME)->getPath();
+ $this->getFirstMedia(static::COLLECTION_NAME)->delete();
+ $this->update([
+ 'has_avatar' => false,
+ ]);
+ @unlink($path);
+ }
+
+ public function getAvatarUrl(): ?string
+ {
+ if ($url = $this->getFirstMediaUrl(static::COLLECTION_NAME, static::CONVERSION_NAME)) {
+ return $url;
+ }
+
+ return null;
+ }
+}
diff --git a/app/Models/Comment.php b/app/Models/Comment.php
new file mode 100644
index 0000000..ea1c5f8
--- /dev/null
+++ b/app/Models/Comment.php
@@ -0,0 +1,19 @@
+ 'required|string|min:3|max:1000',
+ ];
+ }
+}
diff --git a/app/Models/Cost.php b/app/Models/Cost.php
new file mode 100644
index 0000000..98b2e63
--- /dev/null
+++ b/app/Models/Cost.php
@@ -0,0 +1,28 @@
+ 'array',
+ 'tax' => 'float',
+ ];
+
+ public function business()
+ {
+ return $this->belongsTo(Business::class, 'business_id');
+ }
+}
diff --git a/app/Models/File.php b/app/Models/File.php
new file mode 100644
index 0000000..2c4c56d
--- /dev/null
+++ b/app/Models/File.php
@@ -0,0 +1,75 @@
+ 'required',
+ 'business_id' => 'required',
+ 'project_id' => 'required',
+ 'original_name' => 'required',
+ 'name' => 'required',
+ 'extension' => 'required',
+ 'size' => 'required',
+ 'description' => 'nullable',
+ ];
+ }
+
+
+ public function user()
+ {
+ return $this->belongsTo(User::class, 'user_id','id','id',__FUNCTION__);
+ }
+
+ public function business()
+ {
+ return $this->belongsTo(Business::class, 'business_id', 'id', 'id', __FUNCTION__);
+ }
+
+ public function project()
+ {
+ return $this->belongsTo(Project::class, 'project_id', 'id', 'id', __FUNCTION__);
+ }
+
+ public function updateRelations()
+ {
+
+ }
+
+ public function reportActivity()
+ {
+
+ }
+
+ public function getPath()
+ {
+ return $this->business->id . \DIRECTORY_SEPARATOR . $this->project->id . DIRECTORY_SEPARATOR . $this->name;
+ }
+
+ public function getTemporaryLink()
+ {
+ return Storage::disk('s3')->temporaryUrl(
+ $this->getPath(),
+ \Carbon\Carbon::now()->addMinutes(15),
+ [
+ 'Content-Type' => $this->mime,
+ 'Content-Disposition' => $this->original_name,
+ ]
+ );
+ }
+}
diff --git a/app/Models/Fingerprint.php b/app/Models/Fingerprint.php
new file mode 100644
index 0000000..f6cb0d4
--- /dev/null
+++ b/app/Models/Fingerprint.php
@@ -0,0 +1,31 @@
+belongsTo(User::class, 'user_id', 'id', __FUNCTION__);
+ }
+
+ public function rules()
+ {
+ return [
+ 'user_id' => 'required|integer|exists:users,id',
+ 'agent' => 'required|string',
+ 'ip' => 'required|ip',
+ 'os' => 'required|string',
+ 'latitude' => 'required',
+ 'longitude' => 'required',
+ 'token' => 'required|string|min:60',
+ 'fcm_token' => 'nullable',
+ ];
+ }
+}
diff --git a/app/Models/Model.php b/app/Models/Model.php
new file mode 100644
index 0000000..e47e36d
--- /dev/null
+++ b/app/Models/Model.php
@@ -0,0 +1,237 @@
+action = static::CREATED;
+ });
+
+ static::updated(function ($model) {
+ $model->action = static::UPDATED;
+ });
+
+ static::deleted(function ($model) {
+ $model->action = static::DELETED;
+ });
+ }
+
+ /**
+ * @return void
+ * @throw \Exception
+ */
+ public function rules()
+ {
+ return [];
+ }
+
+ /**
+ *
+ *
+ * @param array $attributes
+ * @return void
+ */
+ public function validate(array $attributes = null)
+ {
+ $attributes = $attributes ?? $this->getAttributes();
+
+ /** @var Validator $validator */
+ $validator = app('validator')->make($attributes, $this->rules());
+
+ if ($validator->fails()) {
+ throw new ValidationException(
+ $validator,
+ new JsonResponse($validator->errors()->getMessages(), Response::HTTP_UNPROCESSABLE_ENTITY)
+ );
+ }
+ }
+
+ /**
+ * @return void
+ */
+ public function updateRelations()
+ {
+ }
+
+ /**
+ * @param string|null $key
+ * @return void
+ */
+ public function getValueOf(?string $key)
+ {
+ $values = [];
+
+ if ($key && isset($values, $key)) {
+ return $values[$key];
+ }
+
+ return $values;
+ }
+
+ protected function makeChanges()
+ {
+ if (empty($this->reportable)) {
+ return;
+ }
+
+ $changes = new Collection($this->getDirty());
+
+ // fillable * or field
+ $changes = $changes->filter(function ($value, $key) {
+ foreach ($this->reportable as $i => $name) {
+ if ($key === $name) {
+ return true;
+ }
+ }
+ return false;
+ });
+
+ if (($changes->isEmpty() && $this->action == static::UPDATED)) {
+ return;
+ }
+
+ return [
+ 'original' => $this->getOriginal() + $this->getAttributes(),
+ 'diff' => $changes->toArray(),
+ ];
+
+// return [
+// 'auth' => Auth::id(),
+// 'timestamp' => $this->freshTimestamp(),
+// 'business' => $this->getValueOf('business_id'),
+// 'info' => \request('_business_info')['info'] ?? null,
+// 'project' => $this->getValueOf('project_id'),
+// 'data' => [
+// 'sprint_id' => $this->getValueOf('sprint_id'),
+// 'system_id' => $this->getValueOf('system_id'),
+// 'workflow_id' => $this->getValueOf('workflow_id'),
+// 'status_id' => $this->getValueOf('status_id'),
+// 'user_id' => $this->getValueOf('user_id'),
+// 'table_name' => $this->getTable(),
+// 'crud_id' => $this->action,
+// 'original' => $this->getOriginal() + $this->getAttributes(),
+// 'diff' => $changes->toArray(),
+// ],
+// 'from' => env('CONTAINER_NAME'),
+// ];
+ }
+
+ protected function report($changes): void
+ {
+ if ($this->action == null){
+ return;
+ }
+ $payload = [
+ 'auth' => Auth::id(),
+ 'timestamp' => $this->freshTimestamp(),
+ 'business' => $this->getValueOf('business_id'),
+ 'info' => \request('_business_info') ?? null,
+ 'project' => $this->getValueOf('project_id'),
+ 'data' => [
+ 'sprint_id' => $this->getValueOf('sprint_id'),
+ 'system_id' => $this->getValueOf('system_id'),
+ 'workflow_id' => $this->getValueOf('workflow_id'),
+ 'status_id' => $this->getValueOf('status_id'),
+ 'task_id' => $this->getValueOf('task_id'),
+ 'subject_id' => $this->getValueOf('subject_id'),
+ 'user_id' => $this->getValueOf('user_id'),
+ 'table_name' => $this->getTable(),
+ 'crud_id' => $this->action,
+ 'original' => $changes['original'] + $this->getOriginal(),
+ 'diff' => $changes['diff'],
+ ],
+ 'from' => env('CONTAINER_NAME'),
+ ];
+
+ ModelSaved::dispatch(json_encode($payload));
+ }
+
+ /**
+ * @param array $options
+ * @return void
+ */
+ public function save(array $options = [])
+ {
+ // The validation function is called first
+ $this->validate();
+
+ // Then, because the relationships are set as attributes in this model
+ // we pre-enter their names in filled_relation attribute and store
+ // them in a temporary variable with a loop.
+ foreach ($this->fillable_relations as $relation) {
+ $this->filled_relations[$relation] = $this[$relation];
+ unset($this[$relation]);
+ }
+
+ // all of its action inside one transaction
+ // so if any of them failed the whole
+ // process rollbacked
+ DB::transaction(function () use ($options) {
+ // report to the activity aggregator
+ $changes = $this->makeChanges();
+
+ // save the model with it's attributes
+ parent::save($options);
+
+ // save the model with it's relationships
+ $this->updateRelations();
+
+ is_array($changes) ? $this->report($changes) : true;
+ }, 3);
+ }
+
+ public function delete()
+ {
+ $changes = $this->makeChanges();
+ parent::delete();
+ is_array($changes) ? $this->report($changes) : true;
+ }
+}
diff --git a/app/Models/Project.php b/app/Models/Project.php
new file mode 100644
index 0000000..62acd0a
--- /dev/null
+++ b/app/Models/Project.php
@@ -0,0 +1,206 @@
+ 'project_user']
+ ];
+
+ protected $fillable_relations = ['members'];
+
+ public $detach_relation = false;
+
+ public function rules()
+ {
+ return [
+ 'name' => 'required|string|min:2|max:225',
+ 'slug' => ['required', 'string', 'min:2', 'max:225',
+ Rule::unique($this->table, 'slug')
+ ->where('business_id', request('business_id'))
+ ->whereNull('deleted_at')
+ ->ignore($this->id)],
+ 'order' => 'nullable|numeric|min:0',
+ 'private' => 'nullable|boolean',
+ 'color' => 'nullable|string|min:2|max:255',
+ 'active' => 'nullable|boolean',
+ 'description' => 'nullable|string|min:2|max:1000',
+ // 'members' => empty($this->id) ? '' : 'required|array'
+ ];
+ }
+
+ protected $casts = [
+ 'private' => 'boolean',
+ 'start' => 'date',
+ 'finish' => 'date',
+ 'has_avatar' => 'boolean',
+ ];
+
+ public function getValueOf(?string $key)
+ {
+ $values = [
+ 'business_id' => $this->business_id,
+ 'project_id' => $this->id,
+ 'sprint_id' => null,
+ 'workflow_id' => null,
+ 'status_id' => null,
+ 'system_id' => null,
+ 'actor_id' => auth()->id(),
+ 'user_id' => null,
+ 'task_id' => null,
+ 'subject_id' => $this->id,
+ ];
+
+ if ($key && isset($values, $key)) {
+ return $values[$key];
+ }
+
+ return $values;
+ }
+ public function members()
+ {
+ $permissions = self::$permissions;
+ return $this->belongsToMany(User::class)->using(ReportableRelation::class)
+ ->withPivot($permissions);
+ }
+
+ public function owners()
+ {
+ return $this->members()->wherePivot('level', '=', enum('levels.owner.id'));
+ }
+
+ public function tasks()
+ {
+ return $this->hasMany(Task::class, 'project_id', 'id');
+ }
+
+ public function business()
+ {
+ return $this->belongsTo(Business::class, 'business_id', 'id');
+ }
+
+ public function systems()
+ {
+ return $this->hasMany(System::class, 'project_id', 'id');
+ }
+
+ public function sprints()
+ {
+ return $this->hasMany(Sprint::class, 'project_id', 'id');
+ }
+
+ public function files()
+ {
+ return $this->hasMany(File::class, 'user_id', 'id');
+ }
+
+ public function updateRelations()
+ {
+ // members
+// if (!empty($this->filled_relations['members']) || $this->detach_relation) {
+// $this->dirties['members'] = $this->members()->sync($this->filled_relations['members'], $this->detach_relation);
+// }
+ }
+
+ public function reportActivity()
+ {
+// foreach ($this->dirties as $name => $value) {
+// return \post('task', 'task/v1/log', [
+// 'user_id' => Auth::id(),
+// 'business_id' => $this->business_id,
+// 'loggable_id' => $this->id,
+// 'loggable_type' => $this->getTypeId(),
+// 'action' => $this->getAction(), // id of the action
+// 'data' => [$name => [
+// 'original' => $value['original'],
+// 'diff' => $value['diff'],
+// ]],
+// ]);
+// }
+ }
+
+ public function getPermissions($user_id)
+ {
+ return $this->members->where('id',$user_id)->first();
+ }
+
+ public function registerMediaCollections(): void
+ {
+ $this->addMediaCollection(static::COLLECTION_NAME)
+ ->acceptsMimeTypes([
+ 'image/jpeg',
+ 'image/png',
+ 'image/tiff',
+ 'image/gif',
+ ])
+ ->useDisk('public')
+ ->singleFile();
+ }
+
+ public function registerMediaConversions(Media $media = null): void
+ {
+ $this->addMediaConversion(static::CONVERSION_NAME)
+ ->width(200)
+ ->height(200)
+ ->queued()
+ ->nonOptimized()
+ ->performOnCollections(static::COLLECTION_NAME);
+ }
+
+
+ public function saveAsAvatar(UploadedFile $avatar): void
+ {
+ $this->addMedia($avatar)->toMediaCollection(static::COLLECTION_NAME);
+ $this->update([
+ 'has_avatar' => true,
+ ]);
+ @unlink($this->getFirstMedia(static::COLLECTION_NAME)->getPath());
+ }
+
+ public function deleteAvatar(): void
+ {
+ $path = $this->getFirstMedia(static::COLLECTION_NAME)->getPath();
+ $this->getFirstMedia(static::COLLECTION_NAME)->delete();
+ $this->update([
+ 'has_avatar' => false,
+ ]);
+ @unlink($path);
+ }
+
+ public function getAvatarUrl(): ?string
+ {
+ if ($url = $this->getFirstMediaUrl(static::COLLECTION_NAME, static::CONVERSION_NAME)) {
+ return $url;
+ }
+
+ return null;
+ }
+}
diff --git a/app/Models/ReportableRelation.php b/app/Models/ReportableRelation.php
new file mode 100644
index 0000000..f525bf0
--- /dev/null
+++ b/app/Models/ReportableRelation.php
@@ -0,0 +1,140 @@
+action = static::CREATED;
+ static::report($model);
+ });
+ static::updated(function ($model) {
+ $model->action = static::UPDATED;
+ static::report($model);
+ });
+ static::deleted(function ($model) {
+ $model->action = static::DELETED;
+ static::report($model);
+ });
+ }
+
+ public static function report($model)
+ {
+ $payload = [
+ 'auth' => Auth::id(),
+ 'timestamp' => $model->freshTimestamp(),
+ 'business' => $model->pivotParent->getValueOf('business_id'),
+ 'info' => \request('_business_info') ?? null,
+ 'project' => $model->pivotParent->getValueOf('project_id'),
+ 'data' => [
+ 'sprint_id' => $model->pivotParent->getValueOf('sprint_id'),
+ 'system_id' => $model->pivotParent->getValueOf('system_id'),
+ 'workflow_id' => $model->pivotParent->getValueOf('workflow_id'),
+ 'status_id' => $model->pivotParent->getValueOf('status_id'),
+ 'task_id' => $model->pivotParent->getValueOf('task_id'),
+ 'user_id' => $model->user_id,
+ 'table_name' => $model->getTable(),
+ 'crud_id' => $model->action,
+ 'original' => $model->getOriginal() + $model->getAttributes(),
+ 'diff' => $model->getChanges(),
+ ],
+ 'from' => env('CONTAINER_NAME'),
+ ];
+
+ ModelSaved::dispatch(json_encode($payload));
+// $message = new PublishableMessage(json_encode($payload));
+//
+// $routers = [
+// "activity_exchange" => ["name" => "activity",],
+// "notif_exchange" => ["name" => "notif",],
+// "socket_exchange" => ["name" => "socket",],
+// ];
+//
+// foreach ($routers as $exchange => $properties) {
+// $message->setProperties(["application_headers" => new AMQPTable($properties)]);
+//
+// $message->setExchange(new Exchange($exchange));
+//
+// Amqp::publish($message, "");
+// }
+ }
+
+ public function properties()
+ {
+ return $properties = [
+ // Message properties
+ 'Persistent' => '1', // Marks a message as persistent
+
+ // those familiar with the protocol may choose to use this property instead of Persistent. They control the same thing.
+ // Non-persistent (1) or persistent (2).
+ 'DeliveryMode' => '',
+
+ // Used to describe the mime-type of the encoding. For example for the often used JSON encoding it is a good practice to set this property to: application/json.
+ // MIME content type.
+ // short string (max. 256 characters)
+ 'content_type' => '',
+
+ // Commonly used to name a callback queue.
+ // Address to reply to.
+ // short string (max. 256 characters)
+ 'reply_to' => '',
+
+ // Useful to correlate RPC responses with requests.
+ // Application correlation identifier.
+ // short string (max. 256 characters)
+ 'correlation_id' => '',
+
+ /** Rarley Used Properties */
+
+ // This defines the message priority
+ // Message priority, 0 to 9
+ 'priority' => '',
+
+ // This is the message time stamp
+ // Message timestamp.
+ 'timestamp' => '',
+
+ // This is the broker with whom the user sends the message (by default, it is "guest").
+ // Creating user id.
+ // short string (max. 256 characters)
+ 'user_id' => '',
+
+ // MIME content encoding.
+ // short string (max. 256 characters)
+ 'content_encoding' => '',
+
+ // Message expiration specification.
+ // short string (max. 256 characters)
+ 'expiration' => '',
+
+ // Application message identifier.
+ // short string (max. 256 characters)
+ 'message_id' => '',
+
+ // Message type name.
+ // short string (max. 256 characters)
+ 'type' => '',
+
+ // Creating application id.
+ // short string (max. 256 characters)
+ 'app_id' => '',
+ 'cluster_id' => '',
+ ];
+ }
+}
diff --git a/app/Models/SoftDeletes.php b/app/Models/SoftDeletes.php
new file mode 100644
index 0000000..5b3374b
--- /dev/null
+++ b/app/Models/SoftDeletes.php
@@ -0,0 +1,29 @@
+forceDeleting) {
+ $this->exists = false;
+
+ return $this->setKeysForSaveQuery($this->newModelQuery())->forceDelete();
+ }
+
+ $time = $this->freshTimestamp();
+
+ $this->{$this->getDeletedAtColumn()} = $time;
+
+ if ($this->timestamps && !is_null($this->getUpdatedAtColumn())) {
+ $this->{$this->getUpdatedAtColumn()} = $time;
+ }
+
+ return $this->save();
+ }
+}
diff --git a/app/Models/Sprint.php b/app/Models/Sprint.php
new file mode 100644
index 0000000..21cc40f
--- /dev/null
+++ b/app/Models/Sprint.php
@@ -0,0 +1,70 @@
+ 'bail|required|string|min:2|max:225',
+ 'started_at' => 'bail|required|date|date_format:Y-m-d',
+ 'ended_at' => 'bail|required|date|date_format:Y-m-d|after:started_at',
+ 'active' => 'nullable|boolean',
+ 'description' => 'nullable|string|min:2|max:1000',
+ ];
+ }
+
+ protected $casts = [
+ 'active' => 'boolean'
+ ];
+
+ public function getValueOf(?string $key)
+ {
+ $values = [
+ 'business_id' => $this->business_id,
+ 'project_id' => $this->project_id,
+ 'sprint_id' => $this->id,
+ 'workflow_id' => null,
+ 'status_id' => null,
+ 'system_id' => null,
+ 'user_id' => null,
+ 'task_id' => null,
+ 'subject_id' => $this->id,
+ ];
+
+ if ($key && isset($values, $key)) {
+ return $values[$key];
+ }
+
+ return $values;
+ }
+
+
+ public function business()
+ {
+ return $this->belongsTo(Business::class, 'business_id', 'id');
+ }
+
+ public function projects()
+ {
+ return $this->belongsTo(Project::class, 'project_id', 'id');
+ }
+
+ public function tasks()
+ {
+ return $this->hasMany(Task::class, 'sprint_id', 'id');
+ }
+
+}
diff --git a/app/Models/Status.php b/app/Models/Status.php
new file mode 100644
index 0000000..1bf3b26
--- /dev/null
+++ b/app/Models/Status.php
@@ -0,0 +1,53 @@
+'required|string|min:3|max:255',
+ 'state' => 'nullable|between:0,3',
+ 'order' => 'nullable|numeric|min:0'
+ ];
+ }
+
+ public function getValueOf(?string $key)
+ {
+ $values = [
+ 'business_id' => $this->business_id,
+ 'project_id' => null,
+ 'sprint_id' => null,
+ 'workflow_id' => $this->workflow_id,
+ 'status_id' => $this->id,
+ 'system_id' => null,
+ 'user_id' => null,
+ 'task_id' => null,
+ 'subject_id' => $this->id,
+ ];
+
+ if ($key && isset($values, $key)) {
+ return $values[$key];
+ }
+
+ return $values;
+ }
+
+ public function workflow()
+ {
+ return $this->belongsTo(Workflow::class,'workflow_id','id');
+ }
+}
diff --git a/app/Models/System.php b/app/Models/System.php
new file mode 100644
index 0000000..331f2f6
--- /dev/null
+++ b/app/Models/System.php
@@ -0,0 +1,66 @@
+ 'required|string|min:2|max:225',
+ ];
+ }
+
+ protected $casts = [
+ 'private' => 'boolean'
+ ];
+
+ public function getValueOf(?string $key)
+ {
+ $values = [
+ 'business_id' => $this->business_id,
+ 'project_id' => $this->project_id,
+ 'sprint_id' => null,
+ 'workflow_id' => null,
+ 'status_id' => null,
+ 'system_id' => $this->id,
+ 'user_id' => null,
+ 'task_id' => null,
+ 'subject_id' => $this->id,
+ ];
+
+ if ($key && isset($values, $key)) {
+ return $values[$key];
+ }
+
+ return $values;
+ }
+
+
+ public function business()
+ {
+ return $this->belongsTo(Business::class, 'business_id', 'id');
+ }
+
+ public function project()
+ {
+ return $this->belongsTo(Project::class, 'project_id', 'id');
+ }
+
+
+ public function tasks()
+ {
+ return $this->hasMany(Task::class, 'sub_project_id', 'id');
+ }
+
+}
diff --git a/app/Models/Tag.php b/app/Models/Tag.php
new file mode 100644
index 0000000..308359d
--- /dev/null
+++ b/app/Models/Tag.php
@@ -0,0 +1,56 @@
+ $this->business_id,
+ 'project_id' => null,
+ 'sprint_id' => null,
+ 'workflow_id' => null,
+ 'status_id' => null,
+ 'system_id' => null,
+ 'user_id' => null,
+ 'task_id' => null,
+ 'subject_id' => $this->id,
+ ];
+
+ if ($key && isset($values, $key)) {
+ return $values[$key];
+ }
+
+ return $values;
+ }
+
+ public function rules()
+ {
+ return [
+ 'label' => 'required|string|min:3|max:225',
+ 'color' => 'nullable|string|min:2|max:255',
+ ];
+ }
+
+ public function business()
+ {
+ return $this->belongsTo(Business::class,'business_id','id',__FUNCTION__);
+ }
+
+ public function task()
+ {
+ return $this->belongsToMany(
+ Task::class,'tag_task','tag_id','task_id',
+ 'id','id',__FUNCTION__
+ );
+ }
+}
diff --git a/app/Models/TagTask.php b/app/Models/TagTask.php
new file mode 100644
index 0000000..1f2dd20
--- /dev/null
+++ b/app/Models/TagTask.php
@@ -0,0 +1,12 @@
+ 'tag_task']
+ ];
+ protected $fillable_relations = [
+ 'tags'
+ ];
+
+ protected $casts = [
+ 'work_start' => 'datetime:Y-m-d H:i',
+ 'work_finish' => 'datetime:Y-m-d H:i',
+ 'watchers' => 'array',
+ ];
+
+ public function rules()
+ {
+// dd(\request('_business_info')['info']['workflows']->toArray());
+ $validations = [
+ 'assignee_id' => ['nullable', 'numeric',
+ function ($attribute, $value, $fail) {
+ //check assignee at least guest in project
+ if (!can('isProjectGuest', ['project_id' => request()->route('project'), 'user_id' => $value])) {
+ $fail('The selected '.$attribute.' is invalid.');
+ }
+ if (request()->method() === 'PUT' && ($this->assignee_id != $value && Work::where('task_id', $this->id)->exists())) {
+ //when update execute
+ //check if change assignee then should be on task not work set
+ $fail('The selected '.$attribute.' is invalid.');
+ }
+ }],
+ 'system_id' => ['nullable', 'numeric',
+ Rule::in(\request('_business_info')['info']['projects'][request()->route('project')]['systems'])],
+ 'sprint_id' => ['nullable', 'numeric',
+ Rule::in(\request('_business_info')['info']['projects'][request()->route('project')]['sprints'])],
+ 'workflow_id' => ['required', 'numeric',
+ Rule::in(array_keys(\request('_business_info')['info']['workflows']->toArray()))],
+ 'status_id' => 'required|numeric',
+ 'approver_id' => ['nullable', 'numeric',
+ function ($attribute, $value, $fail) {
+ //check approval at least colleague in project
+ if (!can('isProjectColleague', ['project_id' => request()->route('project'), 'user_id' => $value])) {
+ $fail('The selected '.$attribute.' is invalid.');
+ }
+ }],
+ 'title' => 'required|string|min:3|max:254',
+ 'description' => 'nullable|string|min:2|max:1000',
+ 'priority' => 'nullable|numeric|between:1,10',
+ 'estimated_time' => 'bail|nullable|numeric',
+ 'due_date' => 'bail|nullable|date|date_format:Y-m-d|after_or_equal:'.
+ ((request()->method() === 'POST') ? date('yy-m-d') : $this->created_at->toDateString()),
+ 'tags' => 'nullable|array',
+ 'tags.*' => [new RequiredIf(isset($request->tags)), 'numeric',
+ Rule::in(\request('_business_info')['info']['tags'])],
+ ] ;
+ $workflow_id = $this->workflow_id;
+ if (request()->filled('workflow_id') && isset(request('_business_info')['info']['workflows'][request()->workflow_id])) {
+ $workflow_id = request()->workflow_id;
+ }
+ if (isset($workflow_id)) {
+ $validations['status_id'] = ['bail', 'required', 'numeric',
+ //check status exists in status list
+ Rule::in(\request('_business_info')['info']['workflows'][$workflow_id]['statuses']),
+ function ($attribute, $value, $fail) use ($workflow_id) {
+ //check not close or done
+ $state = \request('_business_info')['workflows'][$workflow_id]['statuses'][$value]['state'];
+ if (request()->method() == 'POST' && ($state == enum('status.states.close.id') || $state == enum('status.states.done.id'))) {
+ $fail('The selected '.$attribute.' is invalid.');
+ }
+ if ((request()->method() == 'PUT') && !can('projectTasks', ['project_id' => request()->route('project')])
+ && $state != enum('status.states.active.id')) {
+ $fail('The selected '.$attribute.' is invalid.');
+ }
+ }];
+ }
+
+ return $validations;
+ }
+ public function updateRelations()
+ {
+ // tags relations
+ $this->dirties['tags'] = $this->tags()->sync($this->filled_relations['tags']);
+
+ //old code
+// if (!empty($this->filled_relations['tags'])) {
+// $this->dirties['tags'] = $this->tags()->sync($this->filled_relations['tags']);
+// }
+
+ }
+
+ public function getValueOf(?string $key)
+ {
+ $values = [
+ 'business_id' => $this->business_id,
+ 'project_id' => $this->project_id,
+ 'sprint_id' => $this->sprint_id,
+ 'workflow_id' => $this->workflow_id,
+ 'status_id' => $this->status_id,
+ 'system_id' => $this->system_id,
+ 'task_id' => $this->id,
+ 'subject_id' => $this->id,
+ 'user_id' => $this->assignee_id,
+ ];
+
+ if ($key && isset($values, $key)) {
+ return $values[$key];
+ }
+
+ return $values;
+ }
+
+ public function business()
+ {
+ return $this->belongsTo(Business::class, 'business_id', 'id', __FUNCTION__);
+ }
+
+ public function creator()
+ {
+ return $this->belongsTo(User::class, 'creator_id', 'id', __FUNCTION__);
+ }
+
+ public function user()
+ {
+ return $this->belongsTo(User::class, 'user_id', 'id', __FUNCTION__);
+ }
+
+ public function project()
+ {
+ return $this->belongsTo(Project::class, 'project_id', 'id', __FUNCTION__);
+ }
+
+ public function tags()
+ {
+ return $this->belongsToMany(Tag::class)->using(ReportableRelation::class);
+ }
+
+ // Todo: are we need this relation????
+ public function tagTask()
+ {
+ return $this->hasMany(TagTask::class, 'task_id', 'id');
+ }
+
+ public function works()
+ {
+ return $this->hasMany(Work::class, 'task_id', 'id');
+ }
+
+ public function comments()
+ {
+ return $this->hasMany(Comment::class, 'task_id', 'id');
+ }
+
+ public function scopePriorityMin($query, $min)
+ {
+ return $query->where('tasks.priority', '>=', $min);
+ }
+ public function scopePriorityMax($query, $max)
+ {
+ return $query->where('tasks.priority', '<=', $max);
+ }
+ public function scopeCreatesBefore($query, $date)
+ {
+ //ToDo: comment lines have better performance but should be test
+// return $query->whereDate('tasks.created_at', '<=', Carbon::parse($date.' 23:59:59'));
+// return $query->where('tasks.created_at', '<', (new DateTime('2014-07-10'))->modify('+1 day')->format('Y-m-d'));
+ return $query->whereDate('tasks.created_at', '<=', Carbon::parse($date));
+ }
+ public function scopeCreatesAfter($query, $date)
+ {
+ return $query->whereDate('tasks.created_at', '>=', Carbon::parse($date));
+ }
+ public function scopeCreatesIn($query, $days)
+ {
+ return $query->whereDate('tasks.created_at', '>=', Carbon::now()->modify('-'.$days.' day')->toDate());
+ }
+ public function scopeUpdatesBefore($query, $date)
+ {
+ return $query->whereDate('tasks.updated_at', '<=', Carbon::parse($date));
+ }
+ public function scopeUpdatesAfter($query, $date)
+ {
+ return $query->whereDate('tasks.updated_at', '>=', Carbon::parse($date));
+ }
+ public function scopeUpdatesIn($query, $days)
+ {
+ return $query->whereDate('tasks.updated_at', '>=', Carbon::now()->modify('-'.$days.' day')->toDate());
+ }
+ public function scopeSpentFrom($query, $minute)
+ {
+ return $query->where('tasks.spent_time', '>=', $minute);
+ }
+ public function scopeSpentTo($query, $minute)
+ {
+ return $query->where('tasks.spent_time', '<=', $minute);
+ }
+ public function scopeEstimatedFrom($query, $minute)
+ {
+ return $query->where('tasks.estimated_time', '>=', $minute);
+ }
+ public function scopeEstimatedTo($query, $minute)
+ {
+ return $query->where('tasks.estimated_time', '<=', $minute);
+ }
+ public function scopeStartsBefore($query, $date)
+ {
+ return $query->whereDate('tasks.work_start', '<=', Carbon::parse($date));
+ }
+ public function scopeStartsAfter($query, $date)
+ {
+ return $query->whereDate('tasks.work_start', '>=', Carbon::parse($date));
+ }
+ public function scopeStartsIn($query, $days)
+ {
+ return $query->whereDate('tasks.work_start', '>=', Carbon::now()->modify('-'.$days.' day')->toDate());
+ }
+ public function scopeFinishBefore($query, $date)
+ {
+ return $query->whereDate('tasks.work_finish', '<=', Carbon::parse($date));
+ }
+ public function scopeFinishAfter($query, $date)
+ {
+ return $query->whereDate('tasks.work_finish', '>=', Carbon::parse($date));
+ }
+ public function scopeFinishIn($query, $days)
+ {
+ return $query->whereDate('tasks.work_finish', '>=', Carbon::now()->modify('-'.$days.' day')->toDate());
+ }
+ public function scopeCompletesBefore($query, $date)
+ {
+ return $query->where('tasks.completed_at', '<=', $date);
+ }
+ public function scopeCompletesAfter($query, $date)
+ {
+ return $query->where('tasks.completed_at', '>=', $date);
+ }
+ public function scopeCompletesIn($query, $days)
+ {
+ return $query->whereDate('tasks.completed_at', '>=', Carbon::now()->modify('-'.$days.' day')->toDate());
+ }
+ public function scopeDueDateBefore($query, $date)
+ {
+ return $query->where('tasks.due_date', '<=', $date);
+ }
+ public function scopeDueDateAfter($query, $date)
+ {
+ return $query->where('tasks.due_date', '>=', $date);
+ }
+ public function scopeDueDateIn($query, $days)
+ {
+ return $query->whereDate('tasks.due_date', '>=', Carbon::now()->modify('-'.$days.' day')->toDate());
+ }
+ public function scopeOverSpentFrom($query, $minute)
+ {
+ return $query->whereColumn('spent_time', '>', 'estimated_time')
+ ->having('over_spent', '>=', $minute);
+ }
+ public function scopeOverSpentTo($query, $minute)
+ {
+ return $query->whereColumn('spent_time', '>', 'estimated_time')
+ ->having('over_spent', '<=', $minute);
+ }
+ public function scopeMyWatching($query)
+ {
+ return $query->whereJsonContains('watchers', auth()->id());
+ }
+ public function scopeOverDue($query)
+ {
+ return $query->whereColumn('due_date', '<', 'completed_at');
+ }
+ public function scopeReport($query, $group_field)
+ {
+ return $query->select(DB::raw($group_field.' , status_id, COUNT(*) as total, SUM(spent_time) as spent_sum,
+ SUM(estimated_time) as estimated_sum,SUM(ready_to_test) as test_sum,
+ COUNT(completed_at) as completed_total, SUM(on_time) as on_time_total,
+ SUM(Case When spent_time > estimated_time Then (spent_time - estimated_time) Else 0 End) as over_spent_sum,
+ SUM(Case When (spent_time <= estimated_time) And (completed_at) Then (estimated_time - spent_time) Else 0 End)
+ as under_spent_sum'))
+ ->groupBy($group_field, 'status_id');
+ }
+}
diff --git a/app/Models/Transaction.php b/app/Models/Transaction.php
new file mode 100644
index 0000000..cafb084
--- /dev/null
+++ b/app/Models/Transaction.php
@@ -0,0 +1,164 @@
+ 'array',
+ 'succeeded' => 'boolean',
+ ];
+
+ protected $fillable_relations = [
+ 'user', 'business'
+ ];
+
+ protected $reportable = [
+ 'user_id', 'business_id', 'amount', 'succeeded', 'options', // fields
+ ];
+
+ public $perPage = 12;
+
+ public function getValueOf(?string $key)
+ {
+ $values = [
+ 'business_id' => $this->business_id,
+ 'project_id' => null,
+ 'sprint_id' => null,
+ 'system_id' => null,
+ 'user_id' => $this->user_id,
+ 'workflow_id' => null,
+ 'status_id' => null,
+ 'task_id' => null,
+ 'subject_id' => $this->id,
+ ];
+
+ if ($key && isset($values, $key)) {
+ return $values[$key];
+ }
+
+ return $values;
+ }
+
+ public function rules(): array
+ {
+ return [
+ 'user_id' => 'required|',
+ 'business_id' => 'required',
+ 'amount' => 'required|integer|min:1',
+ ];
+ }
+
+ public function updateRelations()
+ {
+ // user relations
+ if (!empty($this->filled_relations['user'])) {
+ $this->dirties['user'] = $this->user_id;
+ }
+
+ // business relations
+ if (!empty($this->filled_relations['business'])) {
+ $this->dirties['business'] = $this->business_id;
+ }
+ }
+
+ public function reportActivity()
+ {
+
+ }
+
+ public function user()
+ {
+ return $this->belongsTo(User::class, 'user_id','id',__FUNCTION__);
+ }
+
+ public function business()
+ {
+ return $this->belongsTo(Business::class, 'business_id','id',__FUNCTION__);
+ }
+
+ /**
+ * Receive the authority key from the payment gateway
+ */
+ public function prepare(): Transaction
+ {
+ $results = Zarinpal::request(
+ config('services.zarinpal.callback-url'),
+ $this->amount,
+ config('services.zarinpal.description')
+ );
+
+ $this->options = $results;
+ $this->save();
+
+ return $this;
+ }
+
+ /**
+ * Redirect to the payment gateway
+ */
+ public function redirect()
+ {
+ return Zarinpal::redirect();
+ }
+
+ public function verify(): Transaction
+ {
+ $results = Zarinpal::verify($this->amount, $this->options['Authority']);
+ if ($results['Status'] == 'verified_before') {
+ throw new \Exception("تراکنش قبلا تایید شده است.");
+ }
+
+ if ($results['Status'] == 'success') {
+ $this->succeeded = true;
+ } else {
+ $this->succeeded = false;
+ }
+
+ $options = array_merge($this->options, $results);
+ $this->options = $options;
+ $this->save();
+
+ return $this;
+ }
+
+ /**
+ * Find a transaction via the authoriry key that psp provides us
+ *
+ * @throw ModelNotFound
+ */
+ public static function findByAuthority(string $authority): Transaction
+ {
+ return static::where('options->Authority','=',$authority)->firstOrFail();
+ }
+
+ public function isWentToPaymentGateway(): bool
+ {
+ return !empty($this->options);
+ }
+
+ public function hasBeenAppliedToWallet(): bool
+ {
+ return Arr::get($this->options,"applied", false);
+ }
+
+ public function amountWasAppliedToWallet()
+ {
+ $options = $this->options;
+ $options['applied'] = true;
+ $this->options = $options;
+
+ $this->save();
+ }
+}
diff --git a/app/Models/User.php b/app/Models/User.php
index 804799b..3479edf 100644
--- a/app/Models/User.php
+++ b/app/Models/User.php
@@ -2,42 +2,194 @@
namespace App\Models;
-use Illuminate\Contracts\Auth\MustVerifyEmail;
-use Illuminate\Database\Eloquent\Factories\HasFactory;
-use Illuminate\Foundation\Auth\User as Authenticatable;
+use App\Models\File;
+use App\Models\Model;
+use App\Models\SoftDeletes;
+use Illuminate\Validation\Rule;
+use Illuminate\Http\UploadedFile;
+use Spatie\MediaLibrary\HasMedia;
+use Illuminate\Auth\Authenticatable;
use Illuminate\Notifications\Notifiable;
+use Spatie\MediaLibrary\InteractsWithMedia;
+use Illuminate\Foundation\Auth\Access\Authorizable;
+use Spatie\MediaLibrary\MediaCollections\Models\Media;
+use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
+use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
-class User extends Authenticatable
+class User extends Model implements AuthenticatableContract, AuthorizableContract, HasMedia
{
- use HasFactory, Notifiable;
+ use Notifiable,
+ SoftDeletes,
+ Authorizable,
+ Authenticatable,
+ InteractsWithMedia;
- /**
- * The attributes that are mass assignable.
- *
- * @var array
- */
- protected $fillable = [
- 'name',
- 'email',
- 'password',
+ const CONVERSION_NAME = 'avatar';
+
+ const COLLECTION_NAME = 'avatars';
+
+ public $casts = [
+ 'has_avatar' => 'boolean',
];
- /**
- * The attributes that should be hidden for arrays.
- *
- * @var array
- */
- protected $hidden = [
- 'password',
- 'remember_token',
+ protected $fillable = ['name', 'email','mobile', 'username','password','active','has_avatar'];
+
+ protected $fillable_relations = ['projects'];
+
+ protected $reportable = [
+ 'name', 'username', 'mobile', 'email', // fields
+ ['projects' => 'project_user']
];
+ public $detach_relation = false;
+
/**
- * The attributes that should be cast to native types.
+ * Specifies the user's FCM token
*
- * @var array
+ * @return string
*/
- protected $casts = [
- 'email_verified_at' => 'datetime',
- ];
+ public function routeNotificationForFcm()
+ {
+ return $this->fingerprints->whereNotNull('fcm_token')->pluck('fcm_token')->all();
+ }
+
+ public function updateRelations()
+ {
+ // projects relations
+ if (!empty($this->filled_relations['projects']) || $this->detach_relation) {
+ $this->dirties['projects'] = $this->projects()->sync($this->filled_relations['projects'], $this->detach_relation);
+ }
+ }
+
+ /** =============================== Validations ======================== */
+ public function rules()
+ {
+ return [
+ 'name' => 'required|string|max:225|min:2',
+ 'username' => ['required', Rule::unique('users', 'username')->ignore($this->id)],
+ 'email' => ['required', 'email', Rule::unique('users', 'email')->ignore($this->id)],
+ 'password' => ['required','string','min:8']
+ ];
+ }
+ /** =============================== End Validations ==================== */
+
+
+ /** =============================== Relations ========================== */
+ public function fingerprints()
+ {
+ return $this->hasMany(Fingerprint::class, 'user_id', 'id');
+ }
+
+ public function businesses()
+ {
+ return $this->belongsToMany(
+ Business::class,
+ 'business_user',
+ 'user_id',
+ 'business_id',
+ 'id',
+ 'id',
+ __FUNCTION__
+ );
+ }
+
+
+ public function tasks()
+ {
+ return $this->hasMany(Task::class, 'user_id', 'id');
+ }
+
+ public function projects()
+ {
+ return $this->belongsToMany(
+ Project::class,
+ 'project_user',
+ 'user_id',
+ 'project_id',
+ 'id',
+ 'id',
+ __FUNCTION__
+ )->using(ReportableRelation::class);
+ }
+
+ public function files()
+ {
+ return $this->hasMany(File::class, 'user_id','id');
+ }
+
+
+ /** =============================== End Relations ====================== */
+
+ public function getValueOf(?string $key)
+ {
+ $values = [
+ 'business_id' => request('_business_info')['id'] ?? null,
+ 'user_id' => $this->id,
+ 'workflow_id' => null,
+ 'project_id' => null,
+ 'sprint_id' => null,
+ 'system_id' => null,
+ 'status_id' => null,
+ 'task_id' => null,
+ 'subject_id' => $this->id,
+ ];
+
+ if ($key && isset($values, $key)) {
+ return $values[$key];
+ }
+
+ return $values;
+ }
+
+ public function registerMediaCollections(): void
+ {
+ $this->addMediaCollection(static::COLLECTION_NAME)
+ ->acceptsMimeTypes([
+ 'image/jpeg',
+ 'image/png',
+ 'image/tiff',
+ 'image/gif',
+ ])
+ ->useDisk('public')
+ ->singleFile();
+ }
+
+ public function registerMediaConversions(Media $media = null): void
+ {
+ $this->addMediaConversion(static::CONVERSION_NAME)
+ ->width(200)
+ ->height(200)
+ ->queued()
+ ->nonOptimized()
+ ->performOnCollections(static::COLLECTION_NAME);
+ }
+
+
+ public function saveAsAvatar(UploadedFile $avatar): void
+ {
+ $this->addMedia($avatar)->toMediaCollection(static::COLLECTION_NAME);
+ $this->update([
+ 'has_avatar' => true,
+ ]);
+ @unlink($this->getFirstMedia(static::COLLECTION_NAME)->getPath());
+ }
+
+ public function deleteAvatar(): void
+ {
+ $path = $this->getFirstMedia(static::COLLECTION_NAME)->getPath();
+ $this->getFirstMedia(static::COLLECTION_NAME)->delete();
+ $this->update([
+ 'has_avatar' => false,
+ ]);
+ @unlink($path);
+ }
+
+ public function getAvatarUrl(): ?string
+ {
+ if ($url = $this->getFirstMediaUrl(static::COLLECTION_NAME, static::CONVERSION_NAME)) {
+ return $url;
+ }
+
+ return null;
+ }
}
diff --git a/app/Models/Work.php b/app/Models/Work.php
new file mode 100644
index 0000000..0b6ce81
--- /dev/null
+++ b/app/Models/Work.php
@@ -0,0 +1,126 @@
+ 'datetime:Y-m-d H:i',
+ 'ended_at' => 'datetime:Y-m-d H:i'
+ ];
+
+ protected $reportable = [
+ 'message', 'minute_sum', 'started_at', 'ended_at',
+ ];
+
+ public function rules()
+ {
+ $started_at = request()->started_at ?? $this->started_at->format('Y-m-d H:i');
+ $ended_at = request()->ended_at ?? $this->ended_at->format('Y-m-d H:i');
+ return [
+ 'message' => 'nullable|string|min:3|max:225',
+ 'started_at' => 'required|date_format:Y-m-d H:i|after:'.$this->task()->first()->created_at,
+ 'ended_at' => [
+ 'required', 'date_format:Y-m-d H:i', 'after:'.$started_at,
+ function ($attribute, $value, $fail) use ($ended_at, $started_at) {
+ $works = $this::where([
+ ['ended_at', '>', $started_at],
+ ['ended_at', '<', $ended_at],
+ ])->orWhere([
+ ['started_at', '>', $started_at],
+ ['started_at', '<', $ended_at],
+ ])->orWhere([
+ ['started_at', '>=', $started_at],
+ ['ended_at', '<=', $ended_at],
+ ])->when(isset($this->id), function ($query) {
+ return $query->where('id', '!=', $this->id);
+ })->exists();
+ if ($works) {
+ $fail('The selected work is invalid.');
+ }
+ }
+ ],
+ ];
+
+ }
+
+ public function getValueOf(?string $key)
+ {
+ $values = [
+ 'business_id' => $this->business_id,
+ 'project_id' => $this->project_id,
+ 'sprint_id' => null,
+ 'workflow_id' => null,
+ 'status_id' => null,
+ 'system_id' => null,
+ 'task_id' => $this->task->id,
+ 'subject_id' => $this->id,
+ 'user_id' => null,
+ ];
+
+ if ($key && isset($values, $key)) {
+ return $values[$key];
+ }
+
+ return $values;
+ }
+
+ public function updateRelations()
+ {
+ $this->filled_relations['task']['work_start'] = Work::where('task_id', $this->task_id)->orderBy('started_at')->first()->started_at ?? null;
+ $this->task()->update($this->filled_relations['task']);
+ }
+
+ public function task()
+ {
+ return $this->belongsTo(Task::class, 'task_id', 'id');
+ }
+
+ public function scopeStartedAtIn($query, $days)
+ {
+ return $query->whereDate('started_at', '>=', Carbon::now()->modify('-'.$days.' day')->toDate());
+ }
+
+ public function scopeStartedAt($query, $date)
+ {
+ return $query->whereDate('started_at', '=', $date);
+ }
+
+ public function scopeSpentTimeFrom($query, $data)
+ {
+ return $query->where('minute_sum', '>=', $data);
+ }
+
+ public function scopeSpentTimeTo($query, $data)
+ {
+ return $query->where('minute_sum', '<=', $data);
+ }
+
+ public function scopeReport($query)
+ {
+ return $query->select(
+ DB::raw('user_id , MIN(started_at) as started_at_min, MAX(ended_at) as ended_at_max, SUM(minute_sum) as work_minute_sum')
+ )->groupBy('user_id');
+ }
+
+ public function scopeReportByDate($query)
+ {
+ return $query->select(
+ DB::raw('DATE(started_at) as date, SUM(minute_sum) as work_minute_sum')
+ )->groupBy('date');
+ }
+}
diff --git a/app/Models/Workflow.php b/app/Models/Workflow.php
new file mode 100644
index 0000000..3989836
--- /dev/null
+++ b/app/Models/Workflow.php
@@ -0,0 +1,89 @@
+ 'required|string|min:3|max:225',
+ 'desc' => 'nullable|string|min:3|max:225',
+ 'statuses' => 'required|array|min:2',
+ 'statuses.*' => 'required|array|min:2',
+ 'statuses.*.id' => 'nullable|numeric',
+ 'statuses.*.name' => 'required|string|min:3',
+ 'statuses.*.state' => 'required|numeric|between:0,3',
+ 'statuses.*.order' => 'nullable|numeric',
+ ];
+ }
+
+ public function getValueOf(?string $key)
+ {
+ $values = [
+ 'business_id' => $this->business_id,
+ 'project_id' => null,
+ 'sprint_id' => null,
+ 'workflow_id' => $this->id,
+ 'status_id' => null,
+ 'system_id' => null,
+ 'user_id' => null,
+ 'task_id' => null,
+ 'subject_id' => $this->id,
+ ];
+
+ if ($key && isset($values, $key)) {
+ return $values[$key];
+ }
+
+ return $values;
+ }
+
+ public function updateRelations()
+ {
+ $old_statuses_name = isset(\request('_business_info')['workflows'][$this->id]['statuses']) ?
+ array_keys(collect(\request('_business_info')['workflows'][$this->id]['statuses'])->toArray()) :
+ [];
+ $new_statuses_name = array_keys(collect($this->filled_relations['statuses'])->keyBy('id')->toArray());
+ $removed_statuses_name = array_diff(array_merge($old_statuses_name, $new_statuses_name), $new_statuses_name);
+
+ foreach ($removed_statuses_name as $status_name) {
+ //delete all statuses that removed name's from request->statuses
+ $this->statuses()->where('id', $status_name)->first()->delete();
+ }
+
+ foreach (request('statuses') as $status) {
+ //sync another statuses
+ $this->statuses()
+ ->updateOrCreate(
+ ['id' => $status['id'] ?? null, 'business_id' => $this->business_id, 'workflow_id' => $this->id],
+ ['name' => $status['name'], 'state' => $status['state'], 'order' => $status['order']]
+ );
+ }
+ }
+
+ public function business()
+ {
+ return $this->belongsTo(Business::class, 'business_id');
+ }
+
+ public function statuses()
+ {
+ return $this->hasMany(Status::class, 'workflow_id', 'id');
+ }
+
+ public function tasks()
+ {
+ return $this->hasMany(Task::class, 'workflow_id', 'id');
+ }
+}
diff --git a/app/Notifications/DBNotification.php b/app/Notifications/DBNotification.php
new file mode 100644
index 0000000..006b26a
--- /dev/null
+++ b/app/Notifications/DBNotification.php
@@ -0,0 +1,49 @@
+message = $message;
+ }
+
+ /**
+ * Get the notification's delivery channels.
+ *
+ * @param mixed $notifiable
+ * @return array
+ */
+ public function via($notifiable)
+ {
+ return ['database'];
+ }
+
+ /**
+ * Get the array representation of the notification.
+ *
+ * @param mixed $notifiable
+ * @return array
+ */
+ public function toArray($notifiable)
+ {
+ return [
+ 'data' => $this->message['body']
+ ];
+ }
+}
diff --git a/app/Notifications/FcmNotification.php b/app/Notifications/FcmNotification.php
new file mode 100644
index 0000000..d9f1556
--- /dev/null
+++ b/app/Notifications/FcmNotification.php
@@ -0,0 +1,51 @@
+message = $message;
+ }
+
+ /**
+ * Get the notification's delivery channels.
+ *
+ * @param mixed $notifiable
+ * @return array
+ */
+ public function via($notifiable)
+ {
+ return ['fcm'];
+ }
+
+ /**
+ * Get the voice representation of the notification.
+ *
+ * @param mixed $notifiable
+ * @return FcmMessage
+ */
+ public function toFcm($notifiable)
+ {
+ return (new FcmMessage())
+ ->data([
+ 'title' => $this->message['title'],
+ 'body' => $this->message['body'],
+ ]);
+ }
+
+}
diff --git a/app/Notifications/MailNotification.php b/app/Notifications/MailNotification.php
new file mode 100644
index 0000000..8736089
--- /dev/null
+++ b/app/Notifications/MailNotification.php
@@ -0,0 +1,78 @@
+connection = 'redis';
+ $this->queue = 'mail-queue';
+ $this->afterCommit = true;
+ $this->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)
+ ->greeting($this->message['greeting'])
+ ->line($this->message['body'])
+ ->subject($this->message['subject'])
+ ->action('Notification Action', url('/'));
+ }
+}
diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php
index ee8ca5b..2dae397 100644
--- a/app/Providers/AppServiceProvider.php
+++ b/app/Providers/AppServiceProvider.php
@@ -2,7 +2,11 @@
namespace App\Providers;
+use App\Channels\FcmChannel;
+use Illuminate\Notifications\ChannelManager;
+use Illuminate\Support\Facades\Notification;
use Illuminate\Support\ServiceProvider;
+use GuzzleHttp\Client as HttpClient;
class AppServiceProvider extends ServiceProvider
{
@@ -13,7 +17,11 @@ class AppServiceProvider extends ServiceProvider
*/
public function register()
{
- //
+ Notification::resolved(function (ChannelManager $service) {
+ $service->extend('fcm', function ($app) {
+ return new FcmChannel(new HttpClient, config('fcm.key'));
+ });
+ });
}
/**
diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php
index ce74491..69ff803 100644
--- a/app/Providers/AuthServiceProvider.php
+++ b/app/Providers/AuthServiceProvider.php
@@ -2,8 +2,10 @@
namespace App\Providers;
+use App\Models\Fingerprint;
+use App\Utilities\RequestMixin;
+use App\Utilities\BusinessInfoRequestMixin;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
-use Illuminate\Support\Facades\Gate;
class AuthServiceProvider extends ServiceProvider
{
@@ -25,6 +27,22 @@ class AuthServiceProvider extends ServiceProvider
{
$this->registerPolicies();
- //
+ $this->app['request']->mixin(new RequestMixin);
+ $this->app['request']->mixin(new BusinessInfoRequestMixin);
+
+ $this->app['auth']->viaRequest('token', function ($request) {
+
+ if ($request->bearerToken() === null) {
+ return null;
+ }
+
+ $fingerprint = Fingerprint::where([
+ 'token' => $request->bearerToken(),
+ 'agent' => $request->getAgent(),
+ 'os' => $request->getOS(),
+ ])->firstOrFail();
+
+ return $fingerprint->user->setAttribute('token', $fingerprint->token);
+ });
}
}
diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php
index a9f10a6..cdd86b4 100644
--- a/app/Providers/EventServiceProvider.php
+++ b/app/Providers/EventServiceProvider.php
@@ -2,10 +2,24 @@
namespace App\Providers;
+use App\Events\ProjectUserCreate;
+use App\Events\TagCreate;
+use App\Events\ModelSaved;
+use App\Events\BusinessUpdate;
+use App\Events\TaskCreate;
+use App\Events\TaskUpdate;
+use App\Listeners\NotifHandler;
+use App\Listeners\ProjectUserCreateNotif;
+use App\Listeners\TagCreateNotif;
+use App\Events\BusinessUserCreate;
+use App\Listeners\TaskCreateNotif;
+use App\Listeners\TaskUpdateNotif;
use Illuminate\Auth\Events\Registered;
+use App\Listeners\ActivityRegistration;
+use App\Listeners\BusinessUpdateListener;
+use App\Listeners\BusinessUserCreateNotif;
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
-use Illuminate\Support\Facades\Event;
class EventServiceProvider extends ServiceProvider
{
@@ -18,6 +32,28 @@ class EventServiceProvider extends ServiceProvider
Registered::class => [
SendEmailVerificationNotification::class,
],
+ ModelSaved::class => [
+ ActivityRegistration::class,
+ NotifHandler::class,
+ ],
+ TagCreate::class => [
+ TagCreateNotif::class,
+ ],
+ BusinessUserCreate::class => [
+ BusinessUserCreateNotif::class,
+ ],
+ ProjectUserCreate::class => [
+ ProjectUserCreateNotif::class,
+ ],
+ BusinessUpdate::class => [
+ BusinessUpdateListener::class,
+ ],
+ TaskCreate::class => [
+ TaskCreateNotif::class,
+ ],
+ TaskUpdate::class => [
+ TaskUpdateNotif::class,
+ ],
];
/**
diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php
index 3bd3c81..927be5d 100644
--- a/app/Providers/RouteServiceProvider.php
+++ b/app/Providers/RouteServiceProvider.php
@@ -26,7 +26,7 @@ class RouteServiceProvider extends ServiceProvider
*
* @var string|null
*/
- // protected $namespace = 'App\\Http\\Controllers';
+ protected $namespace = 'App\\Http\\Controllers';
/**
* Define your route model bindings, pattern filters, etc.
diff --git a/app/Rules/MaxBound.php b/app/Rules/MaxBound.php
new file mode 100644
index 0000000..b4a530e
--- /dev/null
+++ b/app/Rules/MaxBound.php
@@ -0,0 +1,40 @@
+bound;
+ }
+
+ /**
+ * Get the validation error message.
+ *
+ * @return string
+ */
+ public function message()
+ {
+ return 'The :attribute is out of bound.';
+ }
+}
diff --git a/app/Utilities/Avatar/DefaultConversionFileNamer.php b/app/Utilities/Avatar/DefaultConversionFileNamer.php
new file mode 100644
index 0000000..2e11692
--- /dev/null
+++ b/app/Utilities/Avatar/DefaultConversionFileNamer.php
@@ -0,0 +1,15 @@
+model->getKey();
+ }
+}
diff --git a/app/Utilities/Avatar/DefaultPathGenerator.php b/app/Utilities/Avatar/DefaultPathGenerator.php
new file mode 100644
index 0000000..1b21029
--- /dev/null
+++ b/app/Utilities/Avatar/DefaultPathGenerator.php
@@ -0,0 +1,41 @@
+model->getTable()."/";
+ }
+
+ /*
+ * Get the path for the given media, relative to the root storage path.
+ */
+ public function getPath(Media $media): string
+ {
+ return $this->getBasePath($media);
+ }
+
+ /*
+ * Get the path for conversions of the given media, relative to the root storage path.
+ */
+ public function getPathForConversions(Media $media): string
+ {
+ return $this->getBasePath($media);
+ }
+
+ /*
+ * Get the path for responsive images of the given media, relative to the root storage path.
+ */
+ public function getPathForResponsiveImages(Media $media): string
+ {
+ return $this->getBasePath($media).'/responsive-images/';
+ }
+}
diff --git a/app/Utilities/BusinessInfoRequestMixin.php b/app/Utilities/BusinessInfoRequestMixin.php
new file mode 100644
index 0000000..2fd1da3
--- /dev/null
+++ b/app/Utilities/BusinessInfoRequestMixin.php
@@ -0,0 +1,43 @@
+getFromBusinessInfo('info.users');
+ };
+ }
+
+ public function isBusinessOwner()
+ {
+ return function(User $user) {
+ return Arr::get($this, "_business_info.info.users.{$user->id}.level") != enum('levels.owner.id');
+ };
+ }
+}
diff --git a/app/Utilities/Exceptions/Handler.php b/app/Utilities/Exceptions/Handler.php
new file mode 100644
index 0000000..8d01e2b
--- /dev/null
+++ b/app/Utilities/Exceptions/Handler.php
@@ -0,0 +1,58 @@
+getMethods(
+ ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED
+ );
+
+
+ $result = [];
+ foreach ($methods as $method) {
+ // invoke the method so we can collect the result of execution it
+ $result[$method->name] = $method->invoke($exception);
+ }
+
+ // Clear the unnecessary method
+ unset($result['getTrace']);
+ unset($result['__toString']);
+
+ // clear the null values then encode it as json
+ // so we can decode it as an object in the Monolog Processor
+ $result = json_encode(array_filter($result));
+
+ return Log::emergency($result);
+ }
+
+ public function render($request, Throwable $exception)
+ {
+ return parent::render($request, $exception);
+ }
+}
diff --git a/app/Utilities/HelperClass/NotificationHelper.php b/app/Utilities/HelperClass/NotificationHelper.php
new file mode 100644
index 0000000..4231cbe
--- /dev/null
+++ b/app/Utilities/HelperClass/NotificationHelper.php
@@ -0,0 +1,72 @@
+ $this->getMessageLine($payload, $route.'greeting'),
+ 'subject' => $this->getMessageLine($payload, $route.'subject'),
+ 'title' => $this->getMessageLine($payload, $route.'title'),
+ 'body' => $this->getMessageLine($payload, $route.'body', $options)
+ ];
+ }
+
+ /**
+ * Fetch message from notifications lang file
+ *
+ * @param $payload
+ * @param $key
+ * @param null $options
+ * @return array|\Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\Translation\Translator|string|null
+ *
+ */
+ public function getMessageLine($payload, $key, $options = [])
+ {
+ return __('notification.'.$payload->data->table_name.'.'.enum('cruds.inverse.'.$payload->data->crud_id.'.singular_name').'.'.$key, $options);
+ }
+
+ /**
+ * Call notifications
+ *
+ * @param $users
+ * @param $notif
+ */
+ public function sendNotifications($users, $notif) {
+// switch ($level) {
+// case "emergency":
+//// Notification::send($users, new SmsNotification($notif));
+// case "critical":
+// Notification::send($users, new MailNotification($notif));
+// case "high":
+// Notification::send($users, new DBNotification($notif));
+// case "medium":
+// Notification::send($users, new FcmNotification($notif));
+// case "low":
+//// Notification::send($users, new SocketNotification($notif));
+// default:
+//// Notification::send($users, new SocketNotification($notif));
+// }
+ Notification::send($users, new MailNotification($notif));
+ Notification::send($users, new DBNotification($notif));
+ Notification::send($users, new FcmNotification($notif));
+ }
+
+}
diff --git a/app/Utilities/Helpers/enum.php b/app/Utilities/Helpers/enum.php
new file mode 100644
index 0000000..ad56050
--- /dev/null
+++ b/app/Utilities/Helpers/enum.php
@@ -0,0 +1,27 @@
+contains(".") ? "" : ".";
+
+ // the first parameter of all enum keys are its filename
+ [$filename, $key] = explode(".", $key, 2);
+
+ // because we do not want to load the file every time use require
+ $enums = require app_path("Enums/$filename.php");
+
+ // if the key that user provided not exists then null return
+ $enums = Arr::get($enums, $key, null);
+
+ // if enum null means that key not found
+ throw_if($enums === null, 'Exception', "Undefined enum '{$key}'");
+
+ // if enum value is array its mean that user want to use it as collection
+ return is_array($enums) ? collect($enums) : $enums;
+ }
+}
diff --git a/app/Utilities/Helpers/http.php b/app/Utilities/Helpers/http.php
new file mode 100644
index 0000000..bba7252
--- /dev/null
+++ b/app/Utilities/Helpers/http.php
@@ -0,0 +1 @@
+attach($piece->getFilename(), $piece);
}
}
// if queue set then queue this command
if ($queue !== null) {
return dispatch(new AsyncCall(...func_get_args()));
}
try {
// otherwise execute the pending request
return $pendingRequest
->withToken($token)
->withoutRedirecting()
->withoutVerifying()
->$method($path, $data);
} catch (Throwable $thr) {
return $thr->response;
}
}
}
\ No newline at end of file
diff --git a/app/Utilities/Helpers/index.php b/app/Utilities/Helpers/index.php
new file mode 100644
index 0000000..64aade6
--- /dev/null
+++ b/app/Utilities/Helpers/index.php
@@ -0,0 +1 @@
+trim(".");
if ($filename->isEmpty()) {
continue;
}
require_once (string) $filename;
}
\ No newline at end of file
diff --git a/app/Utilities/Helpers/permission.php b/app/Utilities/Helpers/permission.php
new file mode 100644
index 0000000..a0e47ef
--- /dev/null
+++ b/app/Utilities/Helpers/permission.php
@@ -0,0 +1,244 @@
+user()->id]['owner'] == true;
+ }
+}
+
+/**
+ * Business Permit
+ */
+
+if (!function_exists('businessAccess')) {
+ function businessAccess($ids)
+ {
+ return isset(request('_business_info')['info']['users'][$ids['user_id']?? auth()->id()]);
+ }
+}
+
+if (!function_exists('isBusinessOwner')) {
+ function isBusinessOwner($ids)
+ {
+ return businessAccess($ids) && request('_business_info')['info']['users'][$ids['user_id'] ?? auth()->id()]['level'] == enum('levels.owner.id');
+ }
+}
+
+if (!function_exists('isAtLeastBusinessAdmin')) {
+ function isAtLeastBusinessAdmin($ids)
+ {
+ return businessAccess($ids) && request('_business_info')['info']['users'][$ids['user_id'] ?? auth()->id()]['level'] >= enum('levels.admin.id');
+ }
+}
+
+if (!function_exists('businessEdit')) {
+ function businessEdit($ids)
+ {
+ return isBusinessOwner($ids);
+ }
+}
+
+if (!function_exists('businessUsers')) {
+ function businessUsers($ids)
+ {
+ return isBusinessOwner($ids);
+ }
+}
+
+if (!function_exists('businessWorkFlows')) {
+ function businessWorkFlows($ids)
+ {
+ return isAtLeastBusinessAdmin($ids);
+ }
+}
+
+if (!function_exists('businessProjects')) {
+ function businessProjects($ids)
+ {
+ return isBusinessOwner($ids);
+ }
+}
+
+if (!function_exists('businessTags')) {
+ function businessTags($ids)
+ {
+ return isAtLeastBusinessAdmin($ids);
+ }
+}
+
+if (!function_exists('businessStatuses')) {
+ function businessStatuses($ids)
+ {
+ return isAtLeastBusinessAdmin($ids);
+ }
+}
+
+/**
+ * Project Permit
+ */
+
+if (!function_exists('isInProject')) {
+ function isInProject($ids)
+ {
+ return isset(request('_business_info')['info']['projects'][$ids['project_id']]['members'][$ids['user_id'] ?? auth()->id()]);
+ }
+}
+
+if (!function_exists('isActiveInProject')) {
+ function isActiveInProject($ids)
+ {
+ return request('_business_info')['info']['projects'][$ids['project_id']]['members'][$ids['user_id'] ?? auth()->id()]['level'] > 0;
+ }
+}
+
+if (!function_exists('projectAccess')) {
+ function projectAccess($ids)
+ {
+ return isInProject($ids) && isActiveInProject($ids);
+ }
+}
+
+if (!function_exists('isOwnerLevelInProject')) {
+ function isOwnerLevelInProject($ids)
+ {
+ return request('_business_info')['info']['projects'][$ids['project_id']]['members'][$ids['user_id'] ?? auth()->id()]['level'] == enum('levels.owner.id');
+ }
+}
+
+if (!function_exists('isAtLeastAdminLevelInProject')) {
+ function isAtLeastAdminLevelInProject($ids)
+ {
+ return request('_business_info')['info']['projects'][$ids['project_id']]['members'][$ids['user_id'] ?? auth()->id()]['level'] >= enum('levels.admin.id');
+ }
+}
+
+if (!function_exists('isAtLeastColleagueLevelInProject')) {
+ function isAtLeastColleagueLevelInProject($ids)
+ {
+ return request('_business_info')['info']['projects'][$ids['project_id']]['members'][$ids['user_id'] ?? auth()->id()]['level'] >= enum('levels.colleague.id');
+ }
+}
+
+if (!function_exists('isAtLeastGuestLevelInProject')) {
+ function isAtLeastGuestLevelInProject($ids)
+ {
+ return request('_business_info')['info']['projects'][$ids['project_id']]['members'][$ids['user_id'] ?? auth()->id()]['level'] >= enum('levels.guest.id');
+ }
+}
+
+if (!function_exists('isDefiniteGuestInProject')) {
+ function isDefiniteGuestInProject($ids)
+ {
+ return isInProject($ids) && request('_business_info')['info']['projects'][$ids['project_id']]['members'][$ids['user_id'] ?? auth()->id()]['level'] == enum('levels.guest.id');
+ }
+}
+
+if (!function_exists('isProjectOwner')) {
+ function isProjectOwner($ids)
+ {
+ return isInProject($ids) && isOwnerLevelInProject($ids);
+ }
+}
+
+if (!function_exists('isProjectAdmin')) {
+ function isProjectAdmin($ids)
+ {
+ return isInProject($ids) && isAtLeastAdminLevelInProject($ids);
+ }
+}
+
+if (!function_exists('isProjectColleague')) {
+ function isProjectColleague($ids)
+ {
+ return isInProject($ids) && isAtLeastColleagueLevelInProject($ids);
+ }
+}
+
+if (!function_exists('isProjectGuest')) {
+ function isProjectGuest($ids)
+ {
+ return isInProject($ids) && isAtLeastGuestLevelInProject($ids);
+ }
+}
+
+if (!function_exists('projectEdit')) {
+ function projectEdit($ids)
+ {
+ return isProjectAdmin($ids);
+ }
+}
+
+if (!function_exists('projectUsers')) {
+ function projectUsers($ids)
+ {
+ return isProjectOwner($ids);
+ }
+}
+
+if (!function_exists('projectSystems')) {
+ function projectSystems($ids)
+ {
+ return isProjectAdmin($ids);
+ }
+}
+
+if (!function_exists('projectTasks')) {
+ function projectTasks($ids)
+ {
+ return isProjectAdmin($ids);
+ }
+}
+
+if (!function_exists('projectSprints')) {
+ function projectSprints($ids)
+ {
+ return isProjectAdmin($ids);
+ }
+}
+
+/**
+ * other
+ */
+
+if (!function_exists('isActiveUser')) {
+ function isActiveUser($ids)
+ {
+ return businessAccess($ids) && request('_business_info')['info']['users'][$ids['user_id'] ?? auth()->id()]['level'] > enum('levels.inactive.id');
+ }
+}
+
diff --git a/app/Utilities/Jobs/AsyncCall.php b/app/Utilities/Jobs/AsyncCall.php
new file mode 100644
index 0000000..6f71a01
--- /dev/null
+++ b/app/Utilities/Jobs/AsyncCall.php
@@ -0,0 +1,41 @@
+method = $method;
+ $this->service = $service;
+ $this->path = $path;
+ $this->data = $data;
+ $this->onQueue($queue);
+ $this->onConnection('database');
+ }
+
+ /**
+ * Execute the job.
+ *
+ * @return void
+ */
+ public function handle()
+ {
+ call($this->method, $this->service, $this->path, $this->data);
+ }
+}
diff --git a/app/Utilities/Logger/CreateCustomLogger.php b/app/Utilities/Logger/CreateCustomLogger.php
new file mode 100644
index 0000000..ab07317
--- /dev/null
+++ b/app/Utilities/Logger/CreateCustomLogger.php
@@ -0,0 +1,83 @@
+ Monolog::DEBUG,
+ 'info' => Monolog::INFO,
+ 'notice' => Monolog::NOTICE,
+ 'warning' => Monolog::WARNING,
+ 'error' => Monolog::ERROR,
+ 'critical' => Monolog::CRITICAL,
+ 'alert' => Monolog::ALERT,
+ 'emergency' => Monolog::EMERGENCY,
+ ];
+
+ public function __invoke(array $config)
+ {
+ if (!is_a($config['handler'], HandlerInterface::class, true)) {
+ throw new InvalidArgumentException(
+ $config['handler'] . ' must be an instance of ' . HandlerInterface::class
+ );
+ }
+
+ $with = array_merge(
+ ['level' => $this->level($config)],
+ $config['with'] ?? [],
+ $config['handler_with'] ?? []
+ );
+
+ $handlers = [
+ $this->prepareHandler(app()->make($config['handler'], $with), $config)
+ ];
+
+ $processors = [
+ new LogServiceRecordProcessor,
+ ];
+
+ return new Monolog('custom', $handlers, $processors, new DateTimeZone('Asia/Tehran'));
+ }
+
+ protected function getFallbackChannelName()
+ {
+ return app()->bound('env') ? app()->environment() : 'production';
+ }
+
+ protected function prepareHandler(HandlerInterface $handler, array $config = [])
+ {
+ $isHandlerFormattable = false;
+
+ if (Monolog::API === 1) {
+ $isHandlerFormattable = true;
+ } elseif (Monolog::API === 2 && $handler instanceof FormattableHandlerInterface) {
+ $isHandlerFormattable = true;
+ }
+
+ if ($isHandlerFormattable && !isset($config['formatter'])) {
+ $handler->setFormatter($this->formatter());
+ } elseif ($isHandlerFormattable && $config['formatter'] !== 'default') {
+ $handler->setFormatter(app()->make($config['formatter'], $config['formatter_with'] ?? []));
+ }
+
+ return $handler;
+ }
+
+ protected function level(array $config)
+ {
+ $level = $config['level'] ?? 'debug';
+
+ if (isset($this->levels[$level])) {
+ return $this->levels[$level];
+ }
+
+ throw new InvalidArgumentException('Invalid log level.');
+ }
+}
diff --git a/app/Utilities/Logger/LogServiceRecordProcessor.php b/app/Utilities/Logger/LogServiceRecordProcessor.php
new file mode 100644
index 0000000..202cd16
--- /dev/null
+++ b/app/Utilities/Logger/LogServiceRecordProcessor.php
@@ -0,0 +1,37 @@
+ Uuid::uuid4()->toString(),
+ 'breadcrumbs' => DB::getQueryLog(),
+ 'request' => app('request'),
+ 'message' => Arr::get($exception, 'getMessage', $record['message']),
+ 'from' => env('CONTAINER_NAME'),
+ 'trace' => Arr::get($exception, 'getTraceAsString'),
+ 'name' => Arr::get($exception, 'getName'),
+ 'code' => Arr::get($exception, 'getCode'),
+ 'file' => Arr::get($exception, 'getFile'),
+ 'line' => Arr::get($exception, 'getLine'),
+ 'user_id' => Auth::hasResolvedGuards() ? Auth::id() : null,
+ 'created_at' => Carbon::now(),
+ ]);
+ } catch (Throwable $exception) {
+ return $record;
+ }
+ }
+}
diff --git a/app/Utilities/Middlewares/BindBusinessInfo.php b/app/Utilities/Middlewares/BindBusinessInfo.php
new file mode 100644
index 0000000..788a54d
--- /dev/null
+++ b/app/Utilities/Middlewares/BindBusinessInfo.php
@@ -0,0 +1,19 @@
+has('business_id') ? $request->business_id : $request->route('business');
+ $businessInfo = Business::info($business_id);
+ $request->merge(['_business_info' => $businessInfo]);
+
+ return $next($request);
+ }
+}
diff --git a/app/Utilities/Payload.php b/app/Utilities/Payload.php
new file mode 100644
index 0000000..a845b28
--- /dev/null
+++ b/app/Utilities/Payload.php
@@ -0,0 +1,83 @@
+payload = $payload;
+ }
+
+ public function getActor(): ?Notifiable
+ {
+ return new Notifiable(Arr::get($this->payload, "info.users." . $this['auth']));
+ }
+
+ public function getSubject(): ?Notifiable
+ {
+ return new Notifiable(
+ Arr::get($this->payload, "info.users." . $this['data']['original']['user_id'])
+ );
+ }
+
+ public function getOwners(): Collection
+ {
+ return new Collection(
+ Arr::where($this->payload['info']['users'], fn ($user) => $user['level'] === 4)
+ );
+ }
+
+ public function getTableName(): ?Stringable
+ {
+ return Str::of(Arr::get($this->payload, "data.table_name"));
+ }
+
+ public function getActId()
+ {
+ return Arr::get($this->payload, "data.crud_id");
+ }
+
+ public function getTitle()
+ {
+ return Arr::get($this->payload, "info.name");
+ }
+
+ public function isPivot(): bool
+ {
+ return $this->getTableName()->contains("_");
+ }
+
+ public function offsetExists($key)
+ {
+ return isset($this->payload[$key]);
+ }
+
+ public function offsetGet($key)
+ {
+ return $this->payload[$key];
+ }
+
+ public function offsetSet($key, $value)
+ {
+ if (is_null($key)) {
+ $this->payload[] = $value;
+ } else {
+ $this->payload[$key] = $value;
+ }
+ }
+
+ public function offsetUnset($key)
+ {
+ unset($this->payload[$key]);
+ }
+}
diff --git a/app/Utilities/Providers/AuthServiceProvider.php b/app/Utilities/Providers/AuthServiceProvider.php
new file mode 100644
index 0000000..4b25477
--- /dev/null
+++ b/app/Utilities/Providers/AuthServiceProvider.php
@@ -0,0 +1,54 @@
+app['auth']->viaRequest('api', function (Request $request) {
+ // with token
+ if ($request->bearerToken()) {
+ return $this->loginUserWithToken($request);
+ }
+
+ // first party
+ if ($request->getHost() === $container_name = getenv('CONTAINER_NAME')) {
+ return $this->loginServiceWithSetUser($container_name);
+ }
+
+ // without token
+ return null;
+ });
+ }
+
+ public function loginUserWithToken(Request $request): User
+ {
+ $attributes = Http::retry(3, 100)
+ ->withToken($request->bearerToken())
+ ->get(env('USER_URL') . 'auth')
+ ->json();
+
+ $attributes = Arr::get($attributes, 'data',[]);
+
+// dd($attributes);
+ return new User($attributes);
+ }
+
+ public function loginServiceWithSetUser(string $container_name): User
+ {
+ Auth::setUser($user = new User([
+ 'name' => $container_name,
+ 'is_service' => true,
+ ]));
+
+ return $user;
+ }
+}
diff --git a/app/Utilities/Providers/HiLibraryServiceProvider.php b/app/Utilities/Providers/HiLibraryServiceProvider.php
new file mode 100644
index 0000000..7ac7c8e
--- /dev/null
+++ b/app/Utilities/Providers/HiLibraryServiceProvider.php
@@ -0,0 +1,49 @@
+app->singleton(
+ \Illuminate\Contracts\Debug\ExceptionHandler::class,
+ \App\HiLib\Exceptions\Handler::class
+ );
+
+ Stringable::macro('then', function($callback) {
+ return new Stringable($callback($this));
+ });
+ }
+
+ /**
+ * Bootstrap any application services.
+ *
+ * @return void
+ */
+ public function boot()
+ {
+ DB::enableQueryLog();
+ }
+
+ /**
+ * Get the services provided by the provider.
+ *
+ * @return array
+ */
+ public function provides()
+ {
+ //
+ }
+}
diff --git a/app/Utilities/RequestMixin.php b/app/Utilities/RequestMixin.php
new file mode 100644
index 0000000..c53192e
--- /dev/null
+++ b/app/Utilities/RequestMixin.php
@@ -0,0 +1,51 @@
+ $this->hasHeader('USER_AGENT') ? (new Agent())->platform() : null;
+ }
+
+ /**
+ * Return the browser
+ *
+ * @return Closure
+ */
+ public function getAgent()
+ {
+ return fn() => $this->hasHeader('USER_AGENT') ? (new Agent())->browser() : null;
+ }
+
+
+ /**
+ * Get the user location's based on her/his IP address
+ *
+ * @return Closure
+ */
+ public function getLocation()
+ {
+ return fn() => geoip()->getLocation($this->getClientIp());
+ }
+
+ public function getCurrentToken()
+ {
+ // todo: how to implement ip lookup for current token
+ return fn() => Auth::user()->token;
+ }
+}
diff --git a/app/Utilities/Zarinpal/Drivers/DriverInterface.php b/app/Utilities/Zarinpal/Drivers/DriverInterface.php
new file mode 100644
index 0000000..8227518
--- /dev/null
+++ b/app/Utilities/Zarinpal/Drivers/DriverInterface.php
@@ -0,0 +1,46 @@
+restCall('PaymentRequest.json', $inputs);
+
+ if ($result['Status'] == 100) {
+ return ['Authority' => $result['Authority']];
+ } else {
+ return ['error' => $result['Status']];
+ }
+ }
+
+ /**
+ * requestWithExtra driver.
+ *
+ * @param $inputs
+ *
+ * @return array
+ */
+ public function requestWithExtra($inputs)
+ {
+ $result = $this->restCall('PaymentRequestWithExtra.json', $inputs);
+
+ if ($result['Status'] == 100) {
+ return ['Authority' => $result['Authority']];
+ } else {
+ return [
+ 'Status' => 'error',
+ 'error' => !empty($result['Status']) ? $result['Status'] : null,
+ 'errorInfo' => !empty($result['errors']) ? $result['errors'] : null,
+ ];
+ }
+ }
+
+ /**
+ * verify driver.
+ *
+ * @param $inputs
+ *
+ * @return array
+ */
+ public function verify($inputs)
+ {
+ $result = $this->restCall('PaymentVerification.json', $inputs);
+
+ if ($result['Status'] == 100) {
+ return [
+ 'Status' => 'success',
+ 'RefID' => $result['RefID'],
+ ];
+ } elseif ($result['Status'] == 101) {
+ return [
+ 'Status' => 'verified_before',
+ 'RefID' => $result['RefID'],
+ ];
+ } else {
+ return [
+ 'Status' => 'error',
+ 'error' => !empty($result['Status']) ? $result['Status'] : null,
+ 'errorInfo' => !empty($result['errors']) ? $result['errors'] : null,
+ ];
+ }
+ }
+
+ /**
+ * verifyWithExtra driver.
+ *
+ * @param $inputs
+ *
+ * @return array
+ */
+ public function verifyWithExtra($inputs)
+ {
+ $result = $this->restCall('PaymentVerificationWithExtra.json', $inputs);
+
+ if ($result['Status'] == 100) {
+ return [
+ 'Status' => 'success',
+ 'RefID' => $result['RefID'],
+ 'ExtraDetail' => $result['ExtraDetail'],
+ ];
+ } elseif ($result['Status'] == 101) {
+ return [
+ 'Status' => 'verified_before',
+ 'RefID' => $result['RefID'],
+ 'ExtraDetail' => $result['ExtraDetail'],
+ ];
+ } else {
+ return [
+ 'Status' => 'error',
+ 'error' => !empty($result['Status']) ? $result['Status'] : null,
+ 'errorInfo' => !empty($result['errors']) ? $result['errors'] : null,
+ ];
+ }
+ }
+
+ /**
+ * unverifiedTransactions driver.
+ *
+ * @param $inputs
+ *
+ * @return array
+ */
+ public function unverifiedTransactions($inputs)
+ {
+ $result = $this->restCall('UnverifiedTransactions.json', $inputs);
+
+ if ($result['Status'] == 100) {
+ return ['Status' => 'success', 'Authorities' => $result['Authorities']];
+ } else {
+ return [
+ 'Status' => 'error',
+ 'error' => !empty($result['Status']) ? $result['Status'] : null,
+ 'errorInfo' => !empty($result['errors']) ? $result['errors'] : null,
+ ];
+ }
+ }
+
+ /**
+ * refreshAuthority driver.
+ *
+ * @param $inputs
+ *
+ * @return array
+ */
+ public function refreshAuthority($inputs)
+ {
+ $result = $this->restCall('RefreshAuthority.json', $inputs);
+
+ if ($result['Status'] == 100) {
+ return ['Status' => 'success', 'refreshed' => true];
+ } else {
+ return ['Status' => 'error', 'error' => $result['Status']];
+ }
+ }
+
+ /**
+ * request rest and return the response.
+ *
+ * @param $uri
+ * @param $data
+ *
+ * @return mixed
+ */
+ private function restCall($uri, $data)
+ {
+ try {
+ $client = new Client(['base_uri' => $this->baseUrl]);
+ $response = $client->request('POST', $uri, ['json' => $data]);
+
+ $rawBody = $response->getBody()->getContents();
+ $body = json_decode($rawBody, true);
+ } catch (RequestException $e) {
+ $response = $e->getResponse();
+ $rawBody = is_null($response) ? '{"Status":-98,"message":"http connection error"}' : $response->getBody()->getContents();
+ $body = json_decode($rawBody, true);
+ }
+
+ if (!isset($result['Status'])) {
+ $result['Status'] = -99;
+ }
+
+ return $body;
+ }
+
+ /**
+ * @param mixed $baseUrl
+ *
+ * @return void
+ */
+ public function setAddress($baseUrl)
+ {
+ $this->baseUrl = $baseUrl;
+ }
+
+ public function enableSandbox()
+ {
+ $this->setAddress('https://sandbox.zarinpal.com/pg/rest/WebGate/');
+ }
+}
diff --git a/app/Utilities/Zarinpal/Laravel/Facade/Zarinpal.php b/app/Utilities/Zarinpal/Laravel/Facade/Zarinpal.php
new file mode 100644
index 0000000..921c2fb
--- /dev/null
+++ b/app/Utilities/Zarinpal/Laravel/Facade/Zarinpal.php
@@ -0,0 +1,24 @@
+app->singleton(DriverInterface::class, function () {
+ return new RestDriver();
+ });
+
+ $this->app->singleton('Zarinpal', function () {
+ $merchantID = config('services.zarinpal.merchantID', config('Zarinpal.merchantID', 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX'));
+
+ $zarinpal = new Zarinpal($merchantID, $this->app->make(DriverInterface::class));
+
+ if (config('services.zarinpal.sandbox', false)) {
+ $zarinpal->enableSandbox();
+ }
+ if (config('services.zarinpal.zarinGate', false)) {
+ $zarinpal->isZarinGate();
+ }
+
+ return $zarinpal;
+ });
+ }
+
+ /**
+ * Publish the plugin configuration.
+ */
+ public function boot()
+ {
+ //
+ }
+}
diff --git a/app/Utilities/Zarinpal/Zarinpal.php b/app/Utilities/Zarinpal/Zarinpal.php
new file mode 100644
index 0000000..6339b33
--- /dev/null
+++ b/app/Utilities/Zarinpal/Zarinpal.php
@@ -0,0 +1,130 @@
+merchantID = $merchantID;
+ $this->driver = $driver;
+ }
+
+ /**
+ * send request for money to zarinpal
+ * and redirect if there was no error.
+ *
+ * @param string $callbackURL
+ * @param string $Amount
+ * @param string $Description
+ * @param string $Email
+ * @param string $Mobile
+ * @param null $additionalData
+ *
+ * @return array|@redirect
+ */
+ public function request($callbackURL, $Amount, $Description, $Email = null, $Mobile = null, $additionalData = null)
+ {
+ $inputs = [
+ 'MerchantID' => $this->merchantID,
+ 'CallbackURL' => $callbackURL,
+ 'Amount' => $Amount,
+ 'Description' => $Description,
+ ];
+ if (!is_null($Email)) {
+ $inputs['Email'] = $Email;
+ }
+ if (!is_null($Mobile)) {
+ $inputs['Mobile'] = $Mobile;
+ }
+ if (!is_null($additionalData)) {
+ $inputs['AdditionalData'] = $additionalData;
+ $results = $this->driver->requestWithExtra($inputs);
+ } else {
+ $results = $this->driver->request($inputs);
+ }
+
+ if (empty($results['Authority'])) {
+ $results['Authority'] = null;
+ }
+ $this->Authority = $results['Authority'];
+
+ return $results;
+ }
+
+ /**
+ * verify that the bill is paid or not
+ * by checking authority, amount and status.
+ *
+ * @param $amount
+ * @param $authority
+ *
+ * @return array
+ */
+ public function verify($amount, $authority)
+ {
+ // backward compatibility
+ if (count(func_get_args()) == 3) {
+ $amount = func_get_arg(1);
+ $authority = func_get_arg(2);
+ }
+
+ $inputs = [
+ 'MerchantID' => $this->merchantID,
+ 'Authority' => $authority,
+ 'Amount' => $amount,
+ ];
+
+ return $this->driver->verifyWithExtra($inputs);
+ }
+
+ public function redirect()
+ {
+ header('Location: ' . sprintf($this->redirectUrl, $this->Authority));
+ die;
+ }
+
+ /**
+ * @return string
+ */
+ public function redirectUrl()
+ {
+ return sprintf($this->redirectUrl, $this->Authority);
+ }
+
+ /**
+ * @return DriverInterface
+ */
+ public function getDriver()
+ {
+ return $this->driver;
+ }
+
+ /**
+ * active sandbox mod for test env.
+ */
+ public function enableSandbox()
+ {
+ $this->redirectUrl = 'https://sandbox.zarinpal.com/pg/StartPay/%u';
+ $this->getDriver()->enableSandbox();
+ }
+
+ /**
+ * active zarinGate mode.
+ */
+ public function isZarinGate()
+ {
+ $this->redirectUrl = $this->redirectUrl . '/ZarinGate';
+ }
+}
diff --git a/bootstrap/app.php b/bootstrap/app.php
index 037e17d..fdcb8bd 100644
--- a/bootstrap/app.php
+++ b/bootstrap/app.php
@@ -52,4 +52,11 @@ $app->singleton(
|
*/
+$app->router->group([
+ 'namespace' => 'App\Http\Controllers',
+ 'prefix' => '/user/v1/'
+], function ($router) {
+ require __DIR__.'/../routes/api.php';
+});
+
return $app;
diff --git a/composer.json b/composer.json
index 3795a6d..6c87a76 100644
--- a/composer.json
+++ b/composer.json
@@ -8,12 +8,24 @@
],
"license": "MIT",
"require": {
+ "ext-gd": "*",
+ "ext-json": "*",
"php": "^7.3|^8.0",
+ "anik/amqp": "^1.3",
+ "torann/geoip": "^3.0",
+ "spatie/image": "^1.0",
+ "morilog/jalali": "3.*",
+ "laravel/tinker": "^2.5",
"fideloper/proxy": "^4.4",
- "fruitcake/laravel-cors": "^2.0",
- "guzzlehttp/guzzle": "^7.0.1",
+ "jenssegers/agent": "^2.6",
+ "laravel/socialite": "^5.1",
"laravel/framework": "^8.12",
- "laravel/tinker": "^2.5"
+ "laravel/legacy-factories": "^1",
+ "fruitcake/laravel-cors": "^2.0",
+ "league/flysystem-aws-s3-v3": "~1.0",
+ "spatie/laravel-medialibrary": "^9.0",
+ "spatie/laravel-query-builder": "^3.3",
+ "league/flysystem-cached-adapter": "~1.0"
},
"require-dev": {
"facade/ignition": "^2.5",
@@ -21,7 +33,8 @@
"laravel/sail": "^1.0.1",
"mockery/mockery": "^1.4.2",
"nunomaduro/collision": "^5.0",
- "phpunit/phpunit": "^9.3.3"
+ "phpunit/phpunit": "^9.3.3",
+ "andreaselia/laravel-api-to-postman":"^1.0"
},
"config": {
"optimize-autoloader": true,
@@ -38,7 +51,14 @@
"App\\": "app/",
"Database\\Factories\\": "database/factories/",
"Database\\Seeders\\": "database/seeders/"
- }
+ },
+ "files": [
+ "app/Utilities/Helpers/index.php"
+ ],
+ "classmap": [
+ "database/seeds",
+ "database/factories"
+ ]
},
"autoload-dev": {
"psr-4": {
diff --git a/composer.lock b/composer.lock
index a5ae24e..89d7c77 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,8 +4,63 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "4a16c3c541bd99241cab1c21ce8e83ac",
+ "content-hash": "5fa03c0090cecfc0c45ad964962ea582",
"packages": [
+ {
+ "name": "anik/amqp",
+ "version": "v1.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/ssi-anik/amqp.git",
+ "reference": "091620674aefc6bd5666be2fd9cbbdccfded93cc"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/ssi-anik/amqp/zipball/091620674aefc6bd5666be2fd9cbbdccfded93cc",
+ "reference": "091620674aefc6bd5666be2fd9cbbdccfded93cc",
+ "shasum": ""
+ },
+ "require": {
+ "illuminate/container": "^5.8|^6.0|^7.0|^8.0",
+ "illuminate/pipeline": "^5.8|^6.0|^7.0|^8.0",
+ "illuminate/support": "^5.8|^6.0|^7.0|^8.0",
+ "laravel/helpers": "^1.2",
+ "php": ">=7.0",
+ "php-amqplib/php-amqplib": "^2.9 !=2.12.0"
+ },
+ "type": "library",
+ "extra": {
+ "laravel": {
+ "providers": [
+ "Anik\\Amqp\\ServiceProviders\\AmqpServiceProvider"
+ ],
+ "aliases": {
+ "Amqp": "Anik\\Amqp\\Facades\\Amqp"
+ }
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Anik\\Amqp\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Syed Sirajul Islam Anik",
+ "email": "sirajul.islam.anik@gmail.com"
+ }
+ ],
+ "description": "php-amqplib wrapper that eases the consumption of RabbitMQ. A painless way of using RabbitMQ",
+ "support": {
+ "issues": "https://github.com/ssi-anik/amqp/issues",
+ "source": "https://github.com/ssi-anik/amqp/tree/v1.5"
+ },
+ "time": "2020-09-25T00:37:54+00:00"
+ },
{
"name": "asm89/stack-cors",
"version": "v2.0.2",
@@ -56,8 +111,167 @@
"cors",
"stack"
],
+ "support": {
+ "issues": "https://github.com/asm89/stack-cors/issues",
+ "source": "https://github.com/asm89/stack-cors/tree/v2.0.2"
+ },
"time": "2020-10-29T16:03:21+00:00"
},
+ {
+ "name": "aws/aws-sdk-php",
+ "version": "3.173.23",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/aws/aws-sdk-php.git",
+ "reference": "259fa1a47a46f81e66b1ea328ed961a58fa061c2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/259fa1a47a46f81e66b1ea328ed961a58fa061c2",
+ "reference": "259fa1a47a46f81e66b1ea328ed961a58fa061c2",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "ext-pcre": "*",
+ "ext-simplexml": "*",
+ "guzzlehttp/guzzle": "^5.3.3|^6.2.1|^7.0",
+ "guzzlehttp/promises": "^1.4.0",
+ "guzzlehttp/psr7": "^1.7.0",
+ "mtdowling/jmespath.php": "^2.6",
+ "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",
+ "paragonie/random_compat": ">= 2",
+ "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"
+ ],
+ "support": {
+ "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
+ "issues": "https://github.com/aws/aws-sdk-php/issues",
+ "source": "https://github.com/aws/aws-sdk-php/tree/3.173.23"
+ },
+ "time": "2021-03-05T19:20:35+00:00"
+ },
+ {
+ "name": "beberlei/assert",
+ "version": "v3.3.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/beberlei/assert.git",
+ "reference": "5367e3895976b49704ae671f75bc5f0ba1b986ab"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/beberlei/assert/zipball/5367e3895976b49704ae671f75bc5f0ba1b986ab",
+ "reference": "5367e3895976b49704ae671f75bc5f0ba1b986ab",
+ "shasum": ""
+ },
+ "require": {
+ "ext-ctype": "*",
+ "ext-intl": "*",
+ "ext-json": "*",
+ "ext-mbstring": "*",
+ "ext-simplexml": "*",
+ "php": "^7.0 || ^8.0"
+ },
+ "require-dev": {
+ "friendsofphp/php-cs-fixer": "*",
+ "phpstan/phpstan": "*",
+ "phpunit/phpunit": ">=6.0.0",
+ "yoast/phpunit-polyfills": "^0.1.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Assert\\": "lib/Assert"
+ },
+ "files": [
+ "lib/Assert/functions.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-2-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Benjamin Eberlei",
+ "email": "kontakt@beberlei.de",
+ "role": "Lead Developer"
+ },
+ {
+ "name": "Richard Quadling",
+ "email": "rquadling@gmail.com",
+ "role": "Collaborator"
+ }
+ ],
+ "description": "Thin assertion library for input validation in business models.",
+ "keywords": [
+ "assert",
+ "assertion",
+ "validation"
+ ],
+ "support": {
+ "issues": "https://github.com/beberlei/assert/issues",
+ "source": "https://github.com/beberlei/assert/tree/v3.3.0"
+ },
+ "time": "2020-11-13T20:02:54+00:00"
+ },
{
"name": "brick/math",
"version": "0.9.2",
@@ -102,6 +316,10 @@
"brick",
"math"
],
+ "support": {
+ "issues": "https://github.com/brick/math/issues",
+ "source": "https://github.com/brick/math/tree/0.9.2"
+ },
"funding": [
{
"url": "https://tidelift.com/funding/github/packagist/brick/math",
@@ -141,6 +359,10 @@
"MIT"
],
"description": "implementation of xdg base directory specification for php",
+ "support": {
+ "issues": "https://github.com/dnoegel/php-xdg-base-dir/issues",
+ "source": "https://github.com/dnoegel/php-xdg-base-dir/tree/v0.1.1"
+ },
"time": "2019-12-04T15:06:13+00:00"
},
{
@@ -218,6 +440,10 @@
"uppercase",
"words"
],
+ "support": {
+ "issues": "https://github.com/doctrine/inflector/issues",
+ "source": "https://github.com/doctrine/inflector/tree/2.0.x"
+ },
"funding": [
{
"url": "https://www.doctrine-project.org/sponsorship.html",
@@ -294,6 +520,10 @@
"parser",
"php"
],
+ "support": {
+ "issues": "https://github.com/doctrine/lexer/issues",
+ "source": "https://github.com/doctrine/lexer/tree/1.2.1"
+ },
"funding": [
{
"url": "https://www.doctrine-project.org/sponsorship.html",
@@ -359,6 +589,10 @@
"cron",
"schedule"
],
+ "support": {
+ "issues": "https://github.com/dragonmantank/cron-expression/issues",
+ "source": "https://github.com/dragonmantank/cron-expression/tree/v3.1.0"
+ },
"funding": [
{
"url": "https://github.com/dragonmantank",
@@ -423,6 +657,10 @@
"validation",
"validator"
],
+ "support": {
+ "issues": "https://github.com/egulias/EmailValidator/issues",
+ "source": "https://github.com/egulias/EmailValidator/tree/2.1.25"
+ },
"funding": [
{
"url": "https://github.com/egulias",
@@ -483,6 +721,10 @@
"proxy",
"trusted proxy"
],
+ "support": {
+ "issues": "https://github.com/fideloper/TrustedProxy/issues",
+ "source": "https://github.com/fideloper/TrustedProxy/tree/4.4.1"
+ },
"time": "2020-10-22T13:48:01+00:00"
},
{
@@ -550,6 +792,10 @@
"crossdomain",
"laravel"
],
+ "support": {
+ "issues": "https://github.com/fruitcake/laravel-cors/issues",
+ "source": "https://github.com/fruitcake/laravel-cors/tree/v2.0.3"
+ },
"funding": [
{
"url": "https://github.com/barryvdh",
@@ -608,6 +854,10 @@
"Result-Type",
"result"
],
+ "support": {
+ "issues": "https://github.com/GrahamCampbell/Result-Type/issues",
+ "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.0.1"
+ },
"funding": [
{
"url": "https://github.com/GrahamCampbell",
@@ -698,6 +948,10 @@
"rest",
"web service"
],
+ "support": {
+ "issues": "https://github.com/guzzle/guzzle/issues",
+ "source": "https://github.com/guzzle/guzzle/tree/7.2.0"
+ },
"funding": [
{
"url": "https://github.com/GrahamCampbell",
@@ -720,16 +974,16 @@
},
{
"name": "guzzlehttp/promises",
- "version": "1.4.0",
+ "version": "1.4.1",
"source": {
"type": "git",
"url": "https://github.com/guzzle/promises.git",
- "reference": "60d379c243457e073cff02bc323a2a86cb355631"
+ "reference": "8e7d04f1f6450fef59366c399cfad4b9383aa30d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/guzzle/promises/zipball/60d379c243457e073cff02bc323a2a86cb355631",
- "reference": "60d379c243457e073cff02bc323a2a86cb355631",
+ "url": "https://api.github.com/repos/guzzle/promises/zipball/8e7d04f1f6450fef59366c399cfad4b9383aa30d",
+ "reference": "8e7d04f1f6450fef59366c399cfad4b9383aa30d",
"shasum": ""
},
"require": {
@@ -767,7 +1021,11 @@
"keywords": [
"promise"
],
- "time": "2020-09-30T07:37:28+00:00"
+ "support": {
+ "issues": "https://github.com/guzzle/promises/issues",
+ "source": "https://github.com/guzzle/promises/tree/1.4.1"
+ },
+ "time": "2021-03-07T09:25:29+00:00"
},
{
"name": "guzzlehttp/psr7",
@@ -838,146 +1096,359 @@
"uri",
"url"
],
+ "support": {
+ "issues": "https://github.com/guzzle/psr7/issues",
+ "source": "https://github.com/guzzle/psr7/tree/1.7.0"
+ },
"time": "2020-09-30T07:37:11+00:00"
},
{
- "name": "laravel/framework",
- "version": "v8.29.0",
+ "name": "intervention/image",
+ "version": "2.5.1",
"source": {
"type": "git",
- "url": "https://github.com/laravel/framework.git",
- "reference": "d2eba352b3b3a3c515b18c5726b373fe5026733e"
+ "url": "https://github.com/Intervention/image.git",
+ "reference": "abbf18d5ab8367f96b3205ca3c89fb2fa598c69e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laravel/framework/zipball/d2eba352b3b3a3c515b18c5726b373fe5026733e",
- "reference": "d2eba352b3b3a3c515b18c5726b373fe5026733e",
+ "url": "https://api.github.com/repos/Intervention/image/zipball/abbf18d5ab8367f96b3205ca3c89fb2fa598c69e",
+ "reference": "abbf18d5ab8367f96b3205ca3c89fb2fa598c69e",
"shasum": ""
},
"require": {
- "doctrine/inflector": "^1.4|^2.0",
- "dragonmantank/cron-expression": "^3.0.2",
- "egulias/email-validator": "^2.1.10",
- "ext-json": "*",
- "ext-mbstring": "*",
- "ext-openssl": "*",
- "league/commonmark": "^1.3",
- "league/flysystem": "^1.1",
- "monolog/monolog": "^2.0",
- "nesbot/carbon": "^2.31",
- "opis/closure": "^3.6",
- "php": "^7.3|^8.0",
- "psr/container": "^1.0",
- "psr/simple-cache": "^1.0",
- "ramsey/uuid": "^4.0",
- "swiftmailer/swiftmailer": "^6.0",
- "symfony/console": "^5.1.4",
- "symfony/error-handler": "^5.1.4",
- "symfony/finder": "^5.1.4",
- "symfony/http-foundation": "^5.1.4",
- "symfony/http-kernel": "^5.1.4",
- "symfony/mime": "^5.1.4",
- "symfony/process": "^5.1.4",
- "symfony/routing": "^5.1.4",
- "symfony/var-dumper": "^5.1.4",
- "tijsverkoyen/css-to-inline-styles": "^2.2.2",
- "vlucas/phpdotenv": "^5.2",
- "voku/portable-ascii": "^1.4.8"
- },
- "conflict": {
- "tightenco/collect": "<5.5.33"
- },
- "provide": {
- "psr/container-implementation": "1.0"
- },
- "replace": {
- "illuminate/auth": "self.version",
- "illuminate/broadcasting": "self.version",
- "illuminate/bus": "self.version",
- "illuminate/cache": "self.version",
- "illuminate/collections": "self.version",
- "illuminate/config": "self.version",
- "illuminate/console": "self.version",
- "illuminate/container": "self.version",
- "illuminate/contracts": "self.version",
- "illuminate/cookie": "self.version",
- "illuminate/database": "self.version",
- "illuminate/encryption": "self.version",
- "illuminate/events": "self.version",
- "illuminate/filesystem": "self.version",
- "illuminate/hashing": "self.version",
- "illuminate/http": "self.version",
- "illuminate/log": "self.version",
- "illuminate/macroable": "self.version",
- "illuminate/mail": "self.version",
- "illuminate/notifications": "self.version",
- "illuminate/pagination": "self.version",
- "illuminate/pipeline": "self.version",
- "illuminate/queue": "self.version",
- "illuminate/redis": "self.version",
- "illuminate/routing": "self.version",
- "illuminate/session": "self.version",
- "illuminate/support": "self.version",
- "illuminate/testing": "self.version",
- "illuminate/translation": "self.version",
- "illuminate/validation": "self.version",
- "illuminate/view": "self.version"
+ "ext-fileinfo": "*",
+ "guzzlehttp/psr7": "~1.1",
+ "php": ">=5.4.0"
},
"require-dev": {
- "aws/aws-sdk-php": "^3.155",
- "doctrine/dbal": "^2.6|^3.0",
- "filp/whoops": "^2.8",
- "guzzlehttp/guzzle": "^6.5.5|^7.0.1",
- "league/flysystem-cached-adapter": "^1.0",
- "mockery/mockery": "^1.4.2",
- "orchestra/testbench-core": "^6.8",
- "pda/pheanstalk": "^4.0",
- "phpunit/phpunit": "^8.5.8|^9.3.3",
- "predis/predis": "^1.1.1",
- "symfony/cache": "^5.1.4"
+ "mockery/mockery": "~0.9.2",
+ "phpunit/phpunit": "^4.8 || ^5.7"
},
"suggest": {
- "aws/aws-sdk-php": "Required to use the SQS queue driver, DynamoDb failed job storage and SES mail driver (^3.155).",
- "brianium/paratest": "Required to run tests in parallel (^6.0).",
- "doctrine/dbal": "Required to rename columns and drop SQLite columns (^2.6|^3.0).",
- "ext-ftp": "Required to use the Flysystem FTP driver.",
- "ext-gd": "Required to use Illuminate\\Http\\Testing\\FileFactory::image().",
- "ext-memcached": "Required to use the memcache cache driver.",
- "ext-pcntl": "Required to use all features of the queue worker.",
- "ext-posix": "Required to use all features of the queue worker.",
- "ext-redis": "Required to use the Redis cache and queue drivers (^4.0|^5.0).",
- "fakerphp/faker": "Required to use the eloquent factory builder (^1.9.1).",
- "filp/whoops": "Required for friendly error pages in development (^2.8).",
- "guzzlehttp/guzzle": "Required to use the HTTP Client, Mailgun mail driver and the ping methods on schedules (^6.5.5|^7.0.1).",
- "laravel/tinker": "Required to use the tinker console command (^2.0).",
- "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^1.0).",
- "league/flysystem-cached-adapter": "Required to use the Flysystem cache (^1.0).",
- "league/flysystem-sftp": "Required to use the Flysystem SFTP driver (^1.0).",
- "mockery/mockery": "Required to use mocking (^1.4.2).",
- "nyholm/psr7": "Required to use PSR-7 bridging features (^1.2).",
- "pda/pheanstalk": "Required to use the beanstalk queue driver (^4.0).",
- "phpunit/phpunit": "Required to use assertions and run tests (^8.5.8|^9.3.3).",
- "predis/predis": "Required to use the predis connector (^1.1.2).",
- "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).",
- "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^4.0|^5.0).",
- "symfony/cache": "Required to PSR-6 cache bridge (^5.1.4).",
- "symfony/filesystem": "Required to enable support for relative symbolic links (^5.1.4).",
- "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^2.0).",
- "wildbit/swiftmailer-postmark": "Required to use Postmark mail driver (^3.0)."
+ "ext-gd": "to use GD library based image processing.",
+ "ext-imagick": "to use Imagick based image processing.",
+ "intervention/imagecache": "Caching extension for the Intervention Image library"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "8.x-dev"
+ "dev-master": "2.4-dev"
+ },
+ "laravel": {
+ "providers": [
+ "Intervention\\Image\\ImageServiceProvider"
+ ],
+ "aliases": {
+ "Image": "Intervention\\Image\\Facades\\Image"
+ }
}
},
"autoload": {
- "files": [
- "src/Illuminate/Collections/helpers.php",
- "src/Illuminate/Events/functions.php",
- "src/Illuminate/Foundation/helpers.php",
- "src/Illuminate/Support/helpers.php"
- ],
+ "psr-4": {
+ "Intervention\\Image\\": "src/Intervention/Image"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Oliver Vogel",
+ "email": "oliver@olivervogel.com",
+ "homepage": "http://olivervogel.com/"
+ }
+ ],
+ "description": "Image handling and manipulation library with support for Laravel integration",
+ "homepage": "http://image.intervention.io/",
+ "keywords": [
+ "gd",
+ "image",
+ "imagick",
+ "laravel",
+ "thumbnail",
+ "watermark"
+ ],
+ "support": {
+ "issues": "https://github.com/Intervention/image/issues",
+ "source": "https://github.com/Intervention/image/tree/master"
+ },
+ "time": "2019-11-02T09:15:47+00:00"
+ },
+ {
+ "name": "jaybizzle/crawler-detect",
+ "version": "v1.2.105",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/JayBizzle/Crawler-Detect.git",
+ "reference": "719c1ed49224857800c3dc40838b6b761d046105"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/JayBizzle/Crawler-Detect/zipball/719c1ed49224857800c3dc40838b6b761d046105",
+ "reference": "719c1ed49224857800c3dc40838b6b761d046105",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.8|^5.5|^6.5|^9.4"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Jaybizzle\\CrawlerDetect\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Mark Beech",
+ "email": "m@rkbee.ch",
+ "role": "Developer"
+ }
+ ],
+ "description": "CrawlerDetect is a PHP class for detecting bots/crawlers/spiders via the user agent",
+ "homepage": "https://github.com/JayBizzle/Crawler-Detect/",
+ "keywords": [
+ "crawler",
+ "crawler detect",
+ "crawler detector",
+ "crawlerdetect",
+ "php crawler detect"
+ ],
+ "support": {
+ "issues": "https://github.com/JayBizzle/Crawler-Detect/issues",
+ "source": "https://github.com/JayBizzle/Crawler-Detect/tree/v1.2.105"
+ },
+ "time": "2021-03-03T20:55:48+00:00"
+ },
+ {
+ "name": "jenssegers/agent",
+ "version": "v2.6.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/jenssegers/agent.git",
+ "reference": "daa11c43729510b3700bc34d414664966b03bffe"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/jenssegers/agent/zipball/daa11c43729510b3700bc34d414664966b03bffe",
+ "reference": "daa11c43729510b3700bc34d414664966b03bffe",
+ "shasum": ""
+ },
+ "require": {
+ "jaybizzle/crawler-detect": "^1.2",
+ "mobiledetect/mobiledetectlib": "^2.7.6",
+ "php": ">=5.6"
+ },
+ "require-dev": {
+ "php-coveralls/php-coveralls": "^2.1",
+ "phpunit/phpunit": "^5.0|^6.0|^7.0"
+ },
+ "suggest": {
+ "illuminate/support": "Required for laravel service providers"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.0-dev"
+ },
+ "laravel": {
+ "providers": [
+ "Jenssegers\\Agent\\AgentServiceProvider"
+ ],
+ "aliases": {
+ "Agent": "Jenssegers\\Agent\\Facades\\Agent"
+ }
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Jenssegers\\Agent\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jens Segers",
+ "homepage": "https://jenssegers.com"
+ }
+ ],
+ "description": "Desktop/mobile user agent parser with support for Laravel, based on Mobiledetect",
+ "homepage": "https://github.com/jenssegers/agent",
+ "keywords": [
+ "Agent",
+ "browser",
+ "desktop",
+ "laravel",
+ "mobile",
+ "platform",
+ "user agent",
+ "useragent"
+ ],
+ "support": {
+ "issues": "https://github.com/jenssegers/agent/issues",
+ "source": "https://github.com/jenssegers/agent/tree/v2.6.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/jenssegers",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/jenssegers/agent",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-06-13T08:05:20+00:00"
+ },
+ {
+ "name": "laravel/framework",
+ "version": "v8.31.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/laravel/framework.git",
+ "reference": "2aa5c2488d25178ebc097052c7897a0e463ddc35"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/laravel/framework/zipball/2aa5c2488d25178ebc097052c7897a0e463ddc35",
+ "reference": "2aa5c2488d25178ebc097052c7897a0e463ddc35",
+ "shasum": ""
+ },
+ "require": {
+ "doctrine/inflector": "^1.4|^2.0",
+ "dragonmantank/cron-expression": "^3.0.2",
+ "egulias/email-validator": "^2.1.10",
+ "ext-json": "*",
+ "ext-mbstring": "*",
+ "ext-openssl": "*",
+ "league/commonmark": "^1.3",
+ "league/flysystem": "^1.1",
+ "monolog/monolog": "^2.0",
+ "nesbot/carbon": "^2.31",
+ "opis/closure": "^3.6",
+ "php": "^7.3|^8.0",
+ "psr/container": "^1.0",
+ "psr/simple-cache": "^1.0",
+ "ramsey/uuid": "^4.0",
+ "swiftmailer/swiftmailer": "^6.0",
+ "symfony/console": "^5.1.4",
+ "symfony/error-handler": "^5.1.4",
+ "symfony/finder": "^5.1.4",
+ "symfony/http-foundation": "^5.1.4",
+ "symfony/http-kernel": "^5.1.4",
+ "symfony/mime": "^5.1.4",
+ "symfony/process": "^5.1.4",
+ "symfony/routing": "^5.1.4",
+ "symfony/var-dumper": "^5.1.4",
+ "tijsverkoyen/css-to-inline-styles": "^2.2.2",
+ "vlucas/phpdotenv": "^5.2",
+ "voku/portable-ascii": "^1.4.8"
+ },
+ "conflict": {
+ "tightenco/collect": "<5.5.33"
+ },
+ "provide": {
+ "psr/container-implementation": "1.0"
+ },
+ "replace": {
+ "illuminate/auth": "self.version",
+ "illuminate/broadcasting": "self.version",
+ "illuminate/bus": "self.version",
+ "illuminate/cache": "self.version",
+ "illuminate/collections": "self.version",
+ "illuminate/config": "self.version",
+ "illuminate/console": "self.version",
+ "illuminate/container": "self.version",
+ "illuminate/contracts": "self.version",
+ "illuminate/cookie": "self.version",
+ "illuminate/database": "self.version",
+ "illuminate/encryption": "self.version",
+ "illuminate/events": "self.version",
+ "illuminate/filesystem": "self.version",
+ "illuminate/hashing": "self.version",
+ "illuminate/http": "self.version",
+ "illuminate/log": "self.version",
+ "illuminate/macroable": "self.version",
+ "illuminate/mail": "self.version",
+ "illuminate/notifications": "self.version",
+ "illuminate/pagination": "self.version",
+ "illuminate/pipeline": "self.version",
+ "illuminate/queue": "self.version",
+ "illuminate/redis": "self.version",
+ "illuminate/routing": "self.version",
+ "illuminate/session": "self.version",
+ "illuminate/support": "self.version",
+ "illuminate/testing": "self.version",
+ "illuminate/translation": "self.version",
+ "illuminate/validation": "self.version",
+ "illuminate/view": "self.version"
+ },
+ "require-dev": {
+ "aws/aws-sdk-php": "^3.155",
+ "doctrine/dbal": "^2.6|^3.0",
+ "filp/whoops": "^2.8",
+ "guzzlehttp/guzzle": "^6.5.5|^7.0.1",
+ "league/flysystem-cached-adapter": "^1.0",
+ "mockery/mockery": "^1.4.2",
+ "orchestra/testbench-core": "^6.8",
+ "pda/pheanstalk": "^4.0",
+ "phpunit/phpunit": "^8.5.8|^9.3.3",
+ "predis/predis": "^1.1.1",
+ "symfony/cache": "^5.1.4"
+ },
+ "suggest": {
+ "aws/aws-sdk-php": "Required to use the SQS queue driver, DynamoDb failed job storage and SES mail driver (^3.155).",
+ "brianium/paratest": "Required to run tests in parallel (^6.0).",
+ "doctrine/dbal": "Required to rename columns and drop SQLite columns (^2.6|^3.0).",
+ "ext-ftp": "Required to use the Flysystem FTP driver.",
+ "ext-gd": "Required to use Illuminate\\Http\\Testing\\FileFactory::image().",
+ "ext-memcached": "Required to use the memcache cache driver.",
+ "ext-pcntl": "Required to use all features of the queue worker.",
+ "ext-posix": "Required to use all features of the queue worker.",
+ "ext-redis": "Required to use the Redis cache and queue drivers (^4.0|^5.0).",
+ "fakerphp/faker": "Required to use the eloquent factory builder (^1.9.1).",
+ "filp/whoops": "Required for friendly error pages in development (^2.8).",
+ "guzzlehttp/guzzle": "Required to use the HTTP Client, Mailgun mail driver and the ping methods on schedules (^6.5.5|^7.0.1).",
+ "laravel/tinker": "Required to use the tinker console command (^2.0).",
+ "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^1.0).",
+ "league/flysystem-cached-adapter": "Required to use the Flysystem cache (^1.0).",
+ "league/flysystem-sftp": "Required to use the Flysystem SFTP driver (^1.0).",
+ "mockery/mockery": "Required to use mocking (^1.4.2).",
+ "nyholm/psr7": "Required to use PSR-7 bridging features (^1.2).",
+ "pda/pheanstalk": "Required to use the beanstalk queue driver (^4.0).",
+ "phpunit/phpunit": "Required to use assertions and run tests (^8.5.8|^9.3.3).",
+ "predis/predis": "Required to use the predis connector (^1.1.2).",
+ "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).",
+ "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^4.0|^5.0).",
+ "symfony/cache": "Required to PSR-6 cache bridge (^5.1.4).",
+ "symfony/filesystem": "Required to enable support for relative symbolic links (^5.1.4).",
+ "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^2.0).",
+ "wildbit/swiftmailer-postmark": "Required to use Postmark mail driver (^3.0)."
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "8.x-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/Illuminate/Collections/helpers.php",
+ "src/Illuminate/Events/functions.php",
+ "src/Illuminate/Foundation/helpers.php",
+ "src/Illuminate/Support/helpers.php"
+ ],
"psr-4": {
"Illuminate\\": "src/Illuminate/",
"Illuminate\\Support\\": [
@@ -992,228 +1463,1673 @@
],
"authors": [
{
- "name": "Taylor Otwell",
- "email": "taylor@laravel.com"
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ }
+ ],
+ "description": "The Laravel Framework.",
+ "homepage": "https://laravel.com",
+ "keywords": [
+ "framework",
+ "laravel"
+ ],
+ "support": {
+ "issues": "https://github.com/laravel/framework/issues",
+ "source": "https://github.com/laravel/framework"
+ },
+ "time": "2021-03-04T15:22:36+00:00"
+ },
+ {
+ "name": "laravel/helpers",
+ "version": "v1.4.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/laravel/helpers.git",
+ "reference": "febb10d8daaf86123825de2cb87f789a3371f0ac"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/laravel/helpers/zipball/febb10d8daaf86123825de2cb87f789a3371f0ac",
+ "reference": "febb10d8daaf86123825de2cb87f789a3371f0ac",
+ "shasum": ""
+ },
+ "require": {
+ "illuminate/support": "~5.8.0|^6.0|^7.0|^8.0",
+ "php": "^7.1.3|^8.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^7.0|^8.0|^9.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/helpers.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ },
+ {
+ "name": "Dries Vints",
+ "email": "dries.vints@gmail.com"
+ }
+ ],
+ "description": "Provides backwards compatibility for helpers in the latest Laravel release.",
+ "keywords": [
+ "helpers",
+ "laravel"
+ ],
+ "support": {
+ "source": "https://github.com/laravel/helpers/tree/v1.4.1"
+ },
+ "time": "2021-02-16T15:27:11+00:00"
+ },
+ {
+ "name": "laravel/legacy-factories",
+ "version": "v1.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/laravel/legacy-factories.git",
+ "reference": "5e3fe2fd5fda64e20ea5c74c831a7346294e902a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/laravel/legacy-factories/zipball/5e3fe2fd5fda64e20ea5c74c831a7346294e902a",
+ "reference": "5e3fe2fd5fda64e20ea5c74c831a7346294e902a",
+ "shasum": ""
+ },
+ "require": {
+ "illuminate/macroable": "^8.0",
+ "php": "^7.3|^8.0",
+ "symfony/finder": "^3.4|^4.0|^5.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.x-dev"
+ },
+ "laravel": {
+ "providers": [
+ "Illuminate\\Database\\Eloquent\\LegacyFactoryServiceProvider"
+ ]
+ }
+ },
+ "autoload": {
+ "files": [
+ "helpers.php"
+ ],
+ "psr-4": {
+ "Illuminate\\Database\\Eloquent\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ }
+ ],
+ "description": "The legacy version of the Laravel Eloquent factories.",
+ "homepage": "http://laravel.com",
+ "support": {
+ "issues": "https://github.com/laravel/framework/issues",
+ "source": "https://github.com/laravel/framework"
+ },
+ "time": "2020-10-27T14:25:32+00:00"
+ },
+ {
+ "name": "laravel/socialite",
+ "version": "v5.2.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/laravel/socialite.git",
+ "reference": "8d25d574b4f2005411c0b9cb527ef5e745c1b07d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/laravel/socialite/zipball/8d25d574b4f2005411c0b9cb527ef5e745c1b07d",
+ "reference": "8d25d574b4f2005411c0b9cb527ef5e745c1b07d",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "guzzlehttp/guzzle": "^6.0|^7.0",
+ "illuminate/http": "^6.0|^7.0|^8.0",
+ "illuminate/support": "^6.0|^7.0|^8.0",
+ "league/oauth1-client": "^1.0",
+ "php": "^7.2|^8.0"
+ },
+ "require-dev": {
+ "illuminate/contracts": "^6.0|^7.0",
+ "mockery/mockery": "^1.0",
+ "orchestra/testbench": "^4.0|^5.0|^6.0",
+ "phpunit/phpunit": "^8.0|^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "5.x-dev"
+ },
+ "laravel": {
+ "providers": [
+ "Laravel\\Socialite\\SocialiteServiceProvider"
+ ],
+ "aliases": {
+ "Socialite": "Laravel\\Socialite\\Facades\\Socialite"
+ }
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Laravel\\Socialite\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ }
+ ],
+ "description": "Laravel wrapper around OAuth 1 & OAuth 2 libraries.",
+ "homepage": "https://laravel.com",
+ "keywords": [
+ "laravel",
+ "oauth"
+ ],
+ "support": {
+ "issues": "https://github.com/laravel/socialite/issues",
+ "source": "https://github.com/laravel/socialite"
+ },
+ "time": "2021-03-02T16:50:47+00:00"
+ },
+ {
+ "name": "laravel/tinker",
+ "version": "v2.6.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/laravel/tinker.git",
+ "reference": "04ad32c1a3328081097a181875733fa51f402083"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/laravel/tinker/zipball/04ad32c1a3328081097a181875733fa51f402083",
+ "reference": "04ad32c1a3328081097a181875733fa51f402083",
+ "shasum": ""
+ },
+ "require": {
+ "illuminate/console": "^6.0|^7.0|^8.0",
+ "illuminate/contracts": "^6.0|^7.0|^8.0",
+ "illuminate/support": "^6.0|^7.0|^8.0",
+ "php": "^7.2.5|^8.0",
+ "psy/psysh": "^0.10.4",
+ "symfony/var-dumper": "^4.3.4|^5.0"
+ },
+ "require-dev": {
+ "mockery/mockery": "~1.3.3|^1.4.2",
+ "phpunit/phpunit": "^8.5.8|^9.3.3"
+ },
+ "suggest": {
+ "illuminate/database": "The Illuminate Database package (^6.0|^7.0|^8.0)."
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.x-dev"
+ },
+ "laravel": {
+ "providers": [
+ "Laravel\\Tinker\\TinkerServiceProvider"
+ ]
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Laravel\\Tinker\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ }
+ ],
+ "description": "Powerful REPL for the Laravel framework.",
+ "keywords": [
+ "REPL",
+ "Tinker",
+ "laravel",
+ "psysh"
+ ],
+ "support": {
+ "issues": "https://github.com/laravel/tinker/issues",
+ "source": "https://github.com/laravel/tinker/tree/v2.6.1"
+ },
+ "time": "2021-03-02T16:53:12+00:00"
+ },
+ {
+ "name": "league/commonmark",
+ "version": "1.5.7",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/thephpleague/commonmark.git",
+ "reference": "11df9b36fd4f1d2b727a73bf14931d81373b9a54"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/11df9b36fd4f1d2b727a73bf14931d81373b9a54",
+ "reference": "11df9b36fd4f1d2b727a73bf14931d81373b9a54",
+ "shasum": ""
+ },
+ "require": {
+ "ext-mbstring": "*",
+ "php": "^7.1 || ^8.0"
+ },
+ "conflict": {
+ "scrutinizer/ocular": "1.7.*"
+ },
+ "require-dev": {
+ "cebe/markdown": "~1.0",
+ "commonmark/commonmark.js": "0.29.2",
+ "erusev/parsedown": "~1.0",
+ "ext-json": "*",
+ "github/gfm": "0.29.0",
+ "michelf/php-markdown": "~1.4",
+ "mikehaertl/php-shellcommand": "^1.4",
+ "phpstan/phpstan": "^0.12",
+ "phpunit/phpunit": "^7.5 || ^8.5 || ^9.2",
+ "scrutinizer/ocular": "^1.5",
+ "symfony/finder": "^4.2"
+ },
+ "bin": [
+ "bin/commonmark"
+ ],
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "League\\CommonMark\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Colin O'Dell",
+ "email": "colinodell@gmail.com",
+ "homepage": "https://www.colinodell.com",
+ "role": "Lead Developer"
+ }
+ ],
+ "description": "Highly-extensible PHP Markdown parser which fully supports the CommonMark spec and Github-Flavored Markdown (GFM)",
+ "homepage": "https://commonmark.thephpleague.com",
+ "keywords": [
+ "commonmark",
+ "flavored",
+ "gfm",
+ "github",
+ "github-flavored",
+ "markdown",
+ "md",
+ "parser"
+ ],
+ "support": {
+ "docs": "https://commonmark.thephpleague.com/",
+ "issues": "https://github.com/thephpleague/commonmark/issues",
+ "rss": "https://github.com/thephpleague/commonmark/releases.atom",
+ "source": "https://github.com/thephpleague/commonmark"
+ },
+ "funding": [
+ {
+ "url": "https://enjoy.gitstore.app/repositories/thephpleague/commonmark",
+ "type": "custom"
+ },
+ {
+ "url": "https://www.colinodell.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://www.paypal.me/colinpodell/10.00",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/colinodell",
+ "type": "github"
+ },
+ {
+ "url": "https://www.patreon.com/colinodell",
+ "type": "patreon"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/league/commonmark",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-10-31T13:49:32+00:00"
+ },
+ {
+ "name": "league/flysystem",
+ "version": "1.1.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/thephpleague/flysystem.git",
+ "reference": "9be3b16c877d477357c015cec057548cf9b2a14a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/9be3b16c877d477357c015cec057548cf9b2a14a",
+ "reference": "9be3b16c877d477357c015cec057548cf9b2a14a",
+ "shasum": ""
+ },
+ "require": {
+ "ext-fileinfo": "*",
+ "league/mime-type-detection": "^1.3",
+ "php": "^7.2.5 || ^8.0"
+ },
+ "conflict": {
+ "league/flysystem-sftp": "<1.0.6"
+ },
+ "require-dev": {
+ "phpspec/prophecy": "^1.11.1",
+ "phpunit/phpunit": "^8.5.8"
+ },
+ "suggest": {
+ "ext-fileinfo": "Required for MimeType",
+ "ext-ftp": "Allows you to use FTP server storage",
+ "ext-openssl": "Allows you to use FTPS server storage",
+ "league/flysystem-aws-s3-v2": "Allows you to use S3 storage with AWS SDK v2",
+ "league/flysystem-aws-s3-v3": "Allows you to use S3 storage with AWS SDK v3",
+ "league/flysystem-azure": "Allows you to use Windows Azure Blob storage",
+ "league/flysystem-cached-adapter": "Flysystem adapter decorator for metadata caching",
+ "league/flysystem-eventable-filesystem": "Allows you to use EventableFilesystem",
+ "league/flysystem-rackspace": "Allows you to use Rackspace Cloud Files",
+ "league/flysystem-sftp": "Allows you to use SFTP server storage via phpseclib",
+ "league/flysystem-webdav": "Allows you to use WebDAV storage",
+ "league/flysystem-ziparchive": "Allows you to use ZipArchive adapter",
+ "spatie/flysystem-dropbox": "Allows you to use Dropbox storage",
+ "srmklive/flysystem-dropbox-v2": "Allows you to use Dropbox storage for PHP 5 applications"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.1-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "League\\Flysystem\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Frank de Jonge",
+ "email": "info@frenky.net"
+ }
+ ],
+ "description": "Filesystem abstraction: Many filesystems, one API.",
+ "keywords": [
+ "Cloud Files",
+ "WebDAV",
+ "abstraction",
+ "aws",
+ "cloud",
+ "copy.com",
+ "dropbox",
+ "file systems",
+ "files",
+ "filesystem",
+ "filesystems",
+ "ftp",
+ "rackspace",
+ "remote",
+ "s3",
+ "sftp",
+ "storage"
+ ],
+ "support": {
+ "issues": "https://github.com/thephpleague/flysystem/issues",
+ "source": "https://github.com/thephpleague/flysystem/tree/1.x"
+ },
+ "funding": [
+ {
+ "url": "https://offset.earth/frankdejonge",
+ "type": "other"
+ }
+ ],
+ "time": "2020-08-23T07:39:11+00:00"
+ },
+ {
+ "name": "league/flysystem-aws-s3-v3",
+ "version": "1.0.29",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/thephpleague/flysystem-aws-s3-v3.git",
+ "reference": "4e25cc0582a36a786c31115e419c6e40498f6972"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/thephpleague/flysystem-aws-s3-v3/zipball/4e25cc0582a36a786c31115e419c6e40498f6972",
+ "reference": "4e25cc0582a36a786c31115e419c6e40498f6972",
+ "shasum": ""
+ },
+ "require": {
+ "aws/aws-sdk-php": "^3.20.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",
+ "support": {
+ "issues": "https://github.com/thephpleague/flysystem-aws-s3-v3/issues",
+ "source": "https://github.com/thephpleague/flysystem-aws-s3-v3/tree/1.0.29"
+ },
+ "time": "2020-10-08T18:58:37+00:00"
+ },
+ {
+ "name": "league/flysystem-cached-adapter",
+ "version": "1.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/thephpleague/flysystem-cached-adapter.git",
+ "reference": "d1925efb2207ac4be3ad0c40b8277175f99ffaff"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/thephpleague/flysystem-cached-adapter/zipball/d1925efb2207ac4be3ad0c40b8277175f99ffaff",
+ "reference": "d1925efb2207ac4be3ad0c40b8277175f99ffaff",
+ "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.",
+ "support": {
+ "issues": "https://github.com/thephpleague/flysystem-cached-adapter/issues",
+ "source": "https://github.com/thephpleague/flysystem-cached-adapter/tree/master"
+ },
+ "time": "2020-07-25T15:56:04+00:00"
+ },
+ {
+ "name": "league/glide",
+ "version": "1.7.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/thephpleague/glide.git",
+ "reference": "ae5e26700573cb678919d28e425a8b87bc71c546"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/thephpleague/glide/zipball/ae5e26700573cb678919d28e425a8b87bc71c546",
+ "reference": "ae5e26700573cb678919d28e425a8b87bc71c546",
+ "shasum": ""
+ },
+ "require": {
+ "intervention/image": "^2.4",
+ "league/flysystem": "^1.0",
+ "php": "^7.2|^8.0",
+ "psr/http-message": "^1.0"
+ },
+ "require-dev": {
+ "mockery/mockery": "^1.3.3",
+ "phpunit/php-token-stream": "^3.1|^4.0",
+ "phpunit/phpunit": "^8.5|^9.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.1-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "League\\Glide\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jonathan Reinink",
+ "email": "jonathan@reinink.ca",
+ "homepage": "http://reinink.ca"
+ }
+ ],
+ "description": "Wonderfully easy on-demand image manipulation library with an HTTP based API.",
+ "homepage": "http://glide.thephpleague.com",
+ "keywords": [
+ "ImageMagick",
+ "editing",
+ "gd",
+ "image",
+ "imagick",
+ "league",
+ "manipulation",
+ "processing"
+ ],
+ "support": {
+ "issues": "https://github.com/thephpleague/glide/issues",
+ "source": "https://github.com/thephpleague/glide/tree/1.7.0"
+ },
+ "time": "2020-11-05T17:34:03+00:00"
+ },
+ {
+ "name": "league/mime-type-detection",
+ "version": "1.7.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/thephpleague/mime-type-detection.git",
+ "reference": "3b9dff8aaf7323590c1d2e443db701eb1f9aa0d3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/3b9dff8aaf7323590c1d2e443db701eb1f9aa0d3",
+ "reference": "3b9dff8aaf7323590c1d2e443db701eb1f9aa0d3",
+ "shasum": ""
+ },
+ "require": {
+ "ext-fileinfo": "*",
+ "php": "^7.2 || ^8.0"
+ },
+ "require-dev": {
+ "friendsofphp/php-cs-fixer": "^2.18",
+ "phpstan/phpstan": "^0.12.68",
+ "phpunit/phpunit": "^8.5.8 || ^9.3"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "League\\MimeTypeDetection\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Frank de Jonge",
+ "email": "info@frankdejonge.nl"
+ }
+ ],
+ "description": "Mime-type detection for Flysystem",
+ "support": {
+ "issues": "https://github.com/thephpleague/mime-type-detection/issues",
+ "source": "https://github.com/thephpleague/mime-type-detection/tree/1.7.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/frankdejonge",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/league/flysystem",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-01-18T20:58:21+00:00"
+ },
+ {
+ "name": "league/oauth1-client",
+ "version": "v1.9.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/thephpleague/oauth1-client.git",
+ "reference": "1e7e6be2dc543bf466236fb171e5b20e1b06aee6"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/thephpleague/oauth1-client/zipball/1e7e6be2dc543bf466236fb171e5b20e1b06aee6",
+ "reference": "1e7e6be2dc543bf466236fb171e5b20e1b06aee6",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "ext-openssl": "*",
+ "guzzlehttp/guzzle": "^6.0|^7.0",
+ "php": ">=7.1||>=8.0"
+ },
+ "require-dev": {
+ "ext-simplexml": "*",
+ "friendsofphp/php-cs-fixer": "^2.17",
+ "mockery/mockery": "^1.3.3",
+ "phpstan/phpstan": "^0.12.42",
+ "phpunit/phpunit": "^7.5||9.5"
+ },
+ "suggest": {
+ "ext-simplexml": "For decoding XML-based responses."
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0-dev",
+ "dev-develop": "2.0-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "League\\OAuth1\\Client\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Ben Corlett",
+ "email": "bencorlett@me.com",
+ "homepage": "http://www.webcomm.com.au",
+ "role": "Developer"
+ }
+ ],
+ "description": "OAuth 1.0 Client Library",
+ "keywords": [
+ "Authentication",
+ "SSO",
+ "authorization",
+ "bitbucket",
+ "identity",
+ "idp",
+ "oauth",
+ "oauth1",
+ "single sign on",
+ "trello",
+ "tumblr",
+ "twitter"
+ ],
+ "support": {
+ "issues": "https://github.com/thephpleague/oauth1-client/issues",
+ "source": "https://github.com/thephpleague/oauth1-client/tree/v1.9.0"
+ },
+ "time": "2021-01-20T01:40:53+00:00"
+ },
+ {
+ "name": "maennchen/zipstream-php",
+ "version": "2.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/maennchen/ZipStream-PHP.git",
+ "reference": "c4c5803cc1f93df3d2448478ef79394a5981cc58"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/c4c5803cc1f93df3d2448478ef79394a5981cc58",
+ "reference": "c4c5803cc1f93df3d2448478ef79394a5981cc58",
+ "shasum": ""
+ },
+ "require": {
+ "myclabs/php-enum": "^1.5",
+ "php": ">= 7.1",
+ "psr/http-message": "^1.0",
+ "symfony/polyfill-mbstring": "^1.0"
+ },
+ "require-dev": {
+ "ext-zip": "*",
+ "guzzlehttp/guzzle": ">= 6.3",
+ "mikey179/vfsstream": "^1.6",
+ "phpunit/phpunit": ">= 7.5"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "ZipStream\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Paul Duncan",
+ "email": "pabs@pablotron.org"
+ },
+ {
+ "name": "Jonatan Männchen",
+ "email": "jonatan@maennchen.ch"
+ },
+ {
+ "name": "Jesse Donat",
+ "email": "donatj@gmail.com"
+ },
+ {
+ "name": "András Kolesár",
+ "email": "kolesar@kolesar.hu"
+ }
+ ],
+ "description": "ZipStream is a library for dynamically streaming dynamic zip files from PHP without writing to the disk at all on the server.",
+ "keywords": [
+ "stream",
+ "zip"
+ ],
+ "support": {
+ "issues": "https://github.com/maennchen/ZipStream-PHP/issues",
+ "source": "https://github.com/maennchen/ZipStream-PHP/tree/master"
+ },
+ "funding": [
+ {
+ "url": "https://opencollective.com/zipstream",
+ "type": "open_collective"
+ }
+ ],
+ "time": "2020-05-30T13:11:16+00:00"
+ },
+ {
+ "name": "mobiledetect/mobiledetectlib",
+ "version": "2.8.37",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/serbanghita/Mobile-Detect.git",
+ "reference": "9841e3c46f5bd0739b53aed8ac677fa712943df7"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/serbanghita/Mobile-Detect/zipball/9841e3c46f5bd0739b53aed8ac677fa712943df7",
+ "reference": "9841e3c46f5bd0739b53aed8ac677fa712943df7",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.0.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.8.35||~5.7"
+ },
+ "type": "library",
+ "autoload": {
+ "classmap": [
+ "Mobile_Detect.php"
+ ],
+ "psr-0": {
+ "Detection": "namespaced/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Serban Ghita",
+ "email": "serbanghita@gmail.com",
+ "homepage": "http://mobiledetect.net",
+ "role": "Developer"
+ }
+ ],
+ "description": "Mobile_Detect is a lightweight PHP class for detecting mobile devices. It uses the User-Agent string combined with specific HTTP headers to detect the mobile environment.",
+ "homepage": "https://github.com/serbanghita/Mobile-Detect",
+ "keywords": [
+ "detect mobile devices",
+ "mobile",
+ "mobile detect",
+ "mobile detector",
+ "php mobile detect"
+ ],
+ "support": {
+ "issues": "https://github.com/serbanghita/Mobile-Detect/issues",
+ "source": "https://github.com/serbanghita/Mobile-Detect/tree/2.8.37"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/serbanghita",
+ "type": "github"
+ }
+ ],
+ "time": "2021-02-19T21:22:57+00:00"
+ },
+ {
+ "name": "monolog/monolog",
+ "version": "2.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/Seldaek/monolog.git",
+ "reference": "1cb1cde8e8dd0f70cc0fe51354a59acad9302084"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/Seldaek/monolog/zipball/1cb1cde8e8dd0f70cc0fe51354a59acad9302084",
+ "reference": "1cb1cde8e8dd0f70cc0fe51354a59acad9302084",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2",
+ "psr/log": "^1.0.1"
+ },
+ "provide": {
+ "psr/log-implementation": "1.0.0"
+ },
+ "require-dev": {
+ "aws/aws-sdk-php": "^2.4.9 || ^3.0",
+ "doctrine/couchdb": "~1.0@dev",
+ "elasticsearch/elasticsearch": "^7",
+ "graylog2/gelf-php": "^1.4.2",
+ "mongodb/mongodb": "^1.8",
+ "php-amqplib/php-amqplib": "~2.4",
+ "php-console/php-console": "^3.1.3",
+ "phpspec/prophecy": "^1.6.1",
+ "phpstan/phpstan": "^0.12.59",
+ "phpunit/phpunit": "^8.5",
+ "predis/predis": "^1.1",
+ "rollbar/rollbar": "^1.3",
+ "ruflin/elastica": ">=0.90 <7.0.1",
+ "swiftmailer/swiftmailer": "^5.3|^6.0"
+ },
+ "suggest": {
+ "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB",
+ "doctrine/couchdb": "Allow sending log messages to a CouchDB server",
+ "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client",
+ "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)",
+ "ext-mbstring": "Allow to work properly with unicode symbols",
+ "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)",
+ "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server",
+ "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)",
+ "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib",
+ "php-console/php-console": "Allow sending log messages to Google Chrome",
+ "rollbar/rollbar": "Allow sending log messages to Rollbar",
+ "ruflin/elastica": "Allow sending log messages to an Elastic Search server"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "2.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Monolog\\": "src/Monolog"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jordi Boggiano",
+ "email": "j.boggiano@seld.be",
+ "homepage": "https://seld.be"
+ }
+ ],
+ "description": "Sends your logs to files, sockets, inboxes, databases and various web services",
+ "homepage": "https://github.com/Seldaek/monolog",
+ "keywords": [
+ "log",
+ "logging",
+ "psr-3"
+ ],
+ "support": {
+ "issues": "https://github.com/Seldaek/monolog/issues",
+ "source": "https://github.com/Seldaek/monolog/tree/2.2.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/Seldaek",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/monolog/monolog",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-12-14T13:15:25+00:00"
+ },
+ {
+ "name": "morilog/jalali",
+ "version": "v3.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/morilog/jalali.git",
+ "reference": "7ea78b84ce3b5546b01217febb2fba4915dac5e5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/morilog/jalali/zipball/7ea78b84ce3b5546b01217febb2fba4915dac5e5",
+ "reference": "7ea78b84ce3b5546b01217febb2fba4915dac5e5",
+ "shasum": ""
+ },
+ "require": {
+ "beberlei/assert": "3.*",
+ "nesbot/carbon": "^1.21 || ^2.0",
+ "php": "^7.0 | ^7.1 | ^7.2 | ^8.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Morilog\\Jalali\\": "src"
+ },
+ "files": [
+ "src/helpers.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Milad Rey",
+ "email": "miladr@gmail.com"
+ },
+ {
+ "name": "Morteza Parvini",
+ "email": "m.parvini@outlook.com"
+ }
+ ],
+ "description": "This Package helps developers to easily work with Jalali (Shamsi or Iranian) dates in PHP applications, based on Jalali (Shamsi) DateTime class.",
+ "keywords": [
+ "Jalali",
+ "date",
+ "datetime",
+ "laravel",
+ "morilog"
+ ],
+ "support": {
+ "issues": "https://github.com/morilog/jalali/issues",
+ "source": "https://github.com/morilog/jalali/tree/v3.2.0"
+ },
+ "time": "2020-12-01T21:26:31+00:00"
+ },
+ {
+ "name": "mtdowling/jmespath.php",
+ "version": "2.6.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/jmespath/jmespath.php.git",
+ "reference": "42dae2cbd13154083ca6d70099692fef8ca84bfb"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/42dae2cbd13154083ca6d70099692fef8ca84bfb",
+ "reference": "42dae2cbd13154083ca6d70099692fef8ca84bfb",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^5.4 || ^7.0 || ^8.0",
+ "symfony/polyfill-mbstring": "^1.17"
+ },
+ "require-dev": {
+ "composer/xdebug-handler": "^1.4",
+ "phpunit/phpunit": "^4.8.36 || ^7.5.15"
+ },
+ "bin": [
+ "bin/jp.php"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.6-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"
+ ],
+ "support": {
+ "issues": "https://github.com/jmespath/jmespath.php/issues",
+ "source": "https://github.com/jmespath/jmespath.php/tree/2.6.0"
+ },
+ "time": "2020-07-31T21:01:56+00:00"
+ },
+ {
+ "name": "myclabs/php-enum",
+ "version": "1.8.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/myclabs/php-enum.git",
+ "reference": "46cf3d8498b095bd33727b13fd5707263af99421"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/myclabs/php-enum/zipball/46cf3d8498b095bd33727b13fd5707263af99421",
+ "reference": "46cf3d8498b095bd33727b13fd5707263af99421",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "php": "^7.3 || ^8.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.5",
+ "squizlabs/php_codesniffer": "1.*",
+ "vimeo/psalm": "^4.5.1"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "MyCLabs\\Enum\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP Enum contributors",
+ "homepage": "https://github.com/myclabs/php-enum/graphs/contributors"
+ }
+ ],
+ "description": "PHP Enum implementation",
+ "homepage": "http://github.com/myclabs/php-enum",
+ "keywords": [
+ "enum"
+ ],
+ "support": {
+ "issues": "https://github.com/myclabs/php-enum/issues",
+ "source": "https://github.com/myclabs/php-enum/tree/1.8.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/mnapoli",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/myclabs/php-enum",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-02-15T16:11:48+00:00"
+ },
+ {
+ "name": "nesbot/carbon",
+ "version": "2.46.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/briannesbitt/Carbon.git",
+ "reference": "2fd2c4a77d58a4e95234c8a61c5df1f157a91bf4"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/2fd2c4a77d58a4e95234c8a61c5df1f157a91bf4",
+ "reference": "2fd2c4a77d58a4e95234c8a61c5df1f157a91bf4",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "php": "^7.1.8 || ^8.0",
+ "symfony/polyfill-mbstring": "^1.0",
+ "symfony/translation": "^3.4 || ^4.0 || ^5.0"
+ },
+ "require-dev": {
+ "doctrine/orm": "^2.7",
+ "friendsofphp/php-cs-fixer": "^2.14 || ^3.0",
+ "kylekatarnls/multi-tester": "^2.0",
+ "phpmd/phpmd": "^2.9",
+ "phpstan/extension-installer": "^1.0",
+ "phpstan/phpstan": "^0.12.54",
+ "phpunit/phpunit": "^7.5.20 || ^8.5.14",
+ "squizlabs/php_codesniffer": "^3.4"
+ },
+ "bin": [
+ "bin/carbon"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.x-dev",
+ "dev-3.x": "3.x-dev"
+ },
+ "laravel": {
+ "providers": [
+ "Carbon\\Laravel\\ServiceProvider"
+ ]
+ },
+ "phpstan": {
+ "includes": [
+ "extension.neon"
+ ]
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Carbon\\": "src/Carbon/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Brian Nesbitt",
+ "email": "brian@nesbot.com",
+ "homepage": "http://nesbot.com"
+ },
+ {
+ "name": "kylekatarnls",
+ "homepage": "http://github.com/kylekatarnls"
+ }
+ ],
+ "description": "An API extension for DateTime that supports 281 different languages.",
+ "homepage": "http://carbon.nesbot.com",
+ "keywords": [
+ "date",
+ "datetime",
+ "time"
+ ],
+ "support": {
+ "issues": "https://github.com/briannesbitt/Carbon/issues",
+ "source": "https://github.com/briannesbitt/Carbon"
+ },
+ "funding": [
+ {
+ "url": "https://opencollective.com/Carbon",
+ "type": "open_collective"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/nesbot/carbon",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-02-24T17:30:44+00:00"
+ },
+ {
+ "name": "nikic/php-parser",
+ "version": "v4.10.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/nikic/PHP-Parser.git",
+ "reference": "c6d052fc58cb876152f89f532b95a8d7907e7f0e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/c6d052fc58cb876152f89f532b95a8d7907e7f0e",
+ "reference": "c6d052fc58cb876152f89f532b95a8d7907e7f0e",
+ "shasum": ""
+ },
+ "require": {
+ "ext-tokenizer": "*",
+ "php": ">=7.0"
+ },
+ "require-dev": {
+ "ircmaxell/php-yacc": "^0.0.7",
+ "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0"
+ },
+ "bin": [
+ "bin/php-parse"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.9-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "PhpParser\\": "lib/PhpParser"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Nikita Popov"
+ }
+ ],
+ "description": "A PHP parser written in PHP",
+ "keywords": [
+ "parser",
+ "php"
+ ],
+ "support": {
+ "issues": "https://github.com/nikic/PHP-Parser/issues",
+ "source": "https://github.com/nikic/PHP-Parser/tree/v4.10.4"
+ },
+ "time": "2020-12-20T10:01:03+00:00"
+ },
+ {
+ "name": "opis/closure",
+ "version": "3.6.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/opis/closure.git",
+ "reference": "943b5d70cc5ae7483f6aff6ff43d7e34592ca0f5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/opis/closure/zipball/943b5d70cc5ae7483f6aff6ff43d7e34592ca0f5",
+ "reference": "943b5d70cc5ae7483f6aff6ff43d7e34592ca0f5",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^5.4 || ^7.0 || ^8.0"
+ },
+ "require-dev": {
+ "jeremeamia/superclosure": "^2.0",
+ "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.6.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Opis\\Closure\\": "src/"
+ },
+ "files": [
+ "functions.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Marius Sarca",
+ "email": "marius.sarca@gmail.com"
+ },
+ {
+ "name": "Sorin Sarca",
+ "email": "sarca_sorin@hotmail.com"
+ }
+ ],
+ "description": "A library that can be used to serialize closures (anonymous functions) and arbitrary objects.",
+ "homepage": "https://opis.io/closure",
+ "keywords": [
+ "anonymous functions",
+ "closure",
+ "function",
+ "serializable",
+ "serialization",
+ "serialize"
+ ],
+ "support": {
+ "issues": "https://github.com/opis/closure/issues",
+ "source": "https://github.com/opis/closure/tree/3.6.1"
+ },
+ "time": "2020-11-07T02:01:34+00:00"
+ },
+ {
+ "name": "php-amqplib/php-amqplib",
+ "version": "v2.12.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-amqplib/php-amqplib.git",
+ "reference": "0eaaa9d5d45335f4342f69603288883388c2fe21"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-amqplib/php-amqplib/zipball/0eaaa9d5d45335f4342f69603288883388c2fe21",
+ "reference": "0eaaa9d5d45335f4342f69603288883388c2fe21",
+ "shasum": ""
+ },
+ "require": {
+ "ext-mbstring": "*",
+ "ext-sockets": "*",
+ "php": ">=5.6.3",
+ "phpseclib/phpseclib": "^2.0.0"
+ },
+ "conflict": {
+ "php": "7.4.0 - 7.4.1"
+ },
+ "replace": {
+ "videlalvaro/php-amqplib": "self.version"
+ },
+ "require-dev": {
+ "ext-curl": "*",
+ "nategood/httpful": "^0.2.20",
+ "phpunit/phpunit": "^5.7|^6.5|^7.0",
+ "squizlabs/php_codesniffer": "^2.5"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.12-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "PhpAmqpLib\\": "PhpAmqpLib/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "LGPL-2.1-or-later"
+ ],
+ "authors": [
+ {
+ "name": "Alvaro Videla",
+ "role": "Original Maintainer"
+ },
+ {
+ "name": "Raúl Araya",
+ "email": "nubeiro@gmail.com",
+ "role": "Maintainer"
+ },
+ {
+ "name": "Luke Bakken",
+ "email": "luke@bakken.io",
+ "role": "Maintainer"
+ },
+ {
+ "name": "Ramūnas Dronga",
+ "email": "github@ramuno.lt",
+ "role": "Maintainer"
}
],
- "description": "The Laravel Framework.",
- "homepage": "https://laravel.com",
+ "description": "Formerly videlalvaro/php-amqplib. This library is a pure PHP implementation of the AMQP protocol. It's been tested against RabbitMQ.",
+ "homepage": "https://github.com/php-amqplib/php-amqplib/",
"keywords": [
- "framework",
- "laravel"
+ "message",
+ "queue",
+ "rabbitmq"
],
- "time": "2021-02-23T14:27:41+00:00"
+ "support": {
+ "issues": "https://github.com/php-amqplib/php-amqplib/issues",
+ "source": "https://github.com/php-amqplib/php-amqplib/tree/v2.12.1"
+ },
+ "time": "2020-09-25T18:34:58+00:00"
},
{
- "name": "laravel/tinker",
- "version": "v2.6.0",
+ "name": "phpoption/phpoption",
+ "version": "1.7.5",
"source": {
"type": "git",
- "url": "https://github.com/laravel/tinker.git",
- "reference": "daae1c43f1300fe88c05d83db6f3d8f76677ad88"
+ "url": "https://github.com/schmittjoh/php-option.git",
+ "reference": "994ecccd8f3283ecf5ac33254543eb0ac946d525"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laravel/tinker/zipball/daae1c43f1300fe88c05d83db6f3d8f76677ad88",
- "reference": "daae1c43f1300fe88c05d83db6f3d8f76677ad88",
+ "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/994ecccd8f3283ecf5ac33254543eb0ac946d525",
+ "reference": "994ecccd8f3283ecf5ac33254543eb0ac946d525",
"shasum": ""
},
"require": {
- "illuminate/console": "^6.0|^7.0|^8.0",
- "illuminate/contracts": "^6.0|^7.0|^8.0",
- "illuminate/support": "^6.0|^7.0|^8.0",
- "php": "^7.2.5|^8.0",
- "psy/psysh": "^0.10.4",
- "symfony/var-dumper": "^4.3.4|^5.0"
+ "php": "^5.5.9 || ^7.0 || ^8.0"
},
"require-dev": {
- "mockery/mockery": "~1.3.3|^1.4.2",
- "phpunit/phpunit": "^8.5.8|^9.3.3"
- },
- "suggest": {
- "illuminate/database": "The Illuminate Database package (^6.0|^7.0|^8.0)."
+ "bamarni/composer-bin-plugin": "^1.4.1",
+ "phpunit/phpunit": "^4.8.35 || ^5.7.27 || ^6.5.6 || ^7.0 || ^8.0 || ^9.0"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "2.x-dev"
- },
- "laravel": {
- "providers": [
- "Laravel\\Tinker\\TinkerServiceProvider"
- ]
+ "dev-master": "1.7-dev"
}
},
"autoload": {
"psr-4": {
- "Laravel\\Tinker\\": "src/"
+ "PhpOption\\": "src/PhpOption/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "MIT"
+ "Apache-2.0"
],
"authors": [
{
- "name": "Taylor Otwell",
- "email": "taylor@laravel.com"
+ "name": "Johannes M. Schmitt",
+ "email": "schmittjoh@gmail.com"
+ },
+ {
+ "name": "Graham Campbell",
+ "email": "graham@alt-three.com"
}
],
- "description": "Powerful REPL for the Laravel framework.",
+ "description": "Option Type for PHP",
"keywords": [
- "REPL",
- "Tinker",
- "laravel",
- "psysh"
+ "language",
+ "option",
+ "php",
+ "type"
+ ],
+ "support": {
+ "issues": "https://github.com/schmittjoh/php-option/issues",
+ "source": "https://github.com/schmittjoh/php-option/tree/1.7.5"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/GrahamCampbell",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption",
+ "type": "tidelift"
+ }
],
- "time": "2021-01-26T20:35:18+00:00"
+ "time": "2020-07-20T17:29:33+00:00"
},
{
- "name": "league/commonmark",
- "version": "1.5.7",
+ "name": "phpseclib/phpseclib",
+ "version": "2.0.30",
"source": {
"type": "git",
- "url": "https://github.com/thephpleague/commonmark.git",
- "reference": "11df9b36fd4f1d2b727a73bf14931d81373b9a54"
+ "url": "https://github.com/phpseclib/phpseclib.git",
+ "reference": "136b9ca7eebef78be14abf90d65c5e57b6bc5d36"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/11df9b36fd4f1d2b727a73bf14931d81373b9a54",
- "reference": "11df9b36fd4f1d2b727a73bf14931d81373b9a54",
+ "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/136b9ca7eebef78be14abf90d65c5e57b6bc5d36",
+ "reference": "136b9ca7eebef78be14abf90d65c5e57b6bc5d36",
"shasum": ""
},
"require": {
- "ext-mbstring": "*",
- "php": "^7.1 || ^8.0"
- },
- "conflict": {
- "scrutinizer/ocular": "1.7.*"
+ "php": ">=5.3.3"
},
"require-dev": {
- "cebe/markdown": "~1.0",
- "commonmark/commonmark.js": "0.29.2",
- "erusev/parsedown": "~1.0",
- "ext-json": "*",
- "github/gfm": "0.29.0",
- "michelf/php-markdown": "~1.4",
- "mikehaertl/php-shellcommand": "^1.4",
- "phpstan/phpstan": "^0.12",
- "phpunit/phpunit": "^7.5 || ^8.5 || ^9.2",
- "scrutinizer/ocular": "^1.5",
- "symfony/finder": "^4.2"
+ "phing/phing": "~2.7",
+ "phpunit/phpunit": "^4.8.35|^5.7|^6.0|^9.4",
+ "squizlabs/php_codesniffer": "~2.0"
+ },
+ "suggest": {
+ "ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.",
+ "ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.",
+ "ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.",
+ "ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations."
},
- "bin": [
- "bin/commonmark"
- ],
"type": "library",
"autoload": {
+ "files": [
+ "phpseclib/bootstrap.php"
+ ],
"psr-4": {
- "League\\CommonMark\\": "src"
+ "phpseclib\\": "phpseclib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "BSD-3-Clause"
+ "MIT"
],
"authors": [
{
- "name": "Colin O'Dell",
- "email": "colinodell@gmail.com",
- "homepage": "https://www.colinodell.com",
+ "name": "Jim Wigginton",
+ "email": "terrafrost@php.net",
"role": "Lead Developer"
- }
- ],
- "description": "Highly-extensible PHP Markdown parser which fully supports the CommonMark spec and Github-Flavored Markdown (GFM)",
- "homepage": "https://commonmark.thephpleague.com",
- "keywords": [
- "commonmark",
- "flavored",
- "gfm",
- "github",
- "github-flavored",
- "markdown",
- "md",
- "parser"
- ],
- "funding": [
+ },
{
- "url": "https://enjoy.gitstore.app/repositories/thephpleague/commonmark",
- "type": "custom"
+ "name": "Patrick Monnerat",
+ "email": "pm@datasphere.ch",
+ "role": "Developer"
},
{
- "url": "https://www.colinodell.com/sponsor",
- "type": "custom"
+ "name": "Andreas Fischer",
+ "email": "bantu@phpbb.com",
+ "role": "Developer"
},
{
- "url": "https://www.paypal.me/colinpodell/10.00",
- "type": "custom"
+ "name": "Hans-Jürgen Petrich",
+ "email": "petrich@tronic-media.com",
+ "role": "Developer"
},
{
- "url": "https://github.com/colinodell",
+ "name": "Graham Campbell",
+ "email": "graham@alt-three.com",
+ "role": "Developer"
+ }
+ ],
+ "description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.",
+ "homepage": "http://phpseclib.sourceforge.net",
+ "keywords": [
+ "BigInteger",
+ "aes",
+ "asn.1",
+ "asn1",
+ "blowfish",
+ "crypto",
+ "cryptography",
+ "encryption",
+ "rsa",
+ "security",
+ "sftp",
+ "signature",
+ "signing",
+ "ssh",
+ "twofish",
+ "x.509",
+ "x509"
+ ],
+ "support": {
+ "issues": "https://github.com/phpseclib/phpseclib/issues",
+ "source": "https://github.com/phpseclib/phpseclib/tree/2.0.30"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/terrafrost",
"type": "github"
},
{
- "url": "https://www.patreon.com/colinodell",
+ "url": "https://www.patreon.com/phpseclib",
"type": "patreon"
},
{
- "url": "https://tidelift.com/funding/github/packagist/league/commonmark",
+ "url": "https://tidelift.com/funding/github/packagist/phpseclib/phpseclib",
"type": "tidelift"
}
],
- "time": "2020-10-31T13:49:32+00:00"
+ "time": "2020-12-17T05:42:04+00:00"
},
{
- "name": "league/flysystem",
- "version": "1.1.3",
+ "name": "psr/cache",
+ "version": "1.0.1",
"source": {
"type": "git",
- "url": "https://github.com/thephpleague/flysystem.git",
- "reference": "9be3b16c877d477357c015cec057548cf9b2a14a"
+ "url": "https://github.com/php-fig/cache.git",
+ "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/9be3b16c877d477357c015cec057548cf9b2a14a",
- "reference": "9be3b16c877d477357c015cec057548cf9b2a14a",
+ "url": "https://api.github.com/repos/php-fig/cache/zipball/d11b50ad223250cf17b86e38383413f5a6764bf8",
+ "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8",
"shasum": ""
},
"require": {
- "ext-fileinfo": "*",
- "league/mime-type-detection": "^1.3",
- "php": "^7.2.5 || ^8.0"
- },
- "conflict": {
- "league/flysystem-sftp": "<1.0.6"
- },
- "require-dev": {
- "phpspec/prophecy": "^1.11.1",
- "phpunit/phpunit": "^8.5.8"
- },
- "suggest": {
- "ext-fileinfo": "Required for MimeType",
- "ext-ftp": "Allows you to use FTP server storage",
- "ext-openssl": "Allows you to use FTPS server storage",
- "league/flysystem-aws-s3-v2": "Allows you to use S3 storage with AWS SDK v2",
- "league/flysystem-aws-s3-v3": "Allows you to use S3 storage with AWS SDK v3",
- "league/flysystem-azure": "Allows you to use Windows Azure Blob storage",
- "league/flysystem-cached-adapter": "Flysystem adapter decorator for metadata caching",
- "league/flysystem-eventable-filesystem": "Allows you to use EventableFilesystem",
- "league/flysystem-rackspace": "Allows you to use Rackspace Cloud Files",
- "league/flysystem-sftp": "Allows you to use SFTP server storage via phpseclib",
- "league/flysystem-webdav": "Allows you to use WebDAV storage",
- "league/flysystem-ziparchive": "Allows you to use ZipArchive adapter",
- "spatie/flysystem-dropbox": "Allows you to use Dropbox storage",
- "srmklive/flysystem-dropbox-v2": "Allows you to use Dropbox storage for PHP 5 applications"
+ "php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.1-dev"
+ "dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
- "League\\Flysystem\\": "src/"
+ "Psr\\Cache\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -1222,65 +3138,42 @@
],
"authors": [
{
- "name": "Frank de Jonge",
- "email": "info@frenky.net"
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
}
],
- "description": "Filesystem abstraction: Many filesystems, one API.",
+ "description": "Common interface for caching libraries",
"keywords": [
- "Cloud Files",
- "WebDAV",
- "abstraction",
- "aws",
- "cloud",
- "copy.com",
- "dropbox",
- "file systems",
- "files",
- "filesystem",
- "filesystems",
- "ftp",
- "rackspace",
- "remote",
- "s3",
- "sftp",
- "storage"
- ],
- "funding": [
- {
- "url": "https://offset.earth/frankdejonge",
- "type": "other"
- }
+ "cache",
+ "psr",
+ "psr-6"
],
- "time": "2020-08-23T07:39:11+00:00"
+ "support": {
+ "source": "https://github.com/php-fig/cache/tree/master"
+ },
+ "time": "2016-08-06T20:24:11+00:00"
},
{
- "name": "league/mime-type-detection",
- "version": "1.7.0",
+ "name": "psr/container",
+ "version": "1.1.1",
"source": {
"type": "git",
- "url": "https://github.com/thephpleague/mime-type-detection.git",
- "reference": "3b9dff8aaf7323590c1d2e443db701eb1f9aa0d3"
+ "url": "https://github.com/php-fig/container.git",
+ "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/3b9dff8aaf7323590c1d2e443db701eb1f9aa0d3",
- "reference": "3b9dff8aaf7323590c1d2e443db701eb1f9aa0d3",
+ "url": "https://api.github.com/repos/php-fig/container/zipball/8622567409010282b7aeebe4bb841fe98b58dcaf",
+ "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf",
"shasum": ""
},
"require": {
- "ext-fileinfo": "*",
- "php": "^7.2 || ^8.0"
- },
- "require-dev": {
- "friendsofphp/php-cs-fixer": "^2.18",
- "phpstan/phpstan": "^0.12.68",
- "phpunit/phpunit": "^8.5.8 || ^9.3"
+ "php": ">=7.2.0"
},
"type": "library",
"autoload": {
"psr-4": {
- "League\\MimeTypeDetection\\": "src"
+ "Psr\\Container\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -1289,83 +3182,51 @@
],
"authors": [
{
- "name": "Frank de Jonge",
- "email": "info@frankdejonge.nl"
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
}
],
- "description": "Mime-type detection for Flysystem",
- "funding": [
- {
- "url": "https://github.com/frankdejonge",
- "type": "github"
- },
- {
- "url": "https://tidelift.com/funding/github/packagist/league/flysystem",
- "type": "tidelift"
- }
+ "description": "Common Container Interface (PHP FIG PSR-11)",
+ "homepage": "https://github.com/php-fig/container",
+ "keywords": [
+ "PSR-11",
+ "container",
+ "container-interface",
+ "container-interop",
+ "psr"
],
- "time": "2021-01-18T20:58:21+00:00"
+ "support": {
+ "issues": "https://github.com/php-fig/container/issues",
+ "source": "https://github.com/php-fig/container/tree/1.1.1"
+ },
+ "time": "2021-03-05T17:36:06+00:00"
},
{
- "name": "monolog/monolog",
- "version": "2.2.0",
+ "name": "psr/event-dispatcher",
+ "version": "1.0.0",
"source": {
"type": "git",
- "url": "https://github.com/Seldaek/monolog.git",
- "reference": "1cb1cde8e8dd0f70cc0fe51354a59acad9302084"
+ "url": "https://github.com/php-fig/event-dispatcher.git",
+ "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/Seldaek/monolog/zipball/1cb1cde8e8dd0f70cc0fe51354a59acad9302084",
- "reference": "1cb1cde8e8dd0f70cc0fe51354a59acad9302084",
+ "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0",
+ "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0",
"shasum": ""
},
"require": {
- "php": ">=7.2",
- "psr/log": "^1.0.1"
- },
- "provide": {
- "psr/log-implementation": "1.0.0"
- },
- "require-dev": {
- "aws/aws-sdk-php": "^2.4.9 || ^3.0",
- "doctrine/couchdb": "~1.0@dev",
- "elasticsearch/elasticsearch": "^7",
- "graylog2/gelf-php": "^1.4.2",
- "mongodb/mongodb": "^1.8",
- "php-amqplib/php-amqplib": "~2.4",
- "php-console/php-console": "^3.1.3",
- "phpspec/prophecy": "^1.6.1",
- "phpstan/phpstan": "^0.12.59",
- "phpunit/phpunit": "^8.5",
- "predis/predis": "^1.1",
- "rollbar/rollbar": "^1.3",
- "ruflin/elastica": ">=0.90 <7.0.1",
- "swiftmailer/swiftmailer": "^5.3|^6.0"
- },
- "suggest": {
- "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB",
- "doctrine/couchdb": "Allow sending log messages to a CouchDB server",
- "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client",
- "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)",
- "ext-mbstring": "Allow to work properly with unicode symbols",
- "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)",
- "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server",
- "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)",
- "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib",
- "php-console/php-console": "Allow sending log messages to Google Chrome",
- "rollbar/rollbar": "Allow sending log messages to Rollbar",
- "ruflin/elastica": "Allow sending log messages to an Elastic Search server"
+ "php": ">=7.2.0"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "2.x-dev"
+ "dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
- "Monolog\\": "src/Monolog"
+ "Psr\\EventDispatcher\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -1374,83 +3235,49 @@
],
"authors": [
{
- "name": "Jordi Boggiano",
- "email": "j.boggiano@seld.be",
- "homepage": "https://seld.be"
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
}
],
- "description": "Sends your logs to files, sockets, inboxes, databases and various web services",
- "homepage": "https://github.com/Seldaek/monolog",
+ "description": "Standard interfaces for event handling.",
"keywords": [
- "log",
- "logging",
- "psr-3"
- ],
- "funding": [
- {
- "url": "https://github.com/Seldaek",
- "type": "github"
- },
- {
- "url": "https://tidelift.com/funding/github/packagist/monolog/monolog",
- "type": "tidelift"
- }
+ "events",
+ "psr",
+ "psr-14"
],
- "time": "2020-12-14T13:15:25+00:00"
+ "support": {
+ "issues": "https://github.com/php-fig/event-dispatcher/issues",
+ "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0"
+ },
+ "time": "2019-01-08T18:20:26+00:00"
},
{
- "name": "nesbot/carbon",
- "version": "2.45.1",
+ "name": "psr/http-client",
+ "version": "1.0.1",
"source": {
"type": "git",
- "url": "https://github.com/briannesbitt/Carbon.git",
- "reference": "528783b188bdb853eb21239b1722831e0f000a8d"
+ "url": "https://github.com/php-fig/http-client.git",
+ "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/528783b188bdb853eb21239b1722831e0f000a8d",
- "reference": "528783b188bdb853eb21239b1722831e0f000a8d",
+ "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
+ "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
"shasum": ""
},
"require": {
- "ext-json": "*",
- "php": "^7.1.8 || ^8.0",
- "symfony/polyfill-mbstring": "^1.0",
- "symfony/translation": "^3.4 || ^4.0 || ^5.0"
- },
- "require-dev": {
- "doctrine/orm": "^2.7",
- "friendsofphp/php-cs-fixer": "^2.14 || ^3.0",
- "kylekatarnls/multi-tester": "^2.0",
- "phpmd/phpmd": "^2.9",
- "phpstan/extension-installer": "^1.0",
- "phpstan/phpstan": "^0.12.54",
- "phpunit/phpunit": "^7.5.20 || ^8.5.14",
- "squizlabs/php_codesniffer": "^3.4"
+ "php": "^7.0 || ^8.0",
+ "psr/http-message": "^1.0"
},
- "bin": [
- "bin/carbon"
- ],
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "2.x-dev",
- "dev-3.x": "3.x-dev"
- },
- "laravel": {
- "providers": [
- "Carbon\\Laravel\\ServiceProvider"
- ]
- },
- "phpstan": {
- "includes": [
- "extension.neon"
- ]
+ "dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
- "Carbon\\": "src/Carbon/"
+ "Psr\\Http\\Client\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -1459,120 +3286,103 @@
],
"authors": [
{
- "name": "Brian Nesbitt",
- "email": "brian@nesbot.com",
- "homepage": "http://nesbot.com"
- },
- {
- "name": "kylekatarnls",
- "homepage": "http://github.com/kylekatarnls"
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
}
],
- "description": "An API extension for DateTime that supports 281 different languages.",
- "homepage": "http://carbon.nesbot.com",
+ "description": "Common interface for HTTP clients",
+ "homepage": "https://github.com/php-fig/http-client",
"keywords": [
- "date",
- "datetime",
- "time"
- ],
- "funding": [
- {
- "url": "https://opencollective.com/Carbon",
- "type": "open_collective"
- },
- {
- "url": "https://tidelift.com/funding/github/packagist/nesbot/carbon",
- "type": "tidelift"
- }
+ "http",
+ "http-client",
+ "psr",
+ "psr-18"
],
- "time": "2021-02-11T18:30:17+00:00"
+ "support": {
+ "source": "https://github.com/php-fig/http-client/tree/master"
+ },
+ "time": "2020-06-29T06:28:15+00:00"
},
{
- "name": "nikic/php-parser",
- "version": "v4.10.4",
+ "name": "psr/http-message",
+ "version": "1.0.1",
"source": {
"type": "git",
- "url": "https://github.com/nikic/PHP-Parser.git",
- "reference": "c6d052fc58cb876152f89f532b95a8d7907e7f0e"
+ "url": "https://github.com/php-fig/http-message.git",
+ "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/c6d052fc58cb876152f89f532b95a8d7907e7f0e",
- "reference": "c6d052fc58cb876152f89f532b95a8d7907e7f0e",
+ "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
+ "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
"shasum": ""
},
"require": {
- "ext-tokenizer": "*",
- "php": ">=7.0"
- },
- "require-dev": {
- "ircmaxell/php-yacc": "^0.0.7",
- "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0"
+ "php": ">=5.3.0"
},
- "bin": [
- "bin/php-parse"
- ],
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "4.9-dev"
+ "dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
- "PhpParser\\": "lib/PhpParser"
+ "Psr\\Http\\Message\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "BSD-3-Clause"
+ "MIT"
],
"authors": [
{
- "name": "Nikita Popov"
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
}
],
- "description": "A PHP parser written in PHP",
+ "description": "Common interface for HTTP messages",
+ "homepage": "https://github.com/php-fig/http-message",
"keywords": [
- "parser",
- "php"
+ "http",
+ "http-message",
+ "psr",
+ "psr-7",
+ "request",
+ "response"
],
- "time": "2020-12-20T10:01:03+00:00"
+ "support": {
+ "source": "https://github.com/php-fig/http-message/tree/master"
+ },
+ "time": "2016-08-06T14:39:51+00:00"
},
{
- "name": "opis/closure",
- "version": "3.6.1",
+ "name": "psr/log",
+ "version": "1.1.3",
"source": {
"type": "git",
- "url": "https://github.com/opis/closure.git",
- "reference": "943b5d70cc5ae7483f6aff6ff43d7e34592ca0f5"
+ "url": "https://github.com/php-fig/log.git",
+ "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/opis/closure/zipball/943b5d70cc5ae7483f6aff6ff43d7e34592ca0f5",
- "reference": "943b5d70cc5ae7483f6aff6ff43d7e34592ca0f5",
+ "url": "https://api.github.com/repos/php-fig/log/zipball/0f73288fd15629204f9d42b7055f72dacbe811fc",
+ "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc",
"shasum": ""
},
"require": {
- "php": "^5.4 || ^7.0 || ^8.0"
- },
- "require-dev": {
- "jeremeamia/superclosure": "^2.0",
- "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.0"
+ "php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "3.6.x-dev"
+ "dev-master": "1.1.x-dev"
}
},
"autoload": {
"psr-4": {
- "Opis\\Closure\\": "src/"
- },
- "files": [
- "functions.php"
- ]
+ "Psr\\Log\\": "Psr/Log/"
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@@ -1580,117 +3390,122 @@
],
"authors": [
{
- "name": "Marius Sarca",
- "email": "marius.sarca@gmail.com"
- },
- {
- "name": "Sorin Sarca",
- "email": "sarca_sorin@hotmail.com"
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
}
],
- "description": "A library that can be used to serialize closures (anonymous functions) and arbitrary objects.",
- "homepage": "https://opis.io/closure",
+ "description": "Common interface for logging libraries",
+ "homepage": "https://github.com/php-fig/log",
"keywords": [
- "anonymous functions",
- "closure",
- "function",
- "serializable",
- "serialization",
- "serialize"
+ "log",
+ "psr",
+ "psr-3"
],
- "time": "2020-11-07T02:01:34+00:00"
+ "support": {
+ "source": "https://github.com/php-fig/log/tree/1.1.3"
+ },
+ "time": "2020-03-23T09:12:05+00:00"
},
- {
- "name": "phpoption/phpoption",
- "version": "1.7.5",
+ {
+ "name": "psr/simple-cache",
+ "version": "1.0.1",
"source": {
"type": "git",
- "url": "https://github.com/schmittjoh/php-option.git",
- "reference": "994ecccd8f3283ecf5ac33254543eb0ac946d525"
+ "url": "https://github.com/php-fig/simple-cache.git",
+ "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/994ecccd8f3283ecf5ac33254543eb0ac946d525",
- "reference": "994ecccd8f3283ecf5ac33254543eb0ac946d525",
+ "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/408d5eafb83c57f6365a3ca330ff23aa4a5fa39b",
+ "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b",
"shasum": ""
},
"require": {
- "php": "^5.5.9 || ^7.0 || ^8.0"
- },
- "require-dev": {
- "bamarni/composer-bin-plugin": "^1.4.1",
- "phpunit/phpunit": "^4.8.35 || ^5.7.27 || ^6.5.6 || ^7.0 || ^8.0 || ^9.0"
+ "php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.7-dev"
+ "dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
- "PhpOption\\": "src/PhpOption/"
+ "Psr\\SimpleCache\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "Apache-2.0"
+ "MIT"
],
"authors": [
{
- "name": "Johannes M. Schmitt",
- "email": "schmittjoh@gmail.com"
- },
- {
- "name": "Graham Campbell",
- "email": "graham@alt-three.com"
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
}
],
- "description": "Option Type for PHP",
+ "description": "Common interfaces for simple caching",
"keywords": [
- "language",
- "option",
- "php",
- "type"
- ],
- "funding": [
- {
- "url": "https://github.com/GrahamCampbell",
- "type": "github"
- },
- {
- "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption",
- "type": "tidelift"
- }
+ "cache",
+ "caching",
+ "psr",
+ "psr-16",
+ "simple-cache"
],
- "time": "2020-07-20T17:29:33+00:00"
+ "support": {
+ "source": "https://github.com/php-fig/simple-cache/tree/master"
+ },
+ "time": "2017-10-23T01:57:42+00:00"
},
{
- "name": "psr/container",
- "version": "1.0.0",
+ "name": "psy/psysh",
+ "version": "v0.10.6",
"source": {
"type": "git",
- "url": "https://github.com/php-fig/container.git",
- "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f"
+ "url": "https://github.com/bobthecow/psysh.git",
+ "reference": "6f990c19f91729de8b31e639d6e204ea59f19cf3"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
- "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
+ "url": "https://api.github.com/repos/bobthecow/psysh/zipball/6f990c19f91729de8b31e639d6e204ea59f19cf3",
+ "reference": "6f990c19f91729de8b31e639d6e204ea59f19cf3",
"shasum": ""
},
"require": {
- "php": ">=5.3.0"
+ "dnoegel/php-xdg-base-dir": "0.1.*",
+ "ext-json": "*",
+ "ext-tokenizer": "*",
+ "nikic/php-parser": "~4.0|~3.0|~2.0|~1.3",
+ "php": "^8.0 || ^7.0 || ^5.5.9",
+ "symfony/console": "~5.0|~4.0|~3.0|^2.4.2|~2.3.10",
+ "symfony/var-dumper": "~5.0|~4.0|~3.0|~2.7"
+ },
+ "require-dev": {
+ "bamarni/composer-bin-plugin": "^1.2",
+ "hoa/console": "3.17.*"
+ },
+ "suggest": {
+ "ext-pcntl": "Enabling the PCNTL extension makes PsySH a lot happier :)",
+ "ext-pdo-sqlite": "The doc command requires SQLite to work.",
+ "ext-posix": "If you have PCNTL, you'll want the POSIX extension as well.",
+ "ext-readline": "Enables support for arrow-key history navigation, and showing and manipulating command history.",
+ "hoa/console": "A pure PHP readline implementation. You'll want this if your PHP install doesn't already support readline or libedit."
},
+ "bin": [
+ "bin/psysh"
+ ],
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.0.x-dev"
+ "dev-main": "0.10.x-dev"
}
},
"autoload": {
+ "files": [
+ "src/functions.php"
+ ],
"psr-4": {
- "Psr\\Container\\": "src/"
+ "Psy\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -1699,48 +3514,51 @@
],
"authors": [
{
- "name": "PHP-FIG",
- "homepage": "http://www.php-fig.org/"
+ "name": "Justin Hileman",
+ "email": "justin@justinhileman.info",
+ "homepage": "http://justinhileman.com"
}
],
- "description": "Common Container Interface (PHP FIG PSR-11)",
- "homepage": "https://github.com/php-fig/container",
+ "description": "An interactive shell for modern PHP.",
+ "homepage": "http://psysh.org",
"keywords": [
- "PSR-11",
- "container",
- "container-interface",
- "container-interop",
- "psr"
+ "REPL",
+ "console",
+ "interactive",
+ "shell"
],
- "time": "2017-02-14T16:28:37+00:00"
+ "support": {
+ "issues": "https://github.com/bobthecow/psysh/issues",
+ "source": "https://github.com/bobthecow/psysh/tree/v0.10.6"
+ },
+ "time": "2021-01-18T15:53:43+00:00"
},
{
- "name": "psr/event-dispatcher",
- "version": "1.0.0",
+ "name": "ralouphie/getallheaders",
+ "version": "3.0.3",
"source": {
"type": "git",
- "url": "https://github.com/php-fig/event-dispatcher.git",
- "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0"
+ "url": "https://github.com/ralouphie/getallheaders.git",
+ "reference": "120b605dfeb996808c31b6477290a714d356e822"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0",
- "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0",
+ "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822",
+ "reference": "120b605dfeb996808c31b6477290a714d356e822",
"shasum": ""
},
"require": {
- "php": ">=7.2.0"
+ "php": ">=5.6"
},
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "1.0.x-dev"
- }
+ "require-dev": {
+ "php-coveralls/php-coveralls": "^2.1",
+ "phpunit/phpunit": "^5 || ^6.5"
},
+ "type": "library",
"autoload": {
- "psr-4": {
- "Psr\\EventDispatcher\\": "src/"
- }
+ "files": [
+ "src/getallheaders.php"
+ ]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@@ -1748,45 +3566,56 @@
],
"authors": [
{
- "name": "PHP-FIG",
- "homepage": "http://www.php-fig.org/"
+ "name": "Ralph Khattar",
+ "email": "ralph.khattar@gmail.com"
}
],
- "description": "Standard interfaces for event handling.",
- "keywords": [
- "events",
- "psr",
- "psr-14"
- ],
- "time": "2019-01-08T18:20:26+00:00"
+ "description": "A polyfill for getallheaders.",
+ "support": {
+ "issues": "https://github.com/ralouphie/getallheaders/issues",
+ "source": "https://github.com/ralouphie/getallheaders/tree/develop"
+ },
+ "time": "2019-03-08T08:55:37+00:00"
},
{
- "name": "psr/http-client",
- "version": "1.0.1",
+ "name": "ramsey/collection",
+ "version": "1.1.3",
"source": {
"type": "git",
- "url": "https://github.com/php-fig/http-client.git",
- "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621"
+ "url": "https://github.com/ramsey/collection.git",
+ "reference": "28a5c4ab2f5111db6a60b2b4ec84057e0f43b9c1"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
- "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
+ "url": "https://api.github.com/repos/ramsey/collection/zipball/28a5c4ab2f5111db6a60b2b4ec84057e0f43b9c1",
+ "reference": "28a5c4ab2f5111db6a60b2b4ec84057e0f43b9c1",
"shasum": ""
},
"require": {
- "php": "^7.0 || ^8.0",
- "psr/http-message": "^1.0"
+ "php": "^7.2 || ^8"
},
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "1.0.x-dev"
- }
+ "require-dev": {
+ "captainhook/captainhook": "^5.3",
+ "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0",
+ "ergebnis/composer-normalize": "^2.6",
+ "fakerphp/faker": "^1.5",
+ "hamcrest/hamcrest-php": "^2",
+ "jangregor/phpstan-prophecy": "^0.8",
+ "mockery/mockery": "^1.3",
+ "phpstan/extension-installer": "^1",
+ "phpstan/phpstan": "^0.12.32",
+ "phpstan/phpstan-mockery": "^0.12.5",
+ "phpstan/phpstan-phpunit": "^0.12.11",
+ "phpunit/phpunit": "^8.5 || ^9",
+ "psy/psysh": "^0.10.4",
+ "slevomat/coding-standard": "^6.3",
+ "squizlabs/php_codesniffer": "^3.5",
+ "vimeo/psalm": "^4.4"
},
+ "type": "library",
"autoload": {
"psr-4": {
- "Psr\\Http\\Client\\": "src/"
+ "Ramsey\\Collection\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -1795,96 +3624,159 @@
],
"authors": [
{
- "name": "PHP-FIG",
- "homepage": "http://www.php-fig.org/"
+ "name": "Ben Ramsey",
+ "email": "ben@benramsey.com",
+ "homepage": "https://benramsey.com"
}
],
- "description": "Common interface for HTTP clients",
- "homepage": "https://github.com/php-fig/http-client",
+ "description": "A PHP 7.2+ library for representing and manipulating collections.",
"keywords": [
- "http",
- "http-client",
- "psr",
- "psr-18"
+ "array",
+ "collection",
+ "hash",
+ "map",
+ "queue",
+ "set"
],
- "time": "2020-06-29T06:28:15+00:00"
+ "support": {
+ "issues": "https://github.com/ramsey/collection/issues",
+ "source": "https://github.com/ramsey/collection/tree/1.1.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/ramsey",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/ramsey/collection",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-01-21T17:40:04+00:00"
},
{
- "name": "psr/http-message",
- "version": "1.0.1",
+ "name": "ramsey/uuid",
+ "version": "4.1.1",
"source": {
"type": "git",
- "url": "https://github.com/php-fig/http-message.git",
- "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
+ "url": "https://github.com/ramsey/uuid.git",
+ "reference": "cd4032040a750077205918c86049aa0f43d22947"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
- "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
+ "url": "https://api.github.com/repos/ramsey/uuid/zipball/cd4032040a750077205918c86049aa0f43d22947",
+ "reference": "cd4032040a750077205918c86049aa0f43d22947",
"shasum": ""
},
- "require": {
- "php": ">=5.3.0"
+ "require": {
+ "brick/math": "^0.8 || ^0.9",
+ "ext-json": "*",
+ "php": "^7.2 || ^8",
+ "ramsey/collection": "^1.0",
+ "symfony/polyfill-ctype": "^1.8"
+ },
+ "replace": {
+ "rhumsaa/uuid": "self.version"
+ },
+ "require-dev": {
+ "codeception/aspect-mock": "^3",
+ "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7.0",
+ "doctrine/annotations": "^1.8",
+ "goaop/framework": "^2",
+ "mockery/mockery": "^1.3",
+ "moontoast/math": "^1.1",
+ "paragonie/random-lib": "^2",
+ "php-mock/php-mock-mockery": "^1.3",
+ "php-mock/php-mock-phpunit": "^2.5",
+ "php-parallel-lint/php-parallel-lint": "^1.1",
+ "phpbench/phpbench": "^0.17.1",
+ "phpstan/extension-installer": "^1.0",
+ "phpstan/phpstan": "^0.12",
+ "phpstan/phpstan-mockery": "^0.12",
+ "phpstan/phpstan-phpunit": "^0.12",
+ "phpunit/phpunit": "^8.5",
+ "psy/psysh": "^0.10.0",
+ "slevomat/coding-standard": "^6.0",
+ "squizlabs/php_codesniffer": "^3.5",
+ "vimeo/psalm": "3.9.4"
+ },
+ "suggest": {
+ "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.",
+ "ext-ctype": "Enables faster processing of character classification using ctype functions.",
+ "ext-gmp": "Enables faster math with arbitrary-precision integers using GMP.",
+ "ext-uuid": "Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.",
+ "paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter",
+ "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type."
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.0.x-dev"
+ "dev-master": "4.x-dev"
}
},
"autoload": {
"psr-4": {
- "Psr\\Http\\Message\\": "src/"
- }
+ "Ramsey\\Uuid\\": "src/"
+ },
+ "files": [
+ "src/functions.php"
+ ]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
- "authors": [
+ "description": "A PHP library for generating and working with universally unique identifiers (UUIDs).",
+ "homepage": "https://github.com/ramsey/uuid",
+ "keywords": [
+ "guid",
+ "identifier",
+ "uuid"
+ ],
+ "support": {
+ "issues": "https://github.com/ramsey/uuid/issues",
+ "rss": "https://github.com/ramsey/uuid/releases.atom",
+ "source": "https://github.com/ramsey/uuid"
+ },
+ "funding": [
{
- "name": "PHP-FIG",
- "homepage": "http://www.php-fig.org/"
+ "url": "https://github.com/ramsey",
+ "type": "github"
}
],
- "description": "Common interface for HTTP messages",
- "homepage": "https://github.com/php-fig/http-message",
- "keywords": [
- "http",
- "http-message",
- "psr",
- "psr-7",
- "request",
- "response"
- ],
- "time": "2016-08-06T14:39:51+00:00"
+ "time": "2020-08-18T17:17:46+00:00"
},
{
- "name": "psr/log",
- "version": "1.1.3",
+ "name": "spatie/image",
+ "version": "1.10.2",
"source": {
"type": "git",
- "url": "https://github.com/php-fig/log.git",
- "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc"
+ "url": "https://github.com/spatie/image.git",
+ "reference": "12662673fbe649bffcd3a24188a404dc31fa118c"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/php-fig/log/zipball/0f73288fd15629204f9d42b7055f72dacbe811fc",
- "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc",
+ "url": "https://api.github.com/repos/spatie/image/zipball/12662673fbe649bffcd3a24188a404dc31fa118c",
+ "reference": "12662673fbe649bffcd3a24188a404dc31fa118c",
"shasum": ""
},
"require": {
- "php": ">=5.3.0"
+ "ext-exif": "*",
+ "ext-mbstring": "*",
+ "league/glide": "^1.6",
+ "php": "^7.2|^8.0",
+ "spatie/image-optimizer": "^1.1",
+ "spatie/temporary-directory": "^1.0",
+ "symfony/process": "^3.0|^4.0|^5.0"
},
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "1.1.x-dev"
- }
+ "require-dev": {
+ "phpunit/phpunit": "^8.0|^9.0",
+ "symfony/var-dumper": "^4.0|^5.0"
},
+ "type": "library",
"autoload": {
"psr-4": {
- "Psr\\Log\\": "Psr/Log/"
+ "Spatie\\Image\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -1893,45 +3785,62 @@
],
"authors": [
{
- "name": "PHP-FIG",
- "homepage": "http://www.php-fig.org/"
+ "name": "Freek Van der Herten",
+ "email": "freek@spatie.be",
+ "homepage": "https://spatie.be",
+ "role": "Developer"
}
],
- "description": "Common interface for logging libraries",
- "homepage": "https://github.com/php-fig/log",
+ "description": "Manipulate images with an expressive API",
+ "homepage": "https://github.com/spatie/image",
"keywords": [
- "log",
- "psr",
- "psr-3"
+ "image",
+ "spatie"
],
- "time": "2020-03-23T09:12:05+00:00"
+ "support": {
+ "issues": "https://github.com/spatie/image/issues",
+ "source": "https://github.com/spatie/image/tree/1.10.2"
+ },
+ "funding": [
+ {
+ "url": "https://spatie.be/open-source/support-us",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/spatie",
+ "type": "github"
+ }
+ ],
+ "time": "2021-01-26T07:53:19+00:00"
},
{
- "name": "psr/simple-cache",
- "version": "1.0.1",
+ "name": "spatie/image-optimizer",
+ "version": "1.3.2",
"source": {
"type": "git",
- "url": "https://github.com/php-fig/simple-cache.git",
- "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b"
+ "url": "https://github.com/spatie/image-optimizer.git",
+ "reference": "6aa170eb292758553d332efee5e0c3977341080c"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/408d5eafb83c57f6365a3ca330ff23aa4a5fa39b",
- "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b",
+ "url": "https://api.github.com/repos/spatie/image-optimizer/zipball/6aa170eb292758553d332efee5e0c3977341080c",
+ "reference": "6aa170eb292758553d332efee5e0c3977341080c",
"shasum": ""
},
"require": {
- "php": ">=5.3.0"
+ "ext-fileinfo": "*",
+ "php": "^7.2|^8.0",
+ "psr/log": "^1.0",
+ "symfony/process": "^4.2|^5.0"
},
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "1.0.x-dev"
- }
+ "require-dev": {
+ "phpunit/phpunit": "^8.0|^9.0",
+ "symfony/var-dumper": "^4.2|^5.0"
},
+ "type": "library",
"autoload": {
"psr-4": {
- "Psr\\SimpleCache\\": "src/"
+ "Spatie\\ImageOptimizer\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -1940,69 +3849,86 @@
],
"authors": [
{
- "name": "PHP-FIG",
- "homepage": "http://www.php-fig.org/"
+ "name": "Freek Van der Herten",
+ "email": "freek@spatie.be",
+ "homepage": "https://spatie.be",
+ "role": "Developer"
}
],
- "description": "Common interfaces for simple caching",
+ "description": "Easily optimize images using PHP",
+ "homepage": "https://github.com/spatie/image-optimizer",
"keywords": [
- "cache",
- "caching",
- "psr",
- "psr-16",
- "simple-cache"
+ "image-optimizer",
+ "spatie"
],
- "time": "2017-10-23T01:57:42+00:00"
+ "support": {
+ "issues": "https://github.com/spatie/image-optimizer/issues",
+ "source": "https://github.com/spatie/image-optimizer/tree/1.3.2"
+ },
+ "time": "2020-11-28T12:37:58+00:00"
},
{
- "name": "psy/psysh",
- "version": "v0.10.6",
+ "name": "spatie/laravel-medialibrary",
+ "version": "9.4.3",
"source": {
"type": "git",
- "url": "https://github.com/bobthecow/psysh.git",
- "reference": "6f990c19f91729de8b31e639d6e204ea59f19cf3"
+ "url": "https://github.com/spatie/laravel-medialibrary.git",
+ "reference": "2eae7416a6d7762e147f26f6cac7cf099eda109b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/bobthecow/psysh/zipball/6f990c19f91729de8b31e639d6e204ea59f19cf3",
- "reference": "6f990c19f91729de8b31e639d6e204ea59f19cf3",
+ "url": "https://api.github.com/repos/spatie/laravel-medialibrary/zipball/2eae7416a6d7762e147f26f6cac7cf099eda109b",
+ "reference": "2eae7416a6d7762e147f26f6cac7cf099eda109b",
"shasum": ""
},
"require": {
- "dnoegel/php-xdg-base-dir": "0.1.*",
+ "ext-exif": "*",
+ "ext-fileinfo": "*",
"ext-json": "*",
- "ext-tokenizer": "*",
- "nikic/php-parser": "~4.0|~3.0|~2.0|~1.3",
- "php": "^8.0 || ^7.0 || ^5.5.9",
- "symfony/console": "~5.0|~4.0|~3.0|^2.4.2|~2.3.10",
- "symfony/var-dumper": "~5.0|~4.0|~3.0|~2.7"
+ "illuminate/bus": "^7.0|^8.0",
+ "illuminate/console": "^7.0|^8.0",
+ "illuminate/database": "^7.0|^8.0",
+ "illuminate/pipeline": "^7.0|^8.0",
+ "illuminate/support": "^7.0|^8.0",
+ "league/flysystem": "^1.0.64",
+ "maennchen/zipstream-php": "^1.0|^2.0",
+ "php": "^7.4|^8.0",
+ "spatie/image": "^1.4.0",
+ "spatie/temporary-directory": "^1.1",
+ "symfony/console": "^4.4|^5.0"
+ },
+ "conflict": {
+ "php-ffmpeg/php-ffmpeg": "<0.6.1"
},
"require-dev": {
- "bamarni/composer-bin-plugin": "^1.2",
- "hoa/console": "3.17.*"
+ "aws/aws-sdk-php": "^3.133.11",
+ "doctrine/dbal": "^2.5.2",
+ "ext-pdo_sqlite": "*",
+ "ext-zip": "*",
+ "guzzlehttp/guzzle": "^6.3|^7.0",
+ "league/flysystem-aws-s3-v3": "^1.0.23",
+ "mockery/mockery": "^1.3",
+ "orchestra/testbench": "^5.0|^6.0",
+ "phpunit/phpunit": "^9.1",
+ "spatie/pdf-to-image": "^2.0",
+ "spatie/phpunit-snapshot-assertions": "^4.0"
},
"suggest": {
- "ext-pcntl": "Enabling the PCNTL extension makes PsySH a lot happier :)",
- "ext-pdo-sqlite": "The doc command requires SQLite to work.",
- "ext-posix": "If you have PCNTL, you'll want the POSIX extension as well.",
- "ext-readline": "Enables support for arrow-key history navigation, and showing and manipulating command history.",
- "hoa/console": "A pure PHP readline implementation. You'll want this if your PHP install doesn't already support readline or libedit."
+ "league/flysystem-aws-s3-v3": "Required to use AWS S3 file storage",
+ "php-ffmpeg/php-ffmpeg": "Required for generating video thumbnails",
+ "spatie/pdf-to-image": "Required for generating thumbsnails of PDFs and SVGs"
},
- "bin": [
- "bin/psysh"
- ],
"type": "library",
"extra": {
- "branch-alias": {
- "dev-main": "0.10.x-dev"
+ "laravel": {
+ "providers": [
+ "Spatie\\MediaLibrary\\MediaLibraryServiceProvider"
+ ]
}
},
"autoload": {
- "files": [
- "src/functions.php"
- ],
"psr-4": {
- "Psy\\": "src/"
+ "Spatie\\MediaLibrary\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -2011,100 +3937,78 @@
],
"authors": [
{
- "name": "Justin Hileman",
- "email": "justin@justinhileman.info",
- "homepage": "http://justinhileman.com"
+ "name": "Freek Van der Herten",
+ "email": "freek@spatie.be",
+ "homepage": "https://spatie.be",
+ "role": "Developer"
}
],
- "description": "An interactive shell for modern PHP.",
- "homepage": "http://psysh.org",
+ "description": "Associate files with Eloquent models",
+ "homepage": "https://github.com/spatie/laravel-medialibrary",
"keywords": [
- "REPL",
- "console",
- "interactive",
- "shell"
+ "cms",
+ "conversion",
+ "downloads",
+ "images",
+ "laravel",
+ "laravel-medialibrary",
+ "media",
+ "spatie"
],
- "time": "2021-01-18T15:53:43+00:00"
- },
- {
- "name": "ralouphie/getallheaders",
- "version": "3.0.3",
- "source": {
- "type": "git",
- "url": "https://github.com/ralouphie/getallheaders.git",
- "reference": "120b605dfeb996808c31b6477290a714d356e822"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822",
- "reference": "120b605dfeb996808c31b6477290a714d356e822",
- "shasum": ""
- },
- "require": {
- "php": ">=5.6"
- },
- "require-dev": {
- "php-coveralls/php-coveralls": "^2.1",
- "phpunit/phpunit": "^5 || ^6.5"
- },
- "type": "library",
- "autoload": {
- "files": [
- "src/getallheaders.php"
- ]
+ "support": {
+ "issues": "https://github.com/spatie/laravel-medialibrary/issues",
+ "source": "https://github.com/spatie/laravel-medialibrary/tree/9.4.3"
},
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
+ "funding": [
{
- "name": "Ralph Khattar",
- "email": "ralph.khattar@gmail.com"
+ "url": "https://spatie.be/open-source/support-us",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/spatie",
+ "type": "github"
}
],
- "description": "A polyfill for getallheaders.",
- "time": "2019-03-08T08:55:37+00:00"
+ "time": "2021-03-07T18:42:19+00:00"
},
{
- "name": "ramsey/collection",
- "version": "1.1.3",
+ "name": "spatie/laravel-query-builder",
+ "version": "3.3.4",
"source": {
"type": "git",
- "url": "https://github.com/ramsey/collection.git",
- "reference": "28a5c4ab2f5111db6a60b2b4ec84057e0f43b9c1"
+ "url": "https://github.com/spatie/laravel-query-builder.git",
+ "reference": "2e131b0c8ae600b6e3aabb5a1501c721862a0b8f"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/ramsey/collection/zipball/28a5c4ab2f5111db6a60b2b4ec84057e0f43b9c1",
- "reference": "28a5c4ab2f5111db6a60b2b4ec84057e0f43b9c1",
+ "url": "https://api.github.com/repos/spatie/laravel-query-builder/zipball/2e131b0c8ae600b6e3aabb5a1501c721862a0b8f",
+ "reference": "2e131b0c8ae600b6e3aabb5a1501c721862a0b8f",
"shasum": ""
},
"require": {
- "php": "^7.2 || ^8"
+ "illuminate/database": "^6.0|^7.0|^8.0",
+ "illuminate/http": "^6.0|^7.0|^8.0",
+ "illuminate/support": "^6.0|^7.0|^8.0",
+ "php": "^7.3|^8.0"
},
"require-dev": {
- "captainhook/captainhook": "^5.3",
- "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0",
- "ergebnis/composer-normalize": "^2.6",
- "fakerphp/faker": "^1.5",
- "hamcrest/hamcrest-php": "^2",
- "jangregor/phpstan-prophecy": "^0.8",
- "mockery/mockery": "^1.3",
- "phpstan/extension-installer": "^1",
- "phpstan/phpstan": "^0.12.32",
- "phpstan/phpstan-mockery": "^0.12.5",
- "phpstan/phpstan-phpunit": "^0.12.11",
- "phpunit/phpunit": "^8.5 || ^9",
- "psy/psysh": "^0.10.4",
- "slevomat/coding-standard": "^6.3",
- "squizlabs/php_codesniffer": "^3.5",
- "vimeo/psalm": "^4.4"
+ "ext-json": "*",
+ "laravel/legacy-factories": "^1.0.4",
+ "mockery/mockery": "^1.4",
+ "orchestra/testbench": "^4.9|^5.8|^6.3",
+ "phpunit/phpunit": "^9.0"
},
"type": "library",
+ "extra": {
+ "laravel": {
+ "providers": [
+ "Spatie\\QueryBuilder\\QueryBuilderServiceProvider"
+ ]
+ }
+ },
"autoload": {
"psr-4": {
- "Ramsey\\Collection\\": "src/"
+ "Spatie\\QueryBuilder\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -2113,131 +4017,93 @@
],
"authors": [
{
- "name": "Ben Ramsey",
- "email": "ben@benramsey.com",
- "homepage": "https://benramsey.com"
+ "name": "Alex Vanderbist",
+ "email": "alex@spatie.be",
+ "homepage": "https://spatie.be",
+ "role": "Developer"
}
],
- "description": "A PHP 7.2+ library for representing and manipulating collections.",
+ "description": "Easily build Eloquent queries from API requests",
+ "homepage": "https://github.com/spatie/laravel-query-builder",
"keywords": [
- "array",
- "collection",
- "hash",
- "map",
- "queue",
- "set"
+ "laravel-query-builder",
+ "spatie"
],
+ "support": {
+ "issues": "https://github.com/spatie/laravel-query-builder/issues",
+ "source": "https://github.com/spatie/laravel-query-builder"
+ },
"funding": [
{
- "url": "https://github.com/ramsey",
- "type": "github"
- },
- {
- "url": "https://tidelift.com/funding/github/packagist/ramsey/collection",
- "type": "tidelift"
+ "url": "https://spatie.be/open-source/support-us",
+ "type": "custom"
}
],
- "time": "2021-01-21T17:40:04+00:00"
+ "time": "2020-11-26T14:51:30+00:00"
},
{
- "name": "ramsey/uuid",
- "version": "4.1.1",
+ "name": "spatie/temporary-directory",
+ "version": "1.3.0",
"source": {
"type": "git",
- "url": "https://github.com/ramsey/uuid.git",
- "reference": "cd4032040a750077205918c86049aa0f43d22947"
+ "url": "https://github.com/spatie/temporary-directory.git",
+ "reference": "f517729b3793bca58f847c5fd383ec16f03ffec6"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/ramsey/uuid/zipball/cd4032040a750077205918c86049aa0f43d22947",
- "reference": "cd4032040a750077205918c86049aa0f43d22947",
+ "url": "https://api.github.com/repos/spatie/temporary-directory/zipball/f517729b3793bca58f847c5fd383ec16f03ffec6",
+ "reference": "f517729b3793bca58f847c5fd383ec16f03ffec6",
"shasum": ""
},
"require": {
- "brick/math": "^0.8 || ^0.9",
- "ext-json": "*",
- "php": "^7.2 || ^8",
- "ramsey/collection": "^1.0",
- "symfony/polyfill-ctype": "^1.8"
- },
- "replace": {
- "rhumsaa/uuid": "self.version"
+ "php": "^7.2|^8.0"
},
"require-dev": {
- "codeception/aspect-mock": "^3",
- "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7.0",
- "doctrine/annotations": "^1.8",
- "goaop/framework": "^2",
- "mockery/mockery": "^1.3",
- "moontoast/math": "^1.1",
- "paragonie/random-lib": "^2",
- "php-mock/php-mock-mockery": "^1.3",
- "php-mock/php-mock-phpunit": "^2.5",
- "php-parallel-lint/php-parallel-lint": "^1.1",
- "phpbench/phpbench": "^0.17.1",
- "phpstan/extension-installer": "^1.0",
- "phpstan/phpstan": "^0.12",
- "phpstan/phpstan-mockery": "^0.12",
- "phpstan/phpstan-phpunit": "^0.12",
- "phpunit/phpunit": "^8.5",
- "psy/psysh": "^0.10.0",
- "slevomat/coding-standard": "^6.0",
- "squizlabs/php_codesniffer": "^3.5",
- "vimeo/psalm": "3.9.4"
- },
- "suggest": {
- "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.",
- "ext-ctype": "Enables faster processing of character classification using ctype functions.",
- "ext-gmp": "Enables faster math with arbitrary-precision integers using GMP.",
- "ext-uuid": "Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.",
- "paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter",
- "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type."
+ "phpunit/phpunit": "^8.0|^9.0"
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "4.x-dev"
- }
- },
"autoload": {
"psr-4": {
- "Ramsey\\Uuid\\": "src/"
- },
- "files": [
- "src/functions.php"
- ]
+ "Spatie\\TemporaryDirectory\\": "src"
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
- "description": "A PHP library for generating and working with universally unique identifiers (UUIDs).",
- "homepage": "https://github.com/ramsey/uuid",
- "keywords": [
- "guid",
- "identifier",
- "uuid"
- ],
- "funding": [
+ "authors": [
{
- "url": "https://github.com/ramsey",
- "type": "github"
+ "name": "Alex Vanderbist",
+ "email": "alex@spatie.be",
+ "homepage": "https://spatie.be",
+ "role": "Developer"
}
],
- "time": "2020-08-18T17:17:46+00:00"
+ "description": "Easily create, use and destroy temporary directories",
+ "homepage": "https://github.com/spatie/temporary-directory",
+ "keywords": [
+ "php",
+ "spatie",
+ "temporary-directory"
+ ],
+ "support": {
+ "issues": "https://github.com/spatie/temporary-directory/issues",
+ "source": "https://github.com/spatie/temporary-directory/tree/1.3.0"
+ },
+ "time": "2020-11-09T15:54:21+00:00"
},
{
"name": "swiftmailer/swiftmailer",
- "version": "v6.2.5",
+ "version": "v6.2.6",
"source": {
"type": "git",
"url": "https://github.com/swiftmailer/swiftmailer.git",
- "reference": "698a6a9f54d7eb321274de3ad19863802c879fb7"
+ "reference": "d2791ff0b73247cdc2096b14f5580aba40c12bff"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/698a6a9f54d7eb321274de3ad19863802c879fb7",
- "reference": "698a6a9f54d7eb321274de3ad19863802c879fb7",
+ "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/d2791ff0b73247cdc2096b14f5580aba40c12bff",
+ "reference": "d2791ff0b73247cdc2096b14f5580aba40c12bff",
"shasum": ""
},
"require": {
@@ -2285,6 +4151,10 @@
"mail",
"mailer"
],
+ "support": {
+ "issues": "https://github.com/swiftmailer/swiftmailer/issues",
+ "source": "https://github.com/swiftmailer/swiftmailer/tree/v6.2.6"
+ },
"funding": [
{
"url": "https://github.com/fabpot",
@@ -2295,20 +4165,20 @@
"type": "tidelift"
}
],
- "time": "2021-01-12T09:35:59+00:00"
+ "time": "2021-03-05T12:08:49+00:00"
},
{
"name": "symfony/console",
- "version": "v5.2.3",
+ "version": "v5.2.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
- "reference": "89d4b176d12a2946a1ae4e34906a025b7b6b135a"
+ "reference": "d6d0cc30d8c0fda4e7b213c20509b0159a8f4556"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/console/zipball/89d4b176d12a2946a1ae4e34906a025b7b6b135a",
- "reference": "89d4b176d12a2946a1ae4e34906a025b7b6b135a",
+ "url": "https://api.github.com/repos/symfony/console/zipball/d6d0cc30d8c0fda4e7b213c20509b0159a8f4556",
+ "reference": "d6d0cc30d8c0fda4e7b213c20509b0159a8f4556",
"shasum": ""
},
"require": {
@@ -2375,6 +4245,9 @@
"console",
"terminal"
],
+ "support": {
+ "source": "https://github.com/symfony/console/tree/v5.2.4"
+ },
"funding": [
{
"url": "https://symfony.com/sponsor",
@@ -2389,11 +4262,11 @@
"type": "tidelift"
}
],
- "time": "2021-01-28T22:06:19+00:00"
+ "time": "2021-02-23T10:08:49+00:00"
},
{
"name": "symfony/css-selector",
- "version": "v5.2.3",
+ "version": "v5.2.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/css-selector.git",
@@ -2437,6 +4310,9 @@
],
"description": "Converts CSS selectors to XPath expressions",
"homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/css-selector/tree/v5.2.4"
+ },
"funding": [
{
"url": "https://symfony.com/sponsor",
@@ -2501,6 +4377,9 @@
],
"description": "A generic function and convention to trigger deprecation notices",
"homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/deprecation-contracts/tree/master"
+ },
"funding": [
{
"url": "https://symfony.com/sponsor",
@@ -2519,16 +4398,16 @@
},
{
"name": "symfony/error-handler",
- "version": "v5.2.3",
+ "version": "v5.2.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/error-handler.git",
- "reference": "48f18b3609e120ea66d59142c23dc53e9562c26d"
+ "reference": "b547d3babcab5c31e01de59ee33e9d9c1421d7d0"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/error-handler/zipball/48f18b3609e120ea66d59142c23dc53e9562c26d",
- "reference": "48f18b3609e120ea66d59142c23dc53e9562c26d",
+ "url": "https://api.github.com/repos/symfony/error-handler/zipball/b547d3babcab5c31e01de59ee33e9d9c1421d7d0",
+ "reference": "b547d3babcab5c31e01de59ee33e9d9c1421d7d0",
"shasum": ""
},
"require": {
@@ -2567,6 +4446,9 @@
],
"description": "Provides tools to manage errors and ease debugging PHP code",
"homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/error-handler/tree/v5.2.4"
+ },
"funding": [
{
"url": "https://symfony.com/sponsor",
@@ -2581,20 +4463,20 @@
"type": "tidelift"
}
],
- "time": "2021-01-28T22:06:19+00:00"
+ "time": "2021-02-11T08:21:20+00:00"
},
{
"name": "symfony/event-dispatcher",
- "version": "v5.2.3",
+ "version": "v5.2.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/event-dispatcher.git",
- "reference": "4f9760f8074978ad82e2ce854dff79a71fe45367"
+ "reference": "d08d6ec121a425897951900ab692b612a61d6240"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/4f9760f8074978ad82e2ce854dff79a71fe45367",
- "reference": "4f9760f8074978ad82e2ce854dff79a71fe45367",
+ "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/d08d6ec121a425897951900ab692b612a61d6240",
+ "reference": "d08d6ec121a425897951900ab692b612a61d6240",
"shasum": ""
},
"require": {
@@ -2649,6 +4531,9 @@
],
"description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them",
"homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/event-dispatcher/tree/v5.2.4"
+ },
"funding": [
{
"url": "https://symfony.com/sponsor",
@@ -2663,7 +4548,7 @@
"type": "tidelift"
}
],
- "time": "2021-01-27T10:36:42+00:00"
+ "time": "2021-02-18T17:12:37+00:00"
},
{
"name": "symfony/event-dispatcher-contracts",
@@ -2725,6 +4610,9 @@
"interoperability",
"standards"
],
+ "support": {
+ "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v2.2.0"
+ },
"funding": [
{
"url": "https://symfony.com/sponsor",
@@ -2743,16 +4631,16 @@
},
{
"name": "symfony/finder",
- "version": "v5.2.3",
+ "version": "v5.2.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/finder.git",
- "reference": "4adc8d172d602008c204c2e16956f99257248e03"
+ "reference": "0d639a0943822626290d169965804f79400e6a04"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/finder/zipball/4adc8d172d602008c204c2e16956f99257248e03",
- "reference": "4adc8d172d602008c204c2e16956f99257248e03",
+ "url": "https://api.github.com/repos/symfony/finder/zipball/0d639a0943822626290d169965804f79400e6a04",
+ "reference": "0d639a0943822626290d169965804f79400e6a04",
"shasum": ""
},
"require": {
@@ -2783,6 +4671,9 @@
],
"description": "Finds files and directories via an intuitive fluent interface",
"homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/finder/tree/v5.2.4"
+ },
"funding": [
{
"url": "https://symfony.com/sponsor",
@@ -2797,7 +4688,7 @@
"type": "tidelift"
}
],
- "time": "2021-01-28T22:06:19+00:00"
+ "time": "2021-02-15T18:55:04+00:00"
},
{
"name": "symfony/http-client-contracts",
@@ -2859,6 +4750,9 @@
"interoperability",
"standards"
],
+ "support": {
+ "source": "https://github.com/symfony/http-client-contracts/tree/v2.3.1"
+ },
"funding": [
{
"url": "https://symfony.com/sponsor",
@@ -2877,16 +4771,16 @@
},
{
"name": "symfony/http-foundation",
- "version": "v5.2.3",
+ "version": "v5.2.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-foundation.git",
- "reference": "20c554c0f03f7cde5ce230ed248470cccbc34c36"
+ "reference": "54499baea7f7418bce7b5ec92770fd0799e8e9bf"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/http-foundation/zipball/20c554c0f03f7cde5ce230ed248470cccbc34c36",
- "reference": "20c554c0f03f7cde5ce230ed248470cccbc34c36",
+ "url": "https://api.github.com/repos/symfony/http-foundation/zipball/54499baea7f7418bce7b5ec92770fd0799e8e9bf",
+ "reference": "54499baea7f7418bce7b5ec92770fd0799e8e9bf",
"shasum": ""
},
"require": {
@@ -2929,6 +4823,9 @@
],
"description": "Defines an object-oriented layer for the HTTP specification",
"homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/http-foundation/tree/v5.2.4"
+ },
"funding": [
{
"url": "https://symfony.com/sponsor",
@@ -2943,20 +4840,20 @@
"type": "tidelift"
}
],
- "time": "2021-02-03T04:42:09+00:00"
+ "time": "2021-02-25T17:16:57+00:00"
},
{
"name": "symfony/http-kernel",
- "version": "v5.2.3",
+ "version": "v5.2.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-kernel.git",
- "reference": "89bac04f29e7b0b52f9fa6a4288ca7a8f90a1a05"
+ "reference": "c452dbe4f385f030c3957821bf921b13815d6140"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/http-kernel/zipball/89bac04f29e7b0b52f9fa6a4288ca7a8f90a1a05",
- "reference": "89bac04f29e7b0b52f9fa6a4288ca7a8f90a1a05",
+ "url": "https://api.github.com/repos/symfony/http-kernel/zipball/c452dbe4f385f030c3957821bf921b13815d6140",
+ "reference": "c452dbe4f385f030c3957821bf921b13815d6140",
"shasum": ""
},
"require": {
@@ -2991,7 +4888,7 @@
"psr/log-implementation": "1.0"
},
"require-dev": {
- "psr/cache": "~1.0",
+ "psr/cache": "^1.0|^2.0|^3.0",
"symfony/browser-kit": "^4.4|^5.0",
"symfony/config": "^5.0",
"symfony/console": "^4.4|^5.0",
@@ -3038,6 +4935,9 @@
],
"description": "Provides a structured process for converting a Request into a Response",
"homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/http-kernel/tree/v5.2.4"
+ },
"funding": [
{
"url": "https://symfony.com/sponsor",
@@ -3052,20 +4952,20 @@
"type": "tidelift"
}
],
- "time": "2021-02-03T04:51:58+00:00"
+ "time": "2021-03-04T18:05:55+00:00"
},
{
"name": "symfony/mime",
- "version": "v5.2.3",
+ "version": "v5.2.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/mime.git",
- "reference": "7dee6a43493f39b51ff6c5bb2bd576fe40a76c86"
+ "reference": "5155d2fe14ef1eb150e3bdbbc1ec1455df95e9cd"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/mime/zipball/7dee6a43493f39b51ff6c5bb2bd576fe40a76c86",
- "reference": "7dee6a43493f39b51ff6c5bb2bd576fe40a76c86",
+ "url": "https://api.github.com/repos/symfony/mime/zipball/5155d2fe14ef1eb150e3bdbbc1ec1455df95e9cd",
+ "reference": "5155d2fe14ef1eb150e3bdbbc1ec1455df95e9cd",
"shasum": ""
},
"require": {
@@ -3117,6 +5017,9 @@
"mime",
"mime-type"
],
+ "support": {
+ "source": "https://github.com/symfony/mime/tree/v5.2.4"
+ },
"funding": [
{
"url": "https://symfony.com/sponsor",
@@ -3131,7 +5034,7 @@
"type": "tidelift"
}
],
- "time": "2021-02-02T06:10:15+00:00"
+ "time": "2021-02-15T18:55:04+00:00"
},
{
"name": "symfony/polyfill-ctype",
@@ -3193,6 +5096,9 @@
"polyfill",
"portable"
],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-ctype/tree/v1.22.1"
+ },
"funding": [
{
"url": "https://symfony.com/sponsor",
@@ -3270,6 +5176,9 @@
"portable",
"shim"
],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-iconv/tree/v1.22.1"
+ },
"funding": [
{
"url": "https://symfony.com/sponsor",
@@ -3348,6 +5257,9 @@
"portable",
"shim"
],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.22.1"
+ },
"funding": [
{
"url": "https://symfony.com/sponsor",
@@ -3432,6 +5344,9 @@
"portable",
"shim"
],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.22.1"
+ },
"funding": [
{
"url": "https://symfony.com/sponsor",
@@ -3513,6 +5428,9 @@
"portable",
"shim"
],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.22.1"
+ },
"funding": [
{
"url": "https://symfony.com/sponsor",
@@ -3590,6 +5508,9 @@
"portable",
"shim"
],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.22.1"
+ },
"funding": [
{
"url": "https://symfony.com/sponsor",
@@ -3663,6 +5584,9 @@
"portable",
"shim"
],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-php72/tree/v1.22.1"
+ },
"funding": [
{
"url": "https://symfony.com/sponsor",
@@ -3739,6 +5663,9 @@
"portable",
"shim"
],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-php73/tree/v1.22.1"
+ },
"funding": [
{
"url": "https://symfony.com/sponsor",
@@ -3819,6 +5746,9 @@
"portable",
"shim"
],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-php80/tree/v1.22.1"
+ },
"funding": [
{
"url": "https://symfony.com/sponsor",
@@ -3837,7 +5767,7 @@
},
{
"name": "symfony/process",
- "version": "v5.2.3",
+ "version": "v5.2.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
@@ -3878,6 +5808,9 @@
],
"description": "Executes commands in sub-processes",
"homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/process/tree/v5.2.4"
+ },
"funding": [
{
"url": "https://symfony.com/sponsor",
@@ -3896,16 +5829,16 @@
},
{
"name": "symfony/routing",
- "version": "v5.2.3",
+ "version": "v5.2.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/routing.git",
- "reference": "348b5917e56546c6d96adbf21d7f92c9ef563661"
+ "reference": "cafa138128dfd6ab6be1abf6279169957b34f662"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/routing/zipball/348b5917e56546c6d96adbf21d7f92c9ef563661",
- "reference": "348b5917e56546c6d96adbf21d7f92c9ef563661",
+ "url": "https://api.github.com/repos/symfony/routing/zipball/cafa138128dfd6ab6be1abf6279169957b34f662",
+ "reference": "cafa138128dfd6ab6be1abf6279169957b34f662",
"shasum": ""
},
"require": {
@@ -3965,6 +5898,9 @@
"uri",
"url"
],
+ "support": {
+ "source": "https://github.com/symfony/routing/tree/v5.2.4"
+ },
"funding": [
{
"url": "https://symfony.com/sponsor",
@@ -3979,7 +5915,7 @@
"type": "tidelift"
}
],
- "time": "2021-01-27T10:15:41+00:00"
+ "time": "2021-02-22T15:48:39+00:00"
},
{
"name": "symfony/service-contracts",
@@ -4041,6 +5977,9 @@
"interoperability",
"standards"
],
+ "support": {
+ "source": "https://github.com/symfony/service-contracts/tree/master"
+ },
"funding": [
{
"url": "https://symfony.com/sponsor",
@@ -4059,16 +5998,16 @@
},
{
"name": "symfony/string",
- "version": "v5.2.3",
+ "version": "v5.2.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/string.git",
- "reference": "c95468897f408dd0aca2ff582074423dd0455122"
+ "reference": "4e78d7d47061fa183639927ec40d607973699609"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/string/zipball/c95468897f408dd0aca2ff582074423dd0455122",
- "reference": "c95468897f408dd0aca2ff582074423dd0455122",
+ "url": "https://api.github.com/repos/symfony/string/zipball/4e78d7d47061fa183639927ec40d607973699609",
+ "reference": "4e78d7d47061fa183639927ec40d607973699609",
"shasum": ""
},
"require": {
@@ -4121,6 +6060,9 @@
"utf-8",
"utf8"
],
+ "support": {
+ "source": "https://github.com/symfony/string/tree/v5.2.4"
+ },
"funding": [
{
"url": "https://symfony.com/sponsor",
@@ -4135,20 +6077,20 @@
"type": "tidelift"
}
],
- "time": "2021-01-25T15:14:59+00:00"
+ "time": "2021-02-16T10:20:28+00:00"
},
{
"name": "symfony/translation",
- "version": "v5.2.3",
+ "version": "v5.2.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/translation.git",
- "reference": "c021864d4354ee55160ddcfd31dc477a1bc77949"
+ "reference": "74b0353ab34ff4cca827a2cf909e325d96815e60"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/translation/zipball/c021864d4354ee55160ddcfd31dc477a1bc77949",
- "reference": "c021864d4354ee55160ddcfd31dc477a1bc77949",
+ "url": "https://api.github.com/repos/symfony/translation/zipball/74b0353ab34ff4cca827a2cf909e325d96815e60",
+ "reference": "74b0353ab34ff4cca827a2cf909e325d96815e60",
"shasum": ""
},
"require": {
@@ -4165,7 +6107,7 @@
"symfony/yaml": "<4.4"
},
"provide": {
- "symfony/translation-implementation": "2.0"
+ "symfony/translation-implementation": "2.3"
},
"require-dev": {
"psr/log": "~1.0",
@@ -4211,6 +6153,9 @@
],
"description": "Provides tools to internationalize your application",
"homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/translation/tree/v5.2.4"
+ },
"funding": [
{
"url": "https://symfony.com/sponsor",
@@ -4225,7 +6170,7 @@
"type": "tidelift"
}
],
- "time": "2021-01-27T10:15:41+00:00"
+ "time": "2021-03-04T15:41:09+00:00"
},
{
"name": "symfony/translation-contracts",
@@ -4286,6 +6231,9 @@
"interoperability",
"standards"
],
+ "support": {
+ "source": "https://github.com/symfony/translation-contracts/tree/v2.3.0"
+ },
"funding": [
{
"url": "https://symfony.com/sponsor",
@@ -4304,16 +6252,16 @@
},
{
"name": "symfony/var-dumper",
- "version": "v5.2.3",
+ "version": "v5.2.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/var-dumper.git",
- "reference": "72ca213014a92223a5d18651ce79ef441c12b694"
+ "reference": "6a81fec0628c468cf6d5c87a4d003725e040e223"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/var-dumper/zipball/72ca213014a92223a5d18651ce79ef441c12b694",
- "reference": "72ca213014a92223a5d18651ce79ef441c12b694",
+ "url": "https://api.github.com/repos/symfony/var-dumper/zipball/6a81fec0628c468cf6d5c87a4d003725e040e223",
+ "reference": "6a81fec0628c468cf6d5c87a4d003725e040e223",
"shasum": ""
},
"require": {
@@ -4371,6 +6319,9 @@
"debug",
"dump"
],
+ "support": {
+ "source": "https://github.com/symfony/var-dumper/tree/v5.2.4"
+ },
"funding": [
{
"url": "https://symfony.com/sponsor",
@@ -4385,7 +6336,7 @@
"type": "tidelift"
}
],
- "time": "2021-01-27T10:15:41+00:00"
+ "time": "2021-02-18T23:11:19+00:00"
},
{
"name": "tijsverkoyen/css-to-inline-styles",
@@ -4434,8 +6385,92 @@
],
"description": "CssToInlineStyles is a class that enables you to convert HTML-pages/files into HTML-pages/files with inline styles. This is very useful when you're sending emails.",
"homepage": "https://github.com/tijsverkoyen/CssToInlineStyles",
+ "support": {
+ "issues": "https://github.com/tijsverkoyen/CssToInlineStyles/issues",
+ "source": "https://github.com/tijsverkoyen/CssToInlineStyles/tree/2.2.3"
+ },
"time": "2020-07-13T06:12:54+00:00"
},
+ {
+ "name": "torann/geoip",
+ "version": "3.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/Torann/laravel-geoip.git",
+ "reference": "f16d5df66ecb6ba4ffaef52abef519fbc19596d3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/Torann/laravel-geoip/zipball/f16d5df66ecb6ba4ffaef52abef519fbc19596d3",
+ "reference": "f16d5df66ecb6ba4ffaef52abef519fbc19596d3",
+ "shasum": ""
+ },
+ "require": {
+ "illuminate/cache": "^8.0",
+ "illuminate/console": "^8.0",
+ "illuminate/support": "^8.0",
+ "php": "^7.2 || ^8.0"
+ },
+ "require-dev": {
+ "geoip2/geoip2": "~2.1",
+ "mockery/mockery": "^1.3",
+ "phpstan/phpstan": "^0.12.14",
+ "phpunit/phpunit": "^8.0",
+ "squizlabs/php_codesniffer": "^3.5",
+ "vlucas/phpdotenv": "^5.0"
+ },
+ "suggest": {
+ "geoip2/geoip2": "Required to use the MaxMind database or web service with GeoIP (~2.1).",
+ "monolog/monolog": "Allows for storing location not found errors to the log"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0-dev"
+ },
+ "laravel": {
+ "providers": [
+ "Torann\\GeoIP\\GeoIPServiceProvider"
+ ],
+ "aliases": {
+ "GeoIP": "Torann\\GeoIP\\Facades\\GeoIP"
+ }
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/helpers.php"
+ ],
+ "psr-4": {
+ "Torann\\GeoIP\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-2-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Daniel Stainback",
+ "email": "torann@gmail.com"
+ }
+ ],
+ "description": "Support for multiple GeoIP services.",
+ "keywords": [
+ "IP API",
+ "geoip",
+ "geolocation",
+ "infoDB",
+ "laravel",
+ "location",
+ "maxmind"
+ ],
+ "support": {
+ "issues": "https://github.com/Torann/laravel-geoip/issues",
+ "source": "https://github.com/Torann/laravel-geoip/tree/3.0.2"
+ },
+ "time": "2020-12-21T19:12:11+00:00"
+ },
{
"name": "vlucas/phpdotenv",
"version": "v5.3.0",
@@ -4500,6 +6535,10 @@
"env",
"environment"
],
+ "support": {
+ "issues": "https://github.com/vlucas/phpdotenv/issues",
+ "source": "https://github.com/vlucas/phpdotenv/tree/v5.3.0"
+ },
"funding": [
{
"url": "https://github.com/GrahamCampbell",
@@ -4558,6 +6597,10 @@
"clean",
"php"
],
+ "support": {
+ "issues": "https://github.com/voku/portable-ascii/issues",
+ "source": "https://github.com/voku/portable-ascii/tree/1.5.6"
+ },
"funding": [
{
"url": "https://www.paypal.me/moelleken",
@@ -4629,10 +6672,77 @@
"check",
"validate"
],
+ "support": {
+ "issues": "https://github.com/webmozarts/assert/issues",
+ "source": "https://github.com/webmozarts/assert/tree/1.9.1"
+ },
"time": "2020-07-08T17:02:28+00:00"
}
],
"packages-dev": [
+ {
+ "name": "andreaselia/laravel-api-to-postman",
+ "version": "v1.7.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/AndreasElia/laravel-api-to-postman.git",
+ "reference": "c40892559137ac356268afb03d3e279ff3efb188"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/AndreasElia/laravel-api-to-postman/zipball/c40892559137ac356268afb03d3e279ff3efb188",
+ "reference": "c40892559137ac356268afb03d3e279ff3efb188",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "illuminate/config": "^6.0|^7.0|^8.0",
+ "illuminate/console": "^6.0|^7.0|^8.0",
+ "illuminate/contracts": "^6.0|^7.0|^8.0",
+ "illuminate/routing": "^6.0|^7.0|^8.0",
+ "illuminate/support": "^6.0|^7.0|^8.0",
+ "php": "^7.4|^8.0"
+ },
+ "require-dev": {
+ "orchestra/testbench": "^6.12"
+ },
+ "type": "library",
+ "extra": {
+ "laravel": {
+ "providers": [
+ "AndreasElia\\PostmanGenerator\\PostmanGeneratorServiceProvider"
+ ]
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "AndreasElia\\PostmanGenerator\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Andreas Elia",
+ "email": "andreaselia@live.co.uk"
+ }
+ ],
+ "description": "Generate a Postman collection automatically from your Laravel API",
+ "keywords": [
+ "Postman",
+ "api",
+ "collection",
+ "generate",
+ "laravel"
+ ],
+ "support": {
+ "issues": "https://github.com/AndreasElia/laravel-api-to-postman/issues",
+ "source": "https://github.com/AndreasElia/laravel-api-to-postman/tree/v1.7.2"
+ },
+ "time": "2021-02-28T23:49:29+00:00"
+ },
{
"name": "doctrine/instantiator",
"version": "1.4.0",
@@ -4682,6 +6792,10 @@
"constructor",
"instantiate"
],
+ "support": {
+ "issues": "https://github.com/doctrine/instantiator/issues",
+ "source": "https://github.com/doctrine/instantiator/tree/1.4.0"
+ },
"funding": [
{
"url": "https://www.doctrine-project.org/sponsorship.html",
@@ -4751,6 +6865,10 @@
"flare",
"reporting"
],
+ "support": {
+ "issues": "https://github.com/facade/flare-client-php/issues",
+ "source": "https://github.com/facade/flare-client-php/tree/1.4.0"
+ },
"funding": [
{
"url": "https://github.com/spatie",
@@ -4761,16 +6879,16 @@
},
{
"name": "facade/ignition",
- "version": "2.5.13",
+ "version": "2.5.14",
"source": {
"type": "git",
"url": "https://github.com/facade/ignition.git",
- "reference": "5e9ef386aaad9985cee2ac23281a27568d083b7e"
+ "reference": "17097f7a83e200d90d1cf9f4d1b35c1001513a47"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/facade/ignition/zipball/5e9ef386aaad9985cee2ac23281a27568d083b7e",
- "reference": "5e9ef386aaad9985cee2ac23281a27568d083b7e",
+ "url": "https://api.github.com/repos/facade/ignition/zipball/17097f7a83e200d90d1cf9f4d1b35c1001513a47",
+ "reference": "17097f7a83e200d90d1cf9f4d1b35c1001513a47",
"shasum": ""
},
"require": {
@@ -4828,7 +6946,13 @@
"laravel",
"page"
],
- "time": "2021-02-16T12:46:19+00:00"
+ "support": {
+ "docs": "https://flareapp.io/docs/ignition-for-laravel/introduction",
+ "forum": "https://twitter.com/flareappio",
+ "issues": "https://github.com/facade/ignition/issues",
+ "source": "https://github.com/facade/ignition"
+ },
+ "time": "2021-03-04T08:48:01+00:00"
},
{
"name": "facade/ignition-contracts",
@@ -4877,6 +7001,10 @@
"flare",
"ignition"
],
+ "support": {
+ "issues": "https://github.com/facade/ignition-contracts/issues",
+ "source": "https://github.com/facade/ignition-contracts/tree/1.0.2"
+ },
"time": "2020-10-16T08:27:54+00:00"
},
{
@@ -4925,6 +7053,10 @@
"faker",
"fixtures"
],
+ "support": {
+ "issues": "https://github.com/FakerPHP/Faker/issues",
+ "source": "https://github.com/FakerPHP/Faker/tree/v1.13.0"
+ },
"time": "2020-12-18T16:50:48+00:00"
},
{
@@ -4986,6 +7118,10 @@
"throwable",
"whoops"
],
+ "support": {
+ "issues": "https://github.com/filp/whoops/issues",
+ "source": "https://github.com/filp/whoops/tree/2.9.2"
+ },
"funding": [
{
"url": "https://github.com/denis-sokolov",
@@ -5039,20 +7175,24 @@
"keywords": [
"test"
],
+ "support": {
+ "issues": "https://github.com/hamcrest/hamcrest-php/issues",
+ "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.0.1"
+ },
"time": "2020-07-09T08:09:16+00:00"
},
{
"name": "laravel/sail",
- "version": "v1.4.3",
+ "version": "v1.4.6",
"source": {
"type": "git",
"url": "https://github.com/laravel/sail.git",
- "reference": "0200ce6e0f697699bce036c42d91f1daab8039a8"
+ "reference": "59ee7e2b2efeb644eabea719186db91d11666733"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laravel/sail/zipball/0200ce6e0f697699bce036c42d91f1daab8039a8",
- "reference": "0200ce6e0f697699bce036c42d91f1daab8039a8",
+ "url": "https://api.github.com/repos/laravel/sail/zipball/59ee7e2b2efeb644eabea719186db91d11666733",
+ "reference": "59ee7e2b2efeb644eabea719186db91d11666733",
"shasum": ""
},
"require": {
@@ -5095,7 +7235,11 @@
"docker",
"laravel"
],
- "time": "2021-02-24T21:20:16+00:00"
+ "support": {
+ "issues": "https://github.com/laravel/sail/issues",
+ "source": "https://github.com/laravel/sail"
+ },
+ "time": "2021-03-03T15:22:44+00:00"
},
{
"name": "mockery/mockery",
@@ -5163,6 +7307,10 @@
"test double",
"testing"
],
+ "support": {
+ "issues": "https://github.com/mockery/mockery/issues",
+ "source": "https://github.com/mockery/mockery/tree/1.4.3"
+ },
"time": "2021-02-24T09:51:49+00:00"
},
{
@@ -5211,6 +7359,10 @@
"object",
"object graph"
],
+ "support": {
+ "issues": "https://github.com/myclabs/DeepCopy/issues",
+ "source": "https://github.com/myclabs/DeepCopy/tree/1.10.2"
+ },
"funding": [
{
"url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy",
@@ -5287,6 +7439,10 @@
"php",
"symfony"
],
+ "support": {
+ "issues": "https://github.com/nunomaduro/collision/issues",
+ "source": "https://github.com/nunomaduro/collision"
+ },
"funding": [
{
"url": "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=66BYDWAT92N6L",
@@ -5357,6 +7513,10 @@
}
],
"description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)",
+ "support": {
+ "issues": "https://github.com/phar-io/manifest/issues",
+ "source": "https://github.com/phar-io/manifest/tree/master"
+ },
"time": "2020-06-27T14:33:11+00:00"
},
{
@@ -5404,6 +7564,10 @@
}
],
"description": "Library for handling version information and constraints",
+ "support": {
+ "issues": "https://github.com/phar-io/version/issues",
+ "source": "https://github.com/phar-io/version/tree/3.1.0"
+ },
"time": "2021-02-23T14:00:09+00:00"
},
{
@@ -5453,6 +7617,10 @@
"reflection",
"static analysis"
],
+ "support": {
+ "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues",
+ "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x"
+ },
"time": "2020-06-27T09:03:43+00:00"
},
{
@@ -5505,6 +7673,10 @@
}
],
"description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
+ "support": {
+ "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues",
+ "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/master"
+ },
"time": "2020-09-03T19:13:55+00:00"
},
{
@@ -5550,6 +7722,10 @@
}
],
"description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
+ "support": {
+ "issues": "https://github.com/phpDocumentor/TypeResolver/issues",
+ "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.4.0"
+ },
"time": "2020-09-17T18:55:26+00:00"
},
{
@@ -5613,6 +7789,10 @@
"spy",
"stub"
],
+ "support": {
+ "issues": "https://github.com/phpspec/prophecy/issues",
+ "source": "https://github.com/phpspec/prophecy/tree/1.12.2"
+ },
"time": "2020-12-19T10:15:11+00:00"
},
{
@@ -5680,6 +7860,10 @@
"testing",
"xunit"
],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
+ "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.5"
+ },
"funding": [
{
"url": "https://github.com/sebastianbergmann",
@@ -5736,6 +7920,10 @@
"filesystem",
"iterator"
],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues",
+ "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.5"
+ },
"funding": [
{
"url": "https://github.com/sebastianbergmann",
@@ -5795,6 +7983,10 @@
"keywords": [
"process"
],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-invoker/issues",
+ "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1"
+ },
"funding": [
{
"url": "https://github.com/sebastianbergmann",
@@ -5850,6 +8042,10 @@
"keywords": [
"template"
],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-text-template/issues",
+ "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4"
+ },
"funding": [
{
"url": "https://github.com/sebastianbergmann",
@@ -5905,6 +8101,10 @@
"keywords": [
"timer"
],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-timer/issues",
+ "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3"
+ },
"funding": [
{
"url": "https://github.com/sebastianbergmann",
@@ -6000,6 +8200,10 @@
"testing",
"xunit"
],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/phpunit/issues",
+ "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.2"
+ },
"funding": [
{
"url": "https://phpunit.de/donate.html",
@@ -6056,6 +8260,10 @@
],
"description": "Library for parsing CLI options",
"homepage": "https://github.com/sebastianbergmann/cli-parser",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/cli-parser/issues",
+ "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1"
+ },
"funding": [
{
"url": "https://github.com/sebastianbergmann",
@@ -6108,6 +8316,10 @@
],
"description": "Collection of value objects that represent the PHP code units",
"homepage": "https://github.com/sebastianbergmann/code-unit",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/code-unit/issues",
+ "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8"
+ },
"funding": [
{
"url": "https://github.com/sebastianbergmann",
@@ -6159,6 +8371,10 @@
],
"description": "Looks up which function or method a line of code belongs to",
"homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues",
+ "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3"
+ },
"funding": [
{
"url": "https://github.com/sebastianbergmann",
@@ -6229,6 +8445,10 @@
"compare",
"equality"
],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/comparator/issues",
+ "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.6"
+ },
"funding": [
{
"url": "https://github.com/sebastianbergmann",
@@ -6282,6 +8502,10 @@
],
"description": "Library for calculating the complexity of PHP code units",
"homepage": "https://github.com/sebastianbergmann/complexity",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/complexity/issues",
+ "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2"
+ },
"funding": [
{
"url": "https://github.com/sebastianbergmann",
@@ -6344,6 +8568,10 @@
"unidiff",
"unified diff"
],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/diff/issues",
+ "source": "https://github.com/sebastianbergmann/diff/tree/4.0.4"
+ },
"funding": [
{
"url": "https://github.com/sebastianbergmann",
@@ -6403,6 +8631,10 @@
"environment",
"hhvm"
],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/environment/issues",
+ "source": "https://github.com/sebastianbergmann/environment/tree/5.1.3"
+ },
"funding": [
{
"url": "https://github.com/sebastianbergmann",
@@ -6476,6 +8708,10 @@
"export",
"exporter"
],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/exporter/issues",
+ "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.3"
+ },
"funding": [
{
"url": "https://github.com/sebastianbergmann",
@@ -6536,6 +8772,10 @@
"keywords": [
"global state"
],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/global-state/issues",
+ "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.2"
+ },
"funding": [
{
"url": "https://github.com/sebastianbergmann",
@@ -6589,6 +8829,10 @@
],
"description": "Library for counting the lines of code in PHP source code",
"homepage": "https://github.com/sebastianbergmann/lines-of-code",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/lines-of-code/issues",
+ "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3"
+ },
"funding": [
{
"url": "https://github.com/sebastianbergmann",
@@ -6642,6 +8886,10 @@
],
"description": "Traverses array structures and object graphs to enumerate all referenced objects",
"homepage": "https://github.com/sebastianbergmann/object-enumerator/",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/object-enumerator/issues",
+ "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4"
+ },
"funding": [
{
"url": "https://github.com/sebastianbergmann",
@@ -6693,6 +8941,10 @@
],
"description": "Allows reflection of object attributes, including inherited and non-public ones",
"homepage": "https://github.com/sebastianbergmann/object-reflector/",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/object-reflector/issues",
+ "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4"
+ },
"funding": [
{
"url": "https://github.com/sebastianbergmann",
@@ -6752,6 +9004,10 @@
],
"description": "Provides functionality to recursively process PHP variables",
"homepage": "http://www.github.com/sebastianbergmann/recursion-context",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/recursion-context/issues",
+ "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.4"
+ },
"funding": [
{
"url": "https://github.com/sebastianbergmann",
@@ -6803,6 +9059,10 @@
],
"description": "Provides a list of PHP built-in functions that operate on resources",
"homepage": "https://www.github.com/sebastianbergmann/resource-operations",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/resource-operations/issues",
+ "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3"
+ },
"funding": [
{
"url": "https://github.com/sebastianbergmann",
@@ -6855,6 +9115,10 @@
],
"description": "Collection of value objects that represent the types of the PHP type system",
"homepage": "https://github.com/sebastianbergmann/type",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/type/issues",
+ "source": "https://github.com/sebastianbergmann/type/tree/2.3.1"
+ },
"funding": [
{
"url": "https://github.com/sebastianbergmann",
@@ -6904,6 +9168,10 @@
],
"description": "Library that helps with managing the version number of Git-hosted PHP projects",
"homepage": "https://github.com/sebastianbergmann/version",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/version/issues",
+ "source": "https://github.com/sebastianbergmann/version/tree/3.0.2"
+ },
"funding": [
{
"url": "https://github.com/sebastianbergmann",
@@ -6950,6 +9218,10 @@
}
],
"description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
+ "support": {
+ "issues": "https://github.com/theseer/tokenizer/issues",
+ "source": "https://github.com/theseer/tokenizer/tree/master"
+ },
"funding": [
{
"url": "https://github.com/theseer",
@@ -6965,8 +9237,10 @@
"prefer-stable": true,
"prefer-lowest": false,
"platform": {
+ "ext-gd": "*",
+ "ext-json": "*",
"php": "^7.3|^8.0"
},
"platform-dev": [],
- "plugin-api-version": "1.1.0"
+ "plugin-api-version": "2.0.0"
}
diff --git a/config/amqp.php b/config/amqp.php
new file mode 100644
index 0000000..6c58ccc
--- /dev/null
+++ b/config/amqp.php
@@ -0,0 +1,73 @@
+ env('AMQP_CONNECTION', 'rabbitmq'),
+
+ /*Available connections*/
+ 'connections' => [
+
+ 'rabbitmq' => [
+ 'connection' => [
+ 'host' => env('AMQP_HOST', 'liwo_rabbitmq_1'),
+ 'port' => env('AMQP_PORT', 5672),
+ 'username' => env('AMQP_USERNAME', 'root'),
+ 'password' => env('AMQP_PASSWORD', 'root'),
+ 'vhost' => env('AMQP_VHOST', '/'),
+ 'connect_options' => [],
+ 'ssl_options' => [],
+ 'ssl_protocol' => env('AMQP_SSL_PROTOCOL', 'ssl'),
+ ],
+
+ 'channel_id' => null,
+
+ 'message' => [
+ 'content_type' => 'text/plain',
+ 'delivery_mode' => env('AMQP_MESSAGE_DELIVERY_MODE', AMQPMessage::DELIVERY_MODE_PERSISTENT),
+ 'content_encoding' => 'UTF-8',
+ ],
+
+ 'exchange' => [
+ 'name' => env('AMQP_EXCHANGE_NAME', 'activity_exchange'),
+ 'declare' => env('AMQP_EXCHANGE_DECLARE', false),
+ 'type' => env('AMQP_EXCHANGE_TYPE', 'headers'),
+ 'passive' => env('AMQP_EXCHANGE_PASSIVE', false),
+ 'durable' => env('AMQP_EXCHANGE_DURABLE', true),
+ 'auto_delete' => env('AMQP_EXCHANGE_AUTO_DEL', false),
+ 'internal' => env('AMQP_EXCHANGE_INTERNAL', false),
+ 'nowait' => env('AMQP_EXCHANGE_NOWAIT', false),
+ 'properties' => [],
+ ],
+
+ 'queue' => [
+ 'declare' => env('AMQP_QUEUE_DECLARE', false),
+ 'passive' => env('AMQP_QUEUE_PASSIVE', false),
+ 'durable' => env('AMQP_QUEUE_DURABLE', true),
+ 'exclusive' => env('AMQP_QUEUE_EXCLUSIVE', false),
+ 'auto_delete' => env('AMQP_QUEUE_AUTO_DEL', false),
+ 'nowait' => env('AMQP_QUEUE_NOWAIT', false),
+ 'd_properties' => [], // queue_declare properties/arguments
+ 'b_properties' => [], // queue_bind properties/arguments
+ ],
+
+ 'consumer' => [
+ 'tag' => env('AMQP_CONSUMER_TAG', ''),
+ 'no_local' => env('AMQP_CONSUMER_NO_LOCAL', false),
+ 'no_ack' => env('AMQP_CONSUMER_NO_ACK', false),
+ 'exclusive' => env('AMQP_CONSUMER_EXCLUSIVE', false),
+ 'nowait' => env('AMQP_CONSUMER_NOWAIT', false),
+ 'ticket' => null,
+ 'properties' => [],
+ ],
+
+ 'qos' => [
+ 'enabled' => env('AMQP_QOS_ENABLED', false),
+ 'qos_prefetch_size' => env('AMQP_QOS_PREF_SIZE', 0),
+ 'qos_prefetch_count' => env('AMQP_QOS_PREF_COUNT', 1),
+ 'qos_a_global' => env('AMQP_QOS_GLOBAL', false),
+ ],
+ ],
+ ],
+];
diff --git a/config/api-postman.php b/config/api-postman.php
new file mode 100644
index 0000000..7d9727b
--- /dev/null
+++ b/config/api-postman.php
@@ -0,0 +1,122 @@
+ env('APP_URL', 'http://localhost'),
+
+ /*
+ |--------------------------------------------------------------------------
+ | Collection Filename
+ |--------------------------------------------------------------------------
+ |
+ | The name for the collection file to be saved.
+ |
+ */
+
+ 'filename' => '{timestamp}_{app}_collection.json',
+
+ /*
+ |--------------------------------------------------------------------------
+ | Structured
+ |--------------------------------------------------------------------------
+ |
+ | If you want folders to be generated based on namespace.
+ |
+ */
+
+ 'structured' => true,
+
+ /*
+ |--------------------------------------------------------------------------
+ | Auth Middleware
+ |--------------------------------------------------------------------------
+ |
+ | The middleware which wraps your authenticated API routes.
+ |
+ | E.g. auth:api, auth:sanctum
+ |
+ */
+
+ 'auth_middleware' => 'auth:api',
+
+ /*
+ |--------------------------------------------------------------------------
+ | Headers
+ |--------------------------------------------------------------------------
+ |
+ | The headers applied to all routes within the collection.
+ |
+ */
+
+ 'headers' => [
+ [
+ 'key' => 'Accept',
+ 'value' => 'application/json',
+ ],
+ [
+ 'key' => 'Content-Type',
+ 'value' => 'application/json',
+ ],
+ [
+ 'key' => 'Authorization',
+ 'value' => 'Bearer SBzUKamo6CrzQD0hzys2zDxeXiHmPnQcCmlmtCgHAo0JRglIApqJuFF0zxIY',
+ ],
+ ],
+
+ /*
+ |--------------------------------------------------------------------------
+ | Enable Form Data
+ |--------------------------------------------------------------------------
+ |
+ | Determines whether or not form data should be handled.
+ |
+ */
+
+ 'enable_formdata' => false,
+
+ /*
+ |--------------------------------------------------------------------------
+ | Form Data
+ |--------------------------------------------------------------------------
+ |
+ | The key/values to requests for form data dummy information.
+ |
+ */
+
+ 'formdata' => [
+ // 'email' => 'john@example.com',
+ // 'password' => 'changeme',
+ ],
+
+ /*
+ |--------------------------------------------------------------------------
+ | Include Middleware
+ |--------------------------------------------------------------------------
+ |
+ | The routes of the included middleware are included in the export.
+ |
+ */
+
+ 'include_middleware' => ['api'],
+
+ /*
+ |--------------------------------------------------------------------------
+ | Disk Driver
+ |--------------------------------------------------------------------------
+ |
+ | Specify the configured disk for storing the postman collection file.
+ |
+ */
+
+ 'disk' => 'local',
+
+];
diff --git a/config/app.php b/config/app.php
index 2a2f0eb..d517cdc 100644
--- a/config/app.php
+++ b/config/app.php
@@ -2,138 +2,28 @@
return [
- /*
- |--------------------------------------------------------------------------
- | Application Name
- |--------------------------------------------------------------------------
- |
- | This value is the name of your application. This value is used when the
- | framework needs to place the application's name in a notification or
- | any other location as required by the application or its packages.
- |
- */
-
'name' => env('APP_NAME', 'Laravel'),
- /*
- |--------------------------------------------------------------------------
- | Application Environment
- |--------------------------------------------------------------------------
- |
- | This value determines the "environment" your application is currently
- | running in. This may determine how you prefer to configure various
- | services the application utilizes. Set this in your ".env" file.
- |
- */
-
'env' => env('APP_ENV', 'production'),
- /*
- |--------------------------------------------------------------------------
- | Application Debug Mode
- |--------------------------------------------------------------------------
- |
- | When your application is in debug mode, detailed error messages with
- | stack traces will be shown on every error that occurs within your
- | application. If disabled, a simple generic error page is shown.
- |
- */
-
'debug' => (bool) env('APP_DEBUG', false),
- /*
- |--------------------------------------------------------------------------
- | Application URL
- |--------------------------------------------------------------------------
- |
- | This URL is used by the console to properly generate URLs when using
- | the Artisan command line tool. You should set this to the root of
- | your application so that it is used when running Artisan tasks.
- |
- */
-
'url' => env('APP_URL', 'http://localhost'),
'asset_url' => env('ASSET_URL', null),
- /*
- |--------------------------------------------------------------------------
- | Application Timezone
- |--------------------------------------------------------------------------
- |
- | Here you may specify the default timezone for your application, which
- | will be used by the PHP date and date-time functions. We have gone
- | ahead and set this to a sensible default for you out of the box.
- |
- */
-
- 'timezone' => 'UTC',
-
- /*
- |--------------------------------------------------------------------------
- | Application Locale Configuration
- |--------------------------------------------------------------------------
- |
- | The application locale determines the default locale that will be used
- | by the translation service provider. You are free to set this value
- | to any of the locales which will be supported by the application.
- |
- */
-
- 'locale' => 'en',
-
- /*
- |--------------------------------------------------------------------------
- | Application Fallback Locale
- |--------------------------------------------------------------------------
- |
- | The fallback locale determines the locale to use when the current one
- | is not available. You may change the value to correspond to any of
- | the language folders that are provided through your application.
- |
- */
+ 'timezone' => 'Asia/Tehran',
+
+ 'locale' => 'fa',
'fallback_locale' => 'en',
- /*
- |--------------------------------------------------------------------------
- | Faker Locale
- |--------------------------------------------------------------------------
- |
- | This locale will be used by the Faker PHP library when generating fake
- | data for your database seeds. For example, this will be used to get
- | localized telephone numbers, street address information and more.
- |
- */
-
- 'faker_locale' => 'en_US',
-
- /*
- |--------------------------------------------------------------------------
- | Encryption Key
- |--------------------------------------------------------------------------
- |
- | This key is used by the Illuminate encrypter service and should be set
- | to a random, 32 character string, otherwise these encrypted strings
- | will not be safe. Please do this before deploying an application!
- |
- */
+ 'faker_locale' => 'fa_IR',
'key' => env('APP_KEY'),
'cipher' => 'AES-256-CBC',
- /*
- |--------------------------------------------------------------------------
- | Autoloaded Service Providers
- |--------------------------------------------------------------------------
- |
- | The service providers listed here will be automatically loaded on the
- | request to your application. Feel free to add your own services to
- | this array to grant expanded functionality to your applications.
- |
- */
-
'providers' => [
/*
@@ -171,23 +61,12 @@ return [
*/
App\Providers\AppServiceProvider::class,
App\Providers\AuthServiceProvider::class,
- // App\Providers\BroadcastServiceProvider::class,
+ App\Providers\BroadcastServiceProvider::class,
App\Providers\EventServiceProvider::class,
App\Providers\RouteServiceProvider::class,
-
+ App\Utilities\Zarinpal\Laravel\ZarinpalServiceProvider::class,
],
- /*
- |--------------------------------------------------------------------------
- | Class Aliases
- |--------------------------------------------------------------------------
- |
- | This array of class aliases will be registered when this application
- | is started. However, feel free to register as many as you wish as
- | the aliases are "lazy" loaded so they don't hinder performance.
- |
- */
-
'aliases' => [
'App' => Illuminate\Support\Facades\App::class,
@@ -215,7 +94,7 @@ return [
'Password' => Illuminate\Support\Facades\Password::class,
'Queue' => Illuminate\Support\Facades\Queue::class,
'Redirect' => Illuminate\Support\Facades\Redirect::class,
- // 'Redis' => Illuminate\Support\Facades\Redis::class,
+ 'Redis' => Illuminate\Support\Facades\Redis::class,
'Request' => Illuminate\Support\Facades\Request::class,
'Response' => Illuminate\Support\Facades\Response::class,
'Route' => Illuminate\Support\Facades\Route::class,
@@ -227,6 +106,6 @@ return [
'Validator' => Illuminate\Support\Facades\Validator::class,
'View' => Illuminate\Support\Facades\View::class,
+ 'View' => App\Utilities\Zarinpal\Laravel\Facade\Zarinpal::class,
],
-
];
diff --git a/config/cors.php b/config/cors.php
index 8a39e6d..b2930d0 100644
--- a/config/cors.php
+++ b/config/cors.php
@@ -21,7 +21,7 @@ return [
'allowed_origins' => ['*'],
- 'allowed_origins_patterns' => [],
+ 'allowed_origins_patterns' => ['Content-Type', 'X-Requested-With'],
'allowed_headers' => ['*'],
diff --git a/config/cors.php.laravel b/config/cors.php.laravel
new file mode 100644
index 0000000..c3ded3c
--- /dev/null
+++ b/config/cors.php.laravel
@@ -0,0 +1,59 @@
+ ['/*'],
+
+ /*
+ * Matches the request method. `['*']` allows all methods.
+ */
+ 'allowed_methods' => ['*'],
+
+ /*
+ * Matches the request origin. `['*']` allows all origins. Wildcards can be used, eg `*.mydomain.com`
+ */
+ 'allowed_origins' => ['*'],
+
+ /*
+ * Patterns that can be used with `preg_match` to match the origin.
+ */
+ 'allowedOriginsPatterns' => ['Content-Type', 'X-Requested-With'],
+
+ /*
+ * Sets the Access-Control-Allow-Headers response header. `['*']` allows all headers.
+ */
+ 'allowed_headers' => ['*'],
+
+ /*
+ * Sets the Access-Control-Expose-Headers response header with these headers.
+ */
+ 'exposed_headers' => [],
+
+ /*
+ * Sets the Access-Control-Max-Age response header when > 0.
+ */
+ 'max_age' => 0,
+
+ /*
+ * Sets the Access-Control-Allow-Credentials header.
+ */
+ 'supports_credentials' => false,
+];
diff --git a/config/cors.php.lumen b/config/cors.php.lumen
new file mode 100644
index 0000000..8a39e6d
--- /dev/null
+++ b/config/cors.php.lumen
@@ -0,0 +1,34 @@
+ ['api/*', 'sanctum/csrf-cookie'],
+
+ 'allowed_methods' => ['*'],
+
+ 'allowed_origins' => ['*'],
+
+ 'allowed_origins_patterns' => [],
+
+ 'allowed_headers' => ['*'],
+
+ 'exposed_headers' => [],
+
+ 'max_age' => 0,
+
+ 'supports_credentials' => false,
+
+];
diff --git a/config/fcm.php b/config/fcm.php
new file mode 100644
index 0000000..9105e5e
--- /dev/null
+++ b/config/fcm.php
@@ -0,0 +1,18 @@
+ env('FCM_KEY'),
+
+];
diff --git a/config/filesystems.php b/config/filesystems.php
index 10c9d9b..e5708dc 100644
--- a/config/filesystems.php
+++ b/config/filesystems.php
@@ -15,6 +15,19 @@ return [
'default' => env('FILESYSTEM_DRIVER', 'local'),
+ /*
+ |--------------------------------------------------------------------------
+ | Default Cloud Filesystem Disk
+ |--------------------------------------------------------------------------
+ |
+ | Many applications store files both locally and in the cloud. For this
+ | reason, you may specify a default "cloud" driver here. This driver
+ | will be bound as the Cloud disk implementation in the container.
+ |
+ */
+
+ 'cloud' => env('FILESYSTEM_CLOUD', 's3'),
+
/*
|--------------------------------------------------------------------------
| Filesystem Disks
@@ -38,7 +51,7 @@ return [
'public' => [
'driver' => 'local',
'root' => storage_path('app/public'),
- 'url' => env('APP_URL').'/storage',
+ 'url' => env('APP_URL') . '/storage',
'visibility' => 'public',
],
@@ -46,8 +59,8 @@ return [
'driver' => 's3',
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
- 'region' => env('AWS_DEFAULT_REGION'),
- 'bucket' => env('AWS_BUCKET'),
+ 'region' => env('AWS_DEFAULT_REGION','us'),
+ 'bucket' => env('AWS_BUCKET','real-test-2'),
'url' => env('AWS_URL'),
'endpoint' => env('AWS_ENDPOINT'),
],
@@ -66,7 +79,7 @@ return [
*/
'links' => [
- public_path('storage') => storage_path('app/public'),
+ base_path('public/storage') => storage_path('app/public'),
],
];
diff --git a/config/geoip.php b/config/geoip.php
new file mode 100644
index 0000000..62925df
--- /dev/null
+++ b/config/geoip.php
@@ -0,0 +1,172 @@
+ false,
+
+ /*
+ |--------------------------------------------------------------------------
+ | Include Currency in Results
+ |--------------------------------------------------------------------------
+ |
+ | When enabled the system will do it's best in deciding the user's currency
+ | by matching their ISO code to a preset list of currencies.
+ |
+ */
+
+ 'include_currency' => false,
+
+ /*
+ |--------------------------------------------------------------------------
+ | Default Service
+ |--------------------------------------------------------------------------
+ |
+ | Here you may specify the default storage driver that should be used
+ | by the framework.
+ |
+ | Supported: "maxmind_database", "maxmind_api", "ipapi"
+ |
+ */
+
+ 'service' => 'ipapi',
+
+ /*
+ |--------------------------------------------------------------------------
+ | Storage Specific Configuration
+ |--------------------------------------------------------------------------
+ |
+ | Here you may configure as many storage drivers as you wish.
+ |
+ */
+
+ 'services' => [
+
+ 'maxmind_database' => [
+ 'class' => MaxMindDatabase::class,
+ 'database_path' => storage_path('app/geoip.mmdb'),
+ 'update_url' => sprintf('https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-City&license_key=%s&suffix=tar.gz', env('MAXMIND_LICENSE_KEY')),
+ 'locales' => ['en'],
+ ],
+
+ 'maxmind_api' => [
+ 'class' => MaxMindWebService::class,
+ 'user_id' => env('MAXMIND_USER_ID'),
+ 'license_key' => env('MAXMIND_LICENSE_KEY'),
+ 'locales' => ['en'],
+ ],
+
+ 'ipapi' => [
+ 'class' => IPApi::class,
+ 'secure' => true,
+ 'key' => env('IPAPI_KEY'),
+ 'continent_path' => storage_path('app/continents.json'),
+ 'lang' => 'en',
+ ],
+
+ 'ipgeolocation' => [
+ 'class' => IPGeoLocation::class,
+ 'secure' => true,
+ 'key' => env('IPGEOLOCATION_KEY'),
+ 'continent_path' => storage_path('app/continents.json'),
+ 'lang' => 'en',
+ ],
+
+ 'ipdata' => [
+ 'class' => IPData::class,
+ 'key' => env('IPDATA_API_KEY'),
+ 'secure' => true,
+ ],
+
+ 'ipfinder' => [
+ 'class' => IPFinder::class,
+ 'key' => env('IPFINDER_API_KEY'),
+ 'secure' => true,
+ 'locales' => ['en'],
+ ],
+
+ ],
+
+ /*
+ |--------------------------------------------------------------------------
+ | Default Cache Driver
+ |--------------------------------------------------------------------------
+ |
+ | Here you may specify the type of caching that should be used
+ | by the package.
+ |
+ | Options:
+ |
+ | all - All location are cached
+ | some - Cache only the requesting user
+ | none - Disable cached
+ |
+ */
+
+ 'cache' => 'none',
+
+ /*
+ |--------------------------------------------------------------------------
+ | Cache Tags
+ |--------------------------------------------------------------------------
+ |
+ | Cache tags are not supported when using the file or database cache
+ | drivers in Laravel. This is done so that only locations can be cleared.
+ |
+ */
+
+ 'cache_tags' => [],
+
+ /*
+ |--------------------------------------------------------------------------
+ | Cache Expiration
+ |--------------------------------------------------------------------------
+ |
+ | Define how long cached location are valid.
+ |
+ */
+
+ 'cache_expires' => 30,
+
+ /*
+ |--------------------------------------------------------------------------
+ | Default Location
+ |--------------------------------------------------------------------------
+ |
+ | Return when a location is not found.
+ |
+ */
+
+ 'default_location' => [
+ 'ip' => '127.0.0.0',
+ 'iso_code' => 'IRN',
+ 'country' => 'Islamic Republic of Iran',
+ 'city' => 'Tehran',
+ 'state' => 'teh',
+ 'state_name' => 'Connecticut',
+ 'postal_code' => '513',
+ 'lat' => 35.6892,
+ 'lon' => 51.3890,
+ 'timezone' => 'Asia/Tehran',
+ 'continent' => 'Asia',
+ 'default' => true,
+ 'currency' => 'IRR',
+ ],
+
+];
diff --git a/config/logging.php b/config/logging.php
index 6aa77fe..c6f3fe5 100644
--- a/config/logging.php
+++ b/config/logging.php
@@ -1,104 +1,44 @@
env('LOG_CHANNEL', 'stack'),
- /*
- |--------------------------------------------------------------------------
- | Log Channels
- |--------------------------------------------------------------------------
- |
- | Here you may configure the log channels for your application. Out of
- | the box, Laravel uses the Monolog PHP logging library. This gives
- | you a variety of powerful log handlers / formatters to utilize.
- |
- | Available Drivers: "single", "daily", "slack", "syslog",
- | "errorlog", "monolog",
- | "custom", "stack"
- |
- */
-
'channels' => [
+ 'hi' => [
+ 'driver' => 'custom',
+ 'via' => CreateCustomLogger::class,
+ 'handler' => AmqpHandler::class,
+ 'with' => [
+ 'exchange' => new AMQPChannel(
+ new AMQPStreamConnection("liwo_rabbitmq_1", 5672, 'root', 'root')
+ ),
+ 'exchangeName' => 'log_exchange'
+ ],
+ ],
+
'stack' => [
'driver' => 'stack',
- 'channels' => ['single'],
+ 'channels' => ['hi', 'daily',],
'ignore_exceptions' => false,
],
'single' => [
'driver' => 'single',
'path' => storage_path('logs/laravel.log'),
- 'level' => env('LOG_LEVEL', 'debug'),
+ 'level' => 'debug',
],
'daily' => [
'driver' => 'daily',
'path' => storage_path('logs/laravel.log'),
- 'level' => env('LOG_LEVEL', 'debug'),
+ 'level' => 'debug',
'days' => 14,
],
-
- 'slack' => [
- 'driver' => 'slack',
- 'url' => env('LOG_SLACK_WEBHOOK_URL'),
- 'username' => 'Laravel Log',
- 'emoji' => ':boom:',
- 'level' => env('LOG_LEVEL', 'critical'),
- ],
-
- 'papertrail' => [
- 'driver' => 'monolog',
- 'level' => env('LOG_LEVEL', 'debug'),
- 'handler' => SyslogUdpHandler::class,
- 'handler_with' => [
- 'host' => env('PAPERTRAIL_URL'),
- 'port' => env('PAPERTRAIL_PORT'),
- ],
- ],
-
- 'stderr' => [
- 'driver' => 'monolog',
- 'handler' => StreamHandler::class,
- 'formatter' => env('LOG_STDERR_FORMATTER'),
- 'with' => [
- 'stream' => 'php://stderr',
- ],
- ],
-
- 'syslog' => [
- 'driver' => 'syslog',
- 'level' => env('LOG_LEVEL', 'debug'),
- ],
-
- 'errorlog' => [
- 'driver' => 'errorlog',
- 'level' => env('LOG_LEVEL', 'debug'),
- ],
-
- 'null' => [
- 'driver' => 'monolog',
- 'handler' => NullHandler::class,
- ],
-
- 'emergency' => [
- 'path' => storage_path('logs/laravel.log'),
- ],
],
-
];
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/config/media-library.php b/config/media-library.php
new file mode 100644
index 0000000..6d21069
--- /dev/null
+++ b/config/media-library.php
@@ -0,0 +1,179 @@
+ env('MEDIA_DISK', 'public'),
+
+ /*
+ * The maximum file size of an item in bytes.
+ * Adding a larger file will result in an exception.
+ */
+ 'max_file_size' => 1024 * 1024 * 10,
+
+ /*
+ * This queue will be used to generate derived and responsive images.
+ * Leave empty to use the default queue.
+ */
+ 'queue_name' => '',
+
+ /*
+ * By default all conversions will be performed on a queue.
+ */
+ 'queue_conversions_by_default' => env('QUEUE_CONVERSIONS_BY_DEFAULT', true),
+
+ /*
+ * The fully qualified class name of the media model.
+ */
+ 'media_model' => Spatie\MediaLibrary\MediaCollections\Models\Media::class,
+
+ 'remote' => [
+ /*
+ * Any extra headers that should be included when uploading media to
+ * a remote disk. Even though supported headers may vary between
+ * different drivers, a sensible default has been provided.
+ *
+ * Supported by S3: CacheControl, Expires, StorageClass,
+ * ServerSideEncryption, Metadata, ACL, ContentEncoding
+ */
+ 'extra_headers' => [
+ 'CacheControl' => 'max-age=604800',
+ ],
+ ],
+
+ 'responsive_images' => [
+
+ /*
+ * This class is responsible for calculating the target widths of the responsive
+ * images. By default we optimize for filesize and create variations that each are 20%
+ * smaller than the previous one. More info in the documentation.
+ *
+ * https://docs.spatie.be/laravel-medialibrary/v8/advanced-usage/generating-responsive-images
+ */
+ 'width_calculator' => Spatie\MediaLibrary\ResponsiveImages\WidthCalculator\FileSizeOptimizedWidthCalculator::class,
+
+ /*
+ * By default rendering media to a responsive image will add some javascript and a tiny placeholder.
+ * This ensures that the browser can already determine the correct layout.
+ */
+ 'use_tiny_placeholders' => true,
+
+ /*
+ * This class will generate the tiny placeholder used for progressive image loading. By default
+ * the media library will use a tiny blurred jpg image.
+ */
+ 'tiny_placeholder_generator' => Spatie\MediaLibrary\ResponsiveImages\TinyPlaceholderGenerator\Blurred::class,
+ ],
+
+ /*
+ * When converting Media instances to response the media library will add
+ * a `loading` attribute to the `img` tag. Here you can set the default
+ * value of that attribute.
+ *
+ * Possible values: 'lazy', 'eager', 'auto' or null if you don't want to set any loading instruction.
+ *
+ * More info: https://css-tricks.com/native-lazy-loading/
+ */
+ 'default_loading_attribute_value' => null,
+
+ /*
+ * This is the class that is responsible for naming conversion files. By default,
+ * it will use the filename of the original and concatenate the conversion name to it.
+ */
+ 'conversion_file_namer' => App\Utilities\Avatar\DefaultConversionFileNamer::class,
+
+ /*
+ * The class that contains the strategy for determining a media file's path.
+ */
+ 'path_generator' => App\Utilities\Avatar\DefaultPathGenerator::class,
+
+ /*
+ * When urls to files get generated, this class will be called. Use the default
+ * if your files are stored locally above the site root or on s3.
+ */
+ 'url_generator' => Spatie\MediaLibrary\Support\UrlGenerator\DefaultUrlGenerator::class,
+
+ /*
+ * Whether to activate versioning when urls to files get generated.
+ * When activated, this attaches a ?v=xx query string to the URL.
+ */
+ 'version_urls' => false,
+
+ /*
+ * The media library will try to optimize all converted images by removing
+ * metadata and applying a little bit of compression. These are
+ * the optimizers that will be used by default.
+ */
+ 'image_optimizers' => [
+ Spatie\ImageOptimizer\Optimizers\Jpegoptim::class => [
+ '--strip-all', // this strips out all text information such as comments and EXIF data
+ '--all-progressive', // this will make sure the resulting image is a progressive one
+ ],
+ Spatie\ImageOptimizer\Optimizers\Pngquant::class => [
+ '--force', // required parameter for this package
+ ],
+ Spatie\ImageOptimizer\Optimizers\Optipng::class => [
+ '-i0', // this will result in a non-interlaced, progressive scanned image
+ '-o2', // this set the optimization level to two (multiple IDAT compression trials)
+ '-quiet', // required parameter for this package
+ ],
+ Spatie\ImageOptimizer\Optimizers\Svgo::class => [
+ '--disable=cleanupIDs', // disabling because it is known to cause troubles
+ ],
+ Spatie\ImageOptimizer\Optimizers\Gifsicle::class => [
+ '-b', // required parameter for this package
+ '-O3', // this produces the slowest but best results
+ ],
+ ],
+
+ /*
+ * These generators will be used to create an image of media files.
+ */
+ 'image_generators' => [
+ Spatie\MediaLibrary\Conversions\ImageGenerators\Image::class,
+ Spatie\MediaLibrary\Conversions\ImageGenerators\Webp::class,
+ Spatie\MediaLibrary\Conversions\ImageGenerators\Pdf::class,
+ Spatie\MediaLibrary\Conversions\ImageGenerators\Svg::class,
+ Spatie\MediaLibrary\Conversions\ImageGenerators\Video::class,
+ ],
+
+ /*
+ * The engine that should perform the image conversions.
+ * Should be either `gd` or `imagick`.
+ */
+ 'image_driver' => env('IMAGE_DRIVER', 'gd'),
+
+ /*
+ * FFMPEG & FFProbe binaries paths, only used if you try to generate video
+ * thumbnails and have installed the php-ffmpeg/php-ffmpeg composer
+ * dependency.
+ */
+ 'ffmpeg_path' => env('FFMPEG_PATH', '/usr/bin/ffmpeg'),
+ 'ffprobe_path' => env('FFPROBE_PATH', '/usr/bin/ffprobe'),
+
+ /*
+ * The path where to store temporary files while performing image conversions.
+ * If set to null, storage_path('media-library/temp') will be used.
+ */
+ 'temporary_directory_path' => null,
+
+ /*
+ * Here you can override the class names of the jobs used by this package. Make sure
+ * your custom jobs extend the ones provided by the package.
+ */
+ 'jobs' => [
+ 'perform_conversions' => Spatie\MediaLibrary\Conversions\Jobs\PerformConversionsJob::class,
+ 'generate_responsive_images' => Spatie\MediaLibrary\ResponsiveImages\Jobs\GenerateResponsiveImagesJob::class,
+ ],
+
+ /*
+ * When using the addMediaFromUrl method you may want to replace the default downloader.
+ * This is particularly useful when the url of the image is behind a firewall and
+ * need to add additional flags, possibly using curl.
+ */
+ 'media_downloader' => Spatie\MediaLibrary\Downloaders\DefaultDownloader::class,
+
+];
diff --git a/config/query-builder.php b/config/query-builder.php
new file mode 100644
index 0000000..b65680f
--- /dev/null
+++ b/config/query-builder.php
@@ -0,0 +1,38 @@
+ [
+ 'include' => 'include',
+
+ 'filter' => 'filter',
+
+ 'sort' => 'sort',
+
+ 'fields' => 'fields',
+
+ 'append' => 'append',
+ ],
+
+ /*
+ * Related model counts are included using the relationship name suffixed with this string.
+ * For example: GET /users?include=postsCount
+ */
+ 'count_suffix' => 'Count',
+
+ /*
+ * By default the package will throw an `InvalidFilterQuery` exception when a filter in the
+ * URL is not allowed in the `allowedFilters()` method.
+ */
+ 'disable_invalid_filter_query_exception' => false,
+
+];
diff --git a/config/services.php b/config/services.php
index 2a1d616..5c0ae34 100644
--- a/config/services.php
+++ b/config/services.php
@@ -1,33 +1,21 @@
[
- 'domain' => env('MAILGUN_DOMAIN'),
- 'secret' => env('MAILGUN_SECRET'),
- 'endpoint' => env('MAILGUN_ENDPOINT', 'api.mailgun.net'),
+ 'task' => 'hi-task-app',
+ 'zarinpal' => [
+ 'merchant-id' => '68d337b0-4b77-11ea-8409-000c295eb8fc',
+ 'type' => 'zarin-gate', // Types: [zarin-gate || normal]
+ 'callback-url' => 'http://127.0.0.1:8000/user/v1/callback',
+ 'server' => 'germany', // Servers: [germany || iran || test]
+ 'email' => 'admin@base.com',
+ 'mobile' => '09123456789',
+ 'description' => env('APP_NAME', 'APP_NAME'),
+ 'sandbox' => true,
],
- 'postmark' => [
- 'token' => env('POSTMARK_TOKEN'),
+ 'google' => [
+ 'client_id' => '1002439248397-oa6hnh25n6qri3q4kst62gvb1k9ki65l.apps.googleusercontent.com',
+ 'client_secret' => 'tKbiyh5hOjYIcj-W1y3N8X5R',
+ 'redirect' => env('APP_URL').'/user/v1/auth/google/callback',
],
-
- 'ses' => [
- 'key' => env('AWS_ACCESS_KEY_ID'),
- 'secret' => env('AWS_SECRET_ACCESS_KEY'),
- 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
- ],
-
];
diff --git a/database/factories/BusinessFactory.php b/database/factories/BusinessFactory.php
new file mode 100644
index 0000000..28f0dae
--- /dev/null
+++ b/database/factories/BusinessFactory.php
@@ -0,0 +1,17 @@
+define(Business::class, function (Faker $faker) {
+ return [
+ 'name' => $name = $faker->unique()->company,
+ 'slug' => Str::slug($name) . $faker->numberBetween(1, 100),
+ 'wallet' => random_int(111111, 999999),
+ 'color' => $faker->colorName,
+ 'calculated_at' => \Carbon\Carbon::now()->subDays(random_int(1, 31)),
+ ];
+});
diff --git a/database/factories/CostFactory.php b/database/factories/CostFactory.php
new file mode 100644
index 0000000..6e84128
--- /dev/null
+++ b/database/factories/CostFactory.php
@@ -0,0 +1,19 @@
+define(Cost::class, function (Faker $faker) {
+ return [
+ 'business_id' => random_int(1,5000),
+ 'type' => $type = $faker->boolean() ? 'users' : 'files',
+ 'month' => jdate(Carbon::now()->subDays(random_int(0,90)))->format("Y-m-01"),
+ 'amount' => random_int(1,1000),
+ 'fee' => $type === 'users' ? enum("business.fee.user") : enum("business.fee.file"),
+ 'duration' => random_int(1,60),
+ 'created_at' => Carbon::now()->subMinutes(random_int(1, 1000)),
+ ];
+});
diff --git a/database/factories/FileFactory.php b/database/factories/FileFactory.php
new file mode 100644
index 0000000..45191da
--- /dev/null
+++ b/database/factories/FileFactory.php
@@ -0,0 +1,35 @@
+define(File::class, function (Faker $faker) {
+ $mimes = ['application/pdf', 'video/mp4', 'image/png', 'image/jpeg', 'audio/x-wav'];
+ $extensions = ['pdf', 'mp4', 'png', 'jpg', 'wav'];
+ $groups = [
+ 'pdf' => 'pdf',
+ 'mp4' => 'video',
+ 'png' => 'image',
+ 'jpg' => 'image',
+ 'wav' => 'audio',
+ ];
+ $sizes = [1, 5, 128, 256, 1024, 2048];
+ $rand_type = $faker->numberBetween(0, 4);
+ return [
+ 'user_id' => $faker->numberBetween(1, 200),
+ 'business_id' => $faker->numberBetween(1, 200),
+ 'project_id' => $faker->numberBetween(1, 200),
+ 'attached_to_id' => $faker->numberBetween(1, 100),
+ 'attached_to_table' => enum('tables.tasks.id'),
+ 'disk' => 's3',
+ 'original_name' => $faker->words(1, true),
+ 'name' => $faker->words(1, true),
+ 'extension' => $extension = $extensions[$rand_type],
+ 'mime' => $mimes[$rand_type],
+ 'group' => $groups[$extension],
+ 'size' => $sizes[$faker->numberBetween(0, 5)],
+ 'description' => $faker->text,
+ ];
+});
diff --git a/database/factories/FingerprintFactory.php b/database/factories/FingerprintFactory.php
new file mode 100644
index 0000000..d8458e0
--- /dev/null
+++ b/database/factories/FingerprintFactory.php
@@ -0,0 +1,37 @@
+define(Fingerprint::class, function () use ($faker) {
+ $os = [
+ $faker->windowsPlatformToken,
+ $faker->linuxPlatformToken,
+ $faker->macPlatformToken
+ ];
+
+ $browsers = [
+ $faker->firefox,
+ $faker->chrome,
+ $faker->opera,
+ $faker->safari,
+ ];
+
+ $detector = new Jenssegers\Agent\Agent();
+
+ return [
+ 'user_id' => $faker->numberBetween(1, 1000),
+ 'agent' => $detector->browser(Arr::random($browsers)),
+ 'ip' => $faker->ipv4,
+ 'os' => $detector->platform(Arr::random($browsers)),
+ 'latitude' => $faker->latitude,
+ 'longitude' => $faker->longitude,
+ 'token' => Str::random(60),
+ ];
+});
diff --git a/database/factories/ProjectFactory.php b/database/factories/ProjectFactory.php
new file mode 100644
index 0000000..d7dce12
--- /dev/null
+++ b/database/factories/ProjectFactory.php
@@ -0,0 +1,22 @@
+define(Project::class, function (Faker $faker) {
+ return [
+ 'business_id' => null,
+ 'name' => $name = $faker->words(3, true),
+ 'slug' => Str::slug($name),
+ 'private' => false,
+ 'budget' => 0,
+ 'start' => null,
+ 'finish' => null,
+ 'color' => $faker->colorName,
+ 'active' => rand(0, 1),
+ 'description' => $faker->paragraph,
+ ];
+});
diff --git a/database/factories/SprintflowFactory.php b/database/factories/SprintflowFactory.php
new file mode 100644
index 0000000..b5c3031
--- /dev/null
+++ b/database/factories/SprintflowFactory.php
@@ -0,0 +1,16 @@
+define(Sprint::class, function (Faker $faker) {
+ return [
+ 'business_id' => null,
+ 'name' => $faker->randomElement(['scrum', 'printing',
+ 'agile', 'develop', 'design', 'writing', 'seo', 'sale']),
+ 'active' => rand(0, 1),
+ 'description' => $faker->paragraph,
+ ];
+});
diff --git a/database/factories/SystemFactory.php b/database/factories/SystemFactory.php
new file mode 100644
index 0000000..0631afb
--- /dev/null
+++ b/database/factories/SystemFactory.php
@@ -0,0 +1,14 @@
+define(System::class, function (Faker $faker) {
+ return [
+ 'business_id' => null,
+ 'project_id' => null,
+ 'name' => $name = $faker->words(3, true),
+ ];
+});
diff --git a/database/factories/TagFactory.php b/database/factories/TagFactory.php
new file mode 100644
index 0000000..5941665
--- /dev/null
+++ b/database/factories/TagFactory.php
@@ -0,0 +1,15 @@
+define(Tag::class, function () use ($faker) {
+ return [
+ 'label' => $faker->colorName,
+ 'color' => $faker->colorName,
+ 'business_id' => null,
+ ];
+});
diff --git a/database/factories/TaskFactory.php b/database/factories/TaskFactory.php
new file mode 100644
index 0000000..66c399a
--- /dev/null
+++ b/database/factories/TaskFactory.php
@@ -0,0 +1,32 @@
+define(Task::class, function (Faker $faker) {
+ return [
+ 'business_id' => $faker->numberBetween(1, 200),
+ 'creator_id' => $faker->numberBetween(1, 5000),
+ 'project_id' => $faker->numberBetween(1, 200),
+ 'assignee_id' => $faker->numberBetween(1, 20),
+ 'workflow_id' => $faker->numberBetween(1, 2500),
+ 'system_id' => $faker->numberBetween(1, 20),
+ 'sprint_id' => $faker->numberBetween(1, 20),
+ 'status_id' => $faker->numberBetween(1, 5000),
+ 'approver_id' => $faker->numberBetween(1, 5000),
+
+ 'title' => $faker->name,
+ 'description' => $faker->sentences(3, true),
+ 'priority' => $faker->numberBetween(1,10),
+ 'on_time' => $faker->boolean,
+ 'ready_to_test' => $faker->boolean,
+ 'spent_time' => $faker->numberBetween(100, 300),
+ 'estimated_time' => $faker->numberBetween(100, 300),
+ 'due_date' => $faker->date(),
+ 'completed_at' => $faker->date(),
+ 'work_start' => $faker->date('1971-01-01','now'),
+ 'work_finish' => $faker->date('1971-01-01','now'),
+ ];
+});
diff --git a/database/factories/TransactionFactory.php b/database/factories/TransactionFactory.php
new file mode 100644
index 0000000..80a58ff
--- /dev/null
+++ b/database/factories/TransactionFactory.php
@@ -0,0 +1,21 @@
+define(Transaction::class, function (Faker $faker) {
+ $business_user = DB::selectOne('select business_id, user_id from business_user where level = 4 order by rand() limit 1');
+
+ return [
+ 'user_id' => $business_user->user_id,
+ 'business_id' => $business_user->business_id,
+ 'amount' => random_int(1000, 9999),
+ 'succeeded' => $faker->boolean(),
+ 'options' => json_encode([]),
+ 'created_at' => Carbon::now()->subMinutes(random_int(1, 1000)),
+ ];
+});
diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php
index 3510ed6..779f795 100644
--- a/database/factories/UserFactory.php
+++ b/database/factories/UserFactory.php
@@ -1,47 +1,16 @@
$this->faker->name,
- 'email' => $this->faker->unique()->safeEmail,
- 'email_verified_at' => now(),
- 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
- 'remember_token' => Str::random(10),
- ];
- }
-
- /**
- * Indicate that the model's email address should be unverified.
- *
- * @return \Illuminate\Database\Eloquent\Factories\Factory
- */
- public function unverified()
- {
- return $this->state(function (array $attributes) {
- return [
- 'email_verified_at' => null,
- ];
- });
- }
-}
+$factory->define(User::class, function (Faker $faker) {
+ return [
+ 'name' => $faker->name,
+ 'email' => $faker->unique()->safeEmail,
+ 'mobile' => $faker->unique()->phoneNumber,
+ 'username' => $faker->unique()->userName,
+ 'password' => '$2y$10$l8jgLtb7RyDd7wbvxYPsuu7gjo/bLBkBYQhXnkpdmm.SVF3CT00UW',
+ ];
+});
diff --git a/database/factories/WorkFactory.php b/database/factories/WorkFactory.php
new file mode 100644
index 0000000..f593e38
--- /dev/null
+++ b/database/factories/WorkFactory.php
@@ -0,0 +1,22 @@
+define(Work::class, function (Faker $faker) {
+ $started_at = $faker->dateTime('now');
+ $ended_at = $faker->dateTime('now');
+// $minute_sum = $ended_at->diffInMinutes($started_at);
+ return [
+ 'business_id' => $faker->numberBetween(1, 200),
+ 'project_id' => $faker->numberBetween(1, 200),
+ 'task_id' => $faker->numberBetween(1, 200),
+ 'user_id' => $faker->numberBetween(1, 5000),
+ 'message' => $faker->name,
+ 'minute_sum' => $faker->numberBetween(100, 5000),
+ 'started_at' => $started_at,
+ 'ended_at' => $ended_at,
+ ];
+});
diff --git a/database/factories/WorkflowFactory.php b/database/factories/WorkflowFactory.php
new file mode 100644
index 0000000..4d51b1c
--- /dev/null
+++ b/database/factories/WorkflowFactory.php
@@ -0,0 +1,15 @@
+define(Workflow::class, function (Faker $faker) {
+ return [
+ 'business_id' => null,
+ 'name' => $faker->randomElement(['scrum', 'printing',
+ 'agile', 'develop', 'design', 'writing', 'seo', 'sale']),
+ 'desc' => $faker->sentences(1, true),
+ ];
+});
diff --git a/database/factories/WorkstatusFactory.php b/database/factories/WorkstatusFactory.php
new file mode 100644
index 0000000..0c5f1fa
--- /dev/null
+++ b/database/factories/WorkstatusFactory.php
@@ -0,0 +1,15 @@
+define(Status::class, function (Faker $faker) {
+ return [
+ 'name' => $faker->randomElement(['idea', 'todo',
+ 'doing', 'done', 'pending', 'hold', 'logestik', 'sew']),
+ 'state' => rand(0, 3),
+ 'order' => rand(0, 10),
+ ];
+});
diff --git a/database/migrations/.gitkeep b/database/migrations/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/database/migrations/2014_10_12_000000_create_users_table.php b/database/migrations/2020_08_18_085016_create_users_table.php
similarity index 62%
rename from database/migrations/2014_10_12_000000_create_users_table.php
rename to database/migrations/2020_08_18_085016_create_users_table.php
index 621a24e..b656a71 100644
--- a/database/migrations/2014_10_12_000000_create_users_table.php
+++ b/database/migrations/2020_08_18_085016_create_users_table.php
@@ -17,10 +17,14 @@ class CreateUsersTable extends Migration
$table->id();
$table->string('name');
$table->string('email')->unique();
- $table->timestamp('email_verified_at')->nullable();
+ $table->string('mobile')->unique()->nullable();
+ $table->string('username')->unique();
$table->string('password');
- $table->rememberToken();
- $table->timestamps();
+ $table->boolean('active')->default(false);
+ $table->boolean('has_avatar')->default(false);
+ $table->timestamp('created_at')->nullable();
+ $table->timestamp('updated_at')->useCurrent();
+ $table->timestamp('deleted_at')->nullable();
});
}
diff --git a/database/migrations/2020_08_18_085017_fingerprints.php b/database/migrations/2020_08_18_085017_fingerprints.php
new file mode 100644
index 0000000..6a72833
--- /dev/null
+++ b/database/migrations/2020_08_18_085017_fingerprints.php
@@ -0,0 +1,39 @@
+id();
+ $table->unsignedBigInteger('user_id');
+ $table->string('agent');
+ $table->ipAddress('ip');
+ $table->string('os');
+ $table->decimal('latitude', 10, 4);
+ $table->decimal('longitude', 11, 4);
+ $table->char('token', 60)->unique();
+ $table->text('fcm_token')->unique()->nullable();
+ $table->timestamps();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::dropIfExists('fingerprints');
+ }
+}
diff --git a/database/migrations/2020_08_18_085018_create_businesses_table.php b/database/migrations/2020_08_18_085018_create_businesses_table.php
new file mode 100644
index 0000000..b0d168f
--- /dev/null
+++ b/database/migrations/2020_08_18_085018_create_businesses_table.php
@@ -0,0 +1,49 @@
+id();
+ $table->string('name');
+ $table->string('slug')->unique();
+ $table->integer('wallet')->default(0);
+ $table->unsignedInteger('files_volume')->default(0);
+ $table->string('color')->nullable();
+ $table->string('description')->nullable();
+ $table->json('cache')->nullable();
+ $table->boolean('has_avatar')->default(false);
+ $table->timestamp('calculated_at')->nullable()->index();
+ $table->timestamp('created_at')->nullable();
+ $table->timestamp('updated_at')->nullable();
+ $table->timestamp('deleted_at')->nullable();
+ });
+
+ Schema::create('business_user', function (Blueprint $table) {
+ $table->unsignedBigInteger('business_id');
+ $table->unsignedBigInteger('user_id');
+ $table->tinyInteger('level')->default(0);
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::dropIfExists('businesses');
+ Schema::dropIfExists('businesses_user');
+ }
+}
diff --git a/database/migrations/2020_08_18_085046_create_projects_table.php b/database/migrations/2020_08_18_085046_create_projects_table.php
new file mode 100644
index 0000000..4b40f42
--- /dev/null
+++ b/database/migrations/2020_08_18_085046_create_projects_table.php
@@ -0,0 +1,58 @@
+id();
+ $table->unsignedBigInteger('business_id');
+ $table->string('name');
+ $table->string('slug');
+ $table->boolean('private')->default(false);
+ $table->unsignedBigInteger('budget')->default(0);
+ $table->string('color')->nullable();
+ $table->boolean('active')->nullable();
+ $table->text('description')->nullable();
+ $table->boolean('has_avatar')->default(false);
+
+ $table->timestamp('start')->nullable();
+ $table->timestamp('finish')->nullable();
+
+ $table->timestamp('created_at')->nullable();
+ $table->timestamp('updated_at')->useCurrent();
+ $table->timestamp('deleted_at')->nullable();
+
+ });
+
+ // Only those users that added directly to the project are stored here.
+ // Users who are members of the business have access to the projects of that business
+ // without being stored here.
+ Schema::create('project_user', function (Blueprint $table) {
+ $table->unsignedBigInteger('project_id');
+ $table->unsignedBigInteger('user_id');
+ $table->tinyInteger('level')->default(0);
+ });
+
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::dropIfExists('projects');
+ Schema::dropIfExists('project_user');
+ }
+}
diff --git a/database/migrations/2014_10_12_100000_create_password_resets_table.php b/database/migrations/2020_08_18_085054_create_workflows_table.php
similarity index 53%
rename from database/migrations/2014_10_12_100000_create_password_resets_table.php
rename to database/migrations/2020_08_18_085054_create_workflows_table.php
index 0ee0a36..d998c40 100644
--- a/database/migrations/2014_10_12_100000_create_password_resets_table.php
+++ b/database/migrations/2020_08_18_085054_create_workflows_table.php
@@ -4,7 +4,7 @@ use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
-class CreatePasswordResetsTable extends Migration
+class CreateWorkflowsTable extends Migration
{
/**
* Run the migrations.
@@ -13,10 +13,13 @@ class CreatePasswordResetsTable extends Migration
*/
public function up()
{
- Schema::create('password_resets', function (Blueprint $table) {
- $table->string('email')->index();
- $table->string('token');
+ Schema::create('workflows', function (Blueprint $table) {
+ $table->id();
+ $table->unsignedBigInteger('business_id');
+ $table->string('name');
+ $table->string('desc')->nullable();
$table->timestamp('created_at')->nullable();
+ $table->timestamp('updated_at')->useCurrent();
});
}
@@ -27,6 +30,6 @@ class CreatePasswordResetsTable extends Migration
*/
public function down()
{
- Schema::dropIfExists('password_resets');
+ Schema::dropIfExists('workflows');
}
}
diff --git a/database/migrations/2020_08_18_085102_create_statuses_table.php b/database/migrations/2020_08_18_085102_create_statuses_table.php
new file mode 100644
index 0000000..03c4203
--- /dev/null
+++ b/database/migrations/2020_08_18_085102_create_statuses_table.php
@@ -0,0 +1,39 @@
+id();
+ $table->unsignedBigInteger('business_id');
+ $table->unsignedBigInteger('workflow_id');
+ $table->string('name');
+ $table->string('state')->default(enum('status.states.inactive.id'));
+ $table->unsignedSmallInteger('order')->default(0);
+ $table->timestamp('created_at')->nullable();
+ $table->timestamp('updated_at')->useCurrent();
+ });
+
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::dropIfExists('statuses');
+// Schema::dropIfExists('status_workflow');
+ }
+}
diff --git a/database/migrations/2020_08_18_085114_create_tags_table.php b/database/migrations/2020_08_18_085114_create_tags_table.php
new file mode 100644
index 0000000..6c00987
--- /dev/null
+++ b/database/migrations/2020_08_18_085114_create_tags_table.php
@@ -0,0 +1,43 @@
+id();
+ $table->string('label');
+ $table->string('color')->nullable();
+ $table->unsignedBigInteger('business_id');
+ $table->timestamp('created_at')->nullable();
+ $table->timestamp('updated_at')->useCurrent();
+ });
+
+ Schema::create('tag_task', function (Blueprint $table) {
+ $table->id();
+ $table->unsignedBigInteger('tag_id');
+ $table->unsignedBigInteger('task_id');
+ $table->timestamps();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::dropIfExists('tags');
+ Schema::dropIfExists('tag_task');
+ }
+}
diff --git a/database/migrations/2020_08_28_095802_create_systems_table.php b/database/migrations/2020_08_28_095802_create_systems_table.php
new file mode 100644
index 0000000..32fbb14
--- /dev/null
+++ b/database/migrations/2020_08_28_095802_create_systems_table.php
@@ -0,0 +1,34 @@
+id();
+ $table->unsignedBigInteger('business_id');
+ $table->unsignedBigInteger('project_id');
+ $table->string('name');
+ $table->timestamps();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::dropIfExists('systems');
+ }
+}
diff --git a/database/migrations/2020_08_28_101545_create_sprint_table.php b/database/migrations/2020_08_28_101545_create_sprint_table.php
new file mode 100644
index 0000000..9a73d99
--- /dev/null
+++ b/database/migrations/2020_08_28_101545_create_sprint_table.php
@@ -0,0 +1,38 @@
+id();
+ $table->unsignedBigInteger('business_id');
+ $table->unsignedBigInteger('project_id');
+ $table->string('name');
+ $table->text('description')->nullable();
+ $table->date('started_at');
+ $table->date('ended_at');
+ $table->boolean('active')->default(true);
+ $table->timestamps();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::dropIfExists('sprints');
+ }
+}
diff --git a/database/migrations/2020_10_31_182018_create_transactions_table.php b/database/migrations/2020_10_31_182018_create_transactions_table.php
new file mode 100644
index 0000000..5fa18e5
--- /dev/null
+++ b/database/migrations/2020_10_31_182018_create_transactions_table.php
@@ -0,0 +1,37 @@
+bigInteger('id', true, true);
+ $table->bigInteger('user_id', false, true);
+ $table->bigInteger('business_id', false, true);
+ $table->integer('amount', false, true);
+ $table->boolean('succeeded')->default(false);
+ $table->json('options')->nullable();
+ $table->timestamp('created_at')->nullable();
+ $table->timestamp('updated_at')->useCurrent();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::dropIfExists('transactions');
+ }
+}
diff --git a/database/migrations/2021_03_02_082607_create_tasks_table.php b/database/migrations/2021_03_02_082607_create_tasks_table.php
new file mode 100644
index 0000000..0ce9c7f
--- /dev/null
+++ b/database/migrations/2021_03_02_082607_create_tasks_table.php
@@ -0,0 +1,55 @@
+id();
+ $table->unsignedBigInteger('business_id');
+ $table->unsignedBigInteger('project_id');
+ $table->unsignedBigInteger('creator_id');
+ $table->unsignedBigInteger('assignee_id')->nullable();
+ $table->unsignedBigInteger('system_id')->nullable();
+ $table->unsignedBigInteger('sprint_id')->nullable();
+ $table->unsignedBigInteger('workflow_id');
+ $table->unsignedBigInteger('status_id');
+ $table->unsignedBigInteger('approver_id')->nullable();
+
+ $table->string('title');
+ $table->text('description')->nullable();
+ $table->integer('priority')->default(1);
+ $table->boolean('on_time')->default(true);
+ $table->boolean('ready_to_test')->default(false);
+
+ $table->json('watchers')->nullable();
+
+ $table->integer('spent_time')->default(0);
+ $table->integer('estimated_time')->default(0);
+ $table->date('due_date')->nullable();
+ $table->date('completed_at')->nullable();
+ $table->timestamp('work_start')->nullable();
+ $table->timestamp('work_finish')->nullable();
+ $table->timestamps();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::dropIfExists('tasks');
+ }
+}
diff --git a/database/migrations/2021_03_02_085913_create_works_table.php b/database/migrations/2021_03_02_085913_create_works_table.php
new file mode 100644
index 0000000..4274ff8
--- /dev/null
+++ b/database/migrations/2021_03_02_085913_create_works_table.php
@@ -0,0 +1,39 @@
+id();
+ $table->unsignedBigInteger('business_id');
+ $table->unsignedBigInteger('project_id');
+ $table->unsignedBigInteger('task_id');
+ $table->unsignedBigInteger('user_id');
+ $table->string('message')->nullable();
+ $table->integer('minute_sum')->default(0);
+ $table->dateTime('started_at');
+ $table->dateTime('ended_at');
+ $table->timestamps();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::dropIfExists('works');
+ }
+}
diff --git a/database/migrations/2021_03_02_092444_create_comments_table.php b/database/migrations/2021_03_02_092444_create_comments_table.php
new file mode 100644
index 0000000..c653af3
--- /dev/null
+++ b/database/migrations/2021_03_02_092444_create_comments_table.php
@@ -0,0 +1,36 @@
+id();
+ $table->unsignedBigInteger('business_id');
+ $table->unsignedBigInteger('project_id');
+ $table->unsignedBigInteger('task_id');
+ $table->unsignedBigInteger('user_id');
+ $table->text('body');
+ $table->timestamps();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::dropIfExists('comments');
+ }
+}
diff --git a/database/migrations/2021_03_03_093326_create_media_table.php b/database/migrations/2021_03_03_093326_create_media_table.php
new file mode 100644
index 0000000..378b046
--- /dev/null
+++ b/database/migrations/2021_03_03_093326_create_media_table.php
@@ -0,0 +1,32 @@
+bigIncrements('id');
+
+ $table->morphs('model');
+ $table->uuid('uuid')->nullable()->unique();
+ $table->string('collection_name');
+ $table->string('name');
+ $table->string('file_name');
+ $table->string('mime_type')->nullable();
+ $table->string('disk');
+ $table->string('conversions_disk')->nullable();
+ $table->unsignedBigInteger('size');
+ $table->json('manipulations');
+ $table->json('custom_properties');
+ $table->json('generated_conversions');
+ $table->json('responsive_images');
+ $table->unsignedInteger('order_column')->nullable();
+
+ $table->nullableTimestamps();
+ });
+ }
+}
diff --git a/database/migrations/2021_03_06_085855_create_activities_table.php b/database/migrations/2021_03_06_085855_create_activities_table.php
new file mode 100644
index 0000000..ee00afe
--- /dev/null
+++ b/database/migrations/2021_03_06_085855_create_activities_table.php
@@ -0,0 +1,45 @@
+id();
+ $table->unsignedBigInteger('business_id');
+ $table->unsignedBigInteger('project_id')->nullable();
+ $table->unsignedBigInteger('system_id')->nullable();
+ $table->unsignedBigInteger('workflow_id')->nullable();
+ $table->unsignedBigInteger('status_id')->nullable();
+ $table->unsignedBigInteger('sprint_id')->nullable();
+ $table->unsignedBigInteger('task_id')->nullable();
+ $table->unsignedBigInteger('subject_id')->nullable();//row id
+ $table->unsignedBigInteger('actor_id');
+ $table->unsignedBigInteger('user_id')->nullable();
+ $table->unsignedBigInteger('crud_id')->nullable();
+ $table->unsignedBigInteger('table_id')->nullable();
+ $table->json('original')->nullable(); // a unique identifier that represent the type of action
+ $table->json('diff')->nullable(); // all data that has been changed
+ $table->timestamps();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::dropIfExists('activities');
+ }
+}
diff --git a/database/migrations/2019_08_19_000000_create_failed_jobs_table.php b/database/migrations/2021_03_06_114918_create_failed_jobs_table.php
similarity index 100%
rename from database/migrations/2019_08_19_000000_create_failed_jobs_table.php
rename to database/migrations/2021_03_06_114918_create_failed_jobs_table.php
diff --git a/database/migrations/2021_03_08_114700_create_notifications_table.php b/database/migrations/2021_03_08_114700_create_notifications_table.php
new file mode 100644
index 0000000..9797596
--- /dev/null
+++ b/database/migrations/2021_03_08_114700_create_notifications_table.php
@@ -0,0 +1,35 @@
+uuid('id')->primary();
+ $table->string('type');
+ $table->morphs('notifiable');
+ $table->text('data');
+ $table->timestamp('read_at')->nullable();
+ $table->timestamps();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::dropIfExists('notifications');
+ }
+}
diff --git a/database/migrations/2021_09_03_085114_create_costs_table.php b/database/migrations/2021_09_03_085114_create_costs_table.php
new file mode 100644
index 0000000..87768ec
--- /dev/null
+++ b/database/migrations/2021_09_03_085114_create_costs_table.php
@@ -0,0 +1,41 @@
+id();
+ $table->unsignedBigInteger('business_id');
+ $table->string('type');
+ $table->date('month');
+ $table->unsignedInteger('amount');
+ $table->unsignedInteger('fee')->default(0);
+ $table->unsignedInteger('duration');
+ $table->unsignedInteger('cost')->storedAs('fee*duration');
+ $table->unsignedFloat('tax', 8, 2)->storedAs('(cost/100)*9');
+ $table->json('additional')->nullable();
+ $table->timestamp('created_at')->nullable();
+ $table->timestamp('updated_at')->useCurrent();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::dropIfExists('costs');
+ }
+}
diff --git a/database/migrations/2022_10_13_085114_create_files_table.php b/database/migrations/2022_10_13_085114_create_files_table.php
new file mode 100644
index 0000000..69dfec8
--- /dev/null
+++ b/database/migrations/2022_10_13_085114_create_files_table.php
@@ -0,0 +1,45 @@
+id();
+ $table->unsignedBigInteger('user_id');
+ $table->unsignedBigInteger('business_id');
+ $table->unsignedBigInteger('project_id');
+ $table->unsignedBigInteger('attached_to_id')->nullable();
+ $table->integer('attached_to_table')->nullable();
+ $table->char('disk',191);
+ $table->char('original_name',191);
+ $table->char('name',191);
+ $table->char('extension',4);
+ $table->string('mime');
+ $table->string('group',255);
+ $table->char('size',191);
+ $table->text('description')->nullable();
+ $table->timestamp('created_at')->nullable();
+ $table->timestamp('updated_at')->useCurrent();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::dropIfExists('files');
+ }
+}
diff --git a/database/migrations/2023_10_13_085115_create_files_relation_table.php b/database/migrations/2023_10_13_085115_create_files_relation_table.php
new file mode 100644
index 0000000..8308d77
--- /dev/null
+++ b/database/migrations/2023_10_13_085115_create_files_relation_table.php
@@ -0,0 +1,35 @@
+id();
+ $table->unsignedBigInteger('file_id');
+ $table->unsignedBigInteger("resource_id");
+ $table->string("resource_type");
+ // the person who create this relation. eg attacher
+ $table->unsignedBigInteger('user_id');
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::dropIfExists('files_relation');
+ }
+}
diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php
deleted file mode 100644
index 57b73b5..0000000
--- a/database/seeders/DatabaseSeeder.php
+++ /dev/null
@@ -1,18 +0,0 @@
-create();
- }
-}
diff --git a/database/seeds/BusinessSeeder.php b/database/seeds/BusinessSeeder.php
new file mode 100644
index 0000000..1f39038
--- /dev/null
+++ b/database/seeds/BusinessSeeder.php
@@ -0,0 +1,144 @@
+raw());
+ $projects = [];
+ $systems = [];
+ $business_user = [];
+ $project_user = [];
+ $project_id = 0;
+ for ($business_id=1; $business_id <= 2000; $business_id++) {
+ // distinct user ids
+ $user_ids = [];
+ $added_user = [];
+ for ($i=0; $i < 10; $i++) $user_ids [] = rand(1, 5000);
+ $user_ids = array_values(array_unique($user_ids));
+ // add 1 to 7 member
+ for ($i=0; $i < rand(1, 8); $i++) {
+ $added_user[] = $user_ids[$i];
+ $business_user[] = [
+ 'business_id' => $business_id,
+ 'user_id' => $user_ids[$i],
+ 'level' => $i == 0? 4: rand(0, 4),
+ ];
+ }
+
+ $business_projects = factory(Project::class, rand(1, 3))->raw();
+ foreach ($business_projects as $project) {
+ $project['business_id'] = $business_id;
+ $projects[] = $project;
+ $project_id++;
+
+ for ($i=0; $i < rand(0, 2); $i++) {
+ if ($added_user[$i] ?? false) {
+ $project_user[] = [
+ 'project_id' => $project_id,
+ 'user_id' => $added_user[$i],
+ 'level' => rand(0, 4),
+ ];
+ }
+ }
+
+ for ($i=0; $i < rand(1, 5); $i++) {
+ $systems[] = factory(System::class)->raw([
+ 'project_id' => $project_id,
+ 'business_id' => $business_id,
+ ]);
+ }
+ }
+
+ // for ($i=0; $i < rand(1, 3); $i++) {
+ // // $project =
+ // }
+ // $business_user[] = [
+ // 'business_id' => $business_id,
+ // 'user_id' => 1,
+ // 'owner' => true,
+ // ];
+ }
+ DB::table('business_user')->insert($business_user);
+ Project::insert($projects);
+ DB::table('project_user')->insert($project_user);
+ DB::table('systems')->insert($systems);
+ /**
+ * Every business has one or more owners, which might be the owner of other business as well
+ */
+ // $this->insertAdmins($business_ids, $owners);
+
+
+ /**
+ * Every business has one or more members, which might be the member of other business as well
+ */
+ // $this->insertMembers($business_ids);
+ }
+
+ /**
+ * @param $business_ids
+ * @param Collection $owners
+ * @return array
+ */
+ public function insertAdmins($business_ids, Collection $owners)
+ {
+ $relations = [];
+ foreach ($business_ids as $id) {
+ $relations[] = [
+ 'business_id' => $id,
+ 'user_id' => $owners->random()->id,
+ 'owner' => true,
+ ];
+ }
+
+ // Specify the percentage of managers who have two or more businesses
+ $multiple = ceil(count($relations) / 100 * 30);
+ foreach (Arr::random($relations, $multiple) as $relation) {
+ $relations[] = [
+ 'business_id' => $relation['business_id'],
+ 'user_id' => $owners->except($relation['user_id'])->random()->id,
+ 'owner' => true,
+ ];
+ }
+
+ DB::table('business_user')->insert($relations);
+ }
+
+ /**
+ * @param $business_ids
+ */
+ public function insertMembers($business_ids): void
+ {
+ /** @var Collection $member_ids */
+ $member_ids = User::whereDoesntHave('businesses')->select('id')->pluck('id');
+ $relations = [];
+ $current = [];
+ foreach ($member_ids as $index => $id) {
+ $relations[] = [
+ 'business_id' => $business = $business_ids->random(),
+ 'user_id' => $id,
+ ];
+
+ $current[$business][] = $index;
+ }
+
+ $multiple = ceil(count($relations) / 100 * 20);
+ foreach (Arr::random($relations, $multiple) as $relation) {
+ $relations[] = [
+ 'business_id' => $relation['business_id'],
+ 'user_id' => $member_ids->except($current[$relation['business_id']])->random(),
+ ];
+ }
+
+ DB::table('business_user')->insert($relations);
+ }
+}
diff --git a/database/seeds/CostSeeder.php b/database/seeds/CostSeeder.php
new file mode 100644
index 0000000..ca76bec
--- /dev/null
+++ b/database/seeds/CostSeeder.php
@@ -0,0 +1,14 @@
+raw()
+ );
+ }
+}
diff --git a/database/seeds/DatabaseSeeder.php b/database/seeds/DatabaseSeeder.php
new file mode 100644
index 0000000..7508ea3
--- /dev/null
+++ b/database/seeds/DatabaseSeeder.php
@@ -0,0 +1,22 @@
+call([
+ UserSeeder::class,
+ BusinessSeeder::class,
+ TagSeeder::class,
+ WorkflowSeeder::class,
+ SprintSeeder::class,
+// TransactionSeeder::class,
+// CostSeeder::class,
+ TaskTmpSeeder::class,
+ // ProjectSeeder::class,
+ // TaskSeeder::class,
+ ]);
+ }
+}
diff --git a/database/seeds/ProjectSeeder.php b/database/seeds/ProjectSeeder.php
new file mode 100644
index 0000000..70e0bec
--- /dev/null
+++ b/database/seeds/ProjectSeeder.php
@@ -0,0 +1,123 @@
+pluck('id');
+
+ /**
+ * One level projects
+ */
+ $this->insertMainProjects($businesses);
+
+ /**
+ * Multi level projects
+ * Be careful with private projects
+ */
+ $this->insertChildProjects();
+
+ $this->insertGrandChildrenProjects();
+
+ /**
+ * Assign people to the projects
+ * * Be careful with private projects
+ */
+ $this->assignPeopleToProjects();
+ }
+
+ /**
+ * @param $businesses
+ * @return void
+ */
+ public function insertMainProjects($businesses): void
+ {
+ $projects = [];
+ foreach ($businesses as $business) {
+ $projects[] = factory(Project::class, 5)->raw([
+ 'business_id' => $business,
+ 'private' => rand(1, 100) <= 10,
+ ]);
+ }
+ DB::table('projects')->insertOrIgnore(Arr::collapse($projects));
+ }
+
+ /**
+ * @return void
+ */
+ public function insertChildProjects(): void
+ {
+ $projects = Project::inRandomOrder()->select('id', 'business_id')->take(100)->get()->toArray();
+ $child_projects = [];
+ foreach ($projects as $project) {
+ $child_projects[] = factory(Project::class)->raw([
+ 'parent_id' => $project['id'],
+ 'business_id' => $project['business_id'],
+ 'private' => rand(1, 100) <= 10,
+ ]);
+ }
+ DB::table('projects')->insert($child_projects);
+ }
+
+ public function insertGrandChildrenProjects(): void
+ {
+ $projects = Project::inRandomOrder()
+ ->select('id', 'business_id', 'parent_id')
+ ->whereNotNull('parent_id')
+ ->take(25)->get()->toArray();
+ $grand_child_projects = [];
+ foreach ($projects as $project) {
+ $grand_child_projects[] = factory(Project::class)->raw([
+ 'parent_id' => $project['id'],
+ 'business_id' => $project['business_id'],
+ 'private' => rand(1, 100) <= 10,
+ ]);
+ }
+ DB::table('projects')->insertOrIgnore($grand_child_projects);
+ }
+
+ public function assignPeopleToProjects(): void
+ {
+ // Schema::create('')
+ // $table->unsignedBigInteger('project_id');
+ // $table->unsignedBigInteger('');
+
+ $project_members = [];
+ $projects = Project::with('parent','children','business')->get();
+ foreach ($projects as $project) {
+ // detect the main projects
+ if ($project->children->isNotEmpty()) {
+ continue;
+ }
+
+ $business_owner = $project->business->owners->random();
+ $business_members = $project->business->members->random($project->business->members->count() - 1);
+ $project_members[] = [
+ 'project_id' => $project->id,
+ 'user_id' => $business_owner->id,
+ ];
+
+ foreach ($business_members as $business_member) {
+ $project_members[] = [
+ 'project_id' => $project->id,
+ 'user_id' => $business_member->id,
+ ];
+ }
+
+ // todo : how to detect a project has sub project
+ // child
+ // grand child
+ }
+ DB::table('project_user')->insert($project_members);
+ }
+}
diff --git a/database/seeds/SprintSeeder.php b/database/seeds/SprintSeeder.php
new file mode 100644
index 0000000..1bbf262
--- /dev/null
+++ b/database/seeds/SprintSeeder.php
@@ -0,0 +1,21 @@
+raw([
+ 'business_id' => $business_id,
+ 'project_id' => rand(1, 4027),
+ ]);
+ }
+ }
+ DB::table('sprints')->insertOrIgnore($sprints);
+ }
+}
diff --git a/database/seeds/TagSeeder.php b/database/seeds/TagSeeder.php
new file mode 100644
index 0000000..e9eff1f
--- /dev/null
+++ b/database/seeds/TagSeeder.php
@@ -0,0 +1,21 @@
+raw([
+ 'business_id' => $business_id,
+ ]);
+ }
+ }
+ DB::table('tags')->insertOrIgnore($tags);
+ }
+}
diff --git a/database/seeds/TaskSeeder.php b/database/seeds/TaskSeeder.php
new file mode 100755
index 0000000..cc746e6
--- /dev/null
+++ b/database/seeds/TaskSeeder.php
@@ -0,0 +1,174 @@
+raw()
+ );
+ }
+
+ $tags = [
+ 1 => 'باربری',
+ 2 => 'زیر چاپ',
+ 3 => 'برون سپاری شود',
+ 4 => 'مشتری نپسندید',
+ ];
+
+ $ids = Task::select('id')->pluck('id')->toArray();
+ $tag_task_relation = [];
+ foreach ($ids as $id) {
+ foreach (array_rand($tags, mt_rand(2, 4)) as $index => $tag) {
+ $tag_task_relation[] = [
+ 'task_id' => $id,
+ 'tag_id' => $tag,
+ ];
+ }
+ }
+
+ TagTask::insert($tag_task_relation);
+ $this->createRelatedData();
+ }
+
+ public function createRelatedData()
+ {
+ Task::whereIn('business_id', [1, 2, 3, 4, 5])
+ ->update(['business_id' => 1779, 'project_id' => 0, 'workflow_id' => 0, 'status_id' => 0,
+ 'system_id' => 0, 'sprint_id' => 0]);
+
+ $count = Task::where('business_id', '=', 1779)->count();
+
+ $projects = [
+ 3566 => [
+ 'systems' => [8967, 8968, 8969, 8970],
+ 'sprints' => [],
+ ],
+ 3567 => [
+ 'systems' => [8971],
+ 'sprints' => [526]
+ ],
+ 3568 => [
+ 'systems' => [8972, 8973],
+ 'sprints' => [5480]
+ ]
+ ];
+
+ $workflows = [
+ 2277 => [5100, 5101],
+ 2278 => [5102, 5103],
+ 2279 => [5104, 5105, 5106, 5107]
+ ];
+ $take = $count > 50 ? 20 : 10;
+ $this->updateField(array_keys($projects), $count, $take, 'project_id');
+ $this->updateField(array_keys($workflows), $count, $take, 'workflow_id');
+ $this->fillSprintAndSystem($projects);
+ $this->fillStatues($workflows);
+ $this->fillTaskTag($count);
+
+ }
+
+ public function fillSprintAndSystem($projects)
+ {
+ foreach ($projects as $key => $value) {
+ $count = Task::where('project_id', $key)->count();
+ $take = $count > 10 ? 2 : 1;
+
+ $sprintIndex = 0;
+ $systemIndex = 0;
+ $i = 0;
+ while ($count > $i) {
+ Task::where('project_id', '=', $key)
+ ->where('system_id', 0)->where('sprint_id', 0)
+ ->take($take)
+ ->update(
+ [
+ 'sprint_id' => isset($value['sprints'][$sprintIndex]) ? $value['sprints'][$sprintIndex] : 0,
+ 'system_id' => isset($value['systems'][$systemIndex]) ? $value['systems'][$systemIndex] : 0,
+ ]);
+
+ $systemIndex += 1;
+ $sprintIndex += 1;
+ if ($systemIndex >= count($value['systems'])) {
+ $systemIndex = 0;
+ }
+ if ($sprintIndex >= count($value['sprints'])) {
+ $sprintIndex = 0;
+ }
+
+
+ $i += $take;
+ }
+ }
+ Task::whereIn('project_id', array_keys($projects))
+ ->where('system_id', 0)
+ ->update(['system_id' => null]);
+
+ Task::whereIn('project_id', array_keys($projects))
+ ->where('sprint_id', 0)
+ ->update(['sprint_id' => null]);
+ }
+
+ public function updateField($values, $count, $take, $field)
+ {
+ $i = 0;
+ $index = 0;
+ while ($count > $i) {
+ Task::where($field, '=', 0)->take($take)->update([$field => $values[$index]]);
+ $index += 1;
+ if (count($values) <= $index) {
+ $index = 0;
+ }
+ $i += $take;
+ }
+ }
+
+ public function fillStatues($workflows)
+ {
+ foreach ($workflows as $key => $value) {
+ $count = Task::where('workflow_id', $key)->count();
+ $take = $count > 10 ? 2 : 1;
+
+ $index = 0;
+ $i = 0;
+ while ($count > $i) {
+ Task::where('workflow_id', '=', $key)
+ ->where('status_id', 0)
+ ->take($take)
+ ->update(
+ [
+ 'status_id' => $value[$index]
+ ]);
+
+ $index += 1;
+ if ($index >= count($value)) {
+ $index = 0;
+ }
+
+
+ $i += $take;
+ }
+ }
+ }
+
+ public function fillTaskTag($count)
+ {
+ $tags = [5037, 5038, 5626];
+ $tasks = Task::where('business_id', 1779)->get();
+ foreach ($tasks as $task) {
+ $rand = rand(0,2);
+ $t = [];
+ do{
+ array_push($t, new TagTask(['task_id' => $task->id, 'tag_id' => $tags[$rand]]));
+ $rand--;
+ }while($rand >= 0);
+ $task->tags()->saveMany($t);
+ }
+
+ }
+}
diff --git a/database/seeds/TaskTmpSeeder.php b/database/seeds/TaskTmpSeeder.php
new file mode 100644
index 0000000..38da127
--- /dev/null
+++ b/database/seeds/TaskTmpSeeder.php
@@ -0,0 +1,33 @@
+business_id)->first();
+ $sprint = \App\Models\Sprint::where('business_id', $system->business_id)->first();
+ $creator = \App\Models\Business::find($system->business_id)->owners()->first();
+
+ array_push($tasks, [
+ 'title' => \Illuminate\Support\Str::random(5),
+ 'business_id' => $system->business_id,
+ 'project_id' => $system->project_id,
+ 'system_id' => $system->id,
+ 'workflow_id' => $status->workflow_id,
+ 'status_id' => $status->id,
+ 'sprint_id' => $sprint->id ?? null,
+ 'creator_id' => $creator->id,
+ ]);
+ if (sizeof($tasks) == 100) {
+ Task::insert($tasks);
+ $tasks = [];
+ }
+ }
+ }
+}
diff --git a/database/seeds/TransactionSeeder.php b/database/seeds/TransactionSeeder.php
new file mode 100644
index 0000000..2d5fe36
--- /dev/null
+++ b/database/seeds/TransactionSeeder.php
@@ -0,0 +1,14 @@
+raw()
+ );
+ }
+}
diff --git a/database/seeds/UserSeeder.php b/database/seeds/UserSeeder.php
new file mode 100644
index 0000000..92bfaa1
--- /dev/null
+++ b/database/seeds/UserSeeder.php
@@ -0,0 +1,20 @@
+raw()
+ );
+ Fingerprint::insert(
+ factory(Fingerprint::class, 5000)->raw()
+ );
+ User::where('id', 1)->update(['mobile' => '09123456789']);
+ }
+}
diff --git a/database/seeds/WorkSeeder.php b/database/seeds/WorkSeeder.php
new file mode 100755
index 0000000..b88ed2e
--- /dev/null
+++ b/database/seeds/WorkSeeder.php
@@ -0,0 +1,36 @@
+raw()
+ );
+
+ $businessId = 1779;
+ Work::inRandomOrder()->take(150)->update(['business_id' => $businessId]);
+
+ $projects = [3566, 3567, 3568];
+
+ foreach ($projects as $projectId) {
+ Work::where('business_id', $businessId)
+ ->whereNotIn('project_id', $projects)
+ ->take(50)->update(['project_id' => $projectId, 'started_at' => \Carbon\Carbon::now()]);
+ }
+
+ $works = Work::where('business_id', $businessId)->get();
+ foreach ($works as $work){
+ \DB::table('works')->where('id', $work->id)
+ ->update(['started_at' => \Carbon\Carbon::now()->subDays(rand(0,90))]);
+ }
+ }
+}
diff --git a/database/seeds/WorkflowSeeder.php b/database/seeds/WorkflowSeeder.php
new file mode 100644
index 0000000..f11678d
--- /dev/null
+++ b/database/seeds/WorkflowSeeder.php
@@ -0,0 +1,50 @@
+raw([
+ 'business_id' => $business_id,
+ ]);
+ $business_workflows[] = ++$workflow_inc;
+ for ($i=0; $i < rand(1, 4); $i++) {
+ $statuses[] = factory(Status::class)->raw([
+ 'business_id' => $business_id,
+ 'workflow_id' => $workflow_inc,
+ ]);
+ $status_inc++;
+ }
+ }
+// for ($i=0; $i < rand(1, 4); $i++) {
+// $statuses[] = factory(Status::class)->raw([
+// 'business_id' => $business_id,
+// ]);
+// $status_inc++;
+// for ($ws=0; $ws < rand(0, count($business_workflows)); $ws++) {
+// $status_workflow[] = [
+// 'status_id' => $status_inc,
+// 'workflow_id' => $business_workflows[rand(0, count($business_workflows)-1)],
+// 'order' => rand(0, 10)
+// ];
+// }
+// }
+ }
+ DB::table('workflows')->insert($workflows);
+ DB::table('statuses')->insert($statuses);
+// DB::table('status_workflow')->insert($status_workflow);
+ }
+}
diff --git a/definitions.json b/definitions.json
new file mode 100644
index 0000000..10ab903
--- /dev/null
+++ b/definitions.json
@@ -0,0 +1,152 @@
+{
+ "queues": [
+ {
+ "arguments": {},
+ "auto_delete": false,
+ "durable": true,
+ "name": "activity_queue",
+ "type": "classic",
+ "vhost": "/"
+ },
+ {
+ "arguments": {},
+ "auto_delete": false,
+ "durable": true,
+ "name": "notif_queue",
+ "type": "classic",
+ "vhost": "/"
+ },
+ {
+ "arguments": {},
+ "auto_delete": false,
+ "durable": true,
+ "name": "socket_queue",
+ "type": "classic",
+ "vhost": "/"
+ },
+ {
+ "arguments": {},
+ "auto_delete": false,
+ "durable": true,
+ "name": "log_queue",
+ "type": "classic",
+ "vhost": "/"
+ }
+ ],
+ "exchanges": [
+ {
+ "arguments": {},
+ "auto_delete": false,
+ "durable": true,
+ "name": "activity_exchange",
+ "type": "headers",
+ "vhost": "/"
+ },
+ {
+ "arguments": {},
+ "auto_delete": false,
+ "durable": true,
+ "name": "notif_exchange",
+ "type": "headers",
+ "vhost": "/"
+ },
+ {
+ "arguments": {},
+ "auto_delete": false,
+ "durable": true,
+ "name": "socket_exchange",
+ "type": "headers",
+ "vhost": "/"
+ },
+ {
+ "arguments": {},
+ "auto_delete": false,
+ "durable": true,
+ "name": "log_exchange",
+ "type": "fanout",
+ "vhost": "/"
+ }
+ ],
+ "bindings": [
+ {
+ "arguments": {
+ "x-match": "any",
+ "name" : "activity"
+ },
+ "destination": "activity_queue",
+ "destination_type": "queue",
+ "routing_key": "",
+ "source": "activity_exchange",
+ "vhost": "/"
+ },
+ {
+ "arguments": {
+ "x-match": "any",
+ "name" : "notif"
+ },
+ "destination": "notif_queue",
+ "destination_type": "queue",
+ "routing_key": "",
+ "source": "notif_exchange",
+ "vhost": "/"
+ },
+ {
+ "arguments": {
+ "x-match": "any",
+ "name" : "socket"
+ },
+ "destination": "socket_queue",
+ "destination_type": "queue",
+ "routing_key": "",
+ "source": "socket_exchange",
+ "vhost": "/"
+ },
+ {
+ "arguments": {},
+ "destination": "log_queue",
+ "destination_type": "queue",
+ "routing_key": "",
+ "source": "log_exchange",
+ "vhost": "/"
+ }
+ ],
+
+ "global_parameters": [
+ {
+ "name": "cluster_name",
+ "value": "rabbit@258a143f3102"
+ }
+ ],
+ "parameters": [],
+ "permissions": [
+ {
+ "configure": ".*",
+ "read": ".*",
+ "user": "root",
+ "vhost": "/",
+ "write": ".*"
+ }
+ ],
+ "policies": [],
+ "rabbit_version": "3.8.5",
+ "rabbitmq_version": "3.8.5",
+ "topic_permissions": [],
+ "users": [
+ {
+ "hashing_algorithm": "rabbit_password_hashing_sha256",
+ "name": "root",
+ "password_hash": "Y0AhwjtK6iQM0t0Hp9t9nBey8zzMBa3LuszL3zlZ1mGtWsif",
+ "tags": "administrator"
+ }
+ ],
+ "vhosts": [
+ {
+ "limits": [],
+ "metadata": {
+ "description": "Default virtual host",
+ "tags": []
+ },
+ "name": "/"
+ }
+ ]
+}
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000..d606ce7
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,119 @@
+# For more information: https://laravel.com/docs/sail
+version: '3'
+services:
+ rabbitmq:
+ image: "rabbitmq:3-management"
+ ports:
+ - 5672:5672
+ - 15672:15672
+ environment:
+ RABBITMQ_ERLANG_COOKIE: "SWQOKODSQALRPCLNMEQG"
+ RABBITMQ_DEFAULT_USER: "root"
+ RABBITMQ_DEFAULT_PASS: "root"
+ RABBITMQ_DEFAULT_VHOST: "/"
+ networks:
+ - sail
+ volumes:
+ - ./definitions.json:/etc/rabbitmq/definitions.json
+ depends_on:
+ - laravel.test
+ minio:
+ image: minio/minio
+ command: server /data
+ ports:
+ - 9000:9000
+ environment:
+ MINIO_ACCESS_KEY: root
+ MINIO_SECRET_KEY: minioroot
+ networks:
+ - sail
+ depends_on:
+ - laravel.test
+ commander:
+ image: rediscommander/redis-commander:latest
+ environment:
+ - REDIS_HOST=redis
+ - REDIS_PORT=6379
+ - HTTP_USER=root
+ - HTTP_PASSWORD=root
+ - REDIS_PASSWORD=root
+ ports:
+ - 8081:8081
+ depends_on:
+ - redis
+ networks:
+ - sail
+ depends_on:
+ - redis
+ redis:
+ image: redis:latest
+ ports:
+ - 6379:6379
+ command: redis-server --requirepass root
+ volumes:
+ - redis-data:/data
+ networks:
+ - sail
+ laravel.test:
+ build:
+ context: ./vendor/laravel/sail/runtimes/8.0
+ dockerfile: Dockerfile
+ args:
+ WWWGROUP: '${WWWGROUP}'
+ image: sail-8.0/app
+ ports:
+ - '${APP_PORT:-80}:80'
+ environment:
+ WWWUSER: '${WWWUSER}'
+ LARAVEL_SAIL: 1
+ volumes:
+ - '.:/var/www/html'
+ networks:
+ - sail
+ depends_on:
+ - mysql
+ mysql:
+ image: 'mysql:8.0'
+ ports:
+ - '${FORWARD_DB_PORT:-3306}:3306'
+ environment:
+ MYSQL_ROOT_PASSWORD: '${DB_PASSWORD}'
+ MYSQL_DATABASE: '${DB_DATABASE}'
+ MYSQL_USER: '${DB_USERNAME}'
+ MYSQL_PASSWORD: '${DB_PASSWORD}'
+ MYSQL_ALLOW_EMPTY_PASSWORD: 'yes'
+ volumes:
+ - 'sailmysql:/var/lib/mysql'
+ networks:
+ - sail
+ healthcheck:
+ test: ["CMD", "mysqladmin", "ping"]
+ pma:
+ image: 'phpmyadmin:latest'
+ ports:
+ - 8080:80
+ environment:
+ MYSQL_ROOT_PASSWORD: '${DB_PASSWORD}'
+ links:
+ - "mysql:db"
+ depends_on:
+ - 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
+volumes:
+ redis-data:
+ driver: local
+ sailmysql:
+ driver: local
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..9e5b113
--- /dev/null
+++ b/resources/lang/fa/notification.php
@@ -0,0 +1,86 @@
+ [
+ 'create' => 'یک تگ ساخته شد.'
+ ],
+
+ 'business_user' => [
+ 'create' =>[
+ 'greeting' => 'سلام کاربر گرامی!',
+ 'subject' => 'افزودن کاربر به کسب و کار',
+ 'title' => 'افزودن کاربر به کسب و کار',
+ 'body' => 'کاربر :user به کسب و کار :business اضافه شد.'
+ ]
+ ],
+
+ 'project_user' => [
+ 'create' =>[
+ 'greeting' => 'سلام کاربر گرامی!',
+ 'subject' => 'افزودن کاربر به پروژه',
+ 'title' => 'افزودن کاربر به پروژه',
+ 'body' => 'کاربر :user به پروژه :project اضافه شد.'
+ ]
+ ],
+
+ 'tasks' => [
+ 'create' => [
+ 'assignee' => [
+ 'greeting' => 'سلام کاربر گرامی!',
+ 'subject' => 'افزودن تسک جدید',
+ 'title' => 'افزودن تسک جدید',
+ 'body' => 'تسک :task به شما واگذار شد.'
+ ],
+ 'approver' => [
+ 'greeting' => 'سلام کاربر گرامی!',
+ 'subject' => 'افزودن تسک جدید',
+ 'title' => 'افزودن تسک جدید',
+ 'body' => 'شما تایید کننده تسک :task شدید.'
+ ]
+ ],
+ 'update' => [
+ 'assignee' => [
+ 'greeting' => 'سلام کاربر گرامی!',
+ 'subject' => 'افزودن تسک جدید',
+ 'title' => 'افزودن تسک جدید',
+ 'body' => 'تسک :task به شما واگذار شد.'
+ ],
+ 'approver' => [
+ 'greeting' => 'سلام کاربر گرامی!',
+ 'subject' => 'افزودن تسک جدید',
+ 'title' => 'افزودن تسک جدید',
+ 'body' => 'شما تایید کننده تسک :task شدید.'
+ ],
+ 'ready' => [
+ 'greeting' => 'سلام کاربر گرامی!',
+ 'subject' => 'افزودن تسک جدید',
+ 'title' => 'افزودن تسک جدید',
+ 'body' => 'تسک :task در حالت انتظار برای تست قرار دارد.'
+ ],
+ 'completed' => [
+ 'greeting' => 'سلام کاربر گرامی!',
+ 'subject' => 'افزودن تسک جدید',
+ 'title' => 'افزودن تسک جدید',
+ 'body' => 'تسک :task به اتمام رسید.'
+ ]
+ ]
+ ],
+
+ 'business' => [
+ 'update' => 'کاربر :user به کسب و کار :business اضافه شد.',
+ 'update_wallet' => 'پول رو میدی یا پولت کنم',
+ ],
+
+];
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' => 'لینک',
+ ],
+];
diff --git a/routes/api.php b/routes/api.php
index bcb8b18..be7bd04 100644
--- a/routes/api.php
+++ b/routes/api.php
@@ -1,19 +1,203 @@
get('/user', function (Request $request) {
- return $request->user();
+/** @var \Illuminate\Routing\$router */
+
+$router->group(['prefix' => 'actions'], function () use ($router) {
+ $router->group(['prefix' => 'businesses'], function () use ($router) {
+ $router->group(['prefix' => '{business}', 'middleware' => 'bindBusiness'], function () use ($router) {
+ $router->get('/info', 'BusinessController@info');
+ });
+ });
+});
+
+$router->get('/callback', 'CreditController@callback');
+$router->get('/{transaction}/redirection', 'CreditController@redirection');
+
+$router->group(['prefix' => 'auth'], function () use ($router) {
+ $router->get('/', 'AuthController@auth')->middleware('auth:api');
+ $router->delete('/', 'AuthController@delete');
+ $router->get('/info', 'AuthController@authWithInfo')->middleware('auth:api');
+ $router->post('login', 'AuthController@login');
+ $router->post('logout', 'AuthController@logout')->middleware('auth:api');
+ $router->post('register', 'AuthController@register');
+ $router->post('revoke/{token}', 'AuthController@revoke');
+
+ $router->post('forget-password', 'AuthController@forgetPassword');
+ $router->post('update-password', 'AuthController@updatePassword');
+
+ $router->post('verification', 'AuthController@verification');
+
+ $router->get('google/redirect', 'AuthController@redirectToGoogle')->name('google.redirect');
+ $router->get('google/callback', 'AuthController@handleGoogleCallback')->name('google.callback');
+});
+
+$router->group(['prefix' => 'businesses', 'middleware' => 'auth:api'], function () use ($router) {
+ $router->get('/', 'BusinessController@index');
+ $router->post('/', 'BusinessController@store');
+
+ $router->group(['prefix' => '{business}', 'middleware' => 'bindBusiness'], function () use ($router) {
+
+ $router->get('/tasks', 'TaskController@index');
+ $router->get('/works', 'WorkController@index');
+ $router->get('statistics', 'StatisticController@index');
+
+ $router->put('/avatar', 'BusinessController@setAvatar');
+ $router->delete('/avatar', 'BusinessController@unSetAvatar');
+
+ $router->get('/', 'BusinessController@show');
+ $router->put('/', 'BusinessController@update');
+ $router->delete('/', 'BusinessController@delete');
+ $router->get('/restore', 'BusinessController@restore');
+
+ $router->get('/files', 'FileController@index');
+ $router->get('/info', 'BusinessController@info');
+ $router->get('/invoices', 'InvoiceController@index');
+ $router->get('/invoices/{date}', 'InvoiceController@show');
+ $router->get('/pay', 'CreditController@pay');
+ $router->get('/payments', 'CreditController@payments');
+
+
+ $router->group(['prefix' => 'projects'], function () use ($router) {
+ $router->get('/', 'ProjectController@index');
+ $router->post('/', 'ProjectController@store');
+ $router->group(['prefix' => '{project}'], function () use ($router) {
+
+ $router->put('/avatar', 'ProjectController@setAvatar');
+ $router->delete('/avatar', 'ProjectController@unSetAvatar');
+ $router->get('/statistics', 'StatisticController@index');
+
+ $router->get('/', 'ProjectController@show');
+ $router->put('/', 'ProjectController@update');
+ $router->delete('/', 'ProjectController@delete');
+ $router->group(['prefix' => 'files', 'as' => 'file.', 'middleware' => 'bindBusiness'], function () use ($router) {
+ // $router->get('/', ['as' => 'index', 'uses' => 'FileController@index']);
+ $router->post('/', ['as' => 'store', 'uses' => 'FileController@store']);
+ $router->group(['prefix' => '{file}'], function () use ($router) {
+ $router->get('/', ['as' => 'download', 'uses' => 'FileController@download']);
+ $router->put('/', ['as' => 'rename', 'uses' => 'FileController@rename']);
+ $router->delete('/', ['as' => 'delete', 'uses' => 'FileController@delete']);
+ });
+ });
+ $router->get('/restore', 'ProjectController@restore');
+
+ $router->group(['prefix' => 'sprints', 'as' => 'sprint.', 'middleware' => 'bindBusiness'], function () use ($router) {
+ $router->get('/', ['as' => 'index', 'uses' => 'SprintController@index']);
+ $router->post('/', ['as' => 'store', 'uses' => 'SprintController@store']);
+ $router->group(['prefix' => '{sprint}'], function () use ($router) {
+ $router->get('/', ['as' => 'show', 'uses' => 'SprintController@show']);
+ $router->put('/', ['as' => 'update', 'uses' => 'SprintController@update']);
+ $router->delete('/', ['as' => 'delete', 'uses' => 'SprintController@delete']);
+ });
+ });
+
+ $router->group(['prefix' => 'systems', 'middleware' => 'bindBusiness'], function () use ($router) {
+ $router->get('/', 'SystemController@index');
+ $router->post('/', 'SystemController@store');
+ $router->group(['prefix' => '{system}'], function () use ($router) {
+ $router->get('/', 'SystemController@show');
+ $router->put('/', 'SystemController@update');
+ $router->delete('/', 'SystemController@delete');
+ });
+ });
+
+ $router->group(['prefix' => 'tasks/{task}/files', 'middleware' => 'bindBusiness'], function () use ($router) {
+ $router->get('/', ['uses' => 'TaskFileController@index']);
+ $router->post('/', ['uses' => 'TaskFileController@sync']);
+ $router->get('/{file}', ['uses' => 'TaskFileController@download']);
+ });
+
+ $router->group(['prefix' => 'users'], function () use ($router){
+ $router->post('/', 'ProjectController@storeOrUpdateUser');
+ $router->group(['prefix' => '{user}'], function () use ($router) {
+ $router->delete('/', 'ProjectController@deleteUser');
+ });
+ });
+
+ $router->group(['prefix' => 'tasks'], function ($router) {
+ $router->post('/', 'TaskController@store');
+ $router->group(['prefix' => '{task}'], function ($router) {
+ $router->get('/', 'TaskController@show');
+ $router->put('/', 'TaskController@update');
+ $router->delete('/', 'TaskController@destroy');
+
+ $router->post('/watchers', 'TaskController@toggleWatcher');
+
+ $router->group(['prefix' => 'works'], function ($router) {
+ $router->post('/', 'WorkController@store');
+ $router->group(['prefix' => '{work}'], function ($router) {
+ $router->get('/', 'WorkController@show');
+ $router->put('/', 'WorkController@update');
+ $router->delete('/', 'WorkController@destroy');
+ });
+ });
+
+ $router->group(['prefix' => 'comments'], function ($router) {
+ $router->get('/', 'CommentController@index');
+ $router->post('/', 'CommentController@store');
+ $router->group(['prefix' => '{comment}'], function ($router) {
+ $router->get('/', 'CommentController@show');
+ $router->put('/', 'CommentController@update');
+ $router->delete('/', 'CommentController@destroy');
+ });
+ });
+
+ });
+ });
+
+ });
+ });
+
+ $router->group(['prefix' => 'tags', 'as' => 'tag.', 'middleware' => 'bindBusiness'], function () use ($router) {
+ $router->get('/', ['as' => 'index', 'uses' => 'TagController@index']);
+ $router->post('/', ['as' => 'store', 'uses' => 'TagController@store']);
+ $router->group(['prefix' => '{tag}'], function () use ($router) {
+ $router->get('/', ['as' => 'show', 'uses' => 'TagController@show']);
+ $router->put('/', ['as' => 'update', 'uses' => 'TagController@update']);
+ $router->delete('/', ['as' => 'delete', 'uses' => 'TagController@delete']);
+ });
+ });
+
+ $router->group(['prefix' => 'workflows', 'as' => 'workflow.', 'middleware' => 'bindBusiness'], function () use ($router) {
+ $router->get('/', ['as' => 'index', 'uses' => 'WorkflowController@index']);
+ $router->post('/', ['as' => 'store', 'uses' => 'WorkflowController@store']);
+ $router->group(['prefix' => '{workflow}'], function () use ($router) {
+ $router->get('/', ['as' => 'show', 'uses' => 'WorkflowController@show']);
+ $router->put('/', ['as' => 'update', 'uses' => 'WorkflowController@update']);
+ $router->delete('/', ['as' => 'delete', 'uses' => 'WorkflowController@delete']);
+ $router->group(['prefix' => 'statuses', 'middleware' => 'bindBusiness'], function () use ($router) {
+ $router->get('/', 'StatusController@index');
+ $router->post('/', 'StatusController@store');
+ $router->group(['prefix' => '{status}'], function () use ($router) {
+ $router->get('/', 'StatusController@show');
+ $router->put('/', 'StatusController@update');
+ $router->delete('/', ['as' => 'delete', 'uses' => 'StatusController@delete']);
+ });
+ });
+ });
+ });
+
+ $router->group(['prefix' => 'users'], function () use ($router){
+ $router->post('/', 'BusinessController@storeOrUpdateUser');
+ $router->group(['prefix' => '{user}'], function () use ($router) {
+ $router->delete('/', 'BusinessController@deleteUser');
+ });
+ });
+
+ $router->group(['prefix' => 'activities'], function () use ($router) {
+ $router->post('/', 'ActivityController@store');
+ $router->get('/', 'ActivityController@index');
+ });
+ });
+});
+
+$router->group(['prefix' => 'users','middleware' => 'bindBusiness'], function () use ($router) {
+ $router->get('/', 'UserController@index');
+ $router->get('/search', 'UserController@search');
+ $router->group(['prefix' => '{user}'], function () use ($router) {
+ $router->get('/', 'UserController@show');
+ $router->put('/', 'UserController@update');
+
+ $router->put('/avatar', 'UserController@setAvatar');
+ $router->delete('/avatar', 'UserController@unSetAvatar');
+ });
});