Completed
Push — master ( ab60f2...c901a1 )
by Ricardo
07:00
created

IssueController::index()   F

Complexity

Conditions 57
Paths > 20000

Size

Total Lines 163

Duplication

Lines 9
Ratio 5.52 %

Importance

Changes 0
Metric Value
dl 9
loc 163
rs 0
c 0
b 0
f 0
cc 57
nc 114800
nop 2

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
namespace Fabrica\Http\Api;
3
4
use Illuminate\Http\Request;
5
use Illuminate\Support\Facades\Event;
6
use Fabrica\Events\IssueEvent;
7
use Fabrica\Events\VersionEvent;
8
9
use Fabrica\Http\Requests;
10
use Fabrica\Http\Api\Controller;
11
use Fabrica\Project\Provider;
12
use Fabrica\Project\Eloquent\File;
13
use Fabrica\Project\Eloquent\Watch;
14
use Fabrica\Project\Eloquent\UserIssueFilters;
15
use Fabrica\Project\Eloquent\UserIssueListColumns;
16
use Fabrica\Project\Eloquent\ProjectIssueListColumns;
17
use Fabrica\Project\Eloquent\Linked;
18
use Fabrica\Project\Eloquent\Worklog;
19
use Fabrica\Project\Eloquent\Version;
20
21
use Fabrica\Project\Eloquent\Board;
22
use Fabrica\Project\Eloquent\Sprint;
23
use Fabrica\Project\Eloquent\BoardRankMap;
24
25
use Fabrica\Project\Eloquent\Labels;
26
27
use Fabrica\Workflow\Workflow;
28
use Fabrica\System\Eloquent\SysSetting;
29
use Fabrica\System\Eloquent\CalendarSingular;
30
use Cartalyst\Sentinel\Users\EloquentUser;
31
use Sentinel;
32
use DB;
33
use Exception;
34
35
use MongoDB\BSON\ObjectID;
36
use Maatwebsite\Excel\Facades\Excel;
37
38
class IssueController extends Controller
39
{
40
    use ExcelTrait, TimeTrackTrait;
41
42
    /**
43
     * Display a listing of the resource.
44
     *
45
     * @return \Illuminate\Http\Response
46
     */
47
    public function index(Request $request, $project_key)
48
    {
49
        $where = $this->getIssueQueryWhere($project_key, $request->all());
50
        $query = DB::collection('issue_' . $project_key)->whereRaw($where); 
51
52
        $from = $request->input('from');
53
        $from_kanban_id = $request->input('from_kanban_id');
54
        if (isset($from) && in_array($from, [ 'kanban', 'active_sprint', 'backlog' ]) && isset($from_kanban_id) && $from_kanban_id) 
55
        {
56
            $board = Board::find($from_kanban_id);
57
            if ($board && isset($board->query) && $board->query)
58
            {
59
                $global_query = $this->getIssueQueryWhere($project_key, $board->query);
60
                $query->whereRaw($global_query);
61
            }
62
63
            if ($from === 'kanban')
64
            {
65
                $query->where(function ($query) {
66
                    $query->whereRaw([ 'resolve_version' => [ '$exists' => 0 ] ])->orWhere('resolve_version', '');
67
                });
68
            }
69
            else if ($from === 'active_sprint' || $from === 'backlog')
70
            {
71
                $active_sprint_issues = [];
72
                $active_sprint = Sprint::where('project_key', $project_key)->where('status', 'active')->first();
73
                if ($from === 'active_sprint' && !$active_sprint) 
74
                {
75
                    Response()->json([ 'ecode' => 0, 'data' => []]);
0 ignored issues
show
Bug introduced by
The method json does only exist in Illuminate\Contracts\Routing\ResponseFactory, but not in Illuminate\Http\Response.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
76
                }
77
                else if ($active_sprint && isset($active_sprint['issues']) && $active_sprint['issues'])
78
                {
79
                    $active_sprint_issues = $active_sprint['issues'];
80
                }
81
82
                $last_column_states = [];
83 View Code Duplication
                if ($board && isset($board->columns))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
84
                {
85
                    $board_columns = $board->columns;
86
                    $last_column = array_pop($board_columns) ?: [];
87
                    if ($last_column && isset($last_column['states']) && $last_column['states'])
88
                    {
89
                        $last_column_states = $last_column['states']; 
90
                    }
91
                }
92
 
93
                $query->where(function ($query) use ($last_column_states, $active_sprint_issues) {
94
                    $query->whereRaw([ 'state' => [ '$nin' => $last_column_states ] ])->orWhereIn('no', $active_sprint_issues);
95
                });
96
            }
97
        }
98
99
        // get total num
100
        $total = $query->count();
101
102
        $orderBy = $request->input('orderBy') ?: '';
103
        if ($orderBy)
104
        {
105
            $orderBy = explode(',', $orderBy);
106
            foreach ($orderBy as $val)
107
            {
108
                $val = explode(' ', trim($val));
109
                $field = array_shift($val);
110
                $sort = array_pop($val) ?: 'asc';
111
                $query = $query->orderBy($field, $sort);
112
            }
113
        }
114
115
        $query->orderBy('_id', isset($from) && $from != 'gantt' ? 'asc' : 'desc');
116
117
        $page_size = $request->input('limit') ? intval($request->input('limit')) : 50;
118
        //$page_size = 200;
119
        $page = $request->input('page') ?: 1;
120
        $query = $query->skip($page_size * ($page - 1))->take($page_size);
121
        $issues = $query->get();
122
123
        if (isset($from) && $from == 'export')
124
        {
125
            $export_fields = $request->input('export_fields');
126
            $this->export($project_key, isset($export_fields) ? explode(',', $export_fields) : [], $issues);
127
            exit();
128
        }
129
130
        $watched_issue_ids = [];
131
        if (!isset($from) || !$from)
132
        {
133
            $watched_issues = Watch::where('project_key', $project_key)
134
                ->where('user.id', $this->user->id)
135
                ->get()
136
                ->toArray();
137
            $watched_issue_ids = array_column($watched_issues, 'issue_id');
138
        }
139
140
        $cache_parents = [];
141
        $issue_ids = [];
142
        foreach ($issues as $key => $issue)
143
        {
144
            $issue_ids[] = $issue['_id']->__toString();
145
            // set issue watching flag
146
            if (in_array($issue['_id']->__toString(), $watched_issue_ids))
147
            {
148
                $issues[$key]['watching'] = true;
149
            }
150
151
            // get the parent issue
152
            if (isset($issue['parent_id']) && $issue['parent_id'])
153
            {
154
                if (isset($cache_parents[$issue['parent_id']]) && $cache_parents[$issue['parent_id']])
155
                {
156
                    $issues[$key]['parent'] = $cache_parents[$issue['parent_id']];
157
                }
158
                else
159
                {
160
                    $parent = DB::collection('issue_' . $project_key)->where('_id', $issue['parent_id'])->first();
161
                    $issues[$key]['parent'] = $parent ? array_only($parent, [ '_id', 'title', 'no', 'type', 'state' ]) : [];
162
                    $cache_parents[$issue['parent_id']] = $issues[$key]['parent'];
163
                }
164
            }
165
166
            if (!isset($from))
167
            {
168
                $issues[$key]['hasSubtasks'] = DB::collection('issue_' . $project_key)->where('parent_id', $issue['_id']->__toString())->exists();
169
            }
170
        }
171
172
        if ($issues && isset($from) && in_array($from, [ 'kanban', 'active_sprint', 'backlog', 'his_sprint' ]))
173
        {
174
            $filter = $request->input('filter') ?: '';
175
            $issues = $this->arrangeIssues($project_key, $issues, $from, $from_kanban_id, $filter === 'all');
176
        }
177
178
        $options = [ 'total' => $total, 'sizePerPage' => $page_size ];
179
180
        if (isset($from) && $from == 'gantt')
181
        {
182
            foreach ($issues as $key => $issue) 
183
            {
184
                if (!isset($issue['parent_id']) || !$issue['parent_id'] || in_array($issue['parent_id'], $issue_ids)) 
185
                {
186
                    continue;
187
                }
188
                if (isset($issue['parent']) && $issue['parent']) 
189
                {
190
                    $issues[] = $issue['parent'];
191
                    $issue_ids[] = $issue['parent_id'];
192
                }
193
            }
194
195
            $singulars = CalendarSingular::all();
196
            $new_singulars = [];
197
            foreach ($singulars as $singular)
198
            {
199
                $tmp = [];
200
                $tmp['notWorking'] = $singular->type == 'holiday' ? 1 : 0;
201
                $tmp['date'] = $singular->date;
202
                $new_singulars[] = $tmp;
203
            }
204
            $options['singulars'] = $new_singulars;
205
            $options['today'] = date('Y/m/d');
206
        }
207
208
        return Response()->json([ 'ecode' => 0, 'data' => parent::arrange($issues), 'options' => $options ]);
0 ignored issues
show
Bug introduced by
The method json does only exist in Illuminate\Contracts\Routing\ResponseFactory, but not in Illuminate\Http\Response.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
Comprehensibility Bug introduced by
It seems like you call parent on a different method (arrange() instead of index()). Are you sure this is correct? If so, you might want to change this to $this->arrange().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
209
    }
210
211
    /**
212
     * search issue.
213
     *
214
     * @return \Illuminate\Http\Response
215
     */
216
    public function search(Request $request, $project_key)
217
    {
218
        $query = DB::collection('issue_' . $project_key)->where('del_flg', '<>', 1);
219
220
        if ($s = $request->input('s'))
221
        {
222
            if (is_numeric($s) && strpos($s, '.') === false)
223
            {
224
                $query->where(function ($query) use ($s) {
225
                    $query->where('no', $s + 0)->orWhere('title', 'like', '%' . $s . '%');
226
                });
227
            }
228
            else
229
            {
230
                $query->where('title', 'like', '%' . $s . '%');
231
            }
232
        }
233
234
        $type = $request->input('type');
235
        if (isset($type))
236
        {
237
            if ($type == 'standard')
238
            {
239
                $query->where(function ($query) { 
240
                    $query->where('parent_id', '')->orWhereNull('parent_id')->orWhere('parent_id', 'exists', false);
241
                });
242
   
243
            } 
244
            else if ($type == 'subtask')
245
            {
246
                $query->where(function ($query) { 
247
                    $query->where('parent_id', 'exists', true)->where('parent_id', '<>', '')->whereNotNull('parent_id');
248
                });
249
            }
250
        }
251
252
        if ($limit = $request->input('limit'))
253
        {
254
            $limit = intval($limit) < 10 ? 10 : intval($limit);
255
        }
256
        else
257
        {
258
            $limit = 10;
259
        }
260
261
        $query->take($limit)->orderBy('created_at', 'desc');
262
        $issues = $query->get();
263
        return Response()->json([ 'ecode' => 0, 'data' => parent::arrange($issues) ]);
0 ignored issues
show
Bug introduced by
The method json does only exist in Illuminate\Contracts\Routing\ResponseFactory, but not in Illuminate\Http\Response.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
Comprehensibility Bug introduced by
It seems like you call parent on a different method (arrange() instead of search()). Are you sure this is correct? If so, you might want to change this to $this->arrange().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
264
    }
265
266
    /**
267
     * Store a newly created resource in storage.
268
     *
269
     * @param  \Illuminate\Http\Request  $request
270
     * @return \Illuminate\Http\Response
271
     */
272
    public function store(Request $request, $project_key)
273
    {
274
        $issue_type = $request->input('type');
275
        if (!$issue_type)
276
        {
277
            throw new \UnexpectedValueException('the issue type can not be empty.', -11100);
278
        }
279
280
        $schema = Provider::getSchemaByType($issue_type);
281
        if (!$schema)
0 ignored issues
show
Bug Best Practice introduced by
The expression $schema of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
282
        {
283
            throw new \UnexpectedValueException('the schema of the type is not existed.', -11101);
284
        }
285
286
        if (!$this->requiredCheck($schema, $request->all(), 'create'))
287
        {
288
            throw new \UnexpectedValueException('the required field is empty.', -11121);
289
        }
290
291
        // handle timetracking
292
        $insValues = [];
293 View Code Duplication
        foreach ($schema as $field)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
294
        {
295
            $fieldValue = $request->input($field['key']);
296
            if (!isset($fieldValue) || !$fieldValue)
297
            {
298
                continue;
299
            }
300
301
            if ($field['type'] == 'TimeTracking')
302
            {
303
                if (!$this->ttCheck($fieldValue))
304
                {
305
                    throw new \UnexpectedValueException('the format of timetracking is incorrect.', -11102);
306
                }
307
                $insValues[$field['key']] = $this->ttHandle($fieldValue);
308
                $insValues[$field['key'] . '_m'] = $this->ttHandleInM($insValues[$field['key']]);
309
            }
310
            else if ($field['type'] == 'DatePicker' || $field['type'] == 'DateTimePicker')
311
            {
312
                if ($this->isTimestamp($fieldValue) === false)
313
                {
314
                    throw new \UnexpectedValueException('the format of datepicker field is incorrect.', -11122);
315
                }
316
            }
317
            else if ($field['type'] == 'SingleUser')
318
            {
319
                $user_info = Sentinel::findById($fieldValue);
320
                if ($user_info)
321
                {
322
                    $insValues[$field['key']] = [ 'id' => $fieldValue, 'name' => $user_info->first_name, 'email' => $user_info->email ];
323
                }
324
            }
325
            else if ($field['type'] == 'MultiUser')
326
            {
327
                $user_ids = $fieldValue;
328
                $new_user_ids = [];
329
                $insValues[$field['key']] = [];
330
                foreach ($user_ids as $uid)
331
                {
332
                    $user_info = Sentinel::findById($uid);
333
                    if ($user_info)
334
                    {
335
                        array_push($insValues[$field['key']], [ 'id' => $uid, 'name' => $user_info->first_name, 'email' => $user_info->email ]);
336
                        $new_user_ids[] = $uid;
337
                    }
338
                }
339
                $insValues[$field['key'] . '_ids'] = $new_user_ids;
340
            }
341
        }
342
343
        // handle assignee
344
        $assignee = [];
345
        $assignee_id = $request->input('assignee');
346
        if (!$assignee_id)
347
        {
348
            $module_ids = $request->input('module');
349
            if ($module_ids)
350
            {
351
                //$module_ids = explode(',', $module_ids);
352
                $module = Provider::getModuleById($module_ids[0]);
353
                if (isset($module['defaultAssignee']) && $module['defaultAssignee'] === 'modulePrincipal')
354
                {
355
                    $assignee2 = $module['principal'] ?: '';
356
                    $assignee_id = isset($assignee2['id']) ? $assignee2['id'] : '';
357
                }
358
                else if (isset($module['defaultAssignee']) && $module['defaultAssignee'] === 'projectPrincipal') 
359
                {
360
                    $assignee2 = Provider::getProjectPrincipal($project_key) ?: '';
361
                    $assignee_id = isset($assignee2['id']) ? $assignee2['id'] : ''; 
362
                }
363
            }
364
        }
365
        if ($assignee_id)
366
        {
367
            if ($assignee_id != $this->user->id && !$this->isPermissionAllowed($project_key, 'assigned_issue', $assignee_id))
368
            {
369
                return Response()->json(['ecode' => -11118, 'emsg' => 'the assigned user has not assigned-issue permission.']);
0 ignored issues
show
Bug introduced by
The method json does only exist in Illuminate\Contracts\Routing\ResponseFactory, but not in Illuminate\Http\Response.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
370
            }
371
372
            $user_info = Sentinel::findById($assignee_id);
373
            if ($user_info)
374
            {
375
                $assignee = [ 'id' => $assignee_id, 'name' => $user_info->first_name, 'email' => $user_info->email ];
376
            }
377
        }
378
        if (!$assignee) 
0 ignored issues
show
Bug Best Practice introduced by
The expression $assignee of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
379
        {
380
            $assignee = [ 'id' => $this->user->id, 'name' => $this->user->first_name, 'email' => $this->user->email ];
381
        }
382
        $insValues['assignee'] = $assignee;
383
384
        //$priority = $request->input('priority'); 
385
        //if (!isset($priority) || !$priority)
386
        //{
387
        //    $insValues['priority'] = Provider::getDefaultPriority($project_key);
388
        //}
389
390
        $resolution = $request->input('resolution'); 
391
        if (!isset($resolution) || !$resolution)
392
        {
393
            $insValues['resolution'] = 'Unresolved'; 
394
        }
395
396
        // get reporter(creator)
397
        $insValues['reporter'] = [ 'id' => $this->user->id, 'name' => $this->user->first_name, 'email' => $this->user->email ];
398
        $insValues['created_at'] = time();
399
400
        $table = 'issue_' . $project_key;
401
        $max_no = DB::collection($table)->count() + 1;
402
        $insValues['no'] = $max_no;
403
404
        // workflow initialize 
405
        $workflow = $this->initializeWorkflow($issue_type);
406
        $insValues = $insValues + $workflow;
407
408
        $valid_keys = $this->getValidKeysBySchema($schema);
409
        // merge all fields
410
        $insValues = $insValues + array_only($request->all(), $valid_keys);
411
412
        // insert into the table
413
        $id = DB::collection($table)->insertGetId($insValues);
414
415
        // add to histroy table
416
        Provider::snap2His($project_key, $id, $schema);
417
        // trigger event of issue created
418
        Event::fire(new IssueEvent($project_key, $id->__toString(), $insValues['reporter'], [ 'event_key' => 'create_issue' ]));
419
420
        // create the Labels for project
421
        if (isset($insValues['labels']) && $insValues['labels'])
422
        {
423
            $this->createLabels($project_key, $insValues['labels']);
424
        }
425
426
        return $this->show($project_key, $id->__toString());
427
    }
428
429
    /**
430
     * initialize the workflow by type.
431
     *
432
     * @param  int  $type
433
     * @return array 
434
     */
435
    public function initializeWorkflow($type)
436
    {
437
        // get workflow definition
438
        $wf_definition = Provider::getWorkflowByType($type);
439
        // create and start workflow instacne
440
        $wf_entry = Workflow::createInstance($wf_definition->id, $this->user->id)->start([ 'caller' => $this->user->id ]);
441
        // get the inital step
442
        $initial_step = $wf_entry->getCurrentSteps()->first();
0 ignored issues
show
Bug introduced by
The method first cannot be called on $wf_entry->getCurrentSteps() (of type array).

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...
443
        $initial_state = $wf_entry->getStepMeta($initial_step->step_id, 'state');
444
445
        $ret['state'] = $initial_state;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$ret was never initialized. Although not strictly required by PHP, it is generally a good practice to add $ret = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
446
        //$ret['resolution'] = 'Unresolved';
447
        $ret['entry_id'] = $wf_entry->getEntryId();
448
        $ret['definition_id'] = $wf_definition->id;
449
450
        return $ret;
451
    }
452
453
    /**
454
     * Display the specified resource.
455
     *
456
     * @param  int  $id
457
     * @return \Illuminate\Http\Response
458
     */
459
    public function show($project_key, $id)
460
    {
461
        $issue = DB::collection('issue_' . $project_key)->where('_id', $id)->first();
462
        $schema = Provider::getSchemaByType($issue['type']);
463
        if (!$schema)
0 ignored issues
show
Bug Best Practice introduced by
The expression $schema of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
464
        {
465
            throw new \UnexpectedValueException('the schema of the type is not existed.', -11101);
466
        }
467
468
        if (isset($issue['assignee']['id']))
469
        {
470
            $user = Sentinel::findById($issue['assignee']['id']);
471
            $issue['assignee']['avatar'] = isset($user->avatar) ? $user->avatar : '';
472
        }
473
474
        foreach ($schema as $field)
475
        {
476
            if ($field['type'] === 'File' && isset($issue[$field['key']]) && $issue[$field['key']]) 
477
            {
478
               foreach ($issue[$field['key']] as $key => $fid)
479
                {
480
                    $issue[$field['key']][$key] = File::find($fid);
481
                }
482
            }
483
        }
484
485
        // get avaliable actions for wf
486
        if (isset($issue['entry_id']) && $issue['entry_id'])
487
        {
488
            try {
489
                $wf = new Workflow($issue['entry_id']);
490
                $issue['wfactions'] = $wf->getAvailableActions([ 'project_key' => $project_key, 'issue_id' => $id, 'caller' => $this->user->id ]);
491
            } catch (Exception $e) {
492
                $issue['wfactions'] = [];
493
            }
494
495
            foreach ($issue['wfactions'] as $key => $action)
496
            {
497
                if (isset($action['screen']) && $action['screen'] && $action['screen'] != 'comments')
498
                {
499
                    $issue['wfactions'][$key]['schema'] = Provider::getSchemaByScreenId($project_key, $issue['type'], $action['screen']);
500
                }
501
            }
502
        }
503
504
        if (isset($issue['parent_id']) && $issue['parent_id']) 
505
        {
506
            $issue['parent'] = DB::collection('issue_' . $project_key)->where('_id', $issue['parent_id'])->first(['no', 'type', 'title', 'state']);
507
        }
508
        else
509
        {
510
            $issue['hasSubtasks'] = DB::collection('issue_' . $project_key)->where('parent_id', $id)->exists();
511
        }
512
513
        $issue['subtasks'] = DB::collection('issue_' . $project_key)->where('parent_id', $id)->where('del_flg', '<>', 1)->orderBy('created_at', 'asc')->get(['no', 'type', 'title', 'state']);
514
515
        $issue['links'] = [];
516
        $links = DB::collection('linked')->where('src', $id)->orWhere('dest', $id)->where('del_flg', '<>', 1)->orderBy('created_at', 'asc')->get();
517
        $link_fields = ['_id', 'no', 'type', 'title', 'state'];
518
        foreach ($links as $link)
519
        {
520 View Code Duplication
            if ($link['src'] == $id)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
521
            {
522
                $link['src'] = array_only($issue, $link_fields);
523
            }
524
            else
525
            {
526
                $src_issue = DB::collection('issue_' . $project_key)->where('_id', $link['src'])->first();
527
                $link['src'] = array_only($src_issue, $link_fields);
528
            }
529
530 View Code Duplication
            if ($link['dest'] == $id)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
531
            {
532
                $link['dest'] = array_only($issue, $link_fields);
533
            }
534
            else
535
            {
536
                $dest_issue = DB::collection('issue_' . $project_key)->where('_id', $link['dest'])->first();
537
                $link['dest'] = array_only($dest_issue, $link_fields);
538
            }
539
            array_push($issue['links'], $link);
540
        }
541
542
        $issue['watchers'] = array_column(Watch::where('issue_id', $id)->orderBy('_id', 'desc')->get()->toArray(), 'user');
543
        
544
        if (Watch::where('issue_id', $id)->where('user.id', $this->user->id)->exists())
545
        {
546
            $issue['watching'] = true;
547
        }
548
549
        $comments_num = 0;
550
        $comments = DB::collection('comments_' . $project_key)
551
            ->where('issue_id', $id)
552
            ->get();
553
        foreach($comments as $comment)
554
        {
555
            $comments_num += 1;
556
            if (isset($comment['reply']))
557
            {
558
                $comments_num += count($comment['reply']);
559
            }
560
        }
561
        $issue['comments_num'] = $comments_num;
562
563
        $issue['gitcommits_num'] = DB::collection('git_commits_' . $project_key)
564
            ->where('issue_id', $id)
565
            ->count();
566
567
        $issue['worklogs_num'] = Worklog::Where('project_key', $project_key)
568
            ->where('issue_id', $id)
569
            ->count();
570
571
        return Response()->json(['ecode' => 0, 'data' => parent::arrange($issue)]);
0 ignored issues
show
Bug introduced by
The method json does only exist in Illuminate\Contracts\Routing\ResponseFactory, but not in Illuminate\Http\Response.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
Comprehensibility Bug introduced by
It seems like you call parent on a different method (arrange() instead of show()). Are you sure this is correct? If so, you might want to change this to $this->arrange().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
572
    }
573
574
    /**
575
     * Display the specified resource.
576
     *
577
     * @param  string  $project_key
578
     * @param  string  $id
579
     * @return \Illuminate\Http\Response
580
     */
581
    public function wfactions($project_key, $id)
582
    {
583
        $issue = DB::collection('issue_' . $project_key)->where('_id', $id)->first();
584
585
        $wf = new Workflow($issue['entry_id']);
586
        $wfactions = $wf->getAvailableActions([ 'project_key' => $project_key, 'issue_id' => $id, 'caller' => $this->user->id ], true);
587
        foreach ($wfactions as $key => $action)
588
        {
589
            if (isset($action['screen']) && $action['screen'])
590
            {
591
                $wfactions[$key]['schema'] = Provider::getSchemaByScreenId($project_key, $issue['type'], $action['screen']);
592
            }
593
        }
594
595
        return Response()->json(['ecode' => 0, 'data' => parent::arrange($wfactions)]);
0 ignored issues
show
Bug introduced by
The method json does only exist in Illuminate\Contracts\Routing\ResponseFactory, but not in Illuminate\Http\Response.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
Comprehensibility Bug introduced by
It seems like you call parent on a different method (arrange() instead of wfactions()). Are you sure this is correct? If so, you might want to change this to $this->arrange().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
596
    }
597
598
    /**
599
     * Display the specified resource.
600
     *
601
     * @param  string  $project_key
602
     * @return \Illuminate\Http\Response
603
     */
604
    public function getOptions($project_key)
605
    {
606
        // get project users
607
        $users = Provider::getUserList($project_key);
608
        // get project users fix me
609
        $assignees = Provider::getAssignedUsers($project_key);
610
        // get state list
611
        $states = Provider::getStateOptions($project_key);
612
        // get resolution list
613
        $resolutions = Provider::getResolutionOptions($project_key);
614
        // get priority list
615
        $priorities = Provider::getPriorityOptions($project_key);
616
        // get version list
617
        $versions = Provider::getVersionList($project_key, ['name']);
618
        // get module list
619
        $modules = Provider::getModuleList($project_key, ['name']);
620
        // get project epics
621
        $epics = Provider::getEpicList($project_key);
622
        // get project labels
623
        $labels = Provider::getLabelOptions($project_key);
624
        // get project types
625
        $types = Provider::getTypeListExt($project_key, [ 'user' => $users, 'assignee' => $assignees, 'state' => $states, 'resolution' => $resolutions, 'priority' => $priorities, 'version' => $versions, 'module' => $modules, 'epic' => $epics, 'labels' => $labels ]);
626
        // get project sprints
627
        $new_sprints = [];
628
        $sprints = Provider::getSprintList($project_key);
629
        foreach ($sprints as $sprint)
630
        {
631
            $new_sprints[] = [ 'no' => $sprint['no'], 'name' => $sprint['name'] ];
632
        }
633
        // get defined fields
634
        $fields = Provider::getFieldList($project_key);
635
        // get defined searchers
636
        $filters = Provider::getIssueFilters($project_key, $this->user->id);
637
        // get defined list columns
638
        $display_columns = Provider::getIssueDisplayColumns($project_key, $this->user->id);
639
        // get timetrack options
640
        $timetrack = $this->getTimeTrackSetting();
641
        // get issue link relations
642
        $relations = $this->getLinkRelations();
643
644
        return Response()->json([ 
0 ignored issues
show
Bug introduced by
The method json does only exist in Illuminate\Contracts\Routing\ResponseFactory, but not in Illuminate\Http\Response.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
645
            'ecode' => 0, 
646
            'data' => parent::arrange([ 
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (arrange() instead of getOptions()). Are you sure this is correct? If so, you might want to change this to $this->arrange().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
647
                'users' => $users, 
648
                'assignees' => $assignees, 
649
                'types' => $types, 
650
                'states' => $states, 
651
                'resolutions' => $resolutions, 
652
                'priorities' => $priorities, 
653
                'modules' => $modules, 
654
                'labels' => $labels, 
655
                'versions' => $versions, 
656
                'epics' => $epics,
657
                'sprints' => $new_sprints,
658
                'filters' => $filters, 
659
                'display_columns' => $display_columns, 
660
                'timetrack' => $timetrack, 
661
                'relations' => $relations, 
662
                'fields' => $fields 
663
            ]) 
664
        ]);
665
    }
666
667
    /**
668
     * update issue assignee.
669
     *
670
     * @param  \Illuminate\Http\Request  $request
671
     * @param  string  $project_key
672
     * @param  string  $id
673
     * @return \Illuminate\Http\Response
674
     */
675
    public function setAssignee(Request $request, $project_key, $id)
676
    {
677
        $table = 'issue_' . $project_key;
678
        $issue = DB::collection($table)->find($id);
679
        if (!$issue)
680
        {
681
            throw new \UnexpectedValueException('the issue does not exist or is not in the project.', -11103);
682
        }
683
684
        if (!$this->isPermissionAllowed($project_key, 'assign_issue'))
685
        {
686
            return Response()->json(['ecode' => -11116, 'emsg' => 'the current user has not assign-issue permission.']);
0 ignored issues
show
Bug introduced by
The method json does only exist in Illuminate\Contracts\Routing\ResponseFactory, but not in Illuminate\Http\Response.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
687
        }
688
689
        $updValues = []; $assignee = [];
690
        $assignee_id = $request->input('assignee');
691
        if (!isset($assignee_id) || !$assignee_id)
692
        {
693
            throw new \UnexpectedValueException('the issue assignee cannot be empty.', -11104);
694
        }
695
696
        if ($assignee_id === 'me')
697
        {
698
            if (!$this->isPermissionAllowed($project_key, 'assigned_issue'))
699
            {
700
                return Response()->json(['ecode' => -11117, 'emsg' => 'the current user has not assigned-issue permission.']);
701
            }
702
703
            $assignee = [ 'id' => $this->user->id, 'name' => $this->user->first_name, 'email' => $this->user->email ];
704
            $updValues['assignee'] = $assignee;
705
        }
706
        else
707
        {
708
            if (!$this->isPermissionAllowed($project_key, 'assigned_issue', $assignee_id))
709
            {
710
                return Response()->json(['ecode' => -11118, 'emsg' => 'the assigned user has not assigned-issue permission.']);
711
            }
712
713
            $user_info = Sentinel::findById($assignee_id);
714
            if ($user_info)
715
            {
716
                $assignee = [ 'id' => $assignee_id, 'name' => $user_info->first_name, 'email' => $user_info->email ];
717
                $updValues['assignee'] = $assignee;
718
            }
719
        }
720
721
        // issue assignee has no change.
722
        if ($assignee['id'] === $issue['assignee']['id'])
723
        {
724
            return $this->show($project_key, $id);
725
        }
726
727
        $updValues['modifier'] = [ 'id' => $this->user->id, 'name' => $this->user->first_name, 'email' => $this->user->email ];
728
        $updValues['updated_at'] = time();
729
        DB::collection($table)->where('_id', $id)->update($updValues);
730
731
        // add to histroy table
732
        $snap_id = Provider::snap2His($project_key, $id, null, [ 'assignee' ]);
0 ignored issues
show
Documentation introduced by
null is of type null, but the function expects a array.

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...
Unused Code introduced by
$snap_id is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
733
        // trigger event of issue edited
734
        Event::fire(new IssueEvent($project_key, $id, $updValues['modifier'], [ 'event_key' => 'assign_issue', 'data' => [ 'old_user' => $issue['assignee'], 'new_user' => $assignee ] ]));
735
736
        return $this->show($project_key, $id);
737
    }
738
739
    /**
740
     * set issue labels.
741
     *
742
     * @param  \Illuminate\Http\Request  $request
743
     * @param  string  $project_key
744
     * @param  string  $id
745
     * @return \Illuminate\Http\Response
746
     */
747
    public function setLabels(Request $request, $project_key, $id)
748
    {
749 View Code Duplication
        if (!$this->isPermissionAllowed($project_key, 'edit_issue'))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
750
        {
751
            return Response()->json(['ecode' => -10002, 'emsg' => 'permission denied.']);
0 ignored issues
show
Bug introduced by
The method json does only exist in Illuminate\Contracts\Routing\ResponseFactory, but not in Illuminate\Http\Response.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
752
        }
753
754
        $labels = $request->input('labels');
755
        if (!isset($labels))
756
        {
757
            return $this->show($project_key, $id);
758
        }
759
760
        $table = 'issue_' . $project_key;
761
        $issue = DB::collection($table)->find($id);
762
        if (!$issue)
763
        {
764
            throw new \UnexpectedValueException('the issue does not exist or is not in the project.', -11103);
765
        }
766
767
        $updValues['labels'] = $labels;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$updValues was never initialized. Although not strictly required by PHP, it is generally a good practice to add $updValues = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
768
769
        $updValues['modifier'] = [ 'id' => $this->user->id, 'name' => $this->user->first_name, 'email' => $this->user->email ];
770
        $updValues['updated_at'] = time();
771
        DB::collection($table)->where('_id', $id)->update($updValues);
772
773
        // add to histroy table
774
        $snap_id = Provider::snap2His($project_key, $id, null, [ 'labels' ]);
0 ignored issues
show
Documentation introduced by
null is of type null, but the function expects a array.

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...
775
        // trigger event of issue edited
776
        Event::fire(new IssueEvent($project_key, $id, $updValues['modifier'], [ 'event_key' => 'edit_issue', 'snap_id' => $snap_id ]));
777
        // create the Labels for project
778
        if ($labels)
779
        {
780
            $this->createLabels($project_key, $labels);
781
        }
782
783
        return $this->show($project_key, $id);
784
    }
785
786
    /**
787
     * create the new labels for project.
788
     *
789
     * @param  string  $project_key
790
     * @param  array $labels
791
     * @return void
792
     */
793
    public function createLabels($project_key, $labels)
794
    {
795
        $created_labels = [];
796
        $project_labels = Labels::where('project_key', $project_key)
797
            ->whereIn('name', $labels)
798
            ->get();
799
        foreach ($project_labels as $label)
800
        {
801
            $created_labels[] = $label->name;
802
        }
803
        // get uncreated labels
804
        $new_labels = array_diff($labels, $created_labels);
805
        foreach ($new_labels as $label)
806
        {
807
            Labels::create([ 'project_key' => $project_key, 'name' => $label ]);
808
        }
809
        return true;
810
    }
811
812
    /**
813
     * check the required field
814
     *
815
     * @param  array $schema
816
     * @param  array $data
817
     * @param  string $mode
818
     * @return bool
819
     */
820
    public function requiredCheck($schema, $data, $mode='create')
821
    {
822
        foreach ($schema as $field)
823
        {
824
            if (isset($field['required']) && $field['required'])
825
            {
826
                if ($mode == 'update')
827
                {
828 View Code Duplication
                    if (isset($data[$field['key']]) && !$data[$field['key']] && $data[$field['key']] !== 0)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
829
                    {
830
                        return false;
831
                    }
832
                }
833 View Code Duplication
                else 
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
834
                {
835
                    if (!isset($data[$field['key']]) || !$data[$field['key']] && $data[$field['key']] !== 0)
836
                    {
837
                        return false;
838
                    }
839
                }
840
            }
841
        }
842
        return true;
843
    }
844
845
    /**
846
     * check if the unix stamp
847
     *
848
     * @param  int $timestamp
849
     * @return bool
850
     */
851
    public function isTimestamp($timestamp) 
852
    {
853
        if(strtotime(date('Y-m-d H:i:s', $timestamp)) === $timestamp) 
854
        {
855
            return $timestamp;
856
        } 
857
        else 
858
        {
859
            return false;
860
        }
861
    }
862
863
    /**
864
     * get valid keys by schema
865
     *
866
     * @param  array $schema
867
     * @return array
868
     */
869
    public function getValidKeysBySchema($schema=[])
870
    {
871
        $valid_keys = array_merge(array_column($schema, 'key'), [ 'type', 'assignee', 'labels', 'parent_id', 'resolution', 'priority', 'progress', 'expect_start_time', 'expect_compete_time' ]);
872
873
        foreach ($schema as $field)
874
        {
875
            if ($field['type'] == 'MultiUser')
876
            {
877
                $valid_keys[] = $field['key'] . '_ids';
878
            }
879
            else if ($field['type'] == 'TimeTracking')
880
            {
881
                $valid_keys[] = $field['key'] . '_m';
882
            }
883
        }
884
885
        return $valid_keys;
886
    }
887
888
    /**
889
     * Update the specified resource in storage.
890
     *
891
     * @param  \Illuminate\Http\Request  $request
892
     * @param  string  $project_key
893
     * @param  string  $id
894
     * @return \Illuminate\Http\Response
895
     */
896
    public function update(Request $request, $project_key, $id)
897
    {
898 View Code Duplication
        if (!$this->isPermissionAllowed($project_key, 'edit_issue') && !$this->isPermissionAllowed($project_key, 'exec_workflow'))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
899
        {
900
            return Response()->json(['ecode' => -10002, 'emsg' => 'permission denied.']);
0 ignored issues
show
Bug introduced by
The method json does only exist in Illuminate\Contracts\Routing\ResponseFactory, but not in Illuminate\Http\Response.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
901
        }
902
903
        if (!$request->all())
904
        {
905
            return $this->show($project_key, $id); 
906
        }
907
908
        $table = 'issue_' . $project_key;
909
        $issue = DB::collection($table)->find($id);
910
        if (!$issue)
911
        {
912
            throw new \UnexpectedValueException('the issue does not exist or is not in the project.', -11103);
913
        }
914
915
        $schema = Provider::getSchemaByType($request->input('type') ?: $issue['type']);
916
        if (!$schema)
0 ignored issues
show
Bug Best Practice introduced by
The expression $schema of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
917
        {
918
            throw new \UnexpectedValueException('the schema of the type is not existed.', -11101);
919
        }
920
921
        if (!$this->requiredCheck($schema, $request->all(), 'update'))
922
        {
923
            throw new \UnexpectedValueException('the required field is empty.', -11121);
924
        }
925
926
        // handle timetracking
927
        $updValues = [];
928 View Code Duplication
        foreach ($schema as $field)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
929
        {
930
            $fieldValue = $request->input($field['key']);
931
            if (!isset($fieldValue) || !$fieldValue)
932
            {
933
                continue;
934
            }
935
936
            if ($field['type'] == 'TimeTracking')
937
            {
938
                if (!$this->ttCheck($fieldValue))
939
                {
940
                    throw new \UnexpectedValueException('the format of timetracking field is incorrect.', -11102);
941
                }
942
943
                $updValues[$field['key']] = $this->ttHandle($fieldValue);
944
                $updValues[$field['key'] . '_m'] = $this->ttHandleInM($updValues[$field['key']]);
945
            }
946
            else if ($field['type'] == 'DatePicker' || $field['type'] == 'DateTimePicker')
947
            {
948
                if ($this->isTimestamp($fieldValue) === false) 
949
                {
950
                    throw new \UnexpectedValueException('the format of datepicker field is incorrect.', -11122);
951
                }
952
            }
953
            else if ($field['type'] == 'SingleUser')
954
            {
955
                $user_info = Sentinel::findById($fieldValue);
956
                if ($user_info)
957
                {
958
                    $updValues[$field['key']] = [ 'id' => $fieldValue, 'name' => $user_info->first_name, 'email' => $user_info->email ];
959
                }
960
            }
961
            else if ($field['type'] == 'MultiUser')
962
            {
963
                $user_ids = $fieldValue;
964
                $updValues[$field['key']] = [];
965
                $new_user_ids = [];
966
                foreach ($user_ids as $uid)
967
                {
968
                    $user_info = Sentinel::findById($uid);
969
                    if ($user_info)
970
                    {
971
                        array_push($updValues[$field['key']], [ 'id' => $uid, 'name' => $user_info->first_name, 'email' => $user_info->email ]);
972
                    }
973
                    $new_user_ids[] = $uid;
974
                }
975
                $updValues[$field['key'] . '_ids'] = $new_user_ids;
976
            }
977
        }
978
979
        $assignee_id = $request->input('assignee');
980
        if ($assignee_id)
981
        {
982
            if ((!isset($issue['assignee']) || (isset($issue['assignee']) && $assignee_id != $issue['assignee']['id'])) && !$this->isPermissionAllowed($project_key, 'assigned_issue', $assignee_id))
983
            {
984
                return Response()->json(['ecode' => -11118, 'emsg' => 'the assigned user has not assigned-issue permission.']);
985
            }
986
987
            $user_info = Sentinel::findById($assignee_id);
988
            if ($user_info)
989
            {
990
                $assignee = [ 'id' => $assignee_id, 'name' => $user_info->first_name, 'email' => $user_info->email ];
991
                $updValues['assignee'] = $assignee;
992
            }
993
        }
994
995
        $valid_keys = $this->getValidKeysBySchema($schema);
996
        $updValues = $updValues + array_only($request->all(), $valid_keys);
997
        if (!$updValues)
0 ignored issues
show
Bug Best Practice introduced by
The expression $updValues of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
998
        {
999
            return $this->show($project_key, $id);
1000
        }
1001
1002
        $updValues['modifier'] = [ 'id' => $this->user->id, 'name' => $this->user->first_name, 'email' => $this->user->email ];
1003
        $updValues['updated_at'] = time();
1004
1005
        DB::collection($table)->where('_id', $id)->update($updValues);
1006
1007
        // add to histroy table
1008
        $snap_id = Provider::snap2His($project_key, $id, $schema, array_keys(array_only($request->all(), $valid_keys)));
1009
        // trigger event of issue edited
1010
        Event::fire(new IssueEvent($project_key, $id, $updValues['modifier'], [ 'event_key' => 'edit_issue', 'snap_id' => $snap_id ]));
1011
        // create the Labels for project
1012
        if (isset($updValues['labels']) && $updValues['labels'])
1013
        {
1014
            $this->createLabels($project_key, $updValues['labels']);
1015
        }
1016
1017
        return $this->show($project_key, $id); 
1018
    }
1019
1020
    /**
1021
     * Remove the specified resource from storage.
1022
     *
1023
     * @param  string  $project_key
1024
     * @param  string  $id
1025
     * @return \Illuminate\Http\Response
1026
     */
1027
    public function destroy($project_key, $id)
1028
    {
1029
        $table = 'issue_' . $project_key;
1030
        $issue = DB::collection($table)
1031
            ->where('_id', $id)
1032
            ->where('del_flg', '<>', 1)
1033
            ->first();
1034
        if (!$issue)
1035
        {
1036
            throw new \UnexpectedValueException('the issue does not exist or is not in the project.', -11103);
1037
        }
1038
1039
        $user = [ 'id' => $this->user->id, 'name' => $this->user->first_name, 'email' => $this->user->email ];
1040
        
1041
        $ids = [ $id ];
1042
        // delete all subtasks of this issue
1043
        $subtasks = DB::collection('issue_' . $project_key)
1044
            ->where('parent_id', $id)
1045
            ->where('del_flg', '<>', 1)
1046
            ->get();
1047 View Code Duplication
        foreach ($subtasks as $subtask)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1048
        {
1049
            $sub_id = $subtask['_id']->__toString();
1050
            DB::collection($table)->where('_id', $sub_id)->update([ 'del_flg' => 1 ]);
1051
1052
            // delete linked relation
1053
            DB::collection('linked')->where('src', $sub_id)->orWhere('dest', $sub_id)->delete();
1054
1055
            Event::fire(new IssueEvent($project_key, $sub_id, $user, [ 'event_key' => 'del_issue' ]));
1056
            $ids[] = $sub_id;
1057
        }
1058
1059
        // delete linked relation
1060
        DB::collection('linked')->where('src', $id)->orWhere('dest', $id)->delete();
1061
        // delete this issue
1062
        DB::collection($table)->where('_id', $id)->update([ 'del_flg' => 1 ]);
1063
        // trigger event of issue deleted 
1064
        Event::fire(new IssueEvent($project_key, $id, $user, [ 'event_key' => 'del_issue' ]));
1065
1066
        return Response()->json(['ecode' => 0, 'data' => [ 'ids' => $ids ]]);
0 ignored issues
show
Bug introduced by
The method json does only exist in Illuminate\Contracts\Routing\ResponseFactory, but not in Illuminate\Http\Response.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
1067
    }
1068
1069
    /**
1070
     * get the project filters.
1071
     *
1072
     * @param  string $project_key
1073
     * @return array
1074
     */
1075
    public function getIssueFilters($project_key)
1076
    {
1077
        return Response()->json([ 'ecode' => 0, 'data' => Provider::getIssueFilters($project_key, $this->user->id) ]);
0 ignored issues
show
Bug introduced by
The method json does only exist in Illuminate\Contracts\Routing\ResponseFactory, but not in Illuminate\Http\Response.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
1078
    }
1079
1080
    /**
1081
     * save the custimized filter.
1082
     *
1083
     * @param  string $project_key
1084
     * @return \Illuminate\Http\Response
1085
     */
1086
    public function saveIssueFilter(Request $request, $project_key)
1087
    {
1088
        $name = $request->input('name');
1089
        if (!$name)
1090
        {
1091
            throw new \UnexpectedValueException('the name can not be empty.', -11105);
1092
        }
1093
1094
        if (UserIssueFilters::whereRaw([ 'name' => $name, 'user' => $this->user->id, 'project_key' => $project_key ])->exists())
1095
        {
1096
            throw new \UnexpectedValueException('filter name cannot be repeated', -11106);
1097
        }
1098
1099
        $query = $request->input('query') ?: [];
1100
        
1101
        $res = UserIssueFilters::where('project_key', $project_key)
1102
            ->where('user', $this->user->id)
1103
            ->first();
1104
        if ($res)
1105
        {
1106
            $filters = isset($res['filters']) ? $res['filters'] : [];
1107 View Code Duplication
            foreach($filters as $filter)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1108
            {
1109
                if (isset($filter['name']) && $filter['name'] === $name)
1110
                {
1111
                    throw new \UnexpectedValueException('filter name cannot be repeated', -11106);
1112
                }
1113
            }
1114
            array_push($filters, [ 'id' => md5(microtime()), 'name' => $name, 'query' => $query ]);
1115
            $res->filters = $filters;
1116
            $res->save();
1117
        }
1118
        else
1119
        {
1120
            $filters = Provider::getDefaultIssueFilters();
1121 View Code Duplication
            foreach($filters as $filter)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1122
            {
1123
                if (isset($filter['name']) && $filter['name'] === $name)
1124
                {
1125
                    throw new \UnexpectedValueException('filter name cannot be repeated', -11106);
1126
                }
1127
            }
1128
            array_push($filters, [ 'id' => md5(microtime()), 'name' => $name, 'query' => $query ]);
1129
            UserIssueFilters::create([ 'project_key' => $project_key, 'user' => $this->user->id, 'filters' => $filters ]); 
1130
        }
1131
        return $this->getIssueFilters($project_key);
1132
    }
1133
1134
    /**
1135
     * reset the issue filters.
1136
     *
1137
     * @param  string $project_key
1138
     * @return \Illuminate\Http\Response
1139
     */
1140
    public function resetIssueFilters(Request $request, $project_key)
1141
    {
1142
        UserIssueFilters::where('project_key', $project_key)
1143
            ->where('user', $this->user->id)
1144
            ->delete();
1145
1146
        return $this->getIssueFilters($project_key);
1147
    }
1148
1149
    /**
1150
     * get the default columns.
1151
     *
1152
     * @param  string $project_key
1153
     * @return array
1154
     */
1155
    public function getDisplayColumns($project_key)
1156
    {
1157
        return Response()->json([ 'ecode' => 0, 'data' => Provider::getIssueDisplayColumns($project_key, $this->user->id) ]);
0 ignored issues
show
Bug introduced by
The method json does only exist in Illuminate\Contracts\Routing\ResponseFactory, but not in Illuminate\Http\Response.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
1158
    }
1159
1160
    /**
1161
     * reset the issue list diplay columns.
1162
     *
1163
     * @param  string $project_key
1164
     * @return \Illuminate\Http\Response
1165
     */
1166
    public function resetDisplayColumns(Request $request, $project_key)
1167
    {
1168
        UserIssueListColumns::where('project_key', $project_key)
1169
            ->where('user', $this->user->id)
1170
            ->delete();
1171
1172
        $delete_from_project = $request->input('delete_from_project') ?: false;
1173
        if ($delete_from_project && $this->isPermissionAllowed($project_key, 'manage_project'))
1174
        {
1175
            ProjectIssueListColumns::where('project_key', $project_key)->delete();
1176
        }
1177
 
1178
        return $this->getDisplayColumns($project_key);
1179
    }
1180
1181
    /**
1182
     * set the issue list display columns.
1183
     *
1184
     * @param  string $project_key
1185
     * @return \Illuminate\Http\Response
1186
     */
1187
    public function setDisplayColumns(Request $request, $project_key)
1188
    {
1189
        $column_keys = [];
1190
        $new_columns = [];
1191
        $columns = $request->input('columns') ?: [];
1192
        foreach ($columns as $column)
1193
        {
1194
            if (!isset($column['key']))
1195
            {
1196
                continue;
1197
            }
1198
1199
            if (in_array($column['key'], $column_keys))
1200
            {
1201
                continue;
1202
            }
1203
            $column_keys[] = $column['key'];
1204
            $new_columns[] = array_only($column, [ 'key', 'width' ]);
1205
        }
1206
1207
        $res = UserIssueListColumns::where('project_key', $project_key)
1208
            ->where('user', $this->user->id)
1209
            ->first();
1210 View Code Duplication
        if ($res)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1211
        {
1212
            $res->columns = $new_columns;
1213
            $res->column_keys = $column_keys;
1214
            $res->save();
1215
        }
1216
        else
1217
        {
1218
            UserIssueListColumns::create([ 'project_key' => $project_key, 'user' => $this->user->id, 'column_keys' => $column_keys, 'columns' => $new_columns ]); 
1219
        }
1220
1221
        $save_for_project = $request->input('save_for_project') ?: false;
1222
        if ($save_for_project && $this->isPermissionAllowed($project_key, 'manage_project'))
1223
        {
1224
            $res = ProjectIssueListColumns::where('project_key', $project_key)->first();
1225 View Code Duplication
            if ($res)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1226
            {
1227
                $res->columns = $new_columns;
1228
                $res->column_keys = $column_keys;
1229
                $res->save();
1230
            }
1231
            else
1232
            {
1233
                ProjectIssueListColumns::create([ 'project_key' => $project_key, 'column_keys' => $column_keys, 'columns' => $new_columns ]);
1234
            }
1235
        }
1236
1237
        return $this->getDisplayColumns($project_key);
1238
    }
1239
1240
    /**
1241
     * edit the mode filters.
1242
     *
1243
     * @param  string $project_key
1244
     * @return \Illuminate\Http\Response
1245
     */
1246
    public function editFilters(Request $request, $project_key)
1247
    {
1248
        $sequence = $request->input('sequence');
1249
        if (isset($sequence))
1250
        {
1251
            $old_filters = Provider::getDefaultIssueFilters();
1252
            $res = UserIssueFilters::where('project_key', $project_key)
1253
                ->where('user', $this->user->id)
1254
                ->first();
1255
            if ($res)
1256
            {
1257
                $old_filters = isset($res->filters) ? $res->filters : [];
1258
            }
1259
            
1260
            $new_filters = [];
1261 View Code Duplication
            foreach ($sequence as $id)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1262
            {
1263
                foreach ($old_filters as $filter)
1264
                {
1265
                    if ($filter['id'] === $id)
1266
                    {
1267
                        $new_filters[] = $filter;
1268
                        break;
1269
                    }
1270
                }
1271
            }
1272 View Code Duplication
            if ($res)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1273
            {
1274
                $res->filters = $new_filters;
1275
                $res->save();
1276
            }
1277
            else
1278
            {
1279
                UserIssueFilters::create([ 'project_key' => $project_key, 'user' => $this->user->id, 'filters' => $new_filters ]); 
1280
            }
1281
        }
1282
        return $this->getIssueFilters($project_key);
1283
    }
1284
1285
    /**
1286
     * reset the issue list columns.
1287
     *
1288
     * @param  string $project_key
1289
     * @return \Illuminate\Http\Response
1290
     */
1291
    public function resetColumns(Request $request, $project_key)
1292
    {
1293
        UserIssueListColumns::where('project_key', $project_key)
1294
            ->where('user', $this->user->id)
1295
            ->delete();
1296
1297
        return $this->getColumns($project_key);
1298
    }
1299
1300
    /**
1301
     * get the history records.
1302
     *
1303
     * @param  string  $project_key
1304
     * @param  string  $id
1305
     * @return \Illuminate\Http\Response
1306
     */
1307
    public function getHistory(Request $request, $project_key, $id)
1308
    {
1309
        $changedRecords = [];
1310
        $records = DB::collection('issue_his_' . $project_key)->where('issue_id', $id)->orderBy('_id', 'asc')->get();
1311
        foreach ($records as $i => $item)
1312
        {
1313
            if ($i == 0)
1314
            {
1315
                $changedRecords[] = [ 'operation' => 'create', 'operator' => $item['operator'], 'operated_at' => $item['operated_at'] ];
1316
            }
1317
            else
1318
            {
1319
                $changed_items = [];
1320
                $changed_items['operation'] = 'modify';
1321
                $changed_items['operated_at'] = $item['operated_at'];
1322
                $changed_items['operator'] = $item['operator'];
1323
1324
                $diff_items = []; $diff_keys = [];
1325
                $after_data = $item['data'];
1326
                $before_data = $records[$i - 1]['data'];
1327
1328
                foreach ($after_data as $key => $val)
1329
                {
1330 View Code Duplication
                    if (!isset($before_data[$key]) || $val !== $before_data[$key])
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1331
                    {
1332
                        $tmp = [];
1333
                        $tmp['field'] = isset($val['name']) ? $val['name'] : '';
1334
                        $tmp['after_value'] = isset($val['value']) ? $val['value'] : '';
1335
                        $tmp['before_value'] = isset($before_data[$key]) && isset($before_data[$key]['value']) ? $before_data[$key]['value'] : '';
1336
1337
                        if (is_array($tmp['after_value']) && is_array($tmp['before_value']))
1338
                        {
1339
                            $diff1 = array_diff($tmp['after_value'], $tmp['before_value']);
1340
                            $diff2 = array_diff($tmp['before_value'], $tmp['after_value']);
1341
                            $tmp['after_value'] = implode(',', $diff1);
1342
                            $tmp['before_value'] = implode(',', $diff2);
1343
                        }
1344
                        else
1345
                        {
1346
                            if (is_array($tmp['after_value']))
1347
                            {
1348
                                $tmp['after_value'] = implode(',', $tmp['after_value']);
1349
                            }
1350
                            if (is_array($tmp['before_value']))
1351
                            {
1352
                                $tmp['before_value'] = implode(',', $tmp['before_value']);
1353
                            }
1354
                        }
1355
                        $diff_items[] = $tmp; 
1356
                        $diff_keys[] = $key; 
1357
                    }
1358
                }
1359
1360
                foreach ($before_data as $key => $val)
1361
                {
1362
                    if (array_search($key, $diff_keys) !== false)
1363
                    {
1364
                        continue;
1365
                    }
1366
1367 View Code Duplication
                    if (!isset($after_data[$key]) || $val !== $after_data[$key])
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1368
                    {
1369
                        $tmp = [];
1370
                        $tmp['field'] = isset($val['name']) ? $val['name'] : '';
1371
                        $tmp['before_value'] = isset($val['value']) ? $val['value'] : '';
1372
                        $tmp['after_value'] = isset($after_data[$key]) && isset($after_data[$key]['value']) ? $after_data[$key]['value'] : '';
1373
                        if (is_array($tmp['after_value']) && is_array($tmp['before_value']))
1374
                        {
1375
                            $diff1 = array_diff($tmp['after_value'], $tmp['before_value']);
1376
                            $diff2 = array_diff($tmp['before_value'], $tmp['after_value']);
1377
                            $tmp['after_value'] = implode(',', $diff1);
1378
                            $tmp['before_value'] = implode(',', $diff2);
1379
                        }
1380
                        else
1381
                        {
1382
                            if (is_array($tmp['after_value']))
1383
                            {
1384
                                $tmp['after_value'] = implode(',', $tmp['after_value']);
1385
                            }
1386
                            if (is_array($tmp['before_value']))
1387
                            {
1388
                                $tmp['before_value'] = implode(',', $tmp['before_value']);
1389
                            }
1390
                        }
1391
1392
                        $diff_items[] = $tmp; 
1393
                    }
1394
                }
1395
1396
                if ($diff_items)
0 ignored issues
show
Bug Best Practice introduced by
The expression $diff_items of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1397
                {
1398
                    $changed_items['data'] = $diff_items;
1399
                    $changedRecords[] = $changed_items;
1400
                }
1401
            }
1402
        }
1403
1404
        $sort = ($request->input('sort') === 'asc') ? 'asc' : 'desc';
1405
        if ($sort === 'desc')
1406
        {
1407
            $changedRecords = array_reverse($changedRecords);
1408
        }
1409
1410
        return Response()->json([ 'ecode' => 0, 'data' => $changedRecords, 'options' => [ 'current_time' => time() ] ]);
0 ignored issues
show
Bug introduced by
The method json does only exist in Illuminate\Contracts\Routing\ResponseFactory, but not in Illuminate\Http\Response.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
1411
    }
1412
1413
    /**
1414
     * workflow action.
1415
     *
1416
     * @param  string  $project_key
1417
     * @param  string  $id
1418
     * @param  string  $action_id
0 ignored issues
show
Bug introduced by
There is no parameter named $action_id. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
1419
     * @return \Illuminate\Http\Response
1420
     */
1421
    public function doAction(Request $request, $project_key, $id, $workflow_id)
1422
    {
1423
        $action_id = $request->input('action_id');
1424
        if (!$action_id)
1425
        {
1426
            throw new Exception('the executed action has error.', -11115);
1427
        }
1428
1429
        try {
1430
            $entry = new Workflow($workflow_id);
1431
            $entry->doAction($action_id, [ 'project_key' => $project_key, 'issue_id' => $id, 'caller' => $this->user->id ] + array_only($request->all(), [ 'comments' ]));
1432
        } catch (Exception $e) {
1433
          throw new Exception('the executed action has error.', -11115);
1434
        }
1435
        return $this->show($project_key, $id); 
1436
    }
1437
1438
    /**
1439
     * workflow action.
1440
     *
1441
     * @param  string  $project_key
1442
     * @param  string  $id
1443
     * @return \Illuminate\Http\Response
1444
     */
1445
    public function watch(Request $request, $project_key, $id)
1446
    {
1447
        Watch::where('issue_id', $id)->where('user.id', $this->user->id)->delete();
1448
1449
        $cur_user = [ 'id' => $this->user->id, 'name' => $this->user->first_name, 'email' => $this->user->email ];
1450
1451
        $flag = $request->input('flag');
1452
        if (isset($flag) && $flag)
1453
        {
1454
            Watch::create([ 'project_key' => $project_key, 'issue_id' => $id, 'user' => $cur_user ]);
1455
            // trigger event of issue watched 
1456
            Event::fire(new IssueEvent($project_key, $id, $cur_user, [ 'event_key' => 'watched_issue' ]));
1457
        }
1458
        else
1459
        {
1460
            $flag = false;
1461
            // trigger event of issue watched 
1462
            Event::fire(new IssueEvent($project_key, $id, $cur_user, [ 'event_key' => 'unwatched_issue' ]));
1463
        }
1464
        
1465
        return Response()->json(['ecode' => 0, 'data' => ['id' => $id, 'user' => $cur_user, 'watching' => $flag]]);
0 ignored issues
show
Bug introduced by
The method json does only exist in Illuminate\Contracts\Routing\ResponseFactory, but not in Illuminate\Http\Response.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
1466
    }
1467
1468
    /**
1469
     * reset issue state.
1470
     *
1471
     * @param  string  $project_key
1472
     * @param  string  $id
1473
     * @return \Illuminate\Http\Response
1474
     */
1475
    public function resetState(Request $request, $project_key, $id)
1476
    {
1477
        $updValues = [];
1478
1479
        $assignee_id = $request->input('assignee');
1480 View Code Duplication
        if (isset($assignee_id))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1481
        {
1482
            if ($assignee_id)
1483
            {
1484
                if (!$this->isPermissionAllowed($project_key, 'assigned_issue', $assignee_id))
1485
                {
1486
                    return Response()->json(['ecode' => -11118, 'emsg' => 'the assigned user has not assigned-issue permission.']);
0 ignored issues
show
Bug introduced by
The method json does only exist in Illuminate\Contracts\Routing\ResponseFactory, but not in Illuminate\Http\Response.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
1487
                }
1488
1489
                $user_info = Sentinel::findById($assignee_id);
1490
                if ($user_info)
1491
                {
1492
                    $assignee = [ 'id' => $assignee_id, 'name' => $user_info->first_name, 'email' => $user_info->email ];
1493
                    $updValues['assignee'] = $assignee;
1494
                }
1495
            }
1496
            else
1497
            {
1498
                throw new \UnexpectedValueException('the issue assignee cannot be empty.', -11104);
1499
            }
1500
        }
1501
1502
        $resolution = $request->input('resolution');
1503
        if (isset($resolution) && $resolution)
1504
        {
1505
            $updValues['resolution'] = $resolution;
1506
        }
1507
1508
        $issue = DB::collection('issue_' . $project_key)->where('_id', $id)->first();
1509
        if (!$issue)
1510
        {
1511
            throw new \UnexpectedValueException('the issue does not exist or is not in the project.', -11103);
1512
        }
1513
1514
        // workflow initialize
1515
        $workflow = $this->initializeWorkflow($issue['type']);
1516
        $updValues = $updValues + $workflow;
1517
1518
        $updValues['modifier'] = [ 'id' => $this->user->id, 'name' => $this->user->first_name, 'email' => $this->user->email ];
1519
        $updValues['updated_at'] = time();
1520
1521
        $table = 'issue_' . $project_key;
1522
        DB::collection($table)->where('_id', $id)->update($updValues);
1523
1524
        // add to histroy table
1525
        $snap_id = Provider::snap2His($project_key, $id, null, array_keys($updValues));
0 ignored issues
show
Documentation introduced by
null is of type null, but the function expects a array.

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...
1526
        // trigger event of issue edited
1527
        Event::fire(new IssueEvent($project_key, $id, $updValues['modifier'], [ 'event_key' => 'reset_issue', 'snap_id' => $snap_id ]));
1528
1529
        return $this->show($project_key, $id);
1530
    }
1531
1532
    /**
1533
     * copy issue.
1534
     *
1535
     * @param  string  $project_key
1536
     * @param  string  $id
0 ignored issues
show
Bug introduced by
There is no parameter named $id. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
1537
     * @return \Illuminate\Http\Response
1538
     */
1539
    public function copy(Request $request, $project_key)
1540
    {
1541
        $title = $request->input('title');
1542
        if (!$title)
1543
        {
1544
            throw new \UnexpectedValueException('the issue title cannot be empty.', -11108);
1545
        }
1546
1547
        $src_id = $request->input('source_id');
1548
        if (!isset($src_id) || !$src_id)
1549
        {
1550
            throw new \UnexpectedValueException('the copied issue id cannot be empty.', -11109);
1551
        }
1552
1553
        $src_issue = DB::collection('issue_' . $project_key)->where('_id', $src_id)->first();
1554
        if (!$src_issue )
1555
        {
1556
            throw new \UnexpectedValueException('the copied issue does not exist or is not in the project.', -11103);
1557
        }
1558
1559
        $schema = Provider::getSchemaByType($src_issue['type']);
1560
        if (!$schema)
0 ignored issues
show
Bug Best Practice introduced by
The expression $schema of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1561
        {
1562
            throw new \UnexpectedValueException('the schema of the type is not existed.', -11101);
1563
        }
1564
1565
        $valid_keys = $this->getValidKeysBySchema($schema);
1566
        $insValues = array_only($src_issue, $valid_keys);
1567
1568
        $insValues['title'] = $title;
1569
        // get reporter(creator)
1570
        $insValues['reporter'] = [ 'id' => $this->user->id, 'name' => $this->user->first_name, 'email' => $this->user->email ];
1571
1572
        $assignee_id = $request->input('assignee');
1573 View Code Duplication
        if (isset($assignee_id))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1574
        {
1575
            if ($assignee_id)
1576
            {
1577
                if (!$this->isPermissionAllowed($project_key, 'assigned_issue', $assignee_id))
1578
                {
1579
                    return Response()->json(['ecode' => -11118, 'emsg' => 'the assigned user has not assigned-issue permission.']);
0 ignored issues
show
Bug introduced by
The method json does only exist in Illuminate\Contracts\Routing\ResponseFactory, but not in Illuminate\Http\Response.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
1580
                }
1581
1582
                $user_info = Sentinel::findById($assignee_id);
1583
                if ($user_info)
1584
                {
1585
                    $assignee = [ 'id' => $assignee_id, 'name' => $user_info->first_name, 'email' => $user_info->email ];
1586
                    $insValues['assignee'] = $assignee;
1587
                }
1588
            }
1589
            else
1590
            {
1591
                throw new \UnexpectedValueException('the issue assignee cannot be empty.', -11104);
1592
            }
1593
        }
1594
1595
        $resolution = $request->input('resolution');
1596
        if (isset($resolution) && $resolution)
1597
        {
1598
            $insValues['resolution'] = $resolution;
1599
        }
1600
1601
        $table = 'issue_' . $project_key;
1602
        $max_no = DB::collection($table)->count() + 1;
1603
        $insValues['no'] = $max_no;
1604
1605
        // workflow initialize
1606
        $workflow = $this->initializeWorkflow($src_issue['type']);
1607
        $insValues = array_merge($insValues, $workflow);
1608
        // created time
1609
        $insValues['created_at'] = time();
1610
1611
        $id = DB::collection($table)->insertGetId($insValues);
1612
1613
        $issue = DB::collection($table)->where('_id', $id)->first();
0 ignored issues
show
Unused Code introduced by
$issue is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1614
        // add to histroy table
1615
        Provider::snap2His($project_key, $id, $schema);
1616
        // create link of clone
1617
        Linked::create([ 'src' => $src_id, 'relation' => 'is cloned by', 'dest' => $id->__toString(), 'creator' => $insValues['reporter'] ]);
1618
        // trigger event of issue created 
1619
        Event::fire(new IssueEvent($project_key, $id->__toString(), $insValues['reporter'], [ 'event_key' => 'create_issue' ]));
1620
        // trigger event of link created 
1621
        Event::fire(new IssueEvent($project_key, $src_id, $insValues['reporter'], [ 'event_key' => 'create_link', 'data' => [ 'relation' => 'is cloned by', 'dest' => $id->__toString() ] ]));
1622
1623
        return $this->show($project_key, $id->__toString());
1624
    }
1625
1626
    /**
1627
     * covert issue from subtask to standard or from standard to subtask.
1628
     *
1629
     * @param  \Illuminate\Http\Request  $request
1630
     * @param  string  $project_key
1631
     * @param  string  $id
1632
     * @return \Illuminate\Http\Response
1633
     */
1634
    public function convert(Request $request, $project_key, $id)
1635
    {
1636
        $table = 'issue_' . $project_key;
1637
        $issue = DB::collection($table)->find($id);
1638
        if (!$issue)
1639
        {
1640
            throw new \UnexpectedValueException('the issue does not exist or is not in the project.', -11103);
1641
        }
1642
1643
        $type = $request->input('type');
1644
        if (!isset($type) || !$type)
1645
        {
1646
            throw new \UnexpectedValueException('the issue type cannot be empty.', -11100);
1647
        }
1648
1649
        $parent_id = $request->input('parent_id');
1650
        if (!isset($parent_id))
1651
        {
1652
            $parent_id = '';
1653
        }
1654
 
1655
        $updValues = [];
1656
        if ($parent_id)
1657
        {
1658
            // standard convert to subtask 
1659
            $hasSubtasks = DB::collection($table)->where('parent_id', $id)->exists();
1660
            if ($hasSubtasks)
1661
            {
1662
                throw new \UnexpectedValueException('the issue can not convert to subtask.', -11114);
1663
            }
1664
1665
            $parent_issue = DB::collection($table)->find($parent_id);
1666
            if (!$parent_issue)
1667
            {
1668
                throw new \UnexpectedValueException('the dest parent issue does not exist or is not in the project.', -11110);
1669
            }
1670
        }
1671
        $updValues['parent_id'] = $parent_id;
1672
        $updValues['type'] = $type;
1673
1674
        $updValues['modifier'] = [ 'id' => $this->user->id, 'name' => $this->user->first_name, 'email' => $this->user->email ];
1675
        $updValues['updated_at'] = time();
1676
        DB::collection($table)->where('_id', $id)->update($updValues);
1677
1678
        // add to histroy table
1679
        $snap_id = Provider::snap2His($project_key, $id, null, [ 'parent_id', 'type' ]);
0 ignored issues
show
Documentation introduced by
null is of type null, but the function expects a array.

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...
1680
        // trigger event of issue moved
1681
        Event::fire(new IssueEvent($project_key, $id, $updValues['modifier'], [ 'event_key' => 'edit_issue', 'snap_id' => $snap_id ] ));
1682
1683
        return $this->show($project_key, $id);
1684
1685
    }
1686
1687
    /**
1688
     * move issue.
1689
     *
1690
     * @param  \Illuminate\Http\Request  $request
1691
     * @param  string  $project_key
1692
     * @param  string  $id
1693
     * @return \Illuminate\Http\Response
1694
     */
1695
    public function move(Request $request, $project_key, $id)
1696
    {
1697
        $table = 'issue_' . $project_key;
1698
        $issue = DB::collection($table)->find($id);
1699
        if (!$issue)
1700
        {
1701
            throw new \UnexpectedValueException('the issue does not exist or is not in the project.', -11103);
1702
        }
1703
1704
        $parent_id = $request->input('parent_id'); 
1705
        if (!isset($parent_id) || !$parent_id)
1706
        {
1707
            throw new \UnexpectedValueException('the dest parent cannot be empty.', -11111);
1708
        }
1709
        $parent_issue = DB::collection($table)->find($parent_id);
1710
        if (!$parent_issue)
1711
        {
1712
            throw new \UnexpectedValueException('the dest parent issue does not exist or is not in the project.', -11110);
1713
        }
1714
1715
        if ($parent_id === $issue['parent_id'])
1716
        {
1717
            return $this->show($project_key, $id);
1718
        }
1719
1720
        $updValues = [];
1721
        $updValues['parent_id'] = $parent_id;
1722
        $updValues['modifier'] = [ 'id' => $this->user->id, 'name' => $this->user->first_name, 'email' => $this->user->email ];
1723
        $updValues['updated_at'] = time();
1724
        DB::collection($table)->where('_id', $id)->update($updValues);
1725
1726
        // add to histroy table
1727
        $snap_id = Provider::snap2His($project_key, $id, null, [ 'parent_id' ]);
0 ignored issues
show
Documentation introduced by
null is of type null, but the function expects a array.

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...
Unused Code introduced by
$snap_id is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1728
        // trigger event of issue moved
1729
        Event::fire(new IssueEvent($project_key, $id, $updValues['modifier'], [ 'event_key' => 'move_issue', 'data' => [ 'old_parent' => $issue['parent_id'], 'new_parent' => $parent_id ] ]));
1730
1731
        return $this->show($project_key, $id);
1732
    }
1733
1734
    /**
1735
     * release issue.
1736
     *
1737
     * @param  \Illuminate\Http\Request  $request
1738
     * @param  string  $project_key
1739
     * @return \Illuminate\Http\Response
1740
     */
1741
    public function release(Request $request, $project_key) 
1742
    {
1743
        $ids = $request->input('ids'); 
1744
        if (!$ids)
1745
        {
1746
            throw new \UnexpectedValueException('the released issues cannot be empty.', -11132);
1747
        }
1748
1749
        $name = $request->input('name');
1750
        if (!$name)
1751
        {
1752
            throw new \UnexpectedValueException('the released version cannot be empty.', -11131);
1753
        }
1754
1755
        $isExisted = Version::where('project_key', $project_key)
1756
            ->where('name', $name)
1757
            ->exists();
1758
        if ($isExisted)
1759
        {
1760
            throw new \UnexpectedValueException('the released version has been existed.', -11133);
1761
        }
1762
1763
        $user = [ 'id' => $this->user->id, 'name' => $this->user->first_name, 'email' => $this->user->email ];
1764
        $version = Version::create([ 'project_key' => $project_key, 'user' => $user, 'status' => 'released', 'released_time' => time() ] + $request->all());
1765
1766
        foreach ($ids as $id)
1767
        {
1768
            DB::collection('issue_' . $project_key)->where('_id', $id)->update([ 'resolve_version' => $version->id ]);
1769
            // add to histroy table
1770
            $snap_id = Provider::snap2His($project_key, $id, null, [ 'resolve_version' ]);
0 ignored issues
show
Documentation introduced by
null is of type null, but the function expects a array.

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...
1771
            // trigger event of issue moved
1772
            Event::fire(new IssueEvent($project_key, $id, $user, [ 'event_key' => 'edit_issue', 'snap_id' => $snap_id ] ));
1773
        }
1774
1775
        $isSendMsg = $request->input('isSendMsg') && true;
1776
        Event::fire(new VersionEvent($project_key, $user, [ 'event_key' => 'create_release_version', 'isSendMsg' => $isSendMsg, 'data' => [ 'released_issues' => $ids, 'release_version' => $version->toArray() ] ]));
1777
1778
        return Response()->json([ 'ecode' => 0, 'data' => [ 'ids' => $ids ] ]);
0 ignored issues
show
Bug introduced by
The method json does only exist in Illuminate\Contracts\Routing\ResponseFactory, but not in Illuminate\Http\Response.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
1779
    }
1780
1781
    /**
1782
     * get timetrack setting.
1783
     *
1784
     * @return array 
1785
     */
1786
    function getTimeTrackSetting() 
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1787
    {
1788
        $options = [ 'w2d' => 5, 'd2h' => 8 ];
1789
1790
        $setting = SysSetting::first();
1791 View Code Duplication
        if ($setting && isset($setting->properties))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1792
        {
1793
            if (isset($setting->properties['week2day']))
1794
            {
1795
                $options['w2d'] = $setting->properties['week2day'];
1796
            }
1797
            if (isset($setting->properties['day2hour']))
1798
            {
1799
                $options['d2h'] = $setting->properties['day2hour'];
1800
            }
1801
        }
1802
        return $options;
1803
    }
1804
1805
    /**
1806
     * get issue link relations.
1807
     *
1808
     * @return array
1809
     */
1810
    function getLinkRelations()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1811
    {
1812
        $relations = [
1813
          [ 'id' => 'blocks', 'out' => 'blocks', 'in' => 'is blocked by' ],
1814
          [ 'id' => 'clones', 'out' => 'clones', 'in' => 'is cloned by' ],
1815
          [ 'id' => 'duplicates', 'out' => 'duplicates', 'in' => 'is duplicated by' ],
1816
          [ 'id' => 'relates', 'out' => 'relates to', 'in' => 'relates to' ],
1817
        ];
1818
        return $relations;
1819
    }
1820
1821
    /**
1822
     * classify issues by parent_id.
1823
     *
1824
     * @param  array  $issues
1825
     * @return array
1826
     */
1827
    public function classifyIssues($issues)
1828
    {
1829
        if (!$issues) { return []; }
0 ignored issues
show
Bug Best Practice introduced by
The expression $issues of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1830
1831
        $classified_issues  = [];
1832
        foreach ($issues as $issue)
1833
        {
1834
            if (isset($issue['parent']) && $issue['parent'])
1835
            {
1836
                if (isset($classified_issues[$issue['parent']['no']]) && $classified_issues[$issue['parent']['no']])
1837
                {
1838
                    $classified_issues[$issue['parent']['no']][] =  $issue;
1839
                }
1840
                else
1841
                {
1842
                    $classified_issues[$issue['parent']['no']] = [ $issue ];
1843
                }
1844
            }
1845
            else
1846
            {
1847
                if (isset($classified_issues[$issue['no']]) && $classified_issues[$issue['no']])
1848
                {
1849
                    array_unshift($classified_issues[$issue['no']], $issue);
1850
                }
1851
                else
1852
                {
1853
                    $classified_issues[$issue['no']] = [ $issue ];
1854
                }
1855
            }
1856
        }
1857
1858
        return $classified_issues;
1859
    }
1860
1861
    /**
1862
     * add avatar for issues
1863
     *
1864
     * @param  array  $issues
1865
     * @return array
1866
     */
1867
    public function addAvatar(&$issues)
1868
    {
1869
        $cache_avatars = [];
1870
        foreach ($issues as $key => $issue)
1871
        {
1872
            if (!isset($issue['assignee']) || !isset($issue['assignee']['id']))
1873
            {
1874
                continue;
1875
            }
1876
            //get assignee avatar for kanban
1877 View Code Duplication
            if (!array_key_exists($issue['assignee']['id'], $cache_avatars))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1878
            {
1879
                $user = Sentinel::findById($issue['assignee']['id']);
1880
                $cache_avatars[$issue['assignee']['id']] = isset($user->avatar) ? $user->avatar : '';
1881
            }
1882
            $issues[$key]['assignee']['avatar'] = $cache_avatars[$issue['assignee']['id']];
1883
        }
1884
        return;
1885
    }
1886
1887
    /**
1888
     * flat issues from 2d to 1d.
1889
     *
1890
     * @param  array  $classifiedissues
0 ignored issues
show
Documentation introduced by
There is no parameter named $classifiedissues. Did you maybe mean $classified_issues?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
1891
     * @return array
1892
     */
1893
    public function flatIssues($classified_issues)
1894
    {
1895
        $issues = [];
1896
        foreach ($classified_issues as $some)
1897
        {
1898
            foreach ($some as $one)
1899
            {
1900
                $issues[] = $one;
1901
            }
1902
        }
1903
        return $issues;
1904
    }
1905
1906
    /**
1907
     * arrange issues for kanban.
1908
     *
1909
     * @param  string $project_key
1910
     * @param  array  $issues
1911
     * @param  string $from
1912
     * @param  string $from_board_id
1913
     * @param  bool   $isUpdRank
1914
     * @return array 
1915
     */
1916
    public function arrangeIssues($project_key, $issues, $from, $from_board_id, $isUpdRank=false)
1917
    {
1918
        if ($from === 'his_sprint')
1919
        {
1920
            $this->addAvatar($issues);
1921
            return $issues;
1922
        }
1923
1924
        // classify the issues
1925
        $classified_issues = $this->classifyIssues($issues);
1926
1927
        // whether the board is ranked
1928
        $rankmap = BoardRankMap::where([ 'board_id' => $from_board_id ])->first();
1929
        if (!$rankmap)
1930
        {
1931
            $issues = $this->flatIssues($classified_issues);
1932
1933
            $rank = [];
1934
            foreach ($issues as $issue)
1935
            {
1936
                $rank[] = $issue['no'];
1937
            }
1938
            
1939
            BoardRankMap::create([ 'board_id' => $from_board_id, 'rank' => $rank ]);
1940
1941
            if ($from === 'active_sprint')
1942
            {
1943
                $issues = $this->sprintFilter($project_key, $issues);
1944
            }
1945
1946
            $this->addAvatar($issues);
1947
            return $issues;
1948
        }
1949
 
1950
        $sub2parent_map = []; 
1951
        foreach ($issues as $issue)
1952
        {
1953
            if (isset($issue['parent']) && $issue['parent'])
1954
            {
1955
                $sub2parent_map[$issue['no']] = $issue['parent']['no'];
1956
            }
1957
        }
1958
1959
        $rank = $rankmap->rank; 
1960
        foreach ($classified_issues as $no => $some)
1961
        {
1962
            if (count($some) <= 1) { continue; }
1963
1964
            $group_issues = [];
1965
            foreach ($some as $one)
1966
            {
1967
                $group_issues[$one['no']] = $one;
1968
            }
1969
1970
            $sorted_group_issues = [];
1971
            foreach ($rank as $val)
1972
            {
1973
                if (isset($group_issues[$val]))
1974
                {
1975
                    $sorted_group_issues[$val] = $group_issues[$val];
1976
                }
1977
            }
1978
1979
            foreach ($group_issues as $no2 => $issue)
1980
            {
1981
                if (!isset($sorted_group_issues[$no2]))
1982
                {
1983
                    $sorted_group_issues[$no2] = $issue;
1984
                }
1985
            }
1986
            $classified_issues[$no] = array_values($sorted_group_issues);
1987
1988
            // prevent the sort confusion 
1989
            $parentInd = 0;
1990
            foreach ($classified_issues[$no] as $sk => $si)
1991
            {
1992
                if ($si['no'] === $no)
1993
                {
1994
                    $parentInd = $sk;
1995
                    break;
1996
                }
1997
            }
1998
            if ($parentInd > 0)
1999
            {
2000
                $pi = array_splice($classified_issues[$no], $parentInd, 1);
2001
                array_unshift($classified_issues[$no], array_pop($pi));
2002
            }
2003
        }
2004
2005
        $sorted_issues = [];
2006
        foreach ($rank as $val)
2007
        {
2008
            if (isset($classified_issues[$val]) && $classified_issues[$val])
2009
            {
2010
                $sorted_issues[$val] = $classified_issues[$val]; 
2011
            }
2012
            else
2013
            {
2014
                if (isset($sub2parent_map[$val]) && $sub2parent_map[$val])
2015
                {
2016
                    $parent = $sub2parent_map[$val];
2017
                    if (!isset($sorted_issues[$parent]))
2018
                    {
2019
                        $sorted_issues[$parent] = $classified_issues[$parent]; 
2020
                    }
2021
                }
2022
            }
2023
        }
2024
2025
        // append some issues which is ranked
2026
        foreach ($classified_issues as $key => $val)
2027
        {
2028
            if (!isset($sorted_issues[$key]))
2029
            {
2030
                $sorted_issues[$key] = $val;
2031
            }
2032
        }
2033
2034
        // convert array to ordered array
2035
        $issues = $this->flatIssues($sorted_issues); 
2036
2037
        if ($isUpdRank)
2038
        {
2039
            $new_rank = [];
2040
            foreach ($issues as $issue)
2041
            {
2042
                $new_rank[] = $issue['no'];
2043
            }
2044
2045
            if (array_diff_assoc($new_rank, $rank) || array_diff_assoc($rank, $new_rank))
2046
            {
2047
                $rankmap = BoardRankMap::where('board_id', $from_board_id)->first();
2048
                $rankmap && $rankmap->update([ 'rank' => $new_rank ]);
2049
            }
2050
        }
2051
2052
        if ($from === 'active_sprint')
2053
        {
2054
            $issues = $this->sprintFilter($project_key, $issues);
2055
        }
2056
2057
        $this->addAvatar($issues);
2058
        return $issues;
2059
    }
2060
2061
    /**
2062
     * sprint filter for issues
2063
     *
2064
     * @param  string $project_key
2065
     * @param  array  $issues
2066
     * @return array
2067
     */
2068
    public function sprintFilter($project_key, $issues)
2069
    {
2070
        $active_sprint_issues = [];
2071
        $active_sprint_issue_nos = [];
2072
        $active_sprint = Sprint::where('project_key', $project_key)->where('status', 'active')->first();
2073
        if ($active_sprint && isset($active_sprint->issues) && $active_sprint->issues)
2074
        {
2075
            $active_sprint_issue_nos = $active_sprint->issues;
2076
        }
2077
2078
        foreach($issues as $issue)
2079
        {
2080
            if (in_array($issue['no'], $active_sprint_issue_nos))
2081
            {
2082
                $active_sprint_issues[] = $issue;
2083
           }
2084
        }
2085
2086
        return $active_sprint_issues;
2087
    }
2088
2089
    /**
2090
     * get some options for export 
2091
     *
2092
     * @param  string $project_key
2093
     * @return array
2094
     */
2095
    public function getOptionsForExport($project_key)
2096
    {
2097
        $types = [];
2098
        $type_list = Provider::getTypeList($project_key);
2099
        foreach ($type_list as $type)
2100
        {
2101
            $types[$type->id] = $type->name;
2102
        }
2103
2104
        $states = [];
2105
        $state_list =  Provider::getStateOptions($project_key);
2106
        foreach ($state_list as $state)
2107
        {
2108
            $states[$state['_id']] = $state['name'];
2109
        }
2110
2111
        $resolutions = [];
2112
        $resolution_list = Provider::getResolutionOptions($project_key);
2113
        foreach ($resolution_list as $resolution)
2114
        {
2115
            $resolutions[$resolution['_id']] = $resolution['name'];
2116
        }
2117
2118
        $priorities = [];
2119
        $priority_list = Provider::getPriorityOptions($project_key);
2120
        foreach ($priority_list as $priority)
2121
        {
2122
            $priorities[$priority['_id']] = $priority['name'];
2123
        }
2124
2125
        $versions = [];
2126
        $version_list = Provider::getVersionList($project_key);
2127
        foreach($version_list as $version)
2128
        {
2129
            $versions[$version->id] = $version->name;
2130
        }
2131
2132
        $modules = [];
2133
        $module_list = Provider::getModuleList($project_key);
2134
        foreach ($module_list as $module)
2135
        {
2136
            $modules[$module->id] = $module->name;
2137
        }
2138
2139
        $epics = [];
2140
        $epic_list = Provider::getEpicList($project_key);
2141
        foreach ($epic_list as $epic)
2142
        {
2143
            $epics[$epic['_id']] = $epic['name'];
2144
        }
2145
2146
        $sprints = [];
2147
        $sprint_list = Provider::getSprintList($project_key);
2148
        foreach ($sprint_list as $sprint)
2149
        {
2150
            $sprints[$sprint['no']] = $sprint['name'];
2151
        }
2152
2153
        $fields = [];
2154
        $field_list = Provider::getFieldList($project_key);
2155
        foreach ($field_list as $field)
2156
        {
2157
            $tmp = [];
2158
            $tmp['name'] = $field->name;
2159
            $tmp['type'] = $field->type;
2160
            if (isset($field->optionValues))
2161
            {
2162
                $tmp['optionValues'] = $field->optionValues;
2163
            }
2164
            $fields[$field->key] = $tmp;
2165
        }
2166
2167
        $fields['no'] = [ 'name' => 'NO', 'type' => 'Number' ];
2168
        $fields['type'] = [ 'name' => '类型', 'type' => 'Select' ];
2169
        $fields['state'] = [ 'name' => '状态', 'type' => 'Select' ];
2170
        $fields['created_at'] = [ 'name' => '创建时间', 'type' => 'DateTimePicker' ];
2171
        $fields['updated_at'] = [ 'name' => '更新时间', 'type' => 'DateTimePicker' ];
2172
        $fields['resolved_at'] = [ 'name' => '解决时间', 'type' => 'DateTimePicker' ];
2173
        $fields['closed_at'] = [ 'name' => '关闭时间', 'type' => 'DateTimePicker' ];
2174
        $fields['reporter'] = [ 'name' => '报告者', 'type' => '' ];
2175
        $fields['resolver'] = [ 'name' => '解决者', 'type' => '' ];
2176
        $fields['closer'] = [ 'name' => '关闭者', 'type' => '' ];
2177
        $fields['sprints'] = [ 'name' => 'Sprint', 'type' => '' ];
2178
2179
        return [
2180
          'types' => $types,
2181
          'states' => $states,
2182
          'resolutions' => $resolutions,
2183
          'priorities' => $priorities,
2184
          'versions' => $versions,
2185
          'modules' => $modules,
2186
          'epics' => $epics,
2187
          'sprints' => $sprints,
2188
          'fields' => $fields,
2189
        ];
2190
2191
    }
2192
2193
    /**
2194
     * export xls for issue list
2195
     *
2196
     * @param  string $project_key
2197
     * @return void
2198
     */
2199
    public function imports(Request $request, $project_key)
2200
    {
2201
        set_time_limit(0);
2202
2203
        if (!($fid = $request->input('fid')))
2204
        {
2205
            throw new \UnexpectedValueException('导入文件ID不能为空。', -11140);
2206
        }
2207
2208
        $pattern = $request->input('pattern');
2209
        if (!isset($pattern))
2210
        {
2211
            $pattern = '1';
2212
        }
2213
2214
        $file = config('filesystems.disks.local.root', '/tmp') . '/' . substr($fid, 0, 2) . '/' . $fid;
2215
        if (!file_exists($file))
2216
        {
2217
            throw new \UnexpectedValueException('获取导入文件失败。', -11141);
2218
        }
2219
2220
        $err_msgs = [];
2221
        $fatal_err_msgs = [];
2222
        Excel::load($file, function($reader) use($project_key, $pattern, &$err_msgs, &$fatal_err_msgs) {
2223
            $reader = $reader->getSheet(0);
2224
            $data = $reader->toArray();
2225
            if (!$data)
2226
            {
2227
                $fatal_err_msgs = $err_msgs = '文件内容不能为空。';
2228
                return;
2229
            }
2230
2231
            $new_fields = [];
2232
            $fields = Provider::getFieldList($project_key);
2233
            foreach($fields as $field)
2234
            {
2235
                if ($field->type !== 'File')
2236
                { 
2237
                    $new_fields[$field->key] = $field->name;
2238
                }
2239
            }
2240
            $new_fields['type'] = '类型';
2241
            $new_fields['state'] = '状态';
2242
            $new_fields['parent'] = '父级任务';
2243
            $new_fields['reporter'] = '报告者';
2244
            $new_fields['created_at'] = '创建时间';
2245
            $new_fields['updated_at'] = '更新时间';
2246
            $new_fields['resolver'] = '解决者';
2247
            $new_fields['resolved_at'] = '解决时间';
2248
            $new_fields['closer'] = '关闭者';
2249
            $new_fields['closed_at'] = '关闭时间';
2250
            $fields = $new_fields;
2251
2252
            // arrange the excel data
2253
            $data = $this->arrangeExcel($data, $fields);
2254
            foreach ($data as $val)
2255
            {
2256
                if (!isset($val['title']) && !isset($val['type']))
2257
                {
2258
                    $fatal_err_msgs = $err_msgs = '主题列和类型列没找到。';
2259
                }
2260
                else if (!isset($val['title']))
2261
                {
2262
                    $fatal_err_msgs = $err_msgs = '主题列没找到。';
2263
                }
2264
                else if (!isset($val['type']))
2265
                {
2266
                    $fatal_err_msgs = $err_msgs = '类型列没找到。';
2267
                }
2268
                else if (!$val['title'])
2269
                {
2270
                    $fatal_err_msgs = $err_msgs = '主题列不能有空值。';
2271
                }
2272
2273
                if ($err_msgs)
2274
                {
2275
                    return;
2276
                }
2277
            }
2278
2279
            // get the type schema
2280
            $new_types = [];
2281
            $standard_type_ids = [];
2282
            $types = Provider::getTypeList($project_key);
2283
            foreach ($types as $type)
2284
            {
2285
                $tmp = [];
2286
                $tmp['id'] = $type->id;
2287
                $tmp['name'] = $type->name;
2288
                $tmp['type'] = $type->type ?: 'standard';
2289
                $tmp['workflow'] = $type->workflow;
2290
                $tmp['schema'] = Provider::getSchemaByType($type->id);
2291
                $new_types[$type->name] = $tmp;
2292
                if ($tmp['type'] == 'standard')
2293
                {
2294
                    $standard_type_ids[] = $tmp['id'];
2295
                }
2296
            }
2297
            $types = $new_types;
2298
2299
            // get the state option
2300
            $new_priorities = [];
2301
            $priorities = Provider::getPriorityOptions($project_key);
2302
            foreach($priorities as $priority)
2303
            {
2304
                $new_priorities[$priority['name']] = $priority['_id'];
2305
            }
2306
            $priorities = $new_priorities;
2307
2308
            // get the state option
2309
            $new_states = [];
2310
            $states = Provider::getStateOptions($project_key);
2311
            foreach($states as $state)
2312
            {
2313
                $new_states[$state['name']] = $state['_id'];
2314
            }
2315
            $states = $new_states;
2316
2317
            // get the state option
2318
            $new_resolutions = [];
2319
            $resolutions = Provider::getResolutionOptions($project_key);
2320
            foreach($resolutions as $resolution)
2321
            {
2322
                $new_resolutions[$resolution['name']] = $resolution['_id'];
2323
            }
2324
            $resolutions = $new_resolutions;
2325
2326
            // initialize the error msg
2327
            foreach ($data as $val)
2328
            {
2329
                $err_msgs[$val['title']] = [];
2330
                $fatal_err_msgs[$val['title']] = [];
2331
            }
2332
2333
            $standard_titles = [];
2334
            $standard_issues = [];
2335
            $subtask_issues = [];
2336
2337
            foreach ($data as $value)
2338
            {
2339
                $issue = [];
2340
                $cur_title = $issue['title'] = $value['title'];
2341
2342
                if (!$value['type'])
2343
                {
2344
                    $fatal_err_msgs[$cur_title][] = $err_msgs[$cur_title][] = '类型列不能有空值。';
2345
                    continue;
2346
                }
2347
                else if (!isset($types[$value['type']]))
2348
                {
2349
                    $fatal_err_msgs[$cur_title][] = $err_msgs[$cur_title][] = '类型列值匹配失败。';
2350
                    continue;
2351
                }
2352
                else 
2353
                {
2354
                    $issue['type'] = $types[$value['type']]['id'];
2355
                }
2356
2357
                if ($types[$value['type']]['type'] === 'subtask' && (!isset($value['parent']) || !$value['parent']))
2358
                {
2359
                    $fatal_err_msgs[$cur_title][] = $err_msgs[$cur_title][] = '父级任务列不能为空。';
2360
                }
2361
                else
2362
                {
2363
                    $issue['parent'] = $value['parent'];
2364
                }
2365
2366 View Code Duplication
                if (isset($value['priority']) && $value['priority'])
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
2367
                {
2368
                    if (!isset($priorities[$value['priority']]) || !$priorities[$value['priority']])
2369
                    {
2370
                        $err_msgs[$cur_title][] = '优先级列值匹配失败。';
2371
                    }
2372
                    else
2373
                    {
2374
                        $issue['priority'] = $priorities[$value['priority']];
2375
                    }
2376
                }
2377
2378
                if (isset($value['state']) && $value['state'])
2379
                {
2380
                    if (!isset($states[$value['state']]) || !$states[$value['state']])
2381
                    {
2382
                        $err_msgs[$cur_title][] = '状态列值匹配失败。';
2383
                    }
2384
                    else
2385
                    {
2386
                        $issue['state'] = $states[$value['state']];
2387
                        $workflow = $types[$value['type']]['workflow'];
2388
                        if (!in_array($issue['state'], $workflow['state_ids']))
2389
                        {
2390
                            $err_msgs[$cur_title][] = '状态列值不在相应流程里。';
2391
                        }
2392
                    }
2393
                }
2394
2395 View Code Duplication
                if (isset($value['resolution']) && $value['resolution'])
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
2396
                {
2397
                    if (!isset($resolutions[$value['resolution']]) || !$resolutions[$value['resolution']])
2398
                    {
2399
                        $err_msgs[$cur_title][] = '解决结果列值匹配失败。';
2400
                    }
2401
                    else
2402
                    {
2403
                        $issue['resolution'] = $resolutions[$value['resolution']];
2404
                    }
2405
                }
2406
2407
                $user_relate_fields = [ 'assignee' => '经办人', 'reporter' => '报告者', 'resolver' => '解决者', 'closer' => '关闭时间' ];
2408
                foreach ($user_relate_fields as $uk => $uv)
2409
                {
2410
                    if (isset($value[$uk]) && $value[$uk])
2411
                    {
2412
                        $tmp_user = EloquentUser::where('first_name', $value[$uk])->first();
2413
                        if (!$tmp_user)
2414
                        {
2415
                            $err_msgs[$cur_title][] = $uv . '列用户不存在。';
2416
                        }
2417
                        else
2418
                        {
2419
                            $issue[$uk] = [ 'id' => $tmp_user->id, 'name' => $tmp_user->first_name, 'email' => $tmp_user->email ];
2420
                            if ($uk == 'resolver')
2421
                            {
2422
                                $issue['his_resolvers'] = [ $tmp_user->id ];
2423
                            }
2424
                        }
2425
                    }
2426
                }
2427
2428
                $time_relate_fields = [ 'created_at' => '创建时间', 'resolved_at' => '解决时间', 'closed_at' => '关闭时间', 'updated_at' => '更新时间' ];
2429
                foreach ($time_relate_fields as $tk => $tv)
2430
                {
2431
                    if (isset($value[$tk]) && $value[$tk])
2432
                    {
2433
                        $stamptime = strtotime($value[$tk]);
2434
                        if ($stamptime === false)
2435
                        {
2436
                            $err_msgs[$cur_title][] = $tv . '列值格式错误。';
2437
                        }
2438
                        else
2439
                        {
2440
                            $issue[$tk] = $stamptime;
2441
                        }
2442
                    }
2443
                }
2444
2445
                $schema = $types[$value['type']]['schema'];
2446
                foreach ($schema as $field)
2447
                {
2448 View Code Duplication
                    if (isset($field['required']) && $field['required'] && (!isset($value[$field['key']]) || !$value[$field['key']]))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
2449
                    {
2450
                        $err_msgs[$cur_title][] = $fields[$field['key']] . '列值不能为空。';
2451
                        continue;
2452
                    }
2453
2454
                    if (isset($value[$field['key']]) && $value[$field['key']])
2455
                    {
2456
                        $field_key = $field['key'];
2457
                        $field_value = $value[$field['key']];
2458
                    }
2459
                    else
2460
                    {
2461
                        continue;
2462
                    }
2463
2464
                    if (in_array($field_key, [ 'priority', 'resolution', 'assignee' ]))
2465
                    {
2466
                        continue;
2467
                    }
2468
2469
                    //if ($field_key === 'Sprint')
2470
                    //{
2471
                    //    $sprints = explode(',', $field_value);
2472
                    //    $new_sprints = [];
2473
                    //    foreach ($sprints as $s)
2474
                    //    {
2475
                    //        $new_sprints[] = intval($s);
2476
                    //    }
2477
                    //    $issue['sprints'] = $new_sprints;
2478
                    //}
2479
                    if ($field_key == 'labels')
2480
                    {
2481
                        $issue['labels'] = [];
2482
                        foreach (explode(',', $field_value) as $val)
2483
                        {
2484
                            if (trim($val))
2485
                            {
2486
                                $issue['labels'][] = trim($val); 
2487
                            }
2488
                        }
2489
                        $issue['labels'] = array_values(array_unique($issue['labels']));
2490
                    }
2491
                    else if ($field['type'] === 'SingleUser' || $field_key === 'assignee')
2492
                    {
2493
                        $tmp_user = EloquentUser::where('first_name', $field_value)->first();
2494
                        if (!$tmp_user)
2495
                        {
2496
                            $err_msgs[$cur_title][] = $fields[$field_key] . '列用户不存在。';
2497
                        }
2498
                        else
2499
                        {
2500
                            $issue[$field_key] = [ 'id' => $tmp_user->id, 'name' => $tmp_user->first_name, 'email' => $tmp_user->email ];
2501
                        }
2502
                    }
2503
                    else if ($field['type'] === 'MultiUser')
2504
                    {
2505
                        $issue[$field_key] = [];
2506
                        $issue[$field_key . '_ids'] = [];
2507
                        foreach(explode(',', $field_value) as $val)
2508
                        {
2509
                            if (!trim($val))
2510
                            {
2511
                                continue;
2512
                            }
2513
2514
                            $tmp_user = EloquentUser::where('first_name', trim($val))->first();
2515
                            if (!$tmp_user)
2516
                            {
2517
                                $err_msgs[$cur_title][] = $fields[$field_key] . '列用户不存在。';
2518
                            }
2519
                            else if (!in_array($tmp_user->id, $issue[$field_key . '_ids']))
2520
                            {
2521
                                $issue[$field_key][] = [ 'id' => $tmp_user->id, 'name' => $tmp_user->first_name, 'email' => $tmp_user->email ];
2522
                                $issue[$field_key . '_ids'][] = $tmp_user->id;
2523
                            }
2524
                        }
2525
                    }
2526
                    else if (in_array($field['type'], [ 'Select', 'RadioGroup', 'SingleVersion' ]))
2527
                    {
2528 View Code Duplication
                        foreach ($field['optionValues'] as $val)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
2529
                        {
2530
                            if ($val['name'] === $field_value)
2531
                            {
2532
                                $issue[$field_key] = $val['id'];
2533
                                break;
2534
                            }
2535
                        }
2536
                        if (!isset($issue[$field_key]))
2537
                        {
2538
                            $err_msgs[$cur_title][] = $fields[$field_key] . '列值匹配失败。';
2539
                        }
2540
                    }
2541
                    else if (in_array($field['type'], [ 'MultiSelect', 'CheckboxGroup', 'MultiVersion' ]))
2542
                    {
2543
                        $issue[$field_key] = [];
2544
                        foreach (explode(',', $field_value) as $val)
2545
                        {
2546
                            $val = trim($val);
2547
                            if (!$val)
2548
                            {
2549
                                continue;
2550
                            }
2551
2552
                            $isMatched = false;
2553 View Code Duplication
                            foreach ($field['optionValues'] as $val2)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
2554
                            {
2555
                                if ($val2['name'] === $val)
2556
                                {
2557
                                    $issue[$field_key][] = $val2['id'];
2558
                                    $isMatched = true;
2559
                                    break;
2560
                                }
2561
                            }
2562
                            if (!$isMatched)
2563
                            {
2564
                                $err_msgs[$cur_title][] = $fields[$field_key] . '列值匹配失败。';
2565
                            }
2566
                        }
2567
                        $issue[$field_key] = array_values(array_unique($issue[$field_key]));
2568
                    }
2569
                    else if (in_array($field['type'], [ 'DatePicker', 'DatetimePicker' ]))
2570
                    {
2571
                        $stamptime = strtotime($field_value);
2572
                        if ($stamptime === false)
2573
                        {
2574
                            $err_msgs[$cur_title][] = $fields[$field_key] . '列值格式错误。';
2575
                        }
2576
                        else
2577
                        {
2578
                            $issue[$field_key] = $stamptime;
2579
                        }
2580
                    }
2581
                    else if ($field['type'] === 'TimeTracking')
2582
                    {
2583
                        if (!$this->ttCheck($field_value))
2584
                        {
2585
                            $err_msgs[$cur_title][] = $fields[$field_key] . '列值格式错误。';
2586
                        }
2587
                        else
2588
                        {
2589
                            $issue[$field_key] = $this->ttHandle($field_value);
2590
                            $issue[$field_key . '_m'] = $this->ttHandleInM($issue[$field_key]);
2591
                        }
2592
                    }
2593
                    else if ($field['type'] === 'Number')
2594
                    {
2595
                        $issue[$field_key] = floatval($field_value);
2596
                    }
2597
                    else
2598
                    {
2599
                        $issue[$field_key] = $field_value;
2600
                    }
2601
                }
2602
2603
                if ($types[$value['type']]['type'] === 'subtask')
2604
                {
2605
                    $subtask_issues[] = $issue;
2606
                }
2607
                else
2608
                {
2609
                    $standard_titles[] = $issue['title'];
2610
                    if (isset($issue['parent']))
2611
                    {
2612
                        unset($issue['parent']);
2613
                    }
2614
                    $standard_issues[] = $issue;
2615
                }
2616
            }
2617
2618
            $new_subtask_issues = [];
2619
            foreach ($standard_titles as $title)
2620
            {
2621
                $new_subtask_issues[$title] = [];
2622
            }
2623
2624
            foreach ($subtask_issues as $issue)
2625
            {
2626
                $parent_issues = array_filter($standard_issues, function($v) use ($issue) { return $v['title'] === $issue['parent']; });
2627
                if (count($parent_issues) > 1)
2628
                {
2629
                    $fatal_err_msgs[$issue['title']][] = $err_msgs[$issue['title']][] = '找到多个父级任务。';
2630
                }
2631
                else if (count($parent_issues) == 1)
2632
                {
2633
                    $parent_issue = array_pop($parent_issues);
2634
                    if (isset($issue['parent']))
2635
                    {
2636
                        unset($issue['parent']);
2637
                    }
2638
                    $new_subtask_issues[$parent_issue['title']][] = $issue;
2639
                }
2640
                else
2641
                {
2642
                    $parent_issues = DB::table('issue_' . $project_key)
2643
                        ->where('title', $issue['parent'])
2644
                        ->whereIn('type', $standard_type_ids)
2645
                        ->where('del_flg', '<>', 1)
2646
                        ->get();
2647
                    if (count($parent_issues) > 1)
2648
                    {
2649
                        $fatal_err_msgs[$issue['title']][] = $err_msgs[$issue['title']][] = '找到多个父级任务。';
2650
                    }
2651
                    else if (count($parent_issues) == 1)
2652
                    {
2653
                        $parent_issue = array_pop($parent_issues);
2654
                        if (isset($issue['parent']))
2655
                        {
2656
                            unset($issue['parent']);
2657
                        }
2658
                        $new_subtask_issues[] = $issue + [ 'parent_id' => $parent_issue['_id']->__toString() ];
2659
                    }
2660
                    else
2661
                    {
2662
                        $fatal_err_msgs[$issue['title']][] = $err_msgs[$issue['title']][] = '父级任务不存在。';
2663
                    }
2664
                }
2665
            }
2666
            $subtask_issues = array_filter($new_subtask_issues);
2667
2668
            $err_msgs = array_filter($err_msgs);
2669
            $fatal_err_msgs = array_filter($fatal_err_msgs);
2670
            if ($pattern == '2')
2671
            {
2672
                if ($fatal_err_msgs)
0 ignored issues
show
Bug Best Practice introduced by
The expression $fatal_err_msgs of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
2673
                {
2674
                    return;
2675
                }
2676
            }
2677
            else if ($err_msgs)
0 ignored issues
show
Bug Best Practice introduced by
The expression $err_msgs of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
2678
            {
2679
                return;
2680
            }
2681
2682
2683
            $new_types = [];
2684
            foreach($types as $type)
2685
            {
2686
                $new_types[$type['id']] = $type;
2687
            }
2688
            $types = $new_types;
2689
2690 View Code Duplication
            foreach ($subtask_issues as $issue)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
2691
            {
2692
                if (isset($issue['parent_id']) && $issue['parent_id'])
2693
                {
2694
                    $this->importIssue($project_key, $issue, $types[$issue['type']]['schema'], $types[$issue['type']]['workflow']);
2695
                }
2696
            }
2697
2698
            foreach ($standard_issues as $issue)
2699
            {
2700
                $id = $this->importIssue($project_key, $issue, $types[$issue['type']]['schema'], $types[$issue['type']]['workflow']);
2701
                if (!isset($subtask_issues[$issue['title']]) || !$subtask_issues[$issue['title']])
2702
                {
2703
                    continue;
2704
                }
2705 View Code Duplication
                foreach ($subtask_issues[$issue['title']] as $sub_issue)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
2706
                {
2707
                    $sub_issue['parent_id'] = $id;
2708
                    $this->importIssue($project_key, $sub_issue, $types[$sub_issue['type']]['schema'], $types[$sub_issue['type']]['workflow']);
2709
                }
2710
            }
2711
        });
2712
2713
2714
        $emsgs = '';
0 ignored issues
show
Unused Code introduced by
$emsgs is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
2715
        if ($pattern == '2')
2716
        {
2717
            $emsgs = array_filter($fatal_err_msgs);
2718
        }
2719
        else
2720
        {
2721
            $emsgs = array_filter($err_msgs);
2722
        }
2723
2724 View Code Duplication
        if ($emsgs)
0 ignored issues
show
Bug Best Practice introduced by
The expression $emsgs of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
Duplication introduced by
This code seems to be duplicated across 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...
2725
        {
2726
            return Response()->json([ 'ecode' => -11146, 'emsg' => $emsgs ]);
0 ignored issues
show
Bug introduced by
The method json does only exist in Illuminate\Contracts\Routing\ResponseFactory, but not in Illuminate\Http\Response.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
2727
        }
2728
        else
2729
        {
2730
            return Response()->json([ 'ecode' => 0, 'emsg' => '' ]);
2731
        }
2732
    }
2733
2734
    /**
2735
     * import the issue into the project 
2736
     *
2737
     * @param  string $project_key
2738
     * @param  array $data
2739
     * @param  array $schema
2740
     * @return string id 
2741
     */
2742
    public function importIssue($project_key, $data, $schema, $workflow)
2743
    {
2744
        $table = 'issue_' . $project_key;
2745
2746
        $insValues = $data;
2747
        if (!isset($insValues['resolution']) || !$insValues['resolution'])
2748
        {
2749
            $insValues['resolution'] = 'Unresolved';
2750
        }
2751
2752
        $max_no = DB::collection($table)->count() + 1;
2753
        $insValues['no'] = $max_no;
2754
2755 View Code Duplication
        if (!isset($insValues['assignee']) || !$insValues['assignee'])
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
2756
        {
2757
            $insValues['assignee'] = [ 'id' => $this->user->id, 'name' => $this->user->first_name, 'email' => $this->user->email ];
2758
        }
2759
2760
        // get reporter(creator)
2761 View Code Duplication
        if (!isset($insValues['reporter']) || !$insValues['reporter'])
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
2762
        {
2763
            $insValues['reporter'] = [ 'id' => $this->user->id, 'name' => $this->user->first_name, 'email' => $this->user->email ];
2764
        }
2765
        if (!isset($insValues['created_at']) || !$insValues['created_at'])
2766
        {
2767
            $insValues['created_at'] = time();
2768
        }
2769
2770
        if (!isset($data['state']) || !$data['state'])
2771
        {
2772
            $wf = $this->initializeWorkflow($data['type']);
2773
            $insValues += $wf;
2774
        }
2775
        else if (in_array($data['state'], $workflow->state_ids ?: []))
2776
        {
2777
            $wf = $this->initializeWorkflowForImport($workflow, $data['state']);
2778
            $insValues += $wf;
2779
        }
2780
2781
        $id = DB::collection('issue_' . $project_key)->insertGetId($insValues);
2782
        $id = $id->__toString();
2783
2784
        // add to histroy table
2785
        Provider::snap2His($project_key, $id, $schema);
2786
        // trigger event of issue created
2787
        Event::fire(new IssueEvent($project_key, $id, $insValues['reporter'], [ 'event_key' => 'create_issue' ]));
2788
2789
        if (isset($insValues['labels']) && $insValues['labels'])
2790
        {
2791
            $this->createLabels($project_key, $insValues['labels']);
2792
        }
2793
2794
        return $id;
2795
    }
2796
2797
    /**
2798
     * initialize the workflow for the issue import.
2799
     *
2800
     * @param  object  $wf_definition
2801
     * @param  string  $state
2802
     * @return array
2803
     */
2804
    public function initializeWorkflowForImport($wf_definition, $state)
2805
    {
2806
        // create and start workflow instacne
2807
        $wf_entry = Workflow::createInstance($wf_definition->id, $this->user->id);
2808
2809
        $wf_contents = $wf_definition->contents ?: [];
2810
        $steps = isset($wf_contents['steps']) && $wf_contents['steps'] ? $wf_contents['steps'] : [];
2811
2812
        $fake_step = [];
2813
        foreach($steps as $step)
2814
        {
2815
            if (isset($step['state']) && $step['state'] == $state)
2816
            {
2817
                $fake_step = $step;
2818
                break;
2819
            }
2820
        }
2821
        if (!$fake_step)
2822
        {
2823
            return [];
2824
        }
2825
2826
        $caller = [ 'id' => $this->user->id, 'name' => $this->user->first_name, 'email' => $this->user->email ];
2827
        $wf_entry->fakeNewCurrentStep($fake_step, $caller);
2828
2829
        $ret['entry_id'] = $wf_entry->getEntryId();
0 ignored issues
show
Coding Style Comprehensibility introduced by
$ret was never initialized. Although not strictly required by PHP, it is generally a good practice to add $ret = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
2830
        $ret['definition_id'] = $wf_definition->id;
2831
2832
        return $ret;
2833
    }
2834
2835
    /**
2836
     * export xls for issue list 
2837
     *
2838
     * @param  string $project_key
2839
     * @param  array $export_fields
2840
     * @param  array $issues
2841
     * @return void
2842
     */
2843
    public function export($project_key, $export_fields, $issues) 
2844
    {
2845
        set_time_limit(0);
2846
2847
        $options = $this->getOptionsForExport($project_key);
2848
        foreach ($options as $key => $val)
2849
        {
2850
            $$key = $val;
2851
        }
2852
2853
        foreach ($export_fields as $key => $field)
2854
        {
2855
            if (!array_key_exists($field, $fields))
0 ignored issues
show
Bug introduced by
The variable $fields does not exist. Did you mean $export_fields?

This check looks for variables that are accessed but have not been defined. It raises an issue if it finds another variable that has a similar name.

The variable may have been renamed without also renaming all references.

Loading history...
2856
            {
2857
                unset($export_fields[$key]);
2858
            }
2859
        }
2860
        $export_fields = array_values($export_fields);
2861
2862
        $headers = [];
2863
        foreach ($export_fields as $fk)
2864
        {
2865
            $headers[] = isset($fields[$fk]) && $fields[$fk] ? $fields[$fk]['name'] : '';
2866
        }
2867
2868
        $new_issues = [];
2869
        foreach ($issues as $issue)
2870
        {
2871
            $tmp = [];
2872
            foreach ($export_fields as $fk)
2873
            {
2874
                if (!isset($issue[$fk]) || (!$issue[$fk] && $issue[$fk] !== 0))
2875
                {
2876
                    $tmp[] = '';
2877
                    continue;
2878
                }
2879
2880
                if (in_array($fk, [ 'assignee', 'reporter', 'closer', 'resolver' ]))
2881
                {
2882
                    $tmp[] = isset($issue[$fk]['name']) ? $issue[$fk]['name'] : '';
2883
                }
2884
                else if ($fk == 'module')
2885
                {
2886
                    $new_modules = [];
2887
                    $module_ids = [];
0 ignored issues
show
Unused Code introduced by
$module_ids is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
2888 View Code Duplication
                    if (is_array($issue[$fk]))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
2889
                    {
2890
                        $module_ids = $issue[$fk];
2891
                    }
2892
                    else
2893
                    {
2894
                        $module_ids = explode(',', $issue[$fk]);
2895
                    }
2896
                    foreach ($module_ids as $id)
2897
                    {
2898
                        if (!isset($modules[$id]) || !$modules[$id])
2899
                        {
2900
                            continue;
2901
                        }
2902
                        $new_modules[] = $modules[$id];
2903
                    }
2904
                    $tmp[] = implode(',', $new_modules);
2905
                }
2906
                else if ($fk == 'type')
2907
                {
2908
                    $tmp[] = isset($types[$issue[$fk]]) && $types[$issue[$fk]] ? $types[$issue[$fk]] : '';
2909
                }
2910
                else if ($fk == 'priority')
2911
                {
2912
                    $tmp[] = isset($priorities[$issue[$fk]]) && $priorities[$issue[$fk]] ? $priorities[$issue[$fk]] : '';
2913
                }
2914
                else if ($fk == 'state')
2915
                {
2916
                    $tmp[] = isset($states[$issue[$fk]]) && $states[$issue[$fk]] ? $states[$issue[$fk]] : '';
2917
                }
2918
                else if ($fk == 'resolution')
2919
                {
2920
                    $tmp[] = isset($resolutions[$issue[$fk]]) && $resolutions[$issue[$fk]] ? $resolutions[$issue[$fk]] : '';
2921
                }
2922
                else if ($fk == 'epic')
2923
                {
2924
                    $tmp[] = isset($epics[$issue[$fk]]) && $epics[$issue[$fk]] ? $epics[$issue[$fk]] : '';
2925
                }
2926
                else if ($fk == 'sprints')
2927
                {
2928
                    $new_sprints = [];
2929
                    foreach ($issue[$fk] as $sn)
2930
                    {
2931
                        if (isset($sprints[$sn]))
2932
                        {
2933
                            $new_sprints[] = $sprints[$sn];
2934
                        }
2935
                    }
2936
                    $tmp[] = implode(',', $new_sprints);
2937
                }
2938
                else if ($fk == 'labels')
2939
                {
2940
                    $tmp[] = implode(',', $issue[$fk]);
2941
                }
2942
                else if ($fk == 'progress')
2943
                {
2944
                    $tmp[] = $issue[$fk] . '%';
2945
                }
2946
                else if (isset($fields[$fk]) && $fields[$fk])
2947
                {
2948
                    if ($fields[$fk]['type'] == 'DateTimePicker')
2949
                    {
2950
                        $tmp[] = date('Y-m-d H:i:s', $issue[$fk]);
2951
                    }
2952
                    else if ($fields[$fk]['type'] == 'DatePicker')
2953
                    {
2954
                        $tmp[] = date('Y-m-d', $issue[$fk]);
2955
                    }
2956
                    else if ($fields[$fk]['type'] == 'SingleVersion' || $fields[$fk]['type'] == 'MultiVersion')
2957
                    {
2958
                        $new_versions = [];
2959
                        $version_ids = [];
0 ignored issues
show
Unused Code introduced by
$version_ids is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
2960 View Code Duplication
                        if (is_array($issue[$fk]))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
2961
                        {
2962
                            $version_ids = $issue[$fk];
2963
                        }
2964
                        else
2965
                        {
2966
                            $version_ids = explode(',', $issue[$fk]);
2967
                        }
2968
                        foreach ($version_ids as $id)
2969
                        {
2970
                            if (isset($versions[$id]) && $versions[$id])
2971
                            {
2972
                                $new_versions[] = $versions[$id];
2973
                            }
2974
                        }
2975
                        $tmp[] = implode(',', $new_versions);
2976
                    } 
2977
                    else if ($fields[$fk]['type'] == 'SingleUser')
2978
                    {
2979
                        $tmp[] = isset($issue[$fk]['name']) ? $issue[$fk]['name'] : '';
2980
                    }
2981
                    else if ($fields[$fk]['type'] == 'MultiUser')
2982
                    {
2983
                        $new_users = [];
2984
                        foreach ($issue[$fk] as $user)
2985
                        {
2986
                            if (isset($user['name']) && $user['name'])
2987
                            {
2988
                                $new_users[] = $user['name'];
2989
                            }
2990
                        }
2991
                        $tmp[] = implode(',', $new_users);
2992
                    }
2993
                    else
2994
                    {
2995
                        if (isset($fields[$fk]['optionValues']) && $fields[$fk]['optionValues'])
2996
                        {
2997
                            $tmpOptions = [];
2998
                            foreach ($fields[$fk]['optionValues'] as $ov)
2999
                            {
3000
                                $tmpOptions[$ov['id']] = $ov['name'];
3001
                            }
3002
                            $ov_ids = [];
0 ignored issues
show
Unused Code introduced by
$ov_ids is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
3003 View Code Duplication
                            if (is_array($issue[$fk]))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
3004
                            {
3005
                                $ov_ids = $issue[$fk]; 
3006
                            }
3007
                            else
3008
                            {
3009
                                $ov_ids = explode(',', $issue[$fk]); 
3010
                            }
3011
                            $ov_names = [];
3012
                            foreach ($ov_ids as $ovid)
3013
                            {
3014
                                $ov_names[] = isset($tmpOptions[$ovid]) ? $tmpOptions[$ovid] : ''; 
3015
                            }
3016
                            $tmp[] = implode(',', array_filter($ov_names));
3017
                        }
3018
                        else
3019
                        {
3020
                            $tmp[] = (string)$issue[$fk];
3021
                        }
3022
                    }
3023
                }
3024
                else
3025
                {
3026
                    $tmp[] = (string)$issue[$fk];
3027
                }
3028
            }
3029
            $new_issues[] = $tmp;
3030
        }
3031
3032
        $file_name = $project_key . '-issues';
3033
        Excel::create($file_name, function ($excel) use($headers, $new_issues) {
3034
            $excel->sheet('Sheetname', function ($sheet) use($headers, $new_issues) {
3035
                $sheet->appendRow($headers);
3036
                foreach ($new_issues as $issue)
3037
                {
3038
                    $sheet->appendRow($issue);
3039
                }
3040
            });
3041
        })->download('xls');
3042
    }
3043
3044
    /**
3045
     * batch handle the issue
3046
     *
3047
     * @param  \Illuminate\Http\Request  $request
3048
     * @param  string $project_key
3049
     * @return \Illuminate\Http\Response
3050
     */
3051
    public function batchHandle(Request $request, $project_key)
3052
    {
3053
        $method = $request->input('method');
3054
        if ($method == 'update')
3055
        {
3056
            $data = $request->input('data');
3057
            if (!$data || !isset($data['ids']) || !$data['ids'] || !is_array($data['ids']) || !isset($data['values']) || !$data['values'] || !is_array($data['values']))
3058
            {
3059
                throw new \UnexpectedValueException('the batch params has errors.', -11124);
3060
            }
3061
            return $this->batchUpdate($project_key, $data['ids'], $data['values']);
3062
        }
3063
        else if ($method == 'delete')
3064
        {
3065
            $data = $request->input('data');
3066
            if (!$data || !isset($data['ids']) || !$data['ids'] || !is_array($data['ids']))
3067
            {
3068
                throw new \UnexpectedValueException('the batch params has errors.', -11124);
3069
            }
3070
            return $this->batchDelete($project_key, $data['ids']);
3071
        }
3072
        else
3073
        {
3074
            throw new \UnexpectedValueException('the batch method has errors.', -11125);
3075
        }
3076
    }
3077
3078
    /**
3079
     * batch update the issue
3080
     *
3081
     * @param  string $project_key
3082
     * @param  array $ids
3083
     * @return \Illuminate\Http\Response
3084
     */
3085
    public function batchDelete($project_key, $ids)
3086
    {
3087
        if (!$this->isPermissionAllowed($project_key, 'delete_issue'))
3088
        {
3089
            return Response()->json(['ecode' => -10002, 'emsg' => 'permission denied.']);
0 ignored issues
show
Bug introduced by
The method json does only exist in Illuminate\Contracts\Routing\ResponseFactory, but not in Illuminate\Http\Response.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
3090
        }
3091
3092
        $user = [ 'id' => $this->user->id, 'name' => $this->user->first_name, 'email' => $this->user->email ];
3093
3094
        $table = 'issue_' . $project_key;
3095
        foreach ($ids as $id)
3096
        {
3097
            $issue = DB::collection($table)
3098
                ->where('_id', $id)
3099
                ->where('del_flg', '<>', 1)
3100
                ->first();
3101
            if (!$issue)
3102
            {
3103
                continue;
3104
            }
3105
3106
            // delete all subtasks of this issue
3107
            $subtasks = DB::collection('issue_' . $project_key)
3108
                ->where('parent_id', $id)
3109
                ->where('del_flg', '<>', 1)
3110
                ->get();
3111 View Code Duplication
            foreach ($subtasks as $subtask)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
3112
            {
3113
                $sub_id = $subtask['_id']->__toString();
3114
                DB::collection($table)->where('_id', $sub_id)->update([ 'del_flg' => 1 ]);
3115
3116
                // delete linked relation
3117
                DB::collection('linked')->where('src', $sub_id)->orWhere('dest', $sub_id)->delete();
3118
3119
                Event::fire(new IssueEvent($project_key, $sub_id, $user, [ 'event_key' => 'del_issue' ]));
3120
            }
3121
3122
            // delete linked relation
3123
            DB::collection('linked')->where('src', $id)->orWhere('dest', $id)->delete();
3124
3125
            // delete this issue
3126
            DB::collection($table)->where('_id', $id)->update([ 'del_flg' => 1 ]);
3127
3128
            // trigger event of issue deleted 
3129
            Event::fire(new IssueEvent($project_key, $id, $user, [ 'event_key' => 'del_issue' ]));
3130
        }
3131
3132
        return Response()->json([ 'ecode' => 0, 'data' => [ 'ids' => $ids ] ]);
3133
    }
3134
3135
    /**
3136
     * batch update the issue
3137
     *
3138
     * @param  string $project_key
3139
     * @param  array $ids
3140
     * @param  array $values
3141
     * @return \Illuminate\Http\Response
3142
     */
3143
    public function batchUpdate($project_key, $ids, $values)
3144
    {
3145 View Code Duplication
        if (!$this->isPermissionAllowed($project_key, 'edit_issue'))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
3146
        {
3147
            return Response()->json(['ecode' => -10002, 'emsg' => 'permission denied.']);
0 ignored issues
show
Bug introduced by
The method json does only exist in Illuminate\Contracts\Routing\ResponseFactory, but not in Illuminate\Http\Response.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
3148
        }
3149
3150
        $schemas = [];
3151
3152
        $updValues = [];
3153
        if (isset($values['type']))
3154
        {
3155
            if (!$values['type'])
3156
            {
3157
                throw new \UnexpectedValueException('the issue type can not be empty.', -11100);
3158
            }
3159
        
3160
            if (!($schemas[$values['type']] = Provider::getSchemaByType($values['type'])))
3161
            {
3162
                throw new \UnexpectedValueException('the schema of the type is not existed.', -11101);
3163
            }
3164
3165
            $updValues['type'] = $values['type'];
3166
        }
3167
3168
        $new_fields = [];
3169
        $fields = Provider::getFieldList($project_key);
3170
        foreach($fields as $field)
3171
        {
3172
            $new_fields[$field->key] = $field;
3173
        }
3174
        $fields = $new_fields;
3175
3176
        foreach ($values as $key => $val)
3177
        {
3178
            if (!isset($fields[$key]) || $fields[$key]->type == 'File')
3179
            {
3180
                continue;
3181
            }
3182
3183
            $field = $fields[$key];
3184
3185
            if ($field->type == 'DateTimePicker' || $field->type == 'DatePicker')
3186
            {
3187
                if ($val && $this->isTimestamp($val) === false)
3188
                {
3189
                    throw new \UnexpectedValueException('the format of datepicker field is incorrect.', -11122);
3190
                }
3191
                $updValues[$key] = $val;
3192
            }
3193
            else if ($field->type == 'TimeTracking')
3194
            {
3195
                if ($val && !$this->ttCheck($val))
3196
                {
3197
                    throw new \UnexpectedValueException('the format of timetracking field is incorrect.', -11102);
3198
                }
3199
                $updValues[$key] = $this->ttHandle($val);
3200
                $updValues[$key . '_m'] = $this->ttHandleInM($updValues[$key]);
3201
            }
3202
            else if ($key == 'assignee' || $field->type == 'SingleUser')
3203
            {
3204
                $user_info = Sentinel::findById($val);
3205
                if ($user_info)
3206
                {
3207
                    $updValues[$key] = [ 'id' => $val, 'name' => $user_info->first_name, 'email' => $user_info->email ];
3208
                }
3209
            }
3210
            else if ($field->type == 'MultiUser')
3211
            {
3212
                $user_ids = $val;
3213
                $updValues[$key] = [];
3214
                $new_user_ids = [];
3215
                foreach ($user_ids as $uid)
3216
                {
3217
                    $user_info = Sentinel::findById($uid);
3218
                    if ($user_info)
3219
                    {
3220
                        array_push($updValues[$key], [ 'id' => $uid, 'name' => $user_info->first_name, 'email' => $user_info->email ]);
3221
                    }
3222
                    $new_user_ids[] = $uid;
3223
                }
3224
                $updValues[$key . '_ids'] = $new_user_ids;
3225
            }
3226
            else if ($field->type === 'Number' || $field->type === 'Integer')
3227
            {
3228
                if ($val === '')
3229
                {
3230
                    $updValues[$key] = '';
3231
                }
3232
                else
3233
                {
3234
                    $updValues[$key] = $field->type === 'Number' ? floatVal($val) : intVal($val);
3235
                }
3236
            }
3237
            else 
3238
            {
3239
                $updValues[$key] = $val;
3240
            }
3241
        }
3242
3243
        $updValues['modifier'] = [ 'id' => $this->user->id, 'name' => $this->user->first_name, 'email' => $this->user->email ];
3244
        $updValues['updated_at'] = time();
3245
3246
        $table = 'issue_' . $project_key;
3247
        foreach ($ids as $id)
3248
        {
3249
            $issue = DB::collection($table)->find($id);
3250
            if (!$issue)
3251
            {
3252
                continue;
3253
            }
3254
3255
            $schema = [];
0 ignored issues
show
Unused Code introduced by
$schema is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
3256
            $type = isset($values['type']) ? $values['type'] : $issue['type'];
3257
            if (!isset($schemas[$type]))
3258
            {
3259
                if (!($schemas[$type] = $schema = Provider::getSchemaByType($type)))
3260
                {
3261
                    continue;
3262
                }
3263
            }
3264
            else 
3265
            {
3266
                $schema = $schemas[$type];
3267
            }
3268
3269
            $valid_keys = $this->getValidKeysBySchema($schema);
3270
            if (!array_only($updValues, $valid_keys))
3271
            {
3272
                continue;
3273
            }
3274
3275
            DB::collection($table)->where('_id', $id)->update(array_only($updValues, $valid_keys));
3276
3277
            // add to histroy table
3278
            $snap_id = Provider::snap2His($project_key, $id, $schema, array_keys(array_only($values, $valid_keys)));
3279
3280
            // trigger event of issue edited
3281
            Event::fire(new IssueEvent($project_key, $id, $updValues['modifier'], [ 'event_key' => 'edit_issue', 'snap_id' => $snap_id ]));
3282
        }
3283
3284
        // create the Labels for project
3285
        if (isset($updValues['labels']) && $updValues['labels'])
3286
        {
3287
            $this->createLabels($project_key, $updValues['labels']);
3288
        }
3289
3290
        return Response()->json([ 'ecode' => 0, 'data' => [ 'ids' => $ids ] ]); 
3291
    }
3292
}
3293