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); } }