IssueController::update()   F
last analyzed

Complexity

Conditions 30
Paths 107

Size

Total Lines 104

Duplication

Lines 54
Ratio 51.92 %

Importance

Changes 0
Metric Value
dl 54
loc 104
rs 3.2866
c 0
b 0
f 0
cc 30
nc 107
nop 3

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
            $board = Board::find($from_kanban_id);
56
            if ($board && isset($board->query) && $board->query) {
57
                $global_query = $this->getIssueQueryWhere($project_key, $board->query);
58
                $query->whereRaw($global_query);
59
            }
60
61
            if ($from === 'kanban') {
62
                $query->where(
63
                    function ($query) {
64
                        $query->whereRaw([ 'resolve_version' => [ '$exists' => 0 ] ])->orWhere('resolve_version', '');
65
                    }
66
                );
67
            }
68
            else if ($from === 'active_sprint' || $from === 'backlog') {
69
                $active_sprint_issues = [];
70
                $active_sprint = Sprint::where('project_key', $project_key)->where('status', 'active')->first();
71
                if ($from === 'active_sprint' && !$active_sprint) {
72
                    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...
73
                }
74
                else if ($active_sprint && isset($active_sprint['issues']) && $active_sprint['issues']) {
75
                    $active_sprint_issues = $active_sprint['issues'];
76
                }
77
78
                $last_column_states = [];
79 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...
80
                    $board_columns = $board->columns;
81
                    $last_column = array_pop($board_columns) ?: [];
82
                    if ($last_column && isset($last_column['states']) && $last_column['states']) {
83
                        $last_column_states = $last_column['states']; 
84
                    }
85
                }
86
 
87
                $query->where(
88
                    function ($query) use ($last_column_states, $active_sprint_issues) {
89
                        $query->whereRaw([ 'state' => [ '$nin' => $last_column_states ] ])->orWhereIn('no', $active_sprint_issues);
90
                    }
91
                );
92
            }
93
        }
94
95
        // get total num
96
        $total = $query->count();
97
98
        $orderBy = $request->input('orderBy') ?: '';
99
        if ($orderBy) {
100
            $orderBy = explode(',', $orderBy);
101
            foreach ($orderBy as $val)
102
            {
103
                $val = explode(' ', trim($val));
104
                $field = array_shift($val);
105
                $sort = array_pop($val) ?: 'asc';
106
                $query = $query->orderBy($field, $sort);
107
            }
108
        }
109
110
        $query->orderBy('_id', isset($from) && $from != 'gantt' ? 'asc' : 'desc');
111
112
        $page_size = $request->input('limit') ? intval($request->input('limit')) : 50;
113
        //$page_size = 200;
114
        $page = $request->input('page') ?: 1;
115
        $query = $query->skip($page_size * ($page - 1))->take($page_size);
116
        $issues = $query->get();
117
118
        if (isset($from) && $from == 'export') {
119
            $export_fields = $request->input('export_fields');
120
            $this->export($project_key, isset($export_fields) ? explode(',', $export_fields) : [], $issues);
121
            exit();
122
        }
123
124
        $watched_issue_ids = [];
125
        if (!isset($from) || !$from) {
126
            $watched_issues = Watch::where('project_key', $project_key)
127
                ->where('user.id', $this->user->id)
128
                ->get()
129
                ->toArray();
130
            $watched_issue_ids = array_column($watched_issues, 'issue_id');
131
        }
132
133
        $cache_parents = [];
134
        $issue_ids = [];
135
        foreach ($issues as $key => $issue)
136
        {
137
            $issue_ids[] = $issue['_id']->__toString();
138
            // set issue watching flag
139
            if (in_array($issue['_id']->__toString(), $watched_issue_ids)) {
140
                $issues[$key]['watching'] = true;
141
            }
142
143
            // get the parent issue
144
            if (isset($issue['parent_id']) && $issue['parent_id']) {
145
                if (isset($cache_parents[$issue['parent_id']]) && $cache_parents[$issue['parent_id']]) {
146
                    $issues[$key]['parent'] = $cache_parents[$issue['parent_id']];
147
                }
148
                else
149
                {
150
                    $parent = DB::collection('issue_' . $project_key)->where('_id', $issue['parent_id'])->first();
151
                    $issues[$key]['parent'] = $parent ? array_only($parent, [ '_id', 'title', 'no', 'type', 'state' ]) : [];
152
                    $cache_parents[$issue['parent_id']] = $issues[$key]['parent'];
153
                }
154
            }
155
156
            if (!isset($from)) {
157
                $issues[$key]['hasSubtasks'] = DB::collection('issue_' . $project_key)->where('parent_id', $issue['_id']->__toString())->exists();
158
            }
159
        }
160
161
        if ($issues && isset($from) && in_array($from, [ 'kanban', 'active_sprint', 'backlog', 'his_sprint' ])) {
162
            $filter = $request->input('filter') ?: '';
163
            $issues = $this->arrangeIssues($project_key, $issues, $from, $from_kanban_id, $filter === 'all');
164
        }
165
166
        $options = [ 'total' => $total, 'sizePerPage' => $page_size ];
167
168
        if (isset($from) && $from == 'gantt') {
169
            foreach ($issues as $key => $issue) 
170
            {
171
                if (!isset($issue['parent_id']) || !$issue['parent_id'] || in_array($issue['parent_id'], $issue_ids)) {
172
                    continue;
173
                }
174
                if (isset($issue['parent']) && $issue['parent']) {
175
                    $issues[] = $issue['parent'];
176
                    $issue_ids[] = $issue['parent_id'];
177
                }
178
            }
179
180
            $singulars = CalendarSingular::all();
181
            $new_singulars = [];
182
            foreach ($singulars as $singular)
183
            {
184
                $tmp = [];
185
                $tmp['notWorking'] = $singular->type == 'holiday' ? 1 : 0;
186
                $tmp['date'] = $singular->date;
187
                $new_singulars[] = $tmp;
188
            }
189
            $options['singulars'] = $new_singulars;
190
            $options['today'] = date('Y/m/d');
191
        }
192
193
        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...
194
    }
195
196
    /**
197
     * search issue.
198
     *
199
     * @return \Illuminate\Http\Response
200
     */
201
    public function search(Request $request, $project_key)
202
    {
203
        $query = DB::collection('issue_' . $project_key)->where('del_flg', '<>', 1);
204
205
        if ($s = $request->input('s')) {
206
            if (is_numeric($s) && strpos($s, '.') === false) {
207
                $query->where(
208
                    function ($query) use ($s) {
209
                        $query->where('no', $s + 0)->orWhere('title', 'like', '%' . $s . '%');
210
                    }
211
                );
212
            }
213
            else
214
            {
215
                $query->where('title', 'like', '%' . $s . '%');
216
            }
217
        }
218
219
        $type = $request->input('type');
220
        if (isset($type)) {
221
            if ($type == 'standard') {
222
                $query->where(
223
                    function ($query) { 
224
                        $query->where('parent_id', '')->orWhereNull('parent_id')->orWhere('parent_id', 'exists', false);
225
                    }
226
                );
227
   
228
            } 
229
            else if ($type == 'subtask') {
230
                $query->where(
231
                    function ($query) { 
232
                        $query->where('parent_id', 'exists', true)->where('parent_id', '<>', '')->whereNotNull('parent_id');
233
                    }
234
                );
235
            }
236
        }
237
238
        if ($limit = $request->input('limit')) {
239
            $limit = intval($limit) < 10 ? 10 : intval($limit);
240
        }
241
        else
242
        {
243
            $limit = 10;
244
        }
245
246
        $query->take($limit)->orderBy('created_at', 'desc');
247
        $issues = $query->get();
248
        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...
249
    }
250
251
    /**
252
     * Store a newly created resource in storage.
253
     *
254
     * @param  \Illuminate\Http\Request $request
255
     * @return \Illuminate\Http\Response
256
     */
257
    public function store(Request $request, $project_key)
258
    {
259
        $issue_type = $request->input('type');
260
        if (!$issue_type) {
261
            throw new \UnexpectedValueException('the issue type can not be empty.', -11100);
262
        }
263
264
        $schema = Provider::getSchemaByType($issue_type);
265
        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...
266
            throw new \UnexpectedValueException('the schema of the type is not existed.', -11101);
267
        }
268
269
        if (!$this->requiredCheck($schema, $request->all(), 'create')) {
270
            throw new \UnexpectedValueException('the required field is empty.', -11121);
271
        }
272
273
        // handle timetracking
274
        $insValues = [];
275 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...
276
        {
277
            $fieldValue = $request->input($field['key']);
278
            if (!isset($fieldValue) || !$fieldValue) {
279
                continue;
280
            }
281
282
            if ($field['type'] == 'TimeTracking') {
283
                if (!$this->ttCheck($fieldValue)) {
284
                    throw new \UnexpectedValueException('the format of timetracking is incorrect.', -11102);
285
                }
286
                $insValues[$field['key']] = $this->ttHandle($fieldValue);
287
                $insValues[$field['key'] . '_m'] = $this->ttHandleInM($insValues[$field['key']]);
288
            }
289
            else if ($field['type'] == 'DatePicker' || $field['type'] == 'DateTimePicker') {
290
                if ($this->isTimestamp($fieldValue) === false) {
291
                    throw new \UnexpectedValueException('the format of datepicker field is incorrect.', -11122);
292
                }
293
            }
294
            else if ($field['type'] == 'SingleUser') {
295
                $user_info = Sentinel::findById($fieldValue);
296
                if ($user_info) {
297
                    $insValues[$field['key']] = [ 'id' => $fieldValue, 'name' => $user_info->first_name, 'email' => $user_info->email ];
298
                }
299
            }
300
            else if ($field['type'] == 'MultiUser') {
301
                $user_ids = $fieldValue;
302
                $new_user_ids = [];
303
                $insValues[$field['key']] = [];
304
                foreach ($user_ids as $uid)
305
                {
306
                    $user_info = Sentinel::findById($uid);
307
                    if ($user_info) {
308
                        array_push($insValues[$field['key']], [ 'id' => $uid, 'name' => $user_info->first_name, 'email' => $user_info->email ]);
309
                        $new_user_ids[] = $uid;
310
                    }
311
                }
312
                $insValues[$field['key'] . '_ids'] = $new_user_ids;
313
            }
314
        }
315
316
        // handle assignee
317
        $assignee = [];
318
        $assignee_id = $request->input('assignee');
319
        if (!$assignee_id) {
320
            $module_ids = $request->input('module');
321
            if ($module_ids) {
322
                //$module_ids = explode(',', $module_ids);
323
                $module = Provider::getModuleById($module_ids[0]);
324
                if (isset($module['defaultAssignee']) && $module['defaultAssignee'] === 'modulePrincipal') {
325
                    $assignee2 = $module['principal'] ?: '';
326
                    $assignee_id = isset($assignee2['id']) ? $assignee2['id'] : '';
327
                }
328
                else if (isset($module['defaultAssignee']) && $module['defaultAssignee'] === 'projectPrincipal') {
329
                    $assignee2 = Provider::getProjectPrincipal($project_key) ?: '';
330
                    $assignee_id = isset($assignee2['id']) ? $assignee2['id'] : ''; 
331
                }
332
            }
333
        }
334
        if ($assignee_id) {
335
            if ($assignee_id != $this->user->id && !$this->isPermissionAllowed($project_key, 'assigned_issue', $assignee_id)) {
336
                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...
337
            }
338
339
            $user_info = Sentinel::findById($assignee_id);
340
            if ($user_info) {
341
                $assignee = [ 'id' => $assignee_id, 'name' => $user_info->first_name, 'email' => $user_info->email ];
342
            }
343
        }
344
        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...
345
            $assignee = [ 'id' => $this->user->id, 'name' => $this->user->first_name, 'email' => $this->user->email ];
346
        }
347
        $insValues['assignee'] = $assignee;
348
349
        //$priority = $request->input('priority'); 
350
        //if (!isset($priority) || !$priority)
351
        //{
352
        //    $insValues['priority'] = Provider::getDefaultPriority($project_key);
353
        //}
354
355
        $resolution = $request->input('resolution'); 
356
        if (!isset($resolution) || !$resolution) {
357
            $insValues['resolution'] = 'Unresolved'; 
358
        }
359
360
        // get reporter(creator)
361
        $insValues['reporter'] = [ 'id' => $this->user->id, 'name' => $this->user->first_name, 'email' => $this->user->email ];
362
        $insValues['created_at'] = time();
363
364
        $table = 'issue_' . $project_key;
365
        $max_no = DB::collection($table)->count() + 1;
366
        $insValues['no'] = $max_no;
367
368
        // workflow initialize 
369
        $workflow = $this->initializeWorkflow($issue_type);
370
        $insValues = $insValues + $workflow;
371
372
        $valid_keys = $this->getValidKeysBySchema($schema);
373
        // merge all fields
374
        $insValues = $insValues + array_only($request->all(), $valid_keys);
375
376
        // insert into the table
377
        $id = DB::collection($table)->insertGetId($insValues);
378
379
        // add to histroy table
380
        Provider::snap2His($project_key, $id, $schema);
381
        // trigger event of issue created
382
        Event::fire(new IssueEvent($project_key, $id->__toString(), $insValues['reporter'], [ 'event_key' => 'create_issue' ]));
383
384
        // create the Labels for project
385
        if (isset($insValues['labels']) && $insValues['labels']) {
386
            $this->createLabels($project_key, $insValues['labels']);
387
        }
388
389
        return $this->show($project_key, $id->__toString());
390
    }
391
392
    /**
393
     * initialize the workflow by type.
394
     *
395
     * @param  int $type
396
     * @return array 
397
     */
398
    public function initializeWorkflow($type)
399
    {
400
        // get workflow definition
401
        $wf_definition = Provider::getWorkflowByType($type);
402
        // create and start workflow instacne
403
        $wf_entry = Workflow::createInstance($wf_definition->id, $this->user->id)->start([ 'caller' => $this->user->id ]);
404
        // get the inital step
405
        $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...
406
        $initial_state = $wf_entry->getStepMeta($initial_step->step_id, 'state');
407
408
        $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...
409
        //$ret['resolution'] = 'Unresolved';
410
        $ret['entry_id'] = $wf_entry->getEntryId();
411
        $ret['definition_id'] = $wf_definition->id;
412
413
        return $ret;
414
    }
415
416
    /**
417
     * Display the specified resource.
418
     *
419
     * @param  int $id
420
     * @return \Illuminate\Http\Response
421
     */
422
    public function show($project_key, $id)
423
    {
424
        $issue = DB::collection('issue_' . $project_key)->where('_id', $id)->first();
425
        $schema = Provider::getSchemaByType($issue['type']);
426
        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...
427
            throw new \UnexpectedValueException('the schema of the type is not existed.', -11101);
428
        }
429
430
        if (isset($issue['assignee']['id'])) {
431
            $user = Sentinel::findById($issue['assignee']['id']);
432
            $issue['assignee']['avatar'] = isset($user->avatar) ? $user->avatar : '';
433
        }
434
435
        foreach ($schema as $field)
436
        {
437
            if ($field['type'] === 'File' && isset($issue[$field['key']]) && $issue[$field['key']]) {
438
                foreach ($issue[$field['key']] as $key => $fid)
439
                {
440
                    $issue[$field['key']][$key] = File::find($fid);
441
                }
442
            }
443
        }
444
445
        // get avaliable actions for wf
446
        if (isset($issue['entry_id']) && $issue['entry_id']) {
447
            try {
448
                $wf = new Workflow($issue['entry_id']);
449
                $issue['wfactions'] = $wf->getAvailableActions([ 'project_key' => $project_key, 'issue_id' => $id, 'caller' => $this->user->id ]);
450
            } catch (Exception $e) {
451
                $issue['wfactions'] = [];
452
            }
453
454
            foreach ($issue['wfactions'] as $key => $action)
455
            {
456
                if (isset($action['screen']) && $action['screen'] && $action['screen'] != 'comments') {
457
                    $issue['wfactions'][$key]['schema'] = Provider::getSchemaByScreenId($project_key, $issue['type'], $action['screen']);
458
                }
459
            }
460
        }
461
462
        if (isset($issue['parent_id']) && $issue['parent_id']) {
463
            $issue['parent'] = DB::collection('issue_' . $project_key)->where('_id', $issue['parent_id'])->first(['no', 'type', 'title', 'state']);
464
        }
465
        else
466
        {
467
            $issue['hasSubtasks'] = DB::collection('issue_' . $project_key)->where('parent_id', $id)->exists();
468
        }
469
470
        $issue['subtasks'] = DB::collection('issue_' . $project_key)->where('parent_id', $id)->where('del_flg', '<>', 1)->orderBy('created_at', 'asc')->get(['no', 'type', 'title', 'state']);
471
472
        $issue['links'] = [];
473
        $links = DB::collection('linked')->where('src', $id)->orWhere('dest', $id)->where('del_flg', '<>', 1)->orderBy('created_at', 'asc')->get();
474
        $link_fields = ['_id', 'no', 'type', 'title', 'state'];
475
        foreach ($links as $link)
476
        {
477 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...
478
                $link['src'] = array_only($issue, $link_fields);
479
            }
480
            else
481
            {
482
                $src_issue = DB::collection('issue_' . $project_key)->where('_id', $link['src'])->first();
483
                $link['src'] = array_only($src_issue, $link_fields);
484
            }
485
486 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...
487
                $link['dest'] = array_only($issue, $link_fields);
488
            }
489
            else
490
            {
491
                $dest_issue = DB::collection('issue_' . $project_key)->where('_id', $link['dest'])->first();
492
                $link['dest'] = array_only($dest_issue, $link_fields);
493
            }
494
            array_push($issue['links'], $link);
495
        }
496
497
        $issue['watchers'] = array_column(Watch::where('issue_id', $id)->orderBy('_id', 'desc')->get()->toArray(), 'user');
498
        
499
        if (Watch::where('issue_id', $id)->where('user.id', $this->user->id)->exists()) {
500
            $issue['watching'] = true;
501
        }
502
503
        $comments_num = 0;
504
        $comments = DB::collection('comments_' . $project_key)
505
            ->where('issue_id', $id)
506
            ->get();
507
        foreach($comments as $comment)
508
        {
509
            $comments_num += 1;
510
            if (isset($comment['reply'])) {
511
                $comments_num += count($comment['reply']);
512
            }
513
        }
514
        $issue['comments_num'] = $comments_num;
515
516
        $issue['gitcommits_num'] = DB::collection('git_commits_' . $project_key)
517
            ->where('issue_id', $id)
518
            ->count();
519
520
        $issue['worklogs_num'] = Worklog::Where('project_key', $project_key)
521
            ->where('issue_id', $id)
522
            ->count();
523
524
        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...
525
    }
526
527
    /**
528
     * Display the specified resource.
529
     *
530
     * @param  string $project_key
531
     * @param  string $id
532
     * @return \Illuminate\Http\Response
533
     */
534
    public function wfactions($project_key, $id)
535
    {
536
        $issue = DB::collection('issue_' . $project_key)->where('_id', $id)->first();
537
538
        $wf = new Workflow($issue['entry_id']);
539
        $wfactions = $wf->getAvailableActions([ 'project_key' => $project_key, 'issue_id' => $id, 'caller' => $this->user->id ], true);
540
        foreach ($wfactions as $key => $action)
541
        {
542
            if (isset($action['screen']) && $action['screen']) {
543
                $wfactions[$key]['schema'] = Provider::getSchemaByScreenId($project_key, $issue['type'], $action['screen']);
544
            }
545
        }
546
547
        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...
548
    }
549
550
    /**
551
     * Display the specified resource.
552
     *
553
     * @param  string $project_key
554
     * @return \Illuminate\Http\Response
555
     */
556
    public function getOptions($project_key)
557
    {
558
        // get project users
559
        $users = Provider::getUserList($project_key);
560
        // get project users fix me
561
        $assignees = Provider::getAssignedUsers($project_key);
562
        // get state list
563
        $states = Provider::getStateOptions($project_key);
564
        // get resolution list
565
        $resolutions = Provider::getResolutionOptions($project_key);
566
        // get priority list
567
        $priorities = Provider::getPriorityOptions($project_key);
568
        // get version list
569
        $versions = Provider::getVersionList($project_key, ['name']);
570
        // get module list
571
        $modules = Provider::getModuleList($project_key, ['name']);
572
        // get project epics
573
        $epics = Provider::getEpicList($project_key);
574
        // get project labels
575
        $labels = Provider::getLabelOptions($project_key);
576
        // get project types
577
        $types = Provider::getTypeListExt($project_key, [ 'user' => $users, 'assignee' => $assignees, 'state' => $states, 'resolution' => $resolutions, 'priority' => $priorities, 'version' => $versions, 'module' => $modules, 'epic' => $epics, 'labels' => $labels ]);
578
        // get project sprints
579
        $new_sprints = [];
580
        $sprints = Provider::getSprintList($project_key);
581
        foreach ($sprints as $sprint)
582
        {
583
            $new_sprints[] = [ 'no' => $sprint['no'], 'name' => $sprint['name'] ];
584
        }
585
        // get defined fields
586
        $fields = Provider::getFieldList($project_key);
587
        // get defined searchers
588
        $filters = Provider::getIssueFilters($project_key, $this->user->id);
589
        // get defined list columns
590
        $display_columns = Provider::getIssueDisplayColumns($project_key, $this->user->id);
591
        // get timetrack options
592
        $timetrack = $this->getTimeTrackSetting();
593
        // get issue link relations
594
        $relations = $this->getLinkRelations();
595
596
        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...
597
            [ 
598
            'ecode' => 0, 
599
            '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...
600
                [ 
601
                'users' => $users, 
602
                'assignees' => $assignees, 
603
                'types' => $types, 
604
                'states' => $states, 
605
                'resolutions' => $resolutions, 
606
                'priorities' => $priorities, 
607
                'modules' => $modules, 
608
                'labels' => $labels, 
609
                'versions' => $versions, 
610
                'epics' => $epics,
611
                'sprints' => $new_sprints,
612
                'filters' => $filters, 
613
                'display_columns' => $display_columns, 
614
                'timetrack' => $timetrack, 
615
                'relations' => $relations, 
616
                'fields' => $fields 
617
                ]
618
            ) 
619
            ]
620
        );
621
    }
622
623
    /**
624
     * update issue assignee.
625
     *
626
     * @param  \Illuminate\Http\Request $request
627
     * @param  string                   $project_key
628
     * @param  string                   $id
629
     * @return \Illuminate\Http\Response
630
     */
631
    public function setAssignee(Request $request, $project_key, $id)
632
    {
633
        $table = 'issue_' . $project_key;
634
        $issue = DB::collection($table)->find($id);
635
        if (!$issue) {
636
            throw new \UnexpectedValueException('the issue does not exist or is not in the project.', -11103);
637
        }
638
639
        if (!$this->isPermissionAllowed($project_key, 'assign_issue')) {
640
            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...
641
        }
642
643
        $updValues = []; $assignee = [];
644
        $assignee_id = $request->input('assignee');
645
        if (!isset($assignee_id) || !$assignee_id) {
646
            throw new \UnexpectedValueException('the issue assignee cannot be empty.', -11104);
647
        }
648
649
        if ($assignee_id === 'me') {
650
            if (!$this->isPermissionAllowed($project_key, 'assigned_issue')) {
651
                return response()->json(['ecode' => -11117, 'emsg' => 'the current user has not assigned-issue permission.']);
652
            }
653
654
            $assignee = [ 'id' => $this->user->id, 'name' => $this->user->first_name, 'email' => $this->user->email ];
655
            $updValues['assignee'] = $assignee;
656
        }
657
        else
658
        {
659
            if (!$this->isPermissionAllowed($project_key, 'assigned_issue', $assignee_id)) {
660
                return response()->json(['ecode' => -11118, 'emsg' => 'the assigned user has not assigned-issue permission.']);
661
            }
662
663
            $user_info = Sentinel::findById($assignee_id);
664
            if ($user_info) {
665
                $assignee = [ 'id' => $assignee_id, 'name' => $user_info->first_name, 'email' => $user_info->email ];
666
                $updValues['assignee'] = $assignee;
667
            }
668
        }
669
670
        // issue assignee has no change.
671
        if ($assignee['id'] === $issue['assignee']['id']) {
672
            return $this->show($project_key, $id);
673
        }
674
675
        $updValues['modifier'] = [ 'id' => $this->user->id, 'name' => $this->user->first_name, 'email' => $this->user->email ];
676
        $updValues['updated_at'] = time();
677
        DB::collection($table)->where('_id', $id)->update($updValues);
678
679
        // add to histroy table
680
        $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...
681
        // trigger event of issue edited
682
        Event::fire(new IssueEvent($project_key, $id, $updValues['modifier'], [ 'event_key' => 'assign_issue', 'data' => [ 'old_user' => $issue['assignee'], 'new_user' => $assignee ] ]));
683
684
        return $this->show($project_key, $id);
685
    }
686
687
    /**
688
     * set issue labels.
689
     *
690
     * @param  \Illuminate\Http\Request $request
691
     * @param  string                   $project_key
692
     * @param  string                   $id
693
     * @return \Illuminate\Http\Response
694
     */
695
    public function setLabels(Request $request, $project_key, $id)
696
    {
697 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...
698
            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...
699
        }
700
701
        $labels = $request->input('labels');
702
        if (!isset($labels)) {
703
            return $this->show($project_key, $id);
704
        }
705
706
        $table = 'issue_' . $project_key;
707
        $issue = DB::collection($table)->find($id);
708
        if (!$issue) {
709
            throw new \UnexpectedValueException('the issue does not exist or is not in the project.', -11103);
710
        }
711
712
        $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...
713
714
        $updValues['modifier'] = [ 'id' => $this->user->id, 'name' => $this->user->first_name, 'email' => $this->user->email ];
715
        $updValues['updated_at'] = time();
716
        DB::collection($table)->where('_id', $id)->update($updValues);
717
718
        // add to histroy table
719
        $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...
720
        // trigger event of issue edited
721
        Event::fire(new IssueEvent($project_key, $id, $updValues['modifier'], [ 'event_key' => 'edit_issue', 'snap_id' => $snap_id ]));
722
        // create the Labels for project
723
        if ($labels) {
724
            $this->createLabels($project_key, $labels);
725
        }
726
727
        return $this->show($project_key, $id);
728
    }
729
730
    /**
731
     * create the new labels for project.
732
     *
733
     * @param  string $project_key
734
     * @param  array  $labels
735
     * @return void
736
     */
737
    public function createLabels($project_key, $labels)
738
    {
739
        $created_labels = [];
740
        $project_labels = Labels::where('project_key', $project_key)
741
            ->whereIn('name', $labels)
742
            ->get();
743
        foreach ($project_labels as $label)
744
        {
745
            $created_labels[] = $label->name;
746
        }
747
        // get uncreated labels
748
        $new_labels = array_diff($labels, $created_labels);
749
        foreach ($new_labels as $label)
750
        {
751
            Labels::create([ 'project_key' => $project_key, 'name' => $label ]);
752
        }
753
        return true;
754
    }
755
756
    /**
757
     * check the required field
758
     *
759
     * @param  array  $schema
760
     * @param  array  $data
761
     * @param  string $mode
762
     * @return bool
763
     */
764
    public function requiredCheck($schema, $data, $mode='create')
765
    {
766
        foreach ($schema as $field)
767
        {
768
            if (isset($field['required']) && $field['required']) {
769
                if ($mode == 'update') {
770 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...
771
                        return false;
772
                    }
773
                }
774 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...
775
                {
776
                    if (!isset($data[$field['key']]) || !$data[$field['key']] && $data[$field['key']] !== 0) {
777
                        return false;
778
                    }
779
                }
780
            }
781
        }
782
        return true;
783
    }
784
785
    /**
786
     * check if the unix stamp
787
     *
788
     * @param  int $timestamp
789
     * @return bool
790
     */
791
    public function isTimestamp($timestamp) 
792
    {
793
        if(strtotime(date('Y-m-d H:i:s', $timestamp)) === $timestamp) {
794
            return $timestamp;
795
        } 
796
        else 
797
        {
798
            return false;
799
        }
800
    }
801
802
    /**
803
     * get valid keys by schema
804
     *
805
     * @param  array $schema
806
     * @return array
807
     */
808
    public function getValidKeysBySchema($schema=[])
809
    {
810
        $valid_keys = array_merge(array_column($schema, 'key'), [ 'type', 'assignee', 'labels', 'parent_id', 'resolution', 'priority', 'progress', 'expect_start_time', 'expect_compete_time' ]);
811
812
        foreach ($schema as $field)
813
        {
814
            if ($field['type'] == 'MultiUser') {
815
                $valid_keys[] = $field['key'] . '_ids';
816
            }
817
            else if ($field['type'] == 'TimeTracking') {
818
                $valid_keys[] = $field['key'] . '_m';
819
            }
820
        }
821
822
        return $valid_keys;
823
    }
824
825
    /**
826
     * Update the specified resource in storage.
827
     *
828
     * @param  \Illuminate\Http\Request $request
829
     * @param  string                   $project_key
830
     * @param  string                   $id
831
     * @return \Illuminate\Http\Response
832
     */
833
    public function update(Request $request, $project_key, $id)
834
    {
835 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...
836
            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...
837
        }
838
839
        if (!$request->all()) {
840
            return $this->show($project_key, $id); 
841
        }
842
843
        $table = 'issue_' . $project_key;
844
        $issue = DB::collection($table)->find($id);
845
        if (!$issue) {
846
            throw new \UnexpectedValueException('the issue does not exist or is not in the project.', -11103);
847
        }
848
849
        $schema = Provider::getSchemaByType($request->input('type') ?: $issue['type']);
850
        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...
851
            throw new \UnexpectedValueException('the schema of the type is not existed.', -11101);
852
        }
853
854
        if (!$this->requiredCheck($schema, $request->all(), 'update')) {
855
            throw new \UnexpectedValueException('the required field is empty.', -11121);
856
        }
857
858
        // handle timetracking
859
        $updValues = [];
860 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...
861
        {
862
            $fieldValue = $request->input($field['key']);
863
            if (!isset($fieldValue) || !$fieldValue) {
864
                continue;
865
            }
866
867
            if ($field['type'] == 'TimeTracking') {
868
                if (!$this->ttCheck($fieldValue)) {
869
                    throw new \UnexpectedValueException('the format of timetracking field is incorrect.', -11102);
870
                }
871
872
                $updValues[$field['key']] = $this->ttHandle($fieldValue);
873
                $updValues[$field['key'] . '_m'] = $this->ttHandleInM($updValues[$field['key']]);
874
            }
875
            else if ($field['type'] == 'DatePicker' || $field['type'] == 'DateTimePicker') {
876
                if ($this->isTimestamp($fieldValue) === false) {
877
                    throw new \UnexpectedValueException('the format of datepicker field is incorrect.', -11122);
878
                }
879
            }
880
            else if ($field['type'] == 'SingleUser') {
881
                $user_info = Sentinel::findById($fieldValue);
882
                if ($user_info) {
883
                    $updValues[$field['key']] = [ 'id' => $fieldValue, 'name' => $user_info->first_name, 'email' => $user_info->email ];
884
                }
885
            }
886
            else if ($field['type'] == 'MultiUser') {
887
                $user_ids = $fieldValue;
888
                $updValues[$field['key']] = [];
889
                $new_user_ids = [];
890
                foreach ($user_ids as $uid)
891
                {
892
                    $user_info = Sentinel::findById($uid);
893
                    if ($user_info) {
894
                        array_push($updValues[$field['key']], [ 'id' => $uid, 'name' => $user_info->first_name, 'email' => $user_info->email ]);
895
                    }
896
                    $new_user_ids[] = $uid;
897
                }
898
                $updValues[$field['key'] . '_ids'] = $new_user_ids;
899
            }
900
        }
901
902
        $assignee_id = $request->input('assignee');
903
        if ($assignee_id) {
904
            if ((!isset($issue['assignee']) || (isset($issue['assignee']) && $assignee_id != $issue['assignee']['id'])) && !$this->isPermissionAllowed($project_key, 'assigned_issue', $assignee_id)) {
905
                return response()->json(['ecode' => -11118, 'emsg' => 'the assigned user has not assigned-issue permission.']);
906
            }
907
908
            $user_info = Sentinel::findById($assignee_id);
909
            if ($user_info) {
910
                $assignee = [ 'id' => $assignee_id, 'name' => $user_info->first_name, 'email' => $user_info->email ];
911
                $updValues['assignee'] = $assignee;
912
            }
913
        }
914
915
        $valid_keys = $this->getValidKeysBySchema($schema);
916
        $updValues = $updValues + array_only($request->all(), $valid_keys);
917
        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...
918
            return $this->show($project_key, $id);
919
        }
920
921
        $updValues['modifier'] = [ 'id' => $this->user->id, 'name' => $this->user->first_name, 'email' => $this->user->email ];
922
        $updValues['updated_at'] = time();
923
924
        DB::collection($table)->where('_id', $id)->update($updValues);
925
926
        // add to histroy table
927
        $snap_id = Provider::snap2His($project_key, $id, $schema, array_keys(array_only($request->all(), $valid_keys)));
928
        // trigger event of issue edited
929
        Event::fire(new IssueEvent($project_key, $id, $updValues['modifier'], [ 'event_key' => 'edit_issue', 'snap_id' => $snap_id ]));
930
        // create the Labels for project
931
        if (isset($updValues['labels']) && $updValues['labels']) {
932
            $this->createLabels($project_key, $updValues['labels']);
933
        }
934
935
        return $this->show($project_key, $id); 
936
    }
937
938
    /**
939
     * Remove the specified resource from storage.
940
     *
941
     * @param  string $project_key
942
     * @param  string $id
943
     * @return \Illuminate\Http\Response
944
     */
945
    public function destroy($project_key, $id)
946
    {
947
        $table = 'issue_' . $project_key;
948
        $issue = DB::collection($table)
949
            ->where('_id', $id)
950
            ->where('del_flg', '<>', 1)
951
            ->first();
952
        if (!$issue) {
953
            throw new \UnexpectedValueException('the issue does not exist or is not in the project.', -11103);
954
        }
955
956
        $user = [ 'id' => $this->user->id, 'name' => $this->user->first_name, 'email' => $this->user->email ];
957
        
958
        $ids = [ $id ];
959
        // delete all subtasks of this issue
960
        $subtasks = DB::collection('issue_' . $project_key)
961
            ->where('parent_id', $id)
962
            ->where('del_flg', '<>', 1)
963
            ->get();
964 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...
965
        {
966
            $sub_id = $subtask['_id']->__toString();
967
            DB::collection($table)->where('_id', $sub_id)->update([ 'del_flg' => 1 ]);
968
969
            // delete linked relation
970
            DB::collection('linked')->where('src', $sub_id)->orWhere('dest', $sub_id)->delete();
971
972
            Event::fire(new IssueEvent($project_key, $sub_id, $user, [ 'event_key' => 'del_issue' ]));
973
            $ids[] = $sub_id;
974
        }
975
976
        // delete linked relation
977
        DB::collection('linked')->where('src', $id)->orWhere('dest', $id)->delete();
978
        // delete this issue
979
        DB::collection($table)->where('_id', $id)->update([ 'del_flg' => 1 ]);
980
        // trigger event of issue deleted 
981
        Event::fire(new IssueEvent($project_key, $id, $user, [ 'event_key' => 'del_issue' ]));
982
983
        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...
984
    }
985
986
    /**
987
     * get the project filters.
988
     *
989
     * @param  string $project_key
990
     * @return array
991
     */
992
    public function getIssueFilters($project_key)
993
    {
994
        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...
995
    }
996
997
    /**
998
     * save the custimized filter.
999
     *
1000
     * @param  string $project_key
1001
     * @return \Illuminate\Http\Response
1002
     */
1003
    public function saveIssueFilter(Request $request, $project_key)
1004
    {
1005
        $name = $request->input('name');
1006
        if (!$name) {
1007
            throw new \UnexpectedValueException('the name can not be empty.', -11105);
1008
        }
1009
1010
        if (UserIssueFilters::whereRaw([ 'name' => $name, 'user' => $this->user->id, 'project_key' => $project_key ])->exists()) {
1011
            throw new \UnexpectedValueException('filter name cannot be repeated', -11106);
1012
        }
1013
1014
        $query = $request->input('query') ?: [];
1015
        
1016
        $res = UserIssueFilters::where('project_key', $project_key)
1017
            ->where('user', $this->user->id)
1018
            ->first();
1019
        if ($res) {
1020
            $filters = isset($res['filters']) ? $res['filters'] : [];
1021 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...
1022
            {
1023
                if (isset($filter['name']) && $filter['name'] === $name) {
1024
                    throw new \UnexpectedValueException('filter name cannot be repeated', -11106);
1025
                }
1026
            }
1027
            array_push($filters, [ 'id' => md5(microtime()), 'name' => $name, 'query' => $query ]);
1028
            $res->filters = $filters;
1029
            $res->save();
1030
        }
1031
        else
1032
        {
1033
            $filters = Provider::getDefaultIssueFilters();
1034 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...
1035
            {
1036
                if (isset($filter['name']) && $filter['name'] === $name) {
1037
                    throw new \UnexpectedValueException('filter name cannot be repeated', -11106);
1038
                }
1039
            }
1040
            array_push($filters, [ 'id' => md5(microtime()), 'name' => $name, 'query' => $query ]);
1041
            UserIssueFilters::create([ 'project_key' => $project_key, 'user' => $this->user->id, 'filters' => $filters ]); 
1042
        }
1043
        return $this->getIssueFilters($project_key);
1044
    }
1045
1046
    /**
1047
     * reset the issue filters.
1048
     *
1049
     * @param  string $project_key
1050
     * @return \Illuminate\Http\Response
1051
     */
1052
    public function resetIssueFilters(Request $request, $project_key)
1053
    {
1054
        UserIssueFilters::where('project_key', $project_key)
1055
            ->where('user', $this->user->id)
1056
            ->delete();
1057
1058
        return $this->getIssueFilters($project_key);
1059
    }
1060
1061
    /**
1062
     * get the default columns.
1063
     *
1064
     * @param  string $project_key
1065
     * @return array
1066
     */
1067
    public function getDisplayColumns($project_key)
1068
    {
1069
        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...
1070
    }
1071
1072
    /**
1073
     * reset the issue list diplay columns.
1074
     *
1075
     * @param  string $project_key
1076
     * @return \Illuminate\Http\Response
1077
     */
1078
    public function resetDisplayColumns(Request $request, $project_key)
1079
    {
1080
        UserIssueListColumns::where('project_key', $project_key)
1081
            ->where('user', $this->user->id)
1082
            ->delete();
1083
1084
        $delete_from_project = $request->input('delete_from_project') ?: false;
1085
        if ($delete_from_project && $this->isPermissionAllowed($project_key, 'manage_project')) {
1086
            ProjectIssueListColumns::where('project_key', $project_key)->delete();
1087
        }
1088
 
1089
        return $this->getDisplayColumns($project_key);
1090
    }
1091
1092
    /**
1093
     * set the issue list display columns.
1094
     *
1095
     * @param  string $project_key
1096
     * @return \Illuminate\Http\Response
1097
     */
1098
    public function setDisplayColumns(Request $request, $project_key)
1099
    {
1100
        $column_keys = [];
1101
        $new_columns = [];
1102
        $columns = $request->input('columns') ?: [];
1103
        foreach ($columns as $column)
1104
        {
1105
            if (!isset($column['key'])) {
1106
                continue;
1107
            }
1108
1109
            if (in_array($column['key'], $column_keys)) {
1110
                continue;
1111
            }
1112
            $column_keys[] = $column['key'];
1113
            $new_columns[] = array_only($column, [ 'key', 'width' ]);
1114
        }
1115
1116
        $res = UserIssueListColumns::where('project_key', $project_key)
1117
            ->where('user', $this->user->id)
1118
            ->first();
1119 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...
1120
            $res->columns = $new_columns;
1121
            $res->column_keys = $column_keys;
1122
            $res->save();
1123
        }
1124
        else
1125
        {
1126
            UserIssueListColumns::create([ 'project_key' => $project_key, 'user' => $this->user->id, 'column_keys' => $column_keys, 'columns' => $new_columns ]); 
1127
        }
1128
1129
        $save_for_project = $request->input('save_for_project') ?: false;
1130
        if ($save_for_project && $this->isPermissionAllowed($project_key, 'manage_project')) {
1131
            $res = ProjectIssueListColumns::where('project_key', $project_key)->first();
1132 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...
1133
                $res->columns = $new_columns;
1134
                $res->column_keys = $column_keys;
1135
                $res->save();
1136
            }
1137
            else
1138
            {
1139
                ProjectIssueListColumns::create([ 'project_key' => $project_key, 'column_keys' => $column_keys, 'columns' => $new_columns ]);
1140
            }
1141
        }
1142
1143
        return $this->getDisplayColumns($project_key);
1144
    }
1145
1146
    /**
1147
     * edit the mode filters.
1148
     *
1149
     * @param  string $project_key
1150
     * @return \Illuminate\Http\Response
1151
     */
1152
    public function editFilters(Request $request, $project_key)
1153
    {
1154
        $sequence = $request->input('sequence');
1155
        if (isset($sequence)) {
1156
            $old_filters = Provider::getDefaultIssueFilters();
1157
            $res = UserIssueFilters::where('project_key', $project_key)
1158
                ->where('user', $this->user->id)
1159
                ->first();
1160
            if ($res) {
1161
                $old_filters = isset($res->filters) ? $res->filters : [];
1162
            }
1163
            
1164
            $new_filters = [];
1165 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...
1166
            {
1167
                foreach ($old_filters as $filter)
1168
                {
1169
                    if ($filter['id'] === $id) {
1170
                        $new_filters[] = $filter;
1171
                        break;
1172
                    }
1173
                }
1174
            }
1175 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...
1176
                $res->filters = $new_filters;
1177
                $res->save();
1178
            }
1179
            else
1180
            {
1181
                UserIssueFilters::create([ 'project_key' => $project_key, 'user' => $this->user->id, 'filters' => $new_filters ]); 
1182
            }
1183
        }
1184
        return $this->getIssueFilters($project_key);
1185
    }
1186
1187
    /**
1188
     * reset the issue list columns.
1189
     *
1190
     * @param  string $project_key
1191
     * @return \Illuminate\Http\Response
1192
     */
1193
    public function resetColumns(Request $request, $project_key)
1194
    {
1195
        UserIssueListColumns::where('project_key', $project_key)
1196
            ->where('user', $this->user->id)
1197
            ->delete();
1198
1199
        return $this->getColumns($project_key);
1200
    }
1201
1202
    /**
1203
     * get the history records.
1204
     *
1205
     * @param  string $project_key
1206
     * @param  string $id
1207
     * @return \Illuminate\Http\Response
1208
     */
1209
    public function getHistory(Request $request, $project_key, $id)
1210
    {
1211
        $changedRecords = [];
1212
        $records = DB::collection('issue_his_' . $project_key)->where('issue_id', $id)->orderBy('_id', 'asc')->get();
1213
        foreach ($records as $i => $item)
1214
        {
1215
            if ($i == 0) {
1216
                $changedRecords[] = [ 'operation' => 'create', 'operator' => $item['operator'], 'operated_at' => $item['operated_at'] ];
1217
            }
1218
            else
1219
            {
1220
                $changed_items = [];
1221
                $changed_items['operation'] = 'modify';
1222
                $changed_items['operated_at'] = $item['operated_at'];
1223
                $changed_items['operator'] = $item['operator'];
1224
1225
                $diff_items = []; $diff_keys = [];
1226
                $after_data = $item['data'];
1227
                $before_data = $records[$i - 1]['data'];
1228
1229
                foreach ($after_data as $key => $val)
1230
                {
1231 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...
1232
                        $tmp = [];
1233
                        $tmp['field'] = isset($val['name']) ? $val['name'] : '';
1234
                        $tmp['after_value'] = isset($val['value']) ? $val['value'] : '';
1235
                        $tmp['before_value'] = isset($before_data[$key]) && isset($before_data[$key]['value']) ? $before_data[$key]['value'] : '';
1236
1237
                        if (is_array($tmp['after_value']) && is_array($tmp['before_value'])) {
1238
                            $diff1 = array_diff($tmp['after_value'], $tmp['before_value']);
1239
                            $diff2 = array_diff($tmp['before_value'], $tmp['after_value']);
1240
                            $tmp['after_value'] = implode(',', $diff1);
1241
                            $tmp['before_value'] = implode(',', $diff2);
1242
                        }
1243
                        else
1244
                        {
1245
                            if (is_array($tmp['after_value'])) {
1246
                                $tmp['after_value'] = implode(',', $tmp['after_value']);
1247
                            }
1248
                            if (is_array($tmp['before_value'])) {
1249
                                $tmp['before_value'] = implode(',', $tmp['before_value']);
1250
                            }
1251
                        }
1252
                        $diff_items[] = $tmp; 
1253
                        $diff_keys[] = $key; 
1254
                    }
1255
                }
1256
1257
                foreach ($before_data as $key => $val)
1258
                {
1259
                    if (array_search($key, $diff_keys) !== false) {
1260
                        continue;
1261
                    }
1262
1263 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...
1264
                        $tmp = [];
1265
                        $tmp['field'] = isset($val['name']) ? $val['name'] : '';
1266
                        $tmp['before_value'] = isset($val['value']) ? $val['value'] : '';
1267
                        $tmp['after_value'] = isset($after_data[$key]) && isset($after_data[$key]['value']) ? $after_data[$key]['value'] : '';
1268
                        if (is_array($tmp['after_value']) && is_array($tmp['before_value'])) {
1269
                            $diff1 = array_diff($tmp['after_value'], $tmp['before_value']);
1270
                            $diff2 = array_diff($tmp['before_value'], $tmp['after_value']);
1271
                            $tmp['after_value'] = implode(',', $diff1);
1272
                            $tmp['before_value'] = implode(',', $diff2);
1273
                        }
1274
                        else
1275
                        {
1276
                            if (is_array($tmp['after_value'])) {
1277
                                $tmp['after_value'] = implode(',', $tmp['after_value']);
1278
                            }
1279
                            if (is_array($tmp['before_value'])) {
1280
                                $tmp['before_value'] = implode(',', $tmp['before_value']);
1281
                            }
1282
                        }
1283
1284
                        $diff_items[] = $tmp; 
1285
                    }
1286
                }
1287
1288
                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...
1289
                    $changed_items['data'] = $diff_items;
1290
                    $changedRecords[] = $changed_items;
1291
                }
1292
            }
1293
        }
1294
1295
        $sort = ($request->input('sort') === 'asc') ? 'asc' : 'desc';
1296
        if ($sort === 'desc') {
1297
            $changedRecords = array_reverse($changedRecords);
1298
        }
1299
1300
        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...
1301
    }
1302
1303
    /**
1304
     * workflow action.
1305
     *
1306
     * @param  string $project_key
1307
     * @param  string $id
1308
     * @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...
1309
     * @return \Illuminate\Http\Response
1310
     */
1311
    public function doAction(Request $request, $project_key, $id, $workflow_id)
1312
    {
1313
        $action_id = $request->input('action_id');
1314
        if (!$action_id) {
1315
            throw new Exception('the executed action has error.', -11115);
1316
        }
1317
1318
        try {
1319
            $entry = new Workflow($workflow_id);
1320
            $entry->doAction($action_id, [ 'project_key' => $project_key, 'issue_id' => $id, 'caller' => $this->user->id ] + array_only($request->all(), [ 'comments' ]));
1321
        } catch (Exception $e) {
1322
            throw new Exception('the executed action has error.', -11115);
1323
        }
1324
        return $this->show($project_key, $id); 
1325
    }
1326
1327
    /**
1328
     * workflow action.
1329
     *
1330
     * @param  string $project_key
1331
     * @param  string $id
1332
     * @return \Illuminate\Http\Response
1333
     */
1334
    public function watch(Request $request, $project_key, $id)
1335
    {
1336
        Watch::where('issue_id', $id)->where('user.id', $this->user->id)->delete();
1337
1338
        $cur_user = [ 'id' => $this->user->id, 'name' => $this->user->first_name, 'email' => $this->user->email ];
1339
1340
        $flag = $request->input('flag');
1341
        if (isset($flag) && $flag) {
1342
            Watch::create([ 'project_key' => $project_key, 'issue_id' => $id, 'user' => $cur_user ]);
1343
            // trigger event of issue watched 
1344
            Event::fire(new IssueEvent($project_key, $id, $cur_user, [ 'event_key' => 'watched_issue' ]));
1345
        }
1346
        else
1347
        {
1348
            $flag = false;
1349
            // trigger event of issue watched 
1350
            Event::fire(new IssueEvent($project_key, $id, $cur_user, [ 'event_key' => 'unwatched_issue' ]));
1351
        }
1352
        
1353
        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...
1354
    }
1355
1356
    /**
1357
     * reset issue state.
1358
     *
1359
     * @param  string $project_key
1360
     * @param  string $id
1361
     * @return \Illuminate\Http\Response
1362
     */
1363
    public function resetState(Request $request, $project_key, $id)
1364
    {
1365
        $updValues = [];
1366
1367
        $assignee_id = $request->input('assignee');
1368 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...
1369
            if ($assignee_id) {
1370
                if (!$this->isPermissionAllowed($project_key, 'assigned_issue', $assignee_id)) {
1371
                    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...
1372
                }
1373
1374
                $user_info = Sentinel::findById($assignee_id);
1375
                if ($user_info) {
1376
                    $assignee = [ 'id' => $assignee_id, 'name' => $user_info->first_name, 'email' => $user_info->email ];
1377
                    $updValues['assignee'] = $assignee;
1378
                }
1379
            }
1380
            else
1381
            {
1382
                throw new \UnexpectedValueException('the issue assignee cannot be empty.', -11104);
1383
            }
1384
        }
1385
1386
        $resolution = $request->input('resolution');
1387
        if (isset($resolution) && $resolution) {
1388
            $updValues['resolution'] = $resolution;
1389
        }
1390
1391
        $issue = DB::collection('issue_' . $project_key)->where('_id', $id)->first();
1392
        if (!$issue) {
1393
            throw new \UnexpectedValueException('the issue does not exist or is not in the project.', -11103);
1394
        }
1395
1396
        // workflow initialize
1397
        $workflow = $this->initializeWorkflow($issue['type']);
1398
        $updValues = $updValues + $workflow;
1399
1400
        $updValues['modifier'] = [ 'id' => $this->user->id, 'name' => $this->user->first_name, 'email' => $this->user->email ];
1401
        $updValues['updated_at'] = time();
1402
1403
        $table = 'issue_' . $project_key;
1404
        DB::collection($table)->where('_id', $id)->update($updValues);
1405
1406
        // add to histroy table
1407
        $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...
1408
        // trigger event of issue edited
1409
        Event::fire(new IssueEvent($project_key, $id, $updValues['modifier'], [ 'event_key' => 'reset_issue', 'snap_id' => $snap_id ]));
1410
1411
        return $this->show($project_key, $id);
1412
    }
1413
1414
    /**
1415
     * copy issue.
1416
     *
1417
     * @param  string $project_key
1418
     * @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...
1419
     * @return \Illuminate\Http\Response
1420
     */
1421
    public function copy(Request $request, $project_key)
1422
    {
1423
        $title = $request->input('title');
1424
        if (!$title) {
1425
            throw new \UnexpectedValueException('the issue title cannot be empty.', -11108);
1426
        }
1427
1428
        $src_id = $request->input('source_id');
1429
        if (!isset($src_id) || !$src_id) {
1430
            throw new \UnexpectedValueException('the copied issue id cannot be empty.', -11109);
1431
        }
1432
1433
        $src_issue = DB::collection('issue_' . $project_key)->where('_id', $src_id)->first();
1434
        if (!$src_issue ) {
1435
            throw new \UnexpectedValueException('the copied issue does not exist or is not in the project.', -11103);
1436
        }
1437
1438
        $schema = Provider::getSchemaByType($src_issue['type']);
1439
        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...
1440
            throw new \UnexpectedValueException('the schema of the type is not existed.', -11101);
1441
        }
1442
1443
        $valid_keys = $this->getValidKeysBySchema($schema);
1444
        $insValues = array_only($src_issue, $valid_keys);
1445
1446
        $insValues['title'] = $title;
1447
        // get reporter(creator)
1448
        $insValues['reporter'] = [ 'id' => $this->user->id, 'name' => $this->user->first_name, 'email' => $this->user->email ];
1449
1450
        $assignee_id = $request->input('assignee');
1451 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...
1452
            if ($assignee_id) {
1453
                if (!$this->isPermissionAllowed($project_key, 'assigned_issue', $assignee_id)) {
1454
                    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...
1455
                }
1456
1457
                $user_info = Sentinel::findById($assignee_id);
1458
                if ($user_info) {
1459
                    $assignee = [ 'id' => $assignee_id, 'name' => $user_info->first_name, 'email' => $user_info->email ];
1460
                    $insValues['assignee'] = $assignee;
1461
                }
1462
            }
1463
            else
1464
            {
1465
                throw new \UnexpectedValueException('the issue assignee cannot be empty.', -11104);
1466
            }
1467
        }
1468
1469
        $resolution = $request->input('resolution');
1470
        if (isset($resolution) && $resolution) {
1471
            $insValues['resolution'] = $resolution;
1472
        }
1473
1474
        $table = 'issue_' . $project_key;
1475
        $max_no = DB::collection($table)->count() + 1;
1476
        $insValues['no'] = $max_no;
1477
1478
        // workflow initialize
1479
        $workflow = $this->initializeWorkflow($src_issue['type']);
1480
        $insValues = array_merge($insValues, $workflow);
1481
        // created time
1482
        $insValues['created_at'] = time();
1483
1484
        $id = DB::collection($table)->insertGetId($insValues);
1485
1486
        $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...
1487
        // add to histroy table
1488
        Provider::snap2His($project_key, $id, $schema);
1489
        // create link of clone
1490
        Linked::create([ 'src' => $src_id, 'relation' => 'is cloned by', 'dest' => $id->__toString(), 'creator' => $insValues['reporter'] ]);
1491
        // trigger event of issue created 
1492
        Event::fire(new IssueEvent($project_key, $id->__toString(), $insValues['reporter'], [ 'event_key' => 'create_issue' ]));
1493
        // trigger event of link created 
1494
        Event::fire(new IssueEvent($project_key, $src_id, $insValues['reporter'], [ 'event_key' => 'create_link', 'data' => [ 'relation' => 'is cloned by', 'dest' => $id->__toString() ] ]));
1495
1496
        return $this->show($project_key, $id->__toString());
1497
    }
1498
1499
    /**
1500
     * covert issue from subtask to standard or from standard to subtask.
1501
     *
1502
     * @param  \Illuminate\Http\Request $request
1503
     * @param  string                   $project_key
1504
     * @param  string                   $id
1505
     * @return \Illuminate\Http\Response
1506
     */
1507
    public function convert(Request $request, $project_key, $id)
1508
    {
1509
        $table = 'issue_' . $project_key;
1510
        $issue = DB::collection($table)->find($id);
1511
        if (!$issue) {
1512
            throw new \UnexpectedValueException('the issue does not exist or is not in the project.', -11103);
1513
        }
1514
1515
        $type = $request->input('type');
1516
        if (!isset($type) || !$type) {
1517
            throw new \UnexpectedValueException('the issue type cannot be empty.', -11100);
1518
        }
1519
1520
        $parent_id = $request->input('parent_id');
1521
        if (!isset($parent_id)) {
1522
            $parent_id = '';
1523
        }
1524
 
1525
        $updValues = [];
1526
        if ($parent_id) {
1527
            // standard convert to subtask 
1528
            $hasSubtasks = DB::collection($table)->where('parent_id', $id)->exists();
1529
            if ($hasSubtasks) {
1530
                throw new \UnexpectedValueException('the issue can not convert to subtask.', -11114);
1531
            }
1532
1533
            $parent_issue = DB::collection($table)->find($parent_id);
1534
            if (!$parent_issue) {
1535
                throw new \UnexpectedValueException('the dest parent issue does not exist or is not in the project.', -11110);
1536
            }
1537
        }
1538
        $updValues['parent_id'] = $parent_id;
1539
        $updValues['type'] = $type;
1540
1541
        $updValues['modifier'] = [ 'id' => $this->user->id, 'name' => $this->user->first_name, 'email' => $this->user->email ];
1542
        $updValues['updated_at'] = time();
1543
        DB::collection($table)->where('_id', $id)->update($updValues);
1544
1545
        // add to histroy table
1546
        $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...
1547
        // trigger event of issue moved
1548
        Event::fire(new IssueEvent($project_key, $id, $updValues['modifier'], [ 'event_key' => 'edit_issue', 'snap_id' => $snap_id ]));
1549
1550
        return $this->show($project_key, $id);
1551
1552
    }
1553
1554
    /**
1555
     * move issue.
1556
     *
1557
     * @param  \Illuminate\Http\Request $request
1558
     * @param  string                   $project_key
1559
     * @param  string                   $id
1560
     * @return \Illuminate\Http\Response
1561
     */
1562
    public function move(Request $request, $project_key, $id)
1563
    {
1564
        $table = 'issue_' . $project_key;
1565
        $issue = DB::collection($table)->find($id);
1566
        if (!$issue) {
1567
            throw new \UnexpectedValueException('the issue does not exist or is not in the project.', -11103);
1568
        }
1569
1570
        $parent_id = $request->input('parent_id'); 
1571
        if (!isset($parent_id) || !$parent_id) {
1572
            throw new \UnexpectedValueException('the dest parent cannot be empty.', -11111);
1573
        }
1574
        $parent_issue = DB::collection($table)->find($parent_id);
1575
        if (!$parent_issue) {
1576
            throw new \UnexpectedValueException('the dest parent issue does not exist or is not in the project.', -11110);
1577
        }
1578
1579
        if ($parent_id === $issue['parent_id']) {
1580
            return $this->show($project_key, $id);
1581
        }
1582
1583
        $updValues = [];
1584
        $updValues['parent_id'] = $parent_id;
1585
        $updValues['modifier'] = [ 'id' => $this->user->id, 'name' => $this->user->first_name, 'email' => $this->user->email ];
1586
        $updValues['updated_at'] = time();
1587
        DB::collection($table)->where('_id', $id)->update($updValues);
1588
1589
        // add to histroy table
1590
        $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...
1591
        // trigger event of issue moved
1592
        Event::fire(new IssueEvent($project_key, $id, $updValues['modifier'], [ 'event_key' => 'move_issue', 'data' => [ 'old_parent' => $issue['parent_id'], 'new_parent' => $parent_id ] ]));
1593
1594
        return $this->show($project_key, $id);
1595
    }
1596
1597
    /**
1598
     * release issue.
1599
     *
1600
     * @param  \Illuminate\Http\Request $request
1601
     * @param  string                   $project_key
1602
     * @return \Illuminate\Http\Response
1603
     */
1604
    public function release(Request $request, $project_key) 
1605
    {
1606
        $ids = $request->input('ids'); 
1607
        if (!$ids) {
1608
            throw new \UnexpectedValueException('the released issues cannot be empty.', -11132);
1609
        }
1610
1611
        $name = $request->input('name');
1612
        if (!$name) {
1613
            throw new \UnexpectedValueException('the released version cannot be empty.', -11131);
1614
        }
1615
1616
        $isExisted = Version::where('project_key', $project_key)
1617
            ->where('name', $name)
1618
            ->exists();
1619
        if ($isExisted) {
1620
            throw new \UnexpectedValueException('the released version has been existed.', -11133);
1621
        }
1622
1623
        $user = [ 'id' => $this->user->id, 'name' => $this->user->first_name, 'email' => $this->user->email ];
1624
        $version = Version::create([ 'project_key' => $project_key, 'user' => $user, 'status' => 'released', 'released_time' => time() ] + $request->all());
1625
1626
        foreach ($ids as $id)
1627
        {
1628
            DB::collection('issue_' . $project_key)->where('_id', $id)->update([ 'resolve_version' => $version->id ]);
1629
            // add to histroy table
1630
            $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...
1631
            // trigger event of issue moved
1632
            Event::fire(new IssueEvent($project_key, $id, $user, [ 'event_key' => 'edit_issue', 'snap_id' => $snap_id ]));
1633
        }
1634
1635
        $isSendMsg = $request->input('isSendMsg') && true;
1636
        Event::fire(new VersionEvent($project_key, $user, [ 'event_key' => 'create_release_version', 'isSendMsg' => $isSendMsg, 'data' => [ 'released_issues' => $ids, 'release_version' => $version->toArray() ] ]));
1637
1638
        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...
1639
    }
1640
1641
    /**
1642
     * get timetrack setting.
1643
     *
1644
     * @return array 
1645
     */
1646
    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...
1647
    {
1648
        $options = [ 'w2d' => 5, 'd2h' => 8 ];
1649
1650
        $setting = SysSetting::first();
1651 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...
1652
            if (isset($setting->properties['week2day'])) {
1653
                $options['w2d'] = $setting->properties['week2day'];
1654
            }
1655
            if (isset($setting->properties['day2hour'])) {
1656
                $options['d2h'] = $setting->properties['day2hour'];
1657
            }
1658
        }
1659
        return $options;
1660
    }
1661
1662
    /**
1663
     * get issue link relations.
1664
     *
1665
     * @return array
1666
     */
1667
    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...
1668
    {
1669
        $relations = [
1670
          [ 'id' => 'blocks', 'out' => 'blocks', 'in' => 'is blocked by' ],
1671
          [ 'id' => 'clones', 'out' => 'clones', 'in' => 'is cloned by' ],
1672
          [ 'id' => 'duplicates', 'out' => 'duplicates', 'in' => 'is duplicated by' ],
1673
          [ 'id' => 'relates', 'out' => 'relates to', 'in' => 'relates to' ],
1674
        ];
1675
        return $relations;
1676
    }
1677
1678
    /**
1679
     * classify issues by parent_id.
1680
     *
1681
     * @param  array $issues
1682
     * @return array
1683
     */
1684
    public function classifyIssues($issues)
1685
    {
1686
        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...
1687
        }
1688
1689
        $classified_issues  = [];
1690
        foreach ($issues as $issue)
1691
        {
1692
            if (isset($issue['parent']) && $issue['parent']) {
1693
                if (isset($classified_issues[$issue['parent']['no']]) && $classified_issues[$issue['parent']['no']]) {
1694
                    $classified_issues[$issue['parent']['no']][] =  $issue;
1695
                }
1696
                else
1697
                {
1698
                    $classified_issues[$issue['parent']['no']] = [ $issue ];
1699
                }
1700
            }
1701
            else
1702
            {
1703
                if (isset($classified_issues[$issue['no']]) && $classified_issues[$issue['no']]) {
1704
                    array_unshift($classified_issues[$issue['no']], $issue);
1705
                }
1706
                else
1707
                {
1708
                    $classified_issues[$issue['no']] = [ $issue ];
1709
                }
1710
            }
1711
        }
1712
1713
        return $classified_issues;
1714
    }
1715
1716
    /**
1717
     * add avatar for issues
1718
     *
1719
     * @param  array $issues
1720
     * @return array
1721
     */
1722
    public function addAvatar(&$issues)
1723
    {
1724
        $cache_avatars = [];
1725
        foreach ($issues as $key => $issue)
1726
        {
1727
            if (!isset($issue['assignee']) || !isset($issue['assignee']['id'])) {
1728
                continue;
1729
            }
1730
            //get assignee avatar for kanban
1731 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...
1732
                $user = Sentinel::findById($issue['assignee']['id']);
1733
                $cache_avatars[$issue['assignee']['id']] = isset($user->avatar) ? $user->avatar : '';
1734
            }
1735
            $issues[$key]['assignee']['avatar'] = $cache_avatars[$issue['assignee']['id']];
1736
        }
1737
        return;
1738
    }
1739
1740
    /**
1741
     * flat issues from 2d to 1d.
1742
     *
1743
     * @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...
1744
     * @return array
1745
     */
1746
    public function flatIssues($classified_issues)
1747
    {
1748
        $issues = [];
1749
        foreach ($classified_issues as $some)
1750
        {
1751
            foreach ($some as $one)
1752
            {
1753
                $issues[] = $one;
1754
            }
1755
        }
1756
        return $issues;
1757
    }
1758
1759
    /**
1760
     * arrange issues for kanban.
1761
     *
1762
     * @param  string $project_key
1763
     * @param  array  $issues
1764
     * @param  string $from
1765
     * @param  string $from_board_id
1766
     * @param  bool   $isUpdRank
1767
     * @return array 
1768
     */
1769
    public function arrangeIssues($project_key, $issues, $from, $from_board_id, $isUpdRank=false)
1770
    {
1771
        if ($from === 'his_sprint') {
1772
            $this->addAvatar($issues);
1773
            return $issues;
1774
        }
1775
1776
        // classify the issues
1777
        $classified_issues = $this->classifyIssues($issues);
1778
1779
        // whether the board is ranked
1780
        $rankmap = BoardRankMap::where([ 'board_id' => $from_board_id ])->first();
1781
        if (!$rankmap) {
1782
            $issues = $this->flatIssues($classified_issues);
1783
1784
            $rank = [];
1785
            foreach ($issues as $issue)
1786
            {
1787
                $rank[] = $issue['no'];
1788
            }
1789
            
1790
            BoardRankMap::create([ 'board_id' => $from_board_id, 'rank' => $rank ]);
1791
1792
            if ($from === 'active_sprint') {
1793
                $issues = $this->sprintFilter($project_key, $issues);
1794
            }
1795
1796
            $this->addAvatar($issues);
1797
            return $issues;
1798
        }
1799
 
1800
        $sub2parent_map = []; 
1801
        foreach ($issues as $issue)
1802
        {
1803
            if (isset($issue['parent']) && $issue['parent']) {
1804
                $sub2parent_map[$issue['no']] = $issue['parent']['no'];
1805
            }
1806
        }
1807
1808
        $rank = $rankmap->rank; 
1809
        foreach ($classified_issues as $no => $some)
1810
        {
1811
            if (count($some) <= 1) { continue; 
1812
            }
1813
1814
            $group_issues = [];
1815
            foreach ($some as $one)
1816
            {
1817
                $group_issues[$one['no']] = $one;
1818
            }
1819
1820
            $sorted_group_issues = [];
1821
            foreach ($rank as $val)
1822
            {
1823
                if (isset($group_issues[$val])) {
1824
                    $sorted_group_issues[$val] = $group_issues[$val];
1825
                }
1826
            }
1827
1828
            foreach ($group_issues as $no2 => $issue)
1829
            {
1830
                if (!isset($sorted_group_issues[$no2])) {
1831
                    $sorted_group_issues[$no2] = $issue;
1832
                }
1833
            }
1834
            $classified_issues[$no] = array_values($sorted_group_issues);
1835
1836
            // prevent the sort confusion 
1837
            $parentInd = 0;
1838
            foreach ($classified_issues[$no] as $sk => $si)
1839
            {
1840
                if ($si['no'] === $no) {
1841
                    $parentInd = $sk;
1842
                    break;
1843
                }
1844
            }
1845
            if ($parentInd > 0) {
1846
                $pi = array_splice($classified_issues[$no], $parentInd, 1);
1847
                array_unshift($classified_issues[$no], array_pop($pi));
1848
            }
1849
        }
1850
1851
        $sorted_issues = [];
1852
        foreach ($rank as $val)
1853
        {
1854
            if (isset($classified_issues[$val]) && $classified_issues[$val]) {
1855
                $sorted_issues[$val] = $classified_issues[$val]; 
1856
            }
1857
            else
1858
            {
1859
                if (isset($sub2parent_map[$val]) && $sub2parent_map[$val]) {
1860
                    $parent = $sub2parent_map[$val];
1861
                    if (!isset($sorted_issues[$parent])) {
1862
                        $sorted_issues[$parent] = $classified_issues[$parent]; 
1863
                    }
1864
                }
1865
            }
1866
        }
1867
1868
        // append some issues which is ranked
1869
        foreach ($classified_issues as $key => $val)
1870
        {
1871
            if (!isset($sorted_issues[$key])) {
1872
                $sorted_issues[$key] = $val;
1873
            }
1874
        }
1875
1876
        // convert array to ordered array
1877
        $issues = $this->flatIssues($sorted_issues); 
1878
1879
        if ($isUpdRank) {
1880
            $new_rank = [];
1881
            foreach ($issues as $issue)
1882
            {
1883
                $new_rank[] = $issue['no'];
1884
            }
1885
1886
            if (array_diff_assoc($new_rank, $rank) || array_diff_assoc($rank, $new_rank)) {
1887
                $rankmap = BoardRankMap::where('board_id', $from_board_id)->first();
1888
                $rankmap && $rankmap->update([ 'rank' => $new_rank ]);
1889
            }
1890
        }
1891
1892
        if ($from === 'active_sprint') {
1893
            $issues = $this->sprintFilter($project_key, $issues);
1894
        }
1895
1896
        $this->addAvatar($issues);
1897
        return $issues;
1898
    }
1899
1900
    /**
1901
     * sprint filter for issues
1902
     *
1903
     * @param  string $project_key
1904
     * @param  array  $issues
1905
     * @return array
1906
     */
1907
    public function sprintFilter($project_key, $issues)
1908
    {
1909
        $active_sprint_issues = [];
1910
        $active_sprint_issue_nos = [];
1911
        $active_sprint = Sprint::where('project_key', $project_key)->where('status', 'active')->first();
1912
        if ($active_sprint && isset($active_sprint->issues) && $active_sprint->issues) {
1913
            $active_sprint_issue_nos = $active_sprint->issues;
1914
        }
1915
1916
        foreach($issues as $issue)
1917
        {
1918
            if (in_array($issue['no'], $active_sprint_issue_nos)) {
1919
                $active_sprint_issues[] = $issue;
1920
            }
1921
        }
1922
1923
        return $active_sprint_issues;
1924
    }
1925
1926
    /**
1927
     * get some options for export 
1928
     *
1929
     * @param  string $project_key
1930
     * @return array
1931
     */
1932
    public function getOptionsForExport($project_key)
1933
    {
1934
        $types = [];
1935
        $type_list = Provider::getTypeList($project_key);
1936
        foreach ($type_list as $type)
1937
        {
1938
            $types[$type->id] = $type->name;
1939
        }
1940
1941
        $states = [];
1942
        $state_list =  Provider::getStateOptions($project_key);
1943
        foreach ($state_list as $state)
1944
        {
1945
            $states[$state['_id']] = $state['name'];
1946
        }
1947
1948
        $resolutions = [];
1949
        $resolution_list = Provider::getResolutionOptions($project_key);
1950
        foreach ($resolution_list as $resolution)
1951
        {
1952
            $resolutions[$resolution['_id']] = $resolution['name'];
1953
        }
1954
1955
        $priorities = [];
1956
        $priority_list = Provider::getPriorityOptions($project_key);
1957
        foreach ($priority_list as $priority)
1958
        {
1959
            $priorities[$priority['_id']] = $priority['name'];
1960
        }
1961
1962
        $versions = [];
1963
        $version_list = Provider::getVersionList($project_key);
1964
        foreach($version_list as $version)
1965
        {
1966
            $versions[$version->id] = $version->name;
1967
        }
1968
1969
        $modules = [];
1970
        $module_list = Provider::getModuleList($project_key);
1971
        foreach ($module_list as $module)
1972
        {
1973
            $modules[$module->id] = $module->name;
1974
        }
1975
1976
        $epics = [];
1977
        $epic_list = Provider::getEpicList($project_key);
1978
        foreach ($epic_list as $epic)
1979
        {
1980
            $epics[$epic['_id']] = $epic['name'];
1981
        }
1982
1983
        $sprints = [];
1984
        $sprint_list = Provider::getSprintList($project_key);
1985
        foreach ($sprint_list as $sprint)
1986
        {
1987
            $sprints[$sprint['no']] = $sprint['name'];
1988
        }
1989
1990
        $fields = [];
1991
        $field_list = Provider::getFieldList($project_key);
1992
        foreach ($field_list as $field)
1993
        {
1994
            $tmp = [];
1995
            $tmp['name'] = $field->name;
1996
            $tmp['type'] = $field->type;
1997
            if (isset($field->optionValues)) {
1998
                $tmp['optionValues'] = $field->optionValues;
1999
            }
2000
            $fields[$field->key] = $tmp;
2001
        }
2002
2003
        $fields['no'] = [ 'name' => 'NO', 'type' => 'Number' ];
2004
        $fields['type'] = [ 'name' => '类型', 'type' => 'Select' ];
2005
        $fields['state'] = [ 'name' => '状态', 'type' => 'Select' ];
2006
        $fields['created_at'] = [ 'name' => '创建时间', 'type' => 'DateTimePicker' ];
2007
        $fields['updated_at'] = [ 'name' => '更新时间', 'type' => 'DateTimePicker' ];
2008
        $fields['resolved_at'] = [ 'name' => '解决时间', 'type' => 'DateTimePicker' ];
2009
        $fields['closed_at'] = [ 'name' => '关闭时间', 'type' => 'DateTimePicker' ];
2010
        $fields['reporter'] = [ 'name' => '报告者', 'type' => '' ];
2011
        $fields['resolver'] = [ 'name' => '解决者', 'type' => '' ];
2012
        $fields['closer'] = [ 'name' => '关闭者', 'type' => '' ];
2013
        $fields['sprints'] = [ 'name' => 'Sprint', 'type' => '' ];
2014
2015
        return [
2016
          'types' => $types,
2017
          'states' => $states,
2018
          'resolutions' => $resolutions,
2019
          'priorities' => $priorities,
2020
          'versions' => $versions,
2021
          'modules' => $modules,
2022
          'epics' => $epics,
2023
          'sprints' => $sprints,
2024
          'fields' => $fields,
2025
        ];
2026
2027
    }
2028
2029
    /**
2030
     * export xls for issue list
2031
     *
2032
     * @param  string $project_key
2033
     * @return void
2034
     */
2035
    public function imports(Request $request, $project_key)
2036
    {
2037
        set_time_limit(0);
2038
2039
        if (!($fid = $request->input('fid'))) {
2040
            throw new \UnexpectedValueException('导入文件ID不能为空。', -11140);
2041
        }
2042
2043
        $pattern = $request->input('pattern');
2044
        if (!isset($pattern)) {
2045
            $pattern = '1';
2046
        }
2047
2048
        $file = config('filesystems.disks.local.root', '/tmp') . '/' . substr($fid, 0, 2) . '/' . $fid;
2049
        if (!file_exists($file)) {
2050
            throw new \UnexpectedValueException('获取导入文件失败。', -11141);
2051
        }
2052
2053
        $err_msgs = [];
2054
        $fatal_err_msgs = [];
2055
        Excel::load(
2056
            $file, function ($reader) use ($project_key, $pattern, &$err_msgs, &$fatal_err_msgs) {
2057
                $reader = $reader->getSheet(0);
2058
                $data = $reader->toArray();
2059
                if (!$data) {
2060
                    $fatal_err_msgs = $err_msgs = '文件内容不能为空。';
2061
                    return;
2062
                }
2063
2064
                $new_fields = [];
2065
                $fields = Provider::getFieldList($project_key);
2066
                foreach($fields as $field)
2067
                {
2068
                    if ($field->type !== 'File') { 
2069
                        $new_fields[$field->key] = $field->name;
2070
                    }
2071
                }
2072
                $new_fields['type'] = '类型';
2073
                $new_fields['state'] = '状态';
2074
                $new_fields['parent'] = '父级任务';
2075
                $new_fields['reporter'] = '报告者';
2076
                $new_fields['created_at'] = '创建时间';
2077
                $new_fields['updated_at'] = '更新时间';
2078
                $new_fields['resolver'] = '解决者';
2079
                $new_fields['resolved_at'] = '解决时间';
2080
                $new_fields['closer'] = '关闭者';
2081
                $new_fields['closed_at'] = '关闭时间';
2082
                $fields = $new_fields;
2083
2084
                // arrange the excel data
2085
                $data = $this->arrangeExcel($data, $fields);
2086
                foreach ($data as $val)
2087
                {
2088
                    if (!isset($val['title']) && !isset($val['type'])) {
2089
                        $fatal_err_msgs = $err_msgs = '主题列和类型列没找到。';
2090
                    }
2091
                    else if (!isset($val['title'])) {
2092
                        $fatal_err_msgs = $err_msgs = '主题列没找到。';
2093
                    }
2094
                    else if (!isset($val['type'])) {
2095
                        $fatal_err_msgs = $err_msgs = '类型列没找到。';
2096
                    }
2097
                    else if (!$val['title']) {
2098
                        $fatal_err_msgs = $err_msgs = '主题列不能有空值。';
2099
                    }
2100
2101
                    if ($err_msgs) {
2102
                        return;
2103
                    }
2104
                }
2105
2106
                // get the type schema
2107
                $new_types = [];
2108
                $standard_type_ids = [];
2109
                $types = Provider::getTypeList($project_key);
2110
                foreach ($types as $type)
2111
                {
2112
                    $tmp = [];
2113
                    $tmp['id'] = $type->id;
2114
                    $tmp['name'] = $type->name;
2115
                    $tmp['type'] = $type->type ?: 'standard';
2116
                    $tmp['workflow'] = $type->workflow;
2117
                    $tmp['schema'] = Provider::getSchemaByType($type->id);
2118
                    $new_types[$type->name] = $tmp;
2119
                    if ($tmp['type'] == 'standard') {
2120
                        $standard_type_ids[] = $tmp['id'];
2121
                    }
2122
                }
2123
                $types = $new_types;
2124
2125
                // get the state option
2126
                $new_priorities = [];
2127
                $priorities = Provider::getPriorityOptions($project_key);
2128
                foreach($priorities as $priority)
2129
                {
2130
                    $new_priorities[$priority['name']] = $priority['_id'];
2131
                }
2132
                $priorities = $new_priorities;
2133
2134
                // get the state option
2135
                $new_states = [];
2136
                $states = Provider::getStateOptions($project_key);
2137
                foreach($states as $state)
2138
                {
2139
                    $new_states[$state['name']] = $state['_id'];
2140
                }
2141
                $states = $new_states;
2142
2143
                // get the state option
2144
                $new_resolutions = [];
2145
                $resolutions = Provider::getResolutionOptions($project_key);
2146
                foreach($resolutions as $resolution)
2147
                {
2148
                    $new_resolutions[$resolution['name']] = $resolution['_id'];
2149
                }
2150
                $resolutions = $new_resolutions;
2151
2152
                // initialize the error msg
2153
                foreach ($data as $val)
2154
                {
2155
                    $err_msgs[$val['title']] = [];
2156
                    $fatal_err_msgs[$val['title']] = [];
2157
                }
2158
2159
                $standard_titles = [];
2160
                $standard_issues = [];
2161
                $subtask_issues = [];
2162
2163
                foreach ($data as $value)
2164
                {
2165
                    $issue = [];
2166
                    $cur_title = $issue['title'] = $value['title'];
2167
2168
                    if (!$value['type']) {
2169
                        $fatal_err_msgs[$cur_title][] = $err_msgs[$cur_title][] = '类型列不能有空值。';
2170
                        continue;
2171
                    }
2172
                    else if (!isset($types[$value['type']])) {
2173
                        $fatal_err_msgs[$cur_title][] = $err_msgs[$cur_title][] = '类型列值匹配失败。';
2174
                        continue;
2175
                    }
2176
                    else 
2177
                    {
2178
                        $issue['type'] = $types[$value['type']]['id'];
2179
                    }
2180
2181
                    if ($types[$value['type']]['type'] === 'subtask' && (!isset($value['parent']) || !$value['parent'])) {
2182
                        $fatal_err_msgs[$cur_title][] = $err_msgs[$cur_title][] = '父级任务列不能为空。';
2183
                    }
2184
                    else
2185
                    {
2186
                        $issue['parent'] = $value['parent'];
2187
                    }
2188
2189 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...
2190
                        if (!isset($priorities[$value['priority']]) || !$priorities[$value['priority']]) {
2191
                            $err_msgs[$cur_title][] = '优先级列值匹配失败。';
2192
                        }
2193
                        else
2194
                        {
2195
                            $issue['priority'] = $priorities[$value['priority']];
2196
                        }
2197
                    }
2198
2199
                    if (isset($value['state']) && $value['state']) {
2200
                        if (!isset($states[$value['state']]) || !$states[$value['state']]) {
2201
                            $err_msgs[$cur_title][] = '状态列值匹配失败。';
2202
                        }
2203
                        else
2204
                        {
2205
                            $issue['state'] = $states[$value['state']];
2206
                            $workflow = $types[$value['type']]['workflow'];
2207
                            if (!in_array($issue['state'], $workflow['state_ids'])) {
2208
                                $err_msgs[$cur_title][] = '状态列值不在相应流程里。';
2209
                            }
2210
                        }
2211
                    }
2212
2213 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...
2214
                        if (!isset($resolutions[$value['resolution']]) || !$resolutions[$value['resolution']]) {
2215
                            $err_msgs[$cur_title][] = '解决结果列值匹配失败。';
2216
                        }
2217
                        else
2218
                        {
2219
                            $issue['resolution'] = $resolutions[$value['resolution']];
2220
                        }
2221
                    }
2222
2223
                    $user_relate_fields = [ 'assignee' => '经办人', 'reporter' => '报告者', 'resolver' => '解决者', 'closer' => '关闭时间' ];
2224
                    foreach ($user_relate_fields as $uk => $uv)
2225
                    {
2226
                        if (isset($value[$uk]) && $value[$uk]) {
2227
                            $tmp_user = EloquentUser::where('first_name', $value[$uk])->first();
2228
                            if (!$tmp_user) {
2229
                                $err_msgs[$cur_title][] = $uv . '列用户不存在。';
2230
                            }
2231
                            else
2232
                            {
2233
                                $issue[$uk] = [ 'id' => $tmp_user->id, 'name' => $tmp_user->first_name, 'email' => $tmp_user->email ];
2234
                                if ($uk == 'resolver') {
2235
                                    $issue['his_resolvers'] = [ $tmp_user->id ];
2236
                                }
2237
                            }
2238
                        }
2239
                    }
2240
2241
                    $time_relate_fields = [ 'created_at' => '创建时间', 'resolved_at' => '解决时间', 'closed_at' => '关闭时间', 'updated_at' => '更新时间' ];
2242
                    foreach ($time_relate_fields as $tk => $tv)
2243
                    {
2244
                        if (isset($value[$tk]) && $value[$tk]) {
2245
                            $stamptime = strtotime($value[$tk]);
2246
                            if ($stamptime === false) {
2247
                                $err_msgs[$cur_title][] = $tv . '列值格式错误。';
2248
                            }
2249
                            else
2250
                            {
2251
                                $issue[$tk] = $stamptime;
2252
                            }
2253
                        }
2254
                    }
2255
2256
                    $schema = $types[$value['type']]['schema'];
2257
                    foreach ($schema as $field)
2258
                    {
2259 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...
2260
                            $err_msgs[$cur_title][] = $fields[$field['key']] . '列值不能为空。';
2261
                            continue;
2262
                        }
2263
2264
                        if (isset($value[$field['key']]) && $value[$field['key']]) {
2265
                            $field_key = $field['key'];
2266
                            $field_value = $value[$field['key']];
2267
                        }
2268
                        else
2269
                        {
2270
                            continue;
2271
                        }
2272
2273
                        if (in_array($field_key, [ 'priority', 'resolution', 'assignee' ])) {
2274
                            continue;
2275
                        }
2276
2277
                        //if ($field_key === 'Sprint')
2278
                        //{
2279
                        //    $sprints = explode(',', $field_value);
2280
                        //    $new_sprints = [];
2281
                        //    foreach ($sprints as $s)
2282
                        //    {
2283
                        //        $new_sprints[] = intval($s);
2284
                        //    }
2285
                        //    $issue['sprints'] = $new_sprints;
2286
                        //}
2287
                        if ($field_key == 'labels') {
2288
                            $issue['labels'] = [];
2289
                            foreach (explode(',', $field_value) as $val)
2290
                            {
2291
                                if (trim($val)) {
2292
                                    $issue['labels'][] = trim($val); 
2293
                                }
2294
                            }
2295
                            $issue['labels'] = array_values(array_unique($issue['labels']));
2296
                        }
2297
                        else if ($field['type'] === 'SingleUser' || $field_key === 'assignee') {
2298
                            $tmp_user = EloquentUser::where('first_name', $field_value)->first();
2299
                            if (!$tmp_user) {
2300
                                $err_msgs[$cur_title][] = $fields[$field_key] . '列用户不存在。';
2301
                            }
2302
                            else
2303
                            {
2304
                                $issue[$field_key] = [ 'id' => $tmp_user->id, 'name' => $tmp_user->first_name, 'email' => $tmp_user->email ];
2305
                            }
2306
                        }
2307
                        else if ($field['type'] === 'MultiUser') {
2308
                            $issue[$field_key] = [];
2309
                            $issue[$field_key . '_ids'] = [];
2310
                            foreach(explode(',', $field_value) as $val)
2311
                            {
2312
                                if (!trim($val)) {
2313
                                    continue;
2314
                                }
2315
2316
                                $tmp_user = EloquentUser::where('first_name', trim($val))->first();
2317
                                if (!$tmp_user) {
2318
                                    $err_msgs[$cur_title][] = $fields[$field_key] . '列用户不存在。';
2319
                                }
2320
                                else if (!in_array($tmp_user->id, $issue[$field_key . '_ids'])) {
2321
                                    $issue[$field_key][] = [ 'id' => $tmp_user->id, 'name' => $tmp_user->first_name, 'email' => $tmp_user->email ];
2322
                                    $issue[$field_key . '_ids'][] = $tmp_user->id;
2323
                                }
2324
                            }
2325
                        }
2326
                        else if (in_array($field['type'], [ 'Select', 'RadioGroup', 'SingleVersion' ])) {
2327 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...
2328
                            {
2329
                                if ($val['name'] === $field_value) {
2330
                                    $issue[$field_key] = $val['id'];
2331
                                    break;
2332
                                }
2333
                            }
2334
                            if (!isset($issue[$field_key])) {
2335
                                $err_msgs[$cur_title][] = $fields[$field_key] . '列值匹配失败。';
2336
                            }
2337
                        }
2338
                        else if (in_array($field['type'], [ 'MultiSelect', 'CheckboxGroup', 'MultiVersion' ])) {
2339
                            $issue[$field_key] = [];
2340
                            foreach (explode(',', $field_value) as $val)
2341
                            {
2342
                                $val = trim($val);
2343
                                if (!$val) {
2344
                                    continue;
2345
                                }
2346
2347
                                $isMatched = false;
2348 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...
2349
                                {
2350
                                    if ($val2['name'] === $val) {
2351
                                        $issue[$field_key][] = $val2['id'];
2352
                                        $isMatched = true;
2353
                                        break;
2354
                                    }
2355
                                }
2356
                                if (!$isMatched) {
2357
                                    $err_msgs[$cur_title][] = $fields[$field_key] . '列值匹配失败。';
2358
                                }
2359
                            }
2360
                            $issue[$field_key] = array_values(array_unique($issue[$field_key]));
2361
                        }
2362
                        else if (in_array($field['type'], [ 'DatePicker', 'DatetimePicker' ])) {
2363
                            $stamptime = strtotime($field_value);
2364
                            if ($stamptime === false) {
2365
                                $err_msgs[$cur_title][] = $fields[$field_key] . '列值格式错误。';
2366
                            }
2367
                            else
2368
                            {
2369
                                $issue[$field_key] = $stamptime;
2370
                            }
2371
                        }
2372
                        else if ($field['type'] === 'TimeTracking') {
2373
                            if (!$this->ttCheck($field_value)) {
2374
                                $err_msgs[$cur_title][] = $fields[$field_key] . '列值格式错误。';
2375
                            }
2376
                            else
2377
                            {
2378
                                $issue[$field_key] = $this->ttHandle($field_value);
2379
                                $issue[$field_key . '_m'] = $this->ttHandleInM($issue[$field_key]);
2380
                            }
2381
                        }
2382
                        else if ($field['type'] === 'Number') {
2383
                            $issue[$field_key] = floatval($field_value);
2384
                        }
2385
                        else
2386
                        {
2387
                            $issue[$field_key] = $field_value;
2388
                        }
2389
                    }
2390
2391
                    if ($types[$value['type']]['type'] === 'subtask') {
2392
                        $subtask_issues[] = $issue;
2393
                    }
2394
                    else
2395
                    {
2396
                        $standard_titles[] = $issue['title'];
2397
                        if (isset($issue['parent'])) {
2398
                            unset($issue['parent']);
2399
                        }
2400
                        $standard_issues[] = $issue;
2401
                    }
2402
                }
2403
2404
                $new_subtask_issues = [];
2405
                foreach ($standard_titles as $title)
2406
                {
2407
                    $new_subtask_issues[$title] = [];
2408
                }
2409
2410
                foreach ($subtask_issues as $issue)
2411
                {
2412
                    $parent_issues = array_filter(
2413
                        $standard_issues, function ($v) use ($issue) {
2414
                            return $v['title'] === $issue['parent']; 
2415
                        }
2416
                    );
2417
                    if (count($parent_issues) > 1) {
2418
                        $fatal_err_msgs[$issue['title']][] = $err_msgs[$issue['title']][] = '找到多个父级任务。';
2419
                    }
2420
                    else if (count($parent_issues) == 1) {
2421
                        $parent_issue = array_pop($parent_issues);
2422
                        if (isset($issue['parent'])) {
2423
                            unset($issue['parent']);
2424
                        }
2425
                        $new_subtask_issues[$parent_issue['title']][] = $issue;
2426
                    }
2427
                    else
2428
                    {
2429
                        $parent_issues = DB::table('issue_' . $project_key)
2430
                            ->where('title', $issue['parent'])
2431
                            ->whereIn('type', $standard_type_ids)
2432
                            ->where('del_flg', '<>', 1)
2433
                            ->get();
2434
                        if (count($parent_issues) > 1) {
2435
                            $fatal_err_msgs[$issue['title']][] = $err_msgs[$issue['title']][] = '找到多个父级任务。';
2436
                        }
2437
                        else if (count($parent_issues) == 1) {
2438
                            $parent_issue = array_pop($parent_issues);
2439
                            if (isset($issue['parent'])) {
2440
                                unset($issue['parent']);
2441
                            }
2442
                            $new_subtask_issues[] = $issue + [ 'parent_id' => $parent_issue['_id']->__toString() ];
2443
                        }
2444
                        else
2445
                        {
2446
                            $fatal_err_msgs[$issue['title']][] = $err_msgs[$issue['title']][] = '父级任务不存在。';
2447
                        }
2448
                    }
2449
                }
2450
                $subtask_issues = array_filter($new_subtask_issues);
2451
2452
                $err_msgs = array_filter($err_msgs);
2453
                $fatal_err_msgs = array_filter($fatal_err_msgs);
2454
                if ($pattern == '2') {
2455
                    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...
2456
                        return;
2457
                    }
2458
                }
2459
                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...
2460
                    return;
2461
                }
2462
2463
2464
                $new_types = [];
2465
                foreach($types as $type)
2466
                {
2467
                    $new_types[$type['id']] = $type;
2468
                }
2469
                $types = $new_types;
2470
2471 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...
2472
                {
2473
                    if (isset($issue['parent_id']) && $issue['parent_id']) {
2474
                        $this->importIssue($project_key, $issue, $types[$issue['type']]['schema'], $types[$issue['type']]['workflow']);
2475
                    }
2476
                }
2477
2478
                foreach ($standard_issues as $issue)
2479
                {
2480
                    $id = $this->importIssue($project_key, $issue, $types[$issue['type']]['schema'], $types[$issue['type']]['workflow']);
2481
                    if (!isset($subtask_issues[$issue['title']]) || !$subtask_issues[$issue['title']]) {
2482
                        continue;
2483
                    }
2484 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...
2485
                    {
2486
                        $sub_issue['parent_id'] = $id;
2487
                        $this->importIssue($project_key, $sub_issue, $types[$sub_issue['type']]['schema'], $types[$sub_issue['type']]['workflow']);
2488
                    }
2489
                }
2490
            }
2491
        );
2492
2493
2494
        $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...
2495
        if ($pattern == '2') {
2496
            $emsgs = array_filter($fatal_err_msgs);
2497
        }
2498
        else
2499
        {
2500
            $emsgs = array_filter($err_msgs);
2501
        }
2502
2503 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...
2504
            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...
2505
        }
2506
        else
2507
        {
2508
            return response()->json([ 'ecode' => 0, 'emsg' => '' ]);
2509
        }
2510
    }
2511
2512
    /**
2513
     * import the issue into the project 
2514
     *
2515
     * @param  string $project_key
2516
     * @param  array  $data
2517
     * @param  array  $schema
2518
     * @return string id 
2519
     */
2520
    public function importIssue($project_key, $data, $schema, $workflow)
2521
    {
2522
        $table = 'issue_' . $project_key;
2523
2524
        $insValues = $data;
2525
        if (!isset($insValues['resolution']) || !$insValues['resolution']) {
2526
            $insValues['resolution'] = 'Unresolved';
2527
        }
2528
2529
        $max_no = DB::collection($table)->count() + 1;
2530
        $insValues['no'] = $max_no;
2531
2532 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...
2533
            $insValues['assignee'] = [ 'id' => $this->user->id, 'name' => $this->user->first_name, 'email' => $this->user->email ];
2534
        }
2535
2536
        // get reporter(creator)
2537 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...
2538
            $insValues['reporter'] = [ 'id' => $this->user->id, 'name' => $this->user->first_name, 'email' => $this->user->email ];
2539
        }
2540
        if (!isset($insValues['created_at']) || !$insValues['created_at']) {
2541
            $insValues['created_at'] = time();
2542
        }
2543
2544
        if (!isset($data['state']) || !$data['state']) {
2545
            $wf = $this->initializeWorkflow($data['type']);
2546
            $insValues += $wf;
2547
        }
2548
        else if (in_array($data['state'], $workflow->state_ids ?: [])) {
2549
            $wf = $this->initializeWorkflowForImport($workflow, $data['state']);
2550
            $insValues += $wf;
2551
        }
2552
2553
        $id = DB::collection('issue_' . $project_key)->insertGetId($insValues);
2554
        $id = $id->__toString();
2555
2556
        // add to histroy table
2557
        Provider::snap2His($project_key, $id, $schema);
2558
        // trigger event of issue created
2559
        Event::fire(new IssueEvent($project_key, $id, $insValues['reporter'], [ 'event_key' => 'create_issue' ]));
2560
2561
        if (isset($insValues['labels']) && $insValues['labels']) {
2562
            $this->createLabels($project_key, $insValues['labels']);
2563
        }
2564
2565
        return $id;
2566
    }
2567
2568
    /**
2569
     * initialize the workflow for the issue import.
2570
     *
2571
     * @param  object $wf_definition
2572
     * @param  string $state
2573
     * @return array
2574
     */
2575
    public function initializeWorkflowForImport($wf_definition, $state)
2576
    {
2577
        // create and start workflow instacne
2578
        $wf_entry = Workflow::createInstance($wf_definition->id, $this->user->id);
2579
2580
        $wf_contents = $wf_definition->contents ?: [];
2581
        $steps = isset($wf_contents['steps']) && $wf_contents['steps'] ? $wf_contents['steps'] : [];
2582
2583
        $fake_step = [];
2584
        foreach($steps as $step)
2585
        {
2586
            if (isset($step['state']) && $step['state'] == $state) {
2587
                $fake_step = $step;
2588
                break;
2589
            }
2590
        }
2591
        if (!$fake_step) {
2592
            return [];
2593
        }
2594
2595
        $caller = [ 'id' => $this->user->id, 'name' => $this->user->first_name, 'email' => $this->user->email ];
2596
        $wf_entry->fakeNewCurrentStep($fake_step, $caller);
2597
2598
        $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...
2599
        $ret['definition_id'] = $wf_definition->id;
2600
2601
        return $ret;
2602
    }
2603
2604
    /**
2605
     * export xls for issue list 
2606
     *
2607
     * @param  string $project_key
2608
     * @param  array  $export_fields
2609
     * @param  array  $issues
2610
     * @return void
2611
     */
2612
    public function export($project_key, $export_fields, $issues) 
2613
    {
2614
        set_time_limit(0);
2615
2616
        $options = $this->getOptionsForExport($project_key);
2617
        foreach ($options as $key => $val)
2618
        {
2619
            $$key = $val;
2620
        }
2621
2622
        foreach ($export_fields as $key => $field)
2623
        {
2624
            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...
2625
                unset($export_fields[$key]);
2626
            }
2627
        }
2628
        $export_fields = array_values($export_fields);
2629
2630
        $headers = [];
2631
        foreach ($export_fields as $fk)
2632
        {
2633
            $headers[] = isset($fields[$fk]) && $fields[$fk] ? $fields[$fk]['name'] : '';
2634
        }
2635
2636
        $new_issues = [];
2637
        foreach ($issues as $issue)
2638
        {
2639
            $tmp = [];
2640
            foreach ($export_fields as $fk)
2641
            {
2642
                if (!isset($issue[$fk]) || (!$issue[$fk] && $issue[$fk] !== 0)) {
2643
                    $tmp[] = '';
2644
                    continue;
2645
                }
2646
2647
                if (in_array($fk, [ 'assignee', 'reporter', 'closer', 'resolver' ])) {
2648
                    $tmp[] = isset($issue[$fk]['name']) ? $issue[$fk]['name'] : '';
2649
                }
2650
                else if ($fk == 'module') {
2651
                    $new_modules = [];
2652
                    $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...
2653 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...
2654
                        $module_ids = $issue[$fk];
2655
                    }
2656
                    else
2657
                    {
2658
                        $module_ids = explode(',', $issue[$fk]);
2659
                    }
2660
                    foreach ($module_ids as $id)
2661
                    {
2662
                        if (!isset($modules[$id]) || !$modules[$id]) {
2663
                            continue;
2664
                        }
2665
                        $new_modules[] = $modules[$id];
2666
                    }
2667
                    $tmp[] = implode(',', $new_modules);
2668
                }
2669
                else if ($fk == 'type') {
2670
                    $tmp[] = isset($types[$issue[$fk]]) && $types[$issue[$fk]] ? $types[$issue[$fk]] : '';
2671
                }
2672
                else if ($fk == 'priority') {
2673
                    $tmp[] = isset($priorities[$issue[$fk]]) && $priorities[$issue[$fk]] ? $priorities[$issue[$fk]] : '';
2674
                }
2675
                else if ($fk == 'state') {
2676
                    $tmp[] = isset($states[$issue[$fk]]) && $states[$issue[$fk]] ? $states[$issue[$fk]] : '';
2677
                }
2678
                else if ($fk == 'resolution') {
2679
                    $tmp[] = isset($resolutions[$issue[$fk]]) && $resolutions[$issue[$fk]] ? $resolutions[$issue[$fk]] : '';
2680
                }
2681
                else if ($fk == 'epic') {
2682
                    $tmp[] = isset($epics[$issue[$fk]]) && $epics[$issue[$fk]] ? $epics[$issue[$fk]] : '';
2683
                }
2684
                else if ($fk == 'sprints') {
2685
                    $new_sprints = [];
2686
                    foreach ($issue[$fk] as $sn)
2687
                    {
2688
                        if (isset($sprints[$sn])) {
2689
                            $new_sprints[] = $sprints[$sn];
2690
                        }
2691
                    }
2692
                    $tmp[] = implode(',', $new_sprints);
2693
                }
2694
                else if ($fk == 'labels') {
2695
                    $tmp[] = implode(',', $issue[$fk]);
2696
                }
2697
                else if ($fk == 'progress') {
2698
                    $tmp[] = $issue[$fk] . '%';
2699
                }
2700
                else if (isset($fields[$fk]) && $fields[$fk]) {
2701
                    if ($fields[$fk]['type'] == 'DateTimePicker') {
2702
                        $tmp[] = date('Y-m-d H:i:s', $issue[$fk]);
2703
                    }
2704
                    else if ($fields[$fk]['type'] == 'DatePicker') {
2705
                        $tmp[] = date('Y-m-d', $issue[$fk]);
2706
                    }
2707
                    else if ($fields[$fk]['type'] == 'SingleVersion' || $fields[$fk]['type'] == 'MultiVersion') {
2708
                        $new_versions = [];
2709
                        $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...
2710 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...
2711
                            $version_ids = $issue[$fk];
2712
                        }
2713
                        else
2714
                        {
2715
                            $version_ids = explode(',', $issue[$fk]);
2716
                        }
2717
                        foreach ($version_ids as $id)
2718
                        {
2719
                            if (isset($versions[$id]) && $versions[$id]) {
2720
                                $new_versions[] = $versions[$id];
2721
                            }
2722
                        }
2723
                        $tmp[] = implode(',', $new_versions);
2724
                    } 
2725
                    else if ($fields[$fk]['type'] == 'SingleUser') {
2726
                        $tmp[] = isset($issue[$fk]['name']) ? $issue[$fk]['name'] : '';
2727
                    }
2728
                    else if ($fields[$fk]['type'] == 'MultiUser') {
2729
                        $new_users = [];
2730
                        foreach ($issue[$fk] as $user)
2731
                        {
2732
                            if (isset($user['name']) && $user['name']) {
2733
                                $new_users[] = $user['name'];
2734
                            }
2735
                        }
2736
                        $tmp[] = implode(',', $new_users);
2737
                    }
2738
                    else
2739
                    {
2740
                        if (isset($fields[$fk]['optionValues']) && $fields[$fk]['optionValues']) {
2741
                            $tmpOptions = [];
2742
                            foreach ($fields[$fk]['optionValues'] as $ov)
2743
                            {
2744
                                $tmpOptions[$ov['id']] = $ov['name'];
2745
                            }
2746
                            $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...
2747 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...
2748
                                $ov_ids = $issue[$fk]; 
2749
                            }
2750
                            else
2751
                            {
2752
                                $ov_ids = explode(',', $issue[$fk]); 
2753
                            }
2754
                            $ov_names = [];
2755
                            foreach ($ov_ids as $ovid)
2756
                            {
2757
                                $ov_names[] = isset($tmpOptions[$ovid]) ? $tmpOptions[$ovid] : ''; 
2758
                            }
2759
                            $tmp[] = implode(',', array_filter($ov_names));
2760
                        }
2761
                        else
2762
                        {
2763
                            $tmp[] = (string)$issue[$fk];
2764
                        }
2765
                    }
2766
                }
2767
                else
2768
                {
2769
                    $tmp[] = (string)$issue[$fk];
2770
                }
2771
            }
2772
            $new_issues[] = $tmp;
2773
        }
2774
2775
        $file_name = $project_key . '-issues';
2776
        Excel::create(
2777
            $file_name, function ($excel) use ($headers, $new_issues) {
2778
                $excel->sheet(
2779
                    'Sheetname', function ($sheet) use ($headers, $new_issues) {
2780
                        $sheet->appendRow($headers);
2781
                        foreach ($new_issues as $issue)
2782
                        {
2783
                            $sheet->appendRow($issue);
2784
                        }
2785
                    }
2786
                );
2787
            }
2788
        )->download('xls');
2789
    }
2790
2791
    /**
2792
     * batch handle the issue
2793
     *
2794
     * @param  \Illuminate\Http\Request $request
2795
     * @param  string                   $project_key
2796
     * @return \Illuminate\Http\Response
2797
     */
2798
    public function batchHandle(Request $request, $project_key)
2799
    {
2800
        $method = $request->input('method');
2801
        if ($method == 'update') {
2802
            $data = $request->input('data');
2803
            if (!$data || !isset($data['ids']) || !$data['ids'] || !is_array($data['ids']) || !isset($data['values']) || !$data['values'] || !is_array($data['values'])) {
2804
                throw new \UnexpectedValueException('the batch params has errors.', -11124);
2805
            }
2806
            return $this->batchUpdate($project_key, $data['ids'], $data['values']);
2807
        }
2808
        else if ($method == 'delete') {
2809
            $data = $request->input('data');
2810
            if (!$data || !isset($data['ids']) || !$data['ids'] || !is_array($data['ids'])) {
2811
                throw new \UnexpectedValueException('the batch params has errors.', -11124);
2812
            }
2813
            return $this->batchDelete($project_key, $data['ids']);
2814
        }
2815
        else
2816
        {
2817
            throw new \UnexpectedValueException('the batch method has errors.', -11125);
2818
        }
2819
    }
2820
2821
    /**
2822
     * batch update the issue
2823
     *
2824
     * @param  string $project_key
2825
     * @param  array  $ids
2826
     * @return \Illuminate\Http\Response
2827
     */
2828
    public function batchDelete($project_key, $ids)
2829
    {
2830
        if (!$this->isPermissionAllowed($project_key, 'delete_issue')) {
2831
            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...
2832
        }
2833
2834
        $user = [ 'id' => $this->user->id, 'name' => $this->user->first_name, 'email' => $this->user->email ];
2835
2836
        $table = 'issue_' . $project_key;
2837
        foreach ($ids as $id)
2838
        {
2839
            $issue = DB::collection($table)
2840
                ->where('_id', $id)
2841
                ->where('del_flg', '<>', 1)
2842
                ->first();
2843
            if (!$issue) {
2844
                continue;
2845
            }
2846
2847
            // delete all subtasks of this issue
2848
            $subtasks = DB::collection('issue_' . $project_key)
2849
                ->where('parent_id', $id)
2850
                ->where('del_flg', '<>', 1)
2851
                ->get();
2852 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...
2853
            {
2854
                $sub_id = $subtask['_id']->__toString();
2855
                DB::collection($table)->where('_id', $sub_id)->update([ 'del_flg' => 1 ]);
2856
2857
                // delete linked relation
2858
                DB::collection('linked')->where('src', $sub_id)->orWhere('dest', $sub_id)->delete();
2859
2860
                Event::fire(new IssueEvent($project_key, $sub_id, $user, [ 'event_key' => 'del_issue' ]));
2861
            }
2862
2863
            // delete linked relation
2864
            DB::collection('linked')->where('src', $id)->orWhere('dest', $id)->delete();
2865
2866
            // delete this issue
2867
            DB::collection($table)->where('_id', $id)->update([ 'del_flg' => 1 ]);
2868
2869
            // trigger event of issue deleted 
2870
            Event::fire(new IssueEvent($project_key, $id, $user, [ 'event_key' => 'del_issue' ]));
2871
        }
2872
2873
        return response()->json([ 'ecode' => 0, 'data' => [ 'ids' => $ids ] ]);
2874
    }
2875
2876
    /**
2877
     * batch update the issue
2878
     *
2879
     * @param  string $project_key
2880
     * @param  array  $ids
2881
     * @param  array  $values
2882
     * @return \Illuminate\Http\Response
2883
     */
2884
    public function batchUpdate($project_key, $ids, $values)
2885
    {
2886 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...
2887
            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...
2888
        }
2889
2890
        $schemas = [];
2891
2892
        $updValues = [];
2893
        if (isset($values['type'])) {
2894
            if (!$values['type']) {
2895
                throw new \UnexpectedValueException('the issue type can not be empty.', -11100);
2896
            }
2897
        
2898
            if (!($schemas[$values['type']] = Provider::getSchemaByType($values['type']))) {
2899
                throw new \UnexpectedValueException('the schema of the type is not existed.', -11101);
2900
            }
2901
2902
            $updValues['type'] = $values['type'];
2903
        }
2904
2905
        $new_fields = [];
2906
        $fields = Provider::getFieldList($project_key);
2907
        foreach($fields as $field)
2908
        {
2909
            $new_fields[$field->key] = $field;
2910
        }
2911
        $fields = $new_fields;
2912
2913
        foreach ($values as $key => $val)
2914
        {
2915
            if (!isset($fields[$key]) || $fields[$key]->type == 'File') {
2916
                continue;
2917
            }
2918
2919
            $field = $fields[$key];
2920
2921
            if ($field->type == 'DateTimePicker' || $field->type == 'DatePicker') {
2922
                if ($val && $this->isTimestamp($val) === false) {
2923
                    throw new \UnexpectedValueException('the format of datepicker field is incorrect.', -11122);
2924
                }
2925
                $updValues[$key] = $val;
2926
            }
2927
            else if ($field->type == 'TimeTracking') {
2928
                if ($val && !$this->ttCheck($val)) {
2929
                    throw new \UnexpectedValueException('the format of timetracking field is incorrect.', -11102);
2930
                }
2931
                $updValues[$key] = $this->ttHandle($val);
2932
                $updValues[$key . '_m'] = $this->ttHandleInM($updValues[$key]);
2933
            }
2934
            else if ($key == 'assignee' || $field->type == 'SingleUser') {
2935
                $user_info = Sentinel::findById($val);
2936
                if ($user_info) {
2937
                    $updValues[$key] = [ 'id' => $val, 'name' => $user_info->first_name, 'email' => $user_info->email ];
2938
                }
2939
            }
2940
            else if ($field->type == 'MultiUser') {
2941
                $user_ids = $val;
2942
                $updValues[$key] = [];
2943
                $new_user_ids = [];
2944
                foreach ($user_ids as $uid)
2945
                {
2946
                    $user_info = Sentinel::findById($uid);
2947
                    if ($user_info) {
2948
                        array_push($updValues[$key], [ 'id' => $uid, 'name' => $user_info->first_name, 'email' => $user_info->email ]);
2949
                    }
2950
                    $new_user_ids[] = $uid;
2951
                }
2952
                $updValues[$key . '_ids'] = $new_user_ids;
2953
            }
2954
            else if ($field->type === 'Number' || $field->type === 'Integer') {
2955
                if ($val === '') {
2956
                    $updValues[$key] = '';
2957
                }
2958
                else
2959
                {
2960
                    $updValues[$key] = $field->type === 'Number' ? floatVal($val) : intVal($val);
2961
                }
2962
            }
2963
            else 
2964
            {
2965
                $updValues[$key] = $val;
2966
            }
2967
        }
2968
2969
        $updValues['modifier'] = [ 'id' => $this->user->id, 'name' => $this->user->first_name, 'email' => $this->user->email ];
2970
        $updValues['updated_at'] = time();
2971
2972
        $table = 'issue_' . $project_key;
2973
        foreach ($ids as $id)
2974
        {
2975
            $issue = DB::collection($table)->find($id);
2976
            if (!$issue) {
2977
                continue;
2978
            }
2979
2980
            $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...
2981
            $type = isset($values['type']) ? $values['type'] : $issue['type'];
2982
            if (!isset($schemas[$type])) {
2983
                if (!($schemas[$type] = $schema = Provider::getSchemaByType($type))) {
2984
                    continue;
2985
                }
2986
            }
2987
            else 
2988
            {
2989
                $schema = $schemas[$type];
2990
            }
2991
2992
            $valid_keys = $this->getValidKeysBySchema($schema);
2993
            if (!array_only($updValues, $valid_keys)) {
2994
                continue;
2995
            }
2996
2997
            DB::collection($table)->where('_id', $id)->update(array_only($updValues, $valid_keys));
2998
2999
            // add to histroy table
3000
            $snap_id = Provider::snap2His($project_key, $id, $schema, array_keys(array_only($values, $valid_keys)));
3001
3002
            // trigger event of issue edited
3003
            Event::fire(new IssueEvent($project_key, $id, $updValues['modifier'], [ 'event_key' => 'edit_issue', 'snap_id' => $snap_id ]));
3004
        }
3005
3006
        // create the Labels for project
3007
        if (isset($updValues['labels']) && $updValues['labels']) {
3008
            $this->createLabels($project_key, $updValues['labels']);
3009
        }
3010
3011
        return response()->json([ 'ecode' => 0, 'data' => [ 'ids' => $ids ] ]); 
3012
    }
3013
}
3014