Completed
Branch develop (a31570)
by Mohamed
08:09 queued 04:45
created

ProjectController::getInactiveUsers()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 6
ccs 3
cts 3
cp 1
rs 9.4286
cc 1
eloc 3
nc 1
nop 1
crap 1
1
<?php
2
3
/*
4
 * This file is part of the Tinyissue package.
5
 *
6
 * (c) Mohamed Alsharaf <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Tinyissue\Http\Controllers;
13
14
use Illuminate\Http\Request;
15
use Tinyissue\Form\FilterIssue as FilterForm;
16
use Tinyissue\Form\Note as NoteForm;
17
use Tinyissue\Form\Project as Form;
18
use Tinyissue\Http\Requests\FormRequest;
19
use Tinyissue\Model\Project;
20
use Tinyissue\Model\Project\Issue;
21
use Tinyissue\Model\Project\Note;
22
use Tinyissue\Services\Exporter;
23
24
/**
25
 * ProjectController is the controller class for managing request related to a project
26
 *
27
 * @author Mohamed Alsharaf <[email protected]>
28
 */
29
class ProjectController extends Controller
30
{
31
    /**
32
     * Display activity for a project
33
     *
34
     * @param Project $project
35
     *
36
     * @return \Illuminate\View\View
37
     */
38 9
    public function getIndex(Project $project)
39
    {
40 9
        $activities = $project->activities()
41 9
            ->with('activity', 'issue', 'user', 'assignTo', 'comment', 'note')
42 9
            ->orderBy('created_at', 'DESC')
43 9
            ->take(10)
44 9
            ->get();
45
46 9
        return view('project.index', [
47 9
            'tabs'       => $this->projectMainViewTabs($project, 'index'),
48 9
            'project'    => $project,
49 9
            'active'     => 'activity',
50 9
            'activities' => $activities,
51 9
            'sidebar'    => 'project',
52
        ]);
53
    }
54
55
    /**
56
     * Display issues for a project
57
     *
58
     * @param FilterForm $filterForm
59
     * @param Request    $request
60
     * @param Project    $project
61
     * @param int        $status
62
     *
63
     * @return \Illuminate\View\View
64
     */
65 2
    public function getIssues(FilterForm $filterForm, Request $request, Project $project, $status = Issue::STATUS_OPEN)
66
    {
67 2
        $active = $status == Issue::STATUS_OPEN ? 'open_issue' : 'closed_issue';
68 2
        $issues = $project->listIssues($status, $request->all());
69
70 2
        return view('project.index', [
71 2
            'tabs'       => $this->projectMainViewTabs($project, 'issues', $issues, $status),
1 ignored issue
show
Documentation introduced by
$issues is of type object<Illuminate\Database\Eloquent\Collection>, but the function expects a null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
$status is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
72 2
            'project'    => $project,
73 2
            'active'     => $active,
74 2
            'issues'     => $issues,
75 2
            'sidebar'    => 'project',
76 2
            'filterForm' => $filterForm,
77
        ]);
78
    }
79
80
    /**
81
     * Display issues assigned to current user for a project
82
     *
83
     * @param Project $project
84
     *
85
     * @return \Illuminate\View\View
86
     */
87 1
    public function getAssigned(Project $project)
88
    {
89 1
        $issues = $project->listAssignedIssues($this->auth->user()->id);
0 ignored issues
show
Bug introduced by
Accessing id on the interface Illuminate\Contracts\Auth\Authenticatable suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
90
91 1
        return view('project.index', [
92 1
            'tabs'    => $this->projectMainViewTabs($project, 'assigned', $issues),
1 ignored issue
show
Documentation introduced by
$issues is of type object<Illuminate\Database\Eloquent\Collection>, but the function expects a null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
93 1
            'project' => $project,
94 1
            'active'  => 'issue_assigned_to_you',
95 1
            'issues'  => $issues,
96 1
            'sidebar' => 'project',
97
        ]);
98
    }
99
100
    /**
101
     * Display notes for a project
102
     *
103
     * @param Project  $project
104
     * @param NoteForm $form
105
     *
106
     * @return \Illuminate\View\View
107
     */
108 7
    public function getNotes(Project $project, NoteForm $form)
109
    {
110 7
        $notes = $project->notes()->with('createdBy')->get();
111
112 7
        return view('project.index', [
113 7
            'tabs'     => $this->projectMainViewTabs($project, 'notes', $notes),
114 7
            'project'  => $project,
115 7
            'active'   => 'notes',
116 7
            'notes'    => $notes,
117 7
            'sidebar'  => 'project',
118 7
            'noteForm' => $form,
119
        ]);
120
    }
121
122
    /**
123
     * @param Project $project
124
     * @param $view
125
     * @param null $data
126
     * @param bool $status
127
     * @return array
128
     */
129 19
    protected function projectMainViewTabs(Project $project, $view, $data = null, $status = false)
130
    {
131 19
        $notesCount = $view === 'note'? $data->count() : $project->notes()->count();
0 ignored issues
show
Bug introduced by
The method count cannot be called on $data (of type null).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
132
133 19
        $assignedIssuesCount = 0;
134 19
        if ($view !== 'assigned' && !$this->auth->guest()) {
135 18
            $assignedIssuesCount = $this->auth->user()->assignedIssuesCount($project->id);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Illuminate\Contracts\Auth\Authenticatable as the method assignedIssuesCount() does only exist in the following implementations of said interface: Tinyissue\Model\User.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
136 1
        } else if  ($view === 'assigned') {
137 1
            $assignedIssuesCount = $data->count();
0 ignored issues
show
Bug introduced by
The method count cannot be called on $data (of type null).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
138
        }
139
140 19
        if ($view === 'issues') {
141 2
            if ($status == Issue::STATUS_OPEN) {
142 2
                $closedIssuesCount = $project->closedIssuesCount()->count();
143 2
                $openIssuesCount = $data->count();
0 ignored issues
show
Bug introduced by
The method count cannot be called on $data (of type null).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
144
            } else {
145 1
                $closedIssuesCount = $data->count();
0 ignored issues
show
Bug introduced by
The method count cannot be called on $data (of type null).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
146 2
                $openIssuesCount = $project->openIssuesCount()->count();
147
            }
148
        } else {
149 17
            $openIssuesCount = $project->openIssuesCount()->count();
150 17
            $closedIssuesCount =  $project->closedIssuesCount()->count();
151
        }
152
153 19
        $tabs = [];
154 19
        $tabs[] = [
155 19
            'url'  => $project->to(),
156 19
            'page' => 'activity',
157
        ];
158 19
        $tabs[] = [
159 19
            'url'    => $project->to('issues'),
160 19
            'page'   => 'open_issue',
161 19
            'prefix' => $openIssuesCount,
162
        ];
163 19
        $tabs[] = [
164 19
            'url'    => $project->to('issues') . '/0',
165 19
            'page'   => 'closed_issue',
166 19
            'prefix' => $closedIssuesCount,
167
        ];
168 19
        if (!$this->auth->guest()) {
169 19
            $tabs[] = [
170 19
                'url'    => $project->to('assigned'),
171 19
                'page'   => 'issue_assigned_to_you',
172 19
                'prefix' => $assignedIssuesCount,
173
            ];
174
        }
175 19
        $tabs[] = [
176 19
            'url'    => $project->to('notes'),
177 19
            'page'   => 'notes',
178 19
            'prefix' => $notesCount,
179
        ];
180
181 19
        return $tabs;
182
    }
183
184
    /**
185
     * Edit the project
186
     *
187
     * @param Project $project
188
     * @param Form    $form
189
     *
190
     * @return \Illuminate\View\View
191
     */
192 2
    public function getEdit(Project $project, Form $form)
193
    {
194 2
        return view('project.edit', [
195 2
            'form'    => $form,
196 2
            'project' => $project,
197 2
            'sidebar' => 'project',
198
        ]);
199
    }
200
201
    /**
202
     * To update project details
203
     *
204
     * @param Project             $project
205
     * @param FormRequest\Project $request
206
     *
207
     * @return \Illuminate\Http\RedirectResponse
208
     */
209 2
    public function postEdit(Project $project, FormRequest\Project $request)
210
    {
211
        // Delete the project
212 2
        if ($request->has('delete-project')) {
213 1
            $project->delete();
214
215 1
            return redirect('projects')
216 1
                ->with('notice', trans('tinyissue.project_has_been_deleted'));
217
        }
218
219 1
        $project->update($request->all());
220
221 1
        return redirect('projects')
222 1
            ->with('notice', trans('tinyissue.project_has_been_updated'));
223
    }
224
225
    /**
226
     * Ajax: returns list of users that are not in the project
227
     *
228
     * @param Project $project
229
     *
230
     * @return \Symfony\Component\HttpFoundation\Response
231
     */
232 1
    public function getInactiveUsers(Project $project)
233
    {
234 1
        $users = $project->usersNotIn();
235
236 1
        return response()->json($users);
237
    }
238
239
    /**
240
     * Ajax: add user to the project
241
     *
242
     * @param Project $project
243
     * @param Request $request
244
     *
245
     * @return \Symfony\Component\HttpFoundation\Response
246
     */
247 1 View Code Duplication
    public function postAssign(Project $project, Request $request)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
248
    {
249 1
        $status = false;
250 1
        if ($request->has('user_id')) {
251 1
            $project->assignUser((int) $request->input('user_id'));
252 1
            $status = true;
253
        }
254
255 1
        return response()->json(['status' => $status]);
256
    }
257
258
    /**
259
     * Ajax: remove user from the project
260
     *
261
     * @param Project $project
262
     * @param Request $request
263
     *
264
     * @return \Symfony\Component\HttpFoundation\Response
265
     */
266 1 View Code Duplication
    public function postUnassign(Project $project, Request $request)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
267
    {
268 1
        $status = false;
269 1
        if ($request->has('user_id')) {
270 1
            $project->unassignUser((int) $request->input('user_id'));
271 1
            $status = true;
272
        }
273
274 1
        return response()->json(['status' => $status]);
275
    }
276
277
    /**
278
     * To add a new note to the project
279
     *
280
     * @param Project          $project
281
     * @param Note             $note
282
     * @param FormRequest\Note $request
283
     *
284
     * @return \Illuminate\Http\RedirectResponse
285
     */
286 2
    public function postAddNote(Project $project, Note $note, FormRequest\Note $request)
287
    {
288 2
        $note->setRelation('project', $project);
289 2
        $note->setRelation('createdBy', $this->auth->user());
290 2
        $note->createNote($request->all());
291
292 2
        return redirect($note->to())->with('notice', trans('tinyissue.your_note_added'));
293
    }
294
295
    /**
296
     * Ajax: To update project note
297
     *
298
     * @param Project $project
299
     * @param Note    $note
300
     * @param Request $request
301
     *
302
     * @return \Symfony\Component\HttpFoundation\Response
303
     */
304 1
    public function postEditNote(Project $project, Project\Note $note, Request $request)
305
    {
306 1
        $body = '';
307 1
        if ($request->has('body')) {
308 1
            $note->setRelation('project', $project);
309 1
            $note->body = $request->input('body');
310 1
            $note->save();
311 1
            $body = \Html::format($note->body);
312
        }
313
314 1
        return response()->json(['status' => true, 'text' => $body]);
315
    }
316
317
    /**
318
     * Ajax: to delete a project note
319
     *
320
     * @param Project $project
321
     * @param Note    $note
322
     *
323
     * @return \Symfony\Component\HttpFoundation\Response
324
     */
325 1
    public function getDeleteNote(Project $project, Project\Note $note)
0 ignored issues
show
Unused Code introduced by
The parameter $project is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
326
    {
327 1
        $note->delete();
328
329 1
        return response()->json(['status' => true]);
330
    }
331
332
    /**
333
     * Ajax: generate the issues export file
334
     *
335
     * @param Project  $project
336
     * @param Exporter $exporter
337
     * @param Request  $request
338
     *
339
     * @return \Symfony\Component\HttpFoundation\Response
340
     */
341 4
    public function postExportIssues(Project $project, Exporter $exporter, Request $request)
342
    {
343
        // Generate export file
344 4
        $info = $exporter->exportFile(
345 4
            'Project\Issue',
346 4
            $request->input('format', Exporter::TYPE_CSV),
0 ignored issues
show
Bug introduced by
It seems like $request->input('format'...ces\Exporter::TYPE_CSV) targeting Illuminate\Http\Request::input() can also be of type array; however, Tinyissue\Services\Exporter::exportFile() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
347 4
            $request->all()
348
        );
349
350
        // Download link
351 4
        $link = link_to(
352 4
            $project->to('download_export/' . $info['file']),
353 4
            trans('tinyissue.download_export'),
354 4
            ['class' => 'btn btn-link']
355
        );
356
357 4
        return response()->json([
358 4
            'link'  => $link,
359 4
            'title' => $info['title'],
360 4
            'file'  => $info['file'],
361 4
            'ext'   => $info['ext'],
362
        ]);
363
    }
364
365
    /**
366
     * Download and then delete an export file
367
     *
368
     * @param Project $project
369
     * @param string  $file
370
     *
371
     * @return \Symfony\Component\HttpFoundation\BinaryFileResponse
372
     */
373 4
    public function getDownloadExport(Project $project, $file)
0 ignored issues
show
Unused Code introduced by
The parameter $project is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
374
    {
375
        // Filter out any characters that are not in pattern
376 4
        $file = preg_replace('/[^a-z0-9\_\.]/mi', '', $file);
377
378
        // Download export
379 4
        return response()->download(storage_path('exports/' . $file), $file)->deleteFileAfterSend(true);
380
    }
381
}
382